# HG changeset patch # User jlahoda # Date 1381316971 -7200 # Node ID 534673718919e5b2a8b1c16cf2aec0195e53aa25 # Parent 4db633bd76961e7ecb61ee3022e4e2dd38702f20 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 diff -r 4db633bd7696 -r 534673718919 langtools/make/build.xml --- 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"/> - + @@ -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 @@ diff -r 4db633bd7696 -r 534673718919 langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java --- 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()) { diff -r 4db633bd7696 -r 534673718919 langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties --- 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} diff -r 4db633bd7696 -r 534673718919 langtools/test/tools/javac/defaultMethods/BadClassfile.java --- /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() { } + } +} diff -r 4db633bd7696 -r 534673718919 langtools/test/tools/javac/diags/examples.not-yet.txt --- 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 diff -r 4db633bd7696 -r 534673718919 langtools/test/tools/javac/diags/examples/InvalidDefaultInterface/InvalidDefaultInterface.java --- /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 { } diff -r 4db633bd7696 -r 534673718919 langtools/test/tools/javac/diags/examples/InvalidDefaultInterface/processors/CreateBadClassFile.java --- /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 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; +} diff -r 4db633bd7696 -r 534673718919 langtools/test/tools/javac/diags/examples/InvalidStaticInterface/InvalidStaticInterface.java --- /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 { } diff -r 4db633bd7696 -r 534673718919 langtools/test/tools/javac/diags/examples/InvalidStaticInterface/processors/CreateBadClassFile.java --- /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 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; +} diff -r 4db633bd7696 -r 534673718919 langtools/test/tools/javac/processing/model/element/TestExecutableElement.java --- 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 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() {} }