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
--- a/langtools/make/build.xml Wed Oct 09 13:06:49 2013 +0200
+++ b/langtools/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/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Wed Oct 09 13:06:49 2013 +0200
+++ b/langtools/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/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Wed Oct 09 13:06:49 2013 +0200
+++ b/langtools/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/langtools/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/langtools/test/tools/javac/diags/examples.not-yet.txt Wed Oct 09 13:06:49 2013 +0200
+++ b/langtools/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/langtools/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/langtools/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/langtools/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/langtools/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/langtools/test/tools/javac/processing/model/element/TestExecutableElement.java Wed Oct 09 13:06:49 2013 +0200
+++ b/langtools/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() {}
}