# HG changeset patch # User plevart # Date 1485355997 -3600 # Node ID ef028cd46c9d820c5dd70a597bde2dc7ce65b551 # Parent 45de1dc92ecd385f96bb645625c133af625f1d1f# Parent 7ec6576be06030a59e126505f8c74718dfbece2a Merge diff -r 45de1dc92ecd -r ef028cd46c9d jdk/test/java/lang/annotation/AnnotationVerifier.java --- a/jdk/test/java/lang/annotation/AnnotationVerifier.java Wed Jan 25 14:10:03 2017 +0000 +++ b/jdk/test/java/lang/annotation/AnnotationVerifier.java Wed Jan 25 15:53:17 2017 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -27,9 +27,15 @@ * Create class file using ASM, slightly modified the ASMifier output */ -import sun.reflect.annotation.AnnotationType; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.lang.annotation.Annotation; import java.lang.annotation.AnnotationFormatError; -import org.testng.annotations.*; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; /* * @test @@ -37,27 +43,380 @@ * @summary Verify valid annotation * @modules java.base/jdk.internal.org.objectweb.asm * @modules java.base/sun.reflect.annotation - * @clean AnnotationWithVoidReturn.class AnnotationWithParameter.class - * @compile -XDignore.symbol.file ClassFileGenerator.java + * @clean AnnotationWithVoidReturn AnnotationWithParameter + * AnnotationWithExtraInterface AnnotationWithException + * AnnotationWithHashCode AnnotationWithDefaultMember + * AnnotationWithoutAnnotationAccessModifier HolderX + * @compile -XDignore.symbol.file ClassFileGenerator.java GoodAnnotation.java * @run main ClassFileGenerator * @run testng AnnotationVerifier */ public class AnnotationVerifier { + //======================================================= + // GoodAnnotation... + + @GoodAnnotation + static class HolderA { + } + + @Test + public void holderA_goodAnnotation() { + testGetAnnotation(HolderA.class, GoodAnnotation.class, true); + } + + @Test + public void holderA_annotations() { + testGetAnnotations(HolderA.class, GoodAnnotation.class); + } + + //======================================================= + // AnnotationWithParameter... + + /* + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationWithParameter { + int m(int x) default -1; + } + */ + + @GoodAnnotation @AnnotationWithParameter + static class HolderB { + } + + @Test + public void holderB_annotationWithParameter() { + testGetAnnotation(HolderB.class, AnnotationWithParameter.class, false); + } + + @Test + public void holderB_goodAnnotation() { + testGetAnnotation(HolderB.class, GoodAnnotation.class, true); + } + + @Test + public void holderB_annotations() { + testGetAnnotations(HolderB.class, GoodAnnotation.class); + } + + //======================================================= + // AnnotationWithVoidReturn... + + /* + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationWithVoidReturn { + void m() default 1; + } + */ + + @GoodAnnotation @AnnotationWithVoidReturn - static class BadAnnotation { + static class HolderC { + } + + @Test(expectedExceptions = AnnotationFormatError.class) + public void holderC_annotationWithVoidReturn() { + testGetAnnotation(HolderC.class, AnnotationWithVoidReturn.class, false); + } + + @Test(expectedExceptions = AnnotationFormatError.class) + public void holderC_goodAnnotation() { + testGetAnnotation(HolderC.class, GoodAnnotation.class, false); + } + + @Test(expectedExceptions = AnnotationFormatError.class) + public void holderC_annotations() { + testGetAnnotations(HolderC.class); + } + + //======================================================= + // AnnotationWithExtraInterface... + + /* + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationWithExtraInterface extends java.io.Serializable { + int m() default 1; + } + */ + + @GoodAnnotation + @AnnotationWithExtraInterface + static class HolderD { + } + + @Test(expectedExceptions = AnnotationFormatError.class) + public void holderD_annotationWithExtraInterface() { + testGetAnnotation(HolderD.class, AnnotationWithExtraInterface.class, false); + } + + @Test(expectedExceptions = AnnotationFormatError.class) + public void holderD_goodAnnotation() { + testGetAnnotation(HolderD.class, GoodAnnotation.class, false); + } + + @Test(expectedExceptions = AnnotationFormatError.class) + public void holderD_annotations() { + testGetAnnotations(HolderD.class); + } + + //======================================================= + // AnnotationWithException... + + /* + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationWithException { + int m() throws Exception default 1; + } + */ + + @GoodAnnotation + @AnnotationWithException + static class HolderE { + } + + @AnnotationWithException + static class HolderE2 { + } + + @Test + public void holderE_annotationWithException() { + testGetAnnotation(HolderE.class, AnnotationWithException.class, true); + } + + @Test + public void holderE_goodAnnotation() { + testGetAnnotation(HolderE.class, GoodAnnotation.class, true); + } + + @Test + public void holderE_annotations() { + testGetAnnotations(HolderE.class, GoodAnnotation.class, AnnotationWithException.class); + } + + @Test(expectedExceptions = AnnotationFormatError.class) + public void holderE_annotationWithException_equals() { + AnnotationWithException ann1, ann2; + try { + ann1 = HolderE.class.getAnnotation(AnnotationWithException.class); + ann2 = HolderE2.class.getAnnotation(AnnotationWithException.class); + } catch (Throwable t) { + throw new AssertionError("Unexpected exception", t); + } + Assert.assertNotNull(ann1); + Assert.assertNotNull(ann2); + + testEquals(ann1, ann2, true); // this throws AnnotationFormatError + } + + //======================================================= + // AnnotationWithHashCode... + + /* + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationWithHashCode { + int hashCode() default 1; + } + */ + + @GoodAnnotation + @AnnotationWithHashCode + static class HolderF { + } + + @AnnotationWithHashCode + static class HolderF2 { + } + + @Test + public void holderF_annotationWithHashCode() { + testGetAnnotation(HolderF.class, AnnotationWithHashCode.class, true); } @Test - @ExpectedExceptions(IllegalArgumentException.class) - public void annotationValidationIAE() { - AnnotationType.getInstance(AnnotationWithParameter.class); + public void holderF_goodAnnotation() { + testGetAnnotation(HolderF.class, GoodAnnotation.class, true); + } + + @Test + public void holderF_annotations() { + testGetAnnotations(HolderF.class, GoodAnnotation.class, AnnotationWithHashCode.class); + } + + @Test(expectedExceptions = AnnotationFormatError.class) + public void holderF_annotationWithHashCode_equals() { + AnnotationWithHashCode ann1, ann2; + try { + ann1 = HolderF.class.getAnnotation(AnnotationWithHashCode.class); + ann2 = HolderF2.class.getAnnotation(AnnotationWithHashCode.class); + } catch (Throwable t) { + throw new AssertionError("Unexpected exception", t); + } + Assert.assertNotNull(ann1); + Assert.assertNotNull(ann2); + + testEquals(ann1, ann2, true); // this throws AnnotationFormatError + } + + //======================================================= + // AnnotationWithDefaultMember... + + /* + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationWithDefaultMember { + int m() default 1; + default int d() default 2 { return 2; } + } + */ + + @GoodAnnotation + @AnnotationWithDefaultMember + static class HolderG { + } + + @AnnotationWithDefaultMember + static class HolderG2 { + } + + @Test + public void holderG_annotationWithDefaultMember() { + testGetAnnotation(HolderG.class, AnnotationWithDefaultMember.class, true); + } + + @Test + public void holderG_goodAnnotation() { + testGetAnnotation(HolderG.class, GoodAnnotation.class, true); + } + + @Test + public void holderG_annotations() { + testGetAnnotations(HolderG.class, GoodAnnotation.class, AnnotationWithDefaultMember.class); } @Test(expectedExceptions = AnnotationFormatError.class) - public void annotationValidationAFE() { - BadAnnotation.class.getAnnotation(AnnotationWithVoidReturn.class); + public void holderG_annotationWithDefaultMember_equals() { + AnnotationWithDefaultMember ann1, ann2; + try { + ann1 = HolderG.class.getAnnotation(AnnotationWithDefaultMember.class); + ann2 = HolderG2.class.getAnnotation(AnnotationWithDefaultMember.class); + } catch (Throwable t) { + throw new AssertionError("Unexpected exception", t); + } + Assert.assertNotNull(ann1); + Assert.assertNotNull(ann2); + + testEquals(ann1, ann2, true); // this throws AnnotationFormatError + } + + //======================================================= + // AnnotationWithoutAnnotationAccessModifier... + + /* + + @Retention(RetentionPolicy.RUNTIME) + public interface AnnotationWithoutAnnotationAccessModifier extends Annotation { + int m() default 1; + } + + @GoodAnnotation + @AnnotationWithoutAnnotationAccessModifier + static class HolderX { + } + + */ + + @Test + public void holderX_annotationWithoutAnnotationAccessModifier() { + testGetAnnotation(HolderX.class, AnnotationWithoutAnnotationAccessModifier.class, false); + } + + @Test + public void holderX_goodAnnotation() { + testGetAnnotation(HolderX.class, GoodAnnotation.class, true); + } + + @Test + public void holderX_annotations() { + testGetAnnotations(HolderX.class, GoodAnnotation.class); + } + + //======================================================= + // utils + // + + private static void testGetAnnotation(Class holderClass, + Class annType, + boolean expectedPresent) { + Object result = null; + try { + try { + result = holderClass.getAnnotation(annType); + if (expectedPresent != (result != null)) { + throw new AssertionError("Expected " + + (expectedPresent ? "non-null" : "null") + + " result, but got: " + result); + } + } catch (Throwable t) { + result = t; + throw t; + } + } finally { + System.out.println("\n" + + holderClass.getSimpleName() + + ".class.getAnnotation(" + + annType.getSimpleName() + + ".class) = " + + result); + } + } + + private static void testGetAnnotations(Class holderClass, + Class ... expectedTypes) { + Object result = null; + try { + try { + Annotation[] anns = holderClass.getAnnotations(); + + Set> gotTypes = + Stream.of(anns) + .map(Annotation::annotationType) + .collect(Collectors.toSet()); + + Set> expTypes = + Stream.of(expectedTypes) + .collect(Collectors.toSet()); + + if (!expTypes.equals(gotTypes)) { + throw new AssertionError("Expected annotation types: " + expTypes + + " but got: " + Arrays.toString(anns)); + } + result = Arrays.toString(anns); + } catch (Throwable t) { + result = t; + throw t; + } + } finally { + System.out.println("\n" + + holderClass.getSimpleName() + + ".class.getAnnotations() = " + + result); + } + } + + private static void testEquals(Annotation ann1, Annotation ann2, boolean expectedEquals) { + Object result = null; + try { + try { + boolean gotEquals = ann1.equals(ann2); + Assert.assertEquals(gotEquals, expectedEquals); + result = gotEquals; + } catch (Throwable t) { + result = t; + throw t; + } + } finally { + System.out.println("\n" + ann1 + ".equals(" + ann2 + ") = " + result); + } } } diff -r 45de1dc92ecd -r ef028cd46c9d jdk/test/java/lang/annotation/ClassFileGenerator.java --- a/jdk/test/java/lang/annotation/ClassFileGenerator.java Wed Jan 25 14:10:03 2017 +0000 +++ b/jdk/test/java/lang/annotation/ClassFileGenerator.java Wed Jan 25 15:53:17 2017 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -40,6 +40,13 @@ public static void main(String... args) throws Exception { classFileWriter("AnnotationWithVoidReturn.class", AnnoationWithVoidReturnDump.dump()); classFileWriter("AnnotationWithParameter.class", AnnoationWithParameterDump.dump()); + classFileWriter("AnnotationWithExtraInterface.class", AnnotationWithExtraInterfaceDump.dump()); + classFileWriter("AnnotationWithException.class", AnnotationWithExceptionDump.dump()); + classFileWriter("AnnotationWithHashCode.class", AnnotationWithHashCodeDump.dump()); + classFileWriter("AnnotationWithDefaultMember.class", AnnotationWithDefaultMemberDump.dump()); + classFileWriter("AnnotationWithoutAnnotationAccessModifier.class", + AnnotationWithoutAnnotationAccessModifierDump.dump()); + classFileWriter("HolderX.class", HolderXDump.dump()); } private static void classFileWriter(String name, byte[] contents) throws IOException { @@ -49,14 +56,13 @@ } } - /* - Following code create equivalent classfile, - which is not allowed by javac. + /* Following code creates equivalent classfile, which is not allowed by javac: @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationWithVoidReturn { - void m() default 1; + void m() default 1; } + */ private static class AnnoationWithVoidReturnDump implements Opcodes { @@ -65,7 +71,7 @@ MethodVisitor mv; AnnotationVisitor av0; - cw.visit(52, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + +ACC_INTERFACE, + cw.visit(52, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE, "AnnotationWithVoidReturn", null, "java/lang/Object", new String[]{"java/lang/annotation/Annotation"}); @@ -91,14 +97,13 @@ } } - /* - Following code create equivalent classfile, - which is not allowed by javac. + /* Following code creates equivalent classfile, which is not allowed by javac: @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationWithParameter { - int m(int x); + int m(int x) default -1; } + */ private static class AnnoationWithParameterDump implements Opcodes { @@ -136,4 +141,250 @@ } } + /* Following code creates equivalent classfile, which is not allowed by javac: + + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationWithExtraInterface extends java.io.Serializable { + int m() default 1; + } + + */ + + private static class AnnotationWithExtraInterfaceDump implements Opcodes { + public static byte[] dump() throws Exception { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + AnnotationVisitor av0; + + cw.visit(52, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE, + "AnnotationWithExtraInterface", null, + "java/lang/Object", new String[]{"java/lang/annotation/Annotation", + "java/io/Serializable"}); + + { + av0 = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true); + av0.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", + "RUNTIME"); + av0.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "m", "()I", null, null); + mv.visitEnd(); + } + { + av0 = mv.visitAnnotationDefault(); + av0.visit(null, new Integer(1)); + av0.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + } + + /* Following code creates equivalent classfile, which is not allowed by javac: + + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationWithException { + int m() throws Exception default 1; + } + + */ + + private static class AnnotationWithExceptionDump implements Opcodes { + public static byte[] dump() throws Exception { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + AnnotationVisitor av0; + + cw.visit(52, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE, + "AnnotationWithException", null, + "java/lang/Object", new String[]{"java/lang/annotation/Annotation"}); + + { + av0 = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true); + av0.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", + "RUNTIME"); + av0.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "m", "()I", null, + new String[] {"java/lang/Exception"}); + mv.visitEnd(); + } + { + av0 = mv.visitAnnotationDefault(); + av0.visit(null, new Integer(1)); + av0.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + } + + /* Following code creates equivalent classfile, which is not allowed by javac: + + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationWithHashCode { + int hashCode() default 1; + } + + */ + + private static class AnnotationWithHashCodeDump implements Opcodes { + public static byte[] dump() throws Exception { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + AnnotationVisitor av0; + + cw.visit(52, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE, + "AnnotationWithHashCode", null, + "java/lang/Object", new String[]{"java/lang/annotation/Annotation"}); + + { + av0 = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true); + av0.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", + "RUNTIME"); + av0.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "hashCode", "()I", null, null); + mv.visitEnd(); + } + { + av0 = mv.visitAnnotationDefault(); + av0.visit(null, new Integer(1)); + av0.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + } + + /* Following code creates equivalent classfile, which is not allowed by javac: + + @Retention(RetentionPolicy.RUNTIME) + public @interface AnnotationWithDefaultMember { + int m() default 1; + default int d() default 2 { return 2; } + } + + */ + + private static class AnnotationWithDefaultMemberDump implements Opcodes { + public static byte[] dump() throws Exception { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv, dv; + AnnotationVisitor av0; + + cw.visit(52, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE, + "AnnotationWithDefaultMember", null, + "java/lang/Object", new String[]{"java/lang/annotation/Annotation"}); + + { + av0 = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true); + av0.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", + "RUNTIME"); + av0.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "m", "()I", null, null); + mv.visitEnd(); + } + { + av0 = mv.visitAnnotationDefault(); + av0.visit(null, new Integer(1)); + av0.visitEnd(); + } + { + dv = cw.visitMethod(ACC_PUBLIC, "d", "()I", null, null); + dv.visitMaxs(1, 1); + dv.visitCode(); + dv.visitInsn(Opcodes.ICONST_2); + dv.visitInsn(Opcodes.IRETURN); + dv.visitEnd(); + } + { + av0 = dv.visitAnnotationDefault(); + av0.visit(null, new Integer(2)); + av0.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + } + + /* Following code creates equivalent classfile, which is not allowed by javac: + + @Retention(RetentionPolicy.RUNTIME) + public interface AnnotationWithoutAnnotationAccessModifier extends java.lang.annotation.Annotation { + int m() default 1; + } + + */ + + private static class AnnotationWithoutAnnotationAccessModifierDump implements Opcodes { + public static byte[] dump() throws Exception { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + AnnotationVisitor av0; + + cw.visit(52, ACC_PUBLIC + /* ACC_ANNOTATION +*/ ACC_ABSTRACT + ACC_INTERFACE, + "AnnotationWithoutAnnotationAccessModifier", null, + "java/lang/Object", new String[]{"java/lang/annotation/Annotation"}); + + { + av0 = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true); + av0.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", + "RUNTIME"); + av0.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "m", "()I", null, null); + mv.visitEnd(); + } + { + av0 = mv.visitAnnotationDefault(); + av0.visit(null, new Integer(1)); + av0.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + } + + /* Following code creates equivalent classfile, which is not allowed by javac + since AnnotationWithoutAnnotationAccessModifier is not marked with ACC_ANNOTATION: + + @GoodAnnotation + @AnnotationWithoutAnnotationAccessModifier + public interface HolderX { + } + + */ + + private static class HolderXDump implements Opcodes { + public static byte[] dump() throws Exception { + ClassWriter cw = new ClassWriter(0); + + cw.visit(52, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, + "HolderX", null, + "java/lang/Object", new String[0]); + + { + AnnotationVisitor av0; + av0 = cw.visitAnnotation("LGoodAnnotation;", true); + av0.visitEnd(); + av0 = cw.visitAnnotation("LAnnotationWithoutAnnotationAccessModifier;", true); + av0.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + } } diff -r 45de1dc92ecd -r ef028cd46c9d jdk/test/java/lang/annotation/GoodAnnotation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/annotation/GoodAnnotation.java Wed Jan 25 15:53:17 2017 +0100 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Simple conforming runtime annotation. + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface GoodAnnotation {}