changeset 2512:1b469fd31e35

8025087: Annotation processing api returns default modifier for interface static method Summary: ClassReader must not set Flags.DEFAULT for interface static methods Reviewed-by: vromero, jjg
author jlahoda
date Wed, 09 Oct 2013 13:09:31 +0200
parents 0be3f1820e8b
children 1e7ad879f15e
files make/build.xml src/share/classes/com/sun/tools/javac/jvm/ClassReader.java src/share/classes/com/sun/tools/javac/resources/compiler.properties test/tools/javac/defaultMethods/BadClassfile.java test/tools/javac/diags/examples.not-yet.txt test/tools/javac/diags/examples/InvalidDefaultInterface/InvalidDefaultInterface.java test/tools/javac/diags/examples/InvalidDefaultInterface/processors/CreateBadClassFile.java test/tools/javac/diags/examples/InvalidStaticInterface/InvalidStaticInterface.java test/tools/javac/diags/examples/InvalidStaticInterface/processors/CreateBadClassFile.java test/tools/javac/processing/model/element/TestExecutableElement.java
diffstat 10 files changed, 365 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/make/build.xml	Wed Oct 09 13:06:49 2013 +0200
+++ b/make/build.xml	Wed Oct 09 13:09:31 2013 +0200
@@ -360,7 +360,7 @@
             datafile="${build.coverage.dir}/cobertura.ser"/>
     </target>
 
-    <target name="diags-examples" depends="build-javac">
+    <target name="diags-examples" depends="build-javac,build-javap">
         <!-- can override the following on the command line if desired. -->
         <property name="diags.examples.out" location="${build.dir}/diag-examples/diags-examples.html"/>
         <mkdir dir="${build.dir}/diag-examples/classes"/>
@@ -370,7 +370,7 @@
             destdir="${build.dir}/diag-examples/classes"
             includes="ArgTypeCompilerFactory.java,Example.java,FileManager.java,HTMLWriter.java,RunExamples.java,DocCommentProcessor.java"
             sourcepath=""
-            classpath="${dist.lib.dir}/javac.jar"
+            classpath="${dist.lib.dir}/javac.jar;${dist.lib.dir}/javap.jar"
             includeAntRuntime="no"
             debug="${javac.debug}"
             debuglevel="${javac.debuglevel}">
@@ -379,7 +379,7 @@
         <java fork="true"
             jvm="${target.java.home}/bin/java"
             dir="test/tools/javac/diags"
-            classpath="${build.dir}/diag-examples/classes;${dist.lib.dir}/javac.jar"
+            classpath="${build.dir}/diag-examples/classes;${dist.lib.dir}/javac.jar;${dist.lib.dir}/javap.jar"
             classname="RunExamples">
             <jvmarg value="-Dtest.classes=${build.dir}/diag-examples/classes"/>
             <arg value="-examples"/>
--- a/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Wed Oct 09 13:06:49 2013 +0200
+++ b/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Wed Oct 09 13:09:31 2013 +0200
@@ -1993,11 +1993,15 @@
                 (flags & ABSTRACT) == 0 && !name.equals(names.clinit)) {
             if (majorVersion > Target.JDK1_8.majorVersion ||
                     (majorVersion == Target.JDK1_8.majorVersion && minorVersion >= Target.JDK1_8.minorVersion)) {
-                currentOwner.flags_field |= DEFAULT;
-                flags |= DEFAULT | ABSTRACT;
+                if ((flags & STATIC) == 0) {
+                    currentOwner.flags_field |= DEFAULT;
+                    flags |= DEFAULT | ABSTRACT;
+                }
             } else {
                 //protect against ill-formed classfiles
-                throw new CompletionFailure(currentOwner, "default method found in pre JDK 8 classfile");
+                throw badClassFile((flags & STATIC) == 0 ? "invalid.default.interface" : "invalid.static.interface",
+                                   Integer.toString(majorVersion),
+                                   Integer.toString(minorVersion));
             }
         }
         if (name == names.init && currentOwner.hasOuterInstance()) {
--- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Wed Oct 09 13:06:49 2013 +0200
+++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Wed Oct 09 13:09:31 2013 +0200
@@ -1699,6 +1699,7 @@
     cannot access {0}\n\
     {1}
 
+# 0: file name, 1: message segment
 compiler.misc.bad.class.file.header=\
     bad class file: {0}\n\
     {1}\n\
@@ -1744,6 +1745,14 @@
 compiler.misc.class.file.not.found=\
     class file for {0} not found
 
+# 0: classfile major version, 1: classfile minor version
+compiler.misc.invalid.default.interface=\
+    default method found in version {0}.{1} classfile
+
+# 0: classfile major version, 1: classfile minor version
+compiler.misc.invalid.static.interface=\
+    static method found in version {0}.{1} classfile
+
 # 0: name
 compiler.misc.file.doesnt.contain.class=\
     file does not contain class {0}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/defaultMethods/BadClassfile.java	Wed Oct 09 13:09:31 2013 +0200
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8025087
+ * @summary Verify that pre-JDK8 classfiles with default and/or static methods
+ *          are refused correctly.
+ * @build BadClassfile
+ * @run main BadClassfile
+ */
+
+import com.sun.tools.classfile.*;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.jvm.ClassReader.BadClassFile;
+import com.sun.tools.javac.jvm.Target;
+import com.sun.tools.javac.util.Assert;
+import com.sun.tools.javac.util.JCDiagnostic;
+import java.io.File;
+import java.util.Arrays;
+import java.util.Objects;
+import javax.tools.JavaCompiler;
+import javax.tools.ToolProvider;
+
+public class BadClassfile {
+    public static void main(String... args) throws Exception {
+        test("BadClassfile$DefaultMethodTest", "compiler.misc.invalid.default.interface");
+        test("BadClassfile$StaticMethodTest", "compiler.misc.invalid.static.interface");
+    }
+
+    private static void test(String classname, String expected) throws Exception {
+        File classfile = new File(System.getProperty("test.classes", "."), classname + ".class");
+        ClassFile cf = ClassFile.read(classfile);
+
+        cf = new ClassFile(cf.magic, Target.JDK1_7.minorVersion,
+                 Target.JDK1_7.majorVersion, cf.constant_pool, cf.access_flags,
+                cf.this_class, cf.super_class, cf.interfaces, cf.fields,
+                cf.methods, cf.attributes);
+
+        new ClassWriter().write(cf, classfile);
+
+        JavaCompiler c = ToolProvider.getSystemJavaCompiler();
+        JavacTaskImpl task = (JavacTaskImpl) c.getTask(null, null, null, Arrays.asList("-classpath", System.getProperty("test.classes", ".")), null, null);
+
+        try {
+            Symbol clazz = com.sun.tools.javac.main.JavaCompiler.instance(task.getContext()).resolveIdent(classname);
+
+            clazz.complete();
+        } catch (BadClassFile f) {
+            JCDiagnostic embeddedDiag = (JCDiagnostic) f.diag.getArgs()[1];
+            assertEquals(expected, embeddedDiag.getCode());
+            assertEquals(Integer.toString(Target.JDK1_7.majorVersion), embeddedDiag.getArgs()[0]);
+            assertEquals(Integer.toString(Target.JDK1_7.minorVersion), embeddedDiag.getArgs()[1]);
+        }
+    }
+
+    private static void assertEquals(Object expected, Object actual) {
+        Assert.check(Objects.equals(expected, actual),
+                     "expected: " + expected + ", but was: " + actual);
+    }
+
+    interface DefaultMethodTest {
+        default void test() { }
+    }
+    interface StaticMethodTest {
+        static void test() { }
+    }
+}
--- a/test/tools/javac/diags/examples.not-yet.txt	Wed Oct 09 13:06:49 2013 +0200
+++ b/test/tools/javac/diags/examples.not-yet.txt	Wed Oct 09 13:09:31 2013 +0200
@@ -40,7 +40,6 @@
 compiler.err.type.var.more.than.once.in.result          # UNUSED
 compiler.err.unexpected.type
 compiler.err.unsupported.cross.fp.lit                   # Scanner: host system dependent
-compiler.misc.bad.class.file.header                     # bad class file
 compiler.misc.bad.class.signature                       # bad class file
 compiler.misc.bad.const.pool.tag                        # bad class file
 compiler.misc.bad.const.pool.tag.at                     # bad class file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/diags/examples/InvalidDefaultInterface/InvalidDefaultInterface.java	Wed Oct 09 13:09:31 2013 +0200
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// key: compiler.misc.invalid.default.interface
+// key: compiler.misc.bad.class.file.header
+// key: compiler.err.cant.access
+// options: -processor CreateBadClassFile
+
+/* The annotation processor will create an invalid classfile with version 51.0
+ * and a non-abstract method in an interface. Loading the classfile will produce
+ * the diagnostic.
+ */
+class InvalidDefaultInterface { }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/diags/examples/InvalidDefaultInterface/processors/CreateBadClassFile.java	Wed Oct 09 13:09:31 2013 +0200
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import com.sun.tools.classfile.*;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Utf8_info;
+import com.sun.tools.classfile.ConstantPool.CPInfo;
+import java.io.*;
+import java.util.*;
+import javax.annotation.processing.*;
+import javax.lang.model.*;
+import javax.lang.model.element.*;
+import javax.tools.*;
+
+/* Create an invalid classfile with version 51.0 and a non-abstract method in an interface.*/
+@SupportedAnnotationTypes("*")
+public class CreateBadClassFile extends AbstractProcessor {
+    public boolean process(Set<? extends TypeElement> elems, RoundEnvironment renv) {
+        if (++round == 1) {
+            ConstantPool cp = new ConstantPool(new CPInfo[] {
+                new CONSTANT_Utf8_info(""),                     //0
+                new CONSTANT_Utf8_info("Test"),                 //1
+                new CONSTANT_Class_info(null, 1),               //2
+                new CONSTANT_Utf8_info("java/lang/Object"),     //3
+                new CONSTANT_Class_info(null, 3),               //4
+                new CONSTANT_Utf8_info("test"),                 //5
+                new CONSTANT_Utf8_info("()V"),                  //6
+            });
+            ClassFile cf = new ClassFile(0xCAFEBABE,
+                          0,
+                          51,
+                          cp,
+                          new AccessFlags(AccessFlags.ACC_ABSTRACT |
+                                          AccessFlags.ACC_INTERFACE |
+                                          AccessFlags.ACC_PUBLIC),
+                          2,
+                          4,
+                          new int[0],
+                          new Field[0],
+                          new Method[] {
+                              //creating non-abstract method in 51.0 classfile:
+                              new Method(new AccessFlags(AccessFlags.ACC_PUBLIC),
+                                         5,
+                                         new Descriptor(6),
+                                         new Attributes(cp, new Attribute[0]))
+                          },
+                          new Attributes(cp, new Attribute[0]));
+            try {
+                JavaFileObject clazz = processingEnv.getFiler().createClassFile("Test");
+                try (OutputStream out = clazz.openOutputStream()) {
+                    new ClassWriter().write(cf, out);
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return false;
+    }
+
+    public SourceVersion getSupportedSourceVersion() {
+        return SourceVersion.latest();
+    }
+
+    int round = 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/diags/examples/InvalidStaticInterface/InvalidStaticInterface.java	Wed Oct 09 13:09:31 2013 +0200
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// key: compiler.misc.invalid.static.interface
+// key: compiler.misc.bad.class.file.header
+// key: compiler.err.cant.access
+// options: -processor CreateBadClassFile
+
+/* The annotation processor will create an invalid classfile with version 51.0
+ * and a static method in an interface. Loading the classfile will produce
+ * the diagnostic.
+ */
+class InvalidDefaultInterface { }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/diags/examples/InvalidStaticInterface/processors/CreateBadClassFile.java	Wed Oct 09 13:09:31 2013 +0200
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import com.sun.tools.classfile.*;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
+import com.sun.tools.classfile.ConstantPool.CONSTANT_Utf8_info;
+import com.sun.tools.classfile.ConstantPool.CPInfo;
+import java.io.*;
+import java.util.*;
+import javax.annotation.processing.*;
+import javax.lang.model.*;
+import javax.lang.model.element.*;
+import javax.tools.*;
+
+/* Create an invalid classfile with version 51.0 and a static method in an interface.*/
+@SupportedAnnotationTypes("*")
+public class CreateBadClassFile extends AbstractProcessor {
+    public boolean process(Set<? extends TypeElement> elems, RoundEnvironment renv) {
+        if (++round == 1) {
+            ConstantPool cp = new ConstantPool(new CPInfo[] {
+                new CONSTANT_Utf8_info(""),                     //0
+                new CONSTANT_Utf8_info("Test"),                 //1
+                new CONSTANT_Class_info(null, 1),               //2
+                new CONSTANT_Utf8_info("java/lang/Object"),     //3
+                new CONSTANT_Class_info(null, 3),               //4
+                new CONSTANT_Utf8_info("test"),                 //5
+                new CONSTANT_Utf8_info("()V"),                  //6
+            });
+            ClassFile cf = new ClassFile(0xCAFEBABE,
+                          0,
+                          51,
+                          cp,
+                          new AccessFlags(AccessFlags.ACC_ABSTRACT |
+                                          AccessFlags.ACC_INTERFACE |
+                                          AccessFlags.ACC_PUBLIC),
+                          2,
+                          4,
+                          new int[0],
+                          new Field[0],
+                          new Method[] {
+                              //creating static method in 51.0 classfile:
+                              new Method(new AccessFlags(AccessFlags.ACC_PUBLIC |
+                                                         AccessFlags.ACC_STATIC),
+                                         5,
+                                         new Descriptor(6),
+                                         new Attributes(cp, new Attribute[0]))
+                          },
+                          new Attributes(cp, new Attribute[0]));
+            try {
+                JavaFileObject clazz = processingEnv.getFiler().createClassFile("Test");
+                try (OutputStream out = clazz.openOutputStream()) {
+                    new ClassWriter().write(cf, out);
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return false;
+    }
+
+    public SourceVersion getSupportedSourceVersion() {
+        return SourceVersion.latest();
+    }
+
+    int round = 0;
+}
--- a/test/tools/javac/processing/model/element/TestExecutableElement.java	Wed Oct 09 13:06:49 2013 +0200
+++ b/test/tools/javac/processing/model/element/TestExecutableElement.java	Wed Oct 09 13:09:31 2013 +0200
@@ -23,54 +23,57 @@
 
 /*
  * @test
- * @bug 8005046 8011052
- * @summary Test basic properties of javax.lang.element.Element
+ * @bug 8005046 8011052 8025087
+ * @summary Test basic properties of javax.lang.element.ExecutableElement
  * @author  Joseph D. Darcy
  * @library /tools/javac/lib
  * @build   JavacTestingAbstractProcessor TestExecutableElement
- * @compile -processor TestExecutableElement -proc:only TestExecutableElement.java
+ * @compile -processor TestExecutableElement -proc:only -AexpectedMethodCount=7 TestExecutableElement.java
+ * @compile/process -processor TestExecutableElement -proc:only -AexpectedMethodCount=3 ProviderOfDefault
  */
 
 import java.lang.annotation.*;
 import java.util.Formatter;
 import java.util.Set;
-import java.util.Objects;
 import java.util.regex.*;
 import javax.annotation.processing.*;
-import javax.lang.model.SourceVersion;
-import static javax.lang.model.SourceVersion.*;
 import javax.lang.model.element.*;
-import javax.lang.model.util.*;
 import static javax.lang.model.util.ElementFilter.*;
 import static javax.tools.Diagnostic.Kind.*;
-import static javax.tools.StandardLocation.*;
 
 /**
  * Test some basic workings of javax.lang.element.ExecutableElement
  */
+@SupportedOptions("expectedMethodCount")
 public class TestExecutableElement extends JavacTestingAbstractProcessor implements ProviderOfDefault {
+    private int seenMethods = 0;
     @IsDefault(false)
     public boolean process(Set<? extends TypeElement> annotations,
                            RoundEnvironment roundEnv) {
-        int errors = 0;
         if (!roundEnv.processingOver()) {
-            boolean hasRun = false;
             for (Element element : roundEnv.getRootElements()) {
                 for (ExecutableElement method : methodsIn(element.getEnclosedElements())) {
-                    hasRun = true;
-                    errors += checkIsDefault(method);
+                    checkIsDefault(method);
+                    seenMethods++;
                 }
             }
+        } else {
+            String expectedMethodCountStr = processingEnv.getOptions().get("expectedMethodCount");
+            if (expectedMethodCountStr == null) {
+                messager.printMessage(ERROR, "No expected method count specified.");
+            } else {
+                int expectedMethodCount = Integer.parseInt(expectedMethodCountStr);
 
-            if (!hasRun) {
-                messager.printMessage(ERROR, "No test cases run; test fails.");
+                if (seenMethods != expectedMethodCount) {
+                    messager.printMessage(ERROR, "Wrong number of seen methods: " + seenMethods);
+                }
             }
         }
         return true;
     }
 
     @IsDefault(false)
-    int checkIsDefault(ExecutableElement method) {
+    void checkIsDefault(ExecutableElement method) {
         System.out.println("Testing " + method);
         IsDefault expectedIsDefault = method.getAnnotation(IsDefault.class);
 
@@ -116,9 +119,7 @@
                                                          expectedDefault,
                                                          methodIsDefault).toString(),
                                   method);
-            return 1;
         }
-        return 0;
     }
 }
 
@@ -142,4 +143,6 @@
 
     @IsDefault(value=true, expectedTextRegex="\\s*@IsDefault\\(.*\\)\\s*default strictfp void quux\\(\\);\\s*$")
     default strictfp void quux() {};
+    @IsDefault(false)
+    static void statik() {}
 }