# HG changeset patch # User cushon # Date 1529022751 25200 # Node ID 9f4c08c444e8ca5161005bf5f1d64334318b21a7 # Parent 5fa19bad622d630fe358b3d593377cbb6d185eb5 7183985: (ann) Class.getAnnotation() throws an ArrayStoreException when the annotation class not present Reviewed-by: darcy, martin, vromero diff -r 5fa19bad622d -r 9f4c08c444e8 src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java --- a/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java Fri Jun 15 09:53:28 2018 -0700 +++ b/src/java.base/share/classes/sun/reflect/annotation/AnnotationParser.java Thu Jun 14 17:32:31 2018 -0700 @@ -715,19 +715,23 @@ ConstantPool constPool, Class> container) { Object[] result = new Class>[length]; - boolean typeMismatch = false; - int tag = 0; + Object exceptionProxy = null; for (int i = 0; i < length; i++) { - tag = buf.get(); + int tag = buf.get(); if (tag == 'c') { - result[i] = parseClassValue(buf, constPool, container); + Object value = parseClassValue(buf, constPool, container); + if (value instanceof ExceptionProxy) { + if (exceptionProxy == null) exceptionProxy = (ExceptionProxy) value; + } else { + result[i] = value; + } } else { skipMemberValue(tag, buf); - typeMismatch = true; + if (exceptionProxy == null) exceptionProxy = exceptionProxy(tag); } } - return typeMismatch ? exceptionProxy(tag) : result; + return (exceptionProxy != null) ? exceptionProxy : result; } private static Object parseEnumArray(int length, Class extends Enum>> enumType, @@ -735,19 +739,23 @@ ConstantPool constPool, Class> container) { Object[] result = (Object[]) Array.newInstance(enumType, length); - boolean typeMismatch = false; - int tag = 0; + Object exceptionProxy = null; for (int i = 0; i < length; i++) { - tag = buf.get(); + int tag = buf.get(); if (tag == 'e') { - result[i] = parseEnumValue(enumType, buf, constPool, container); + Object value = parseEnumValue(enumType, buf, constPool, container); + if (value instanceof ExceptionProxy) { + if (exceptionProxy == null) exceptionProxy = (ExceptionProxy) value; + } else { + result[i] = value; + } } else { skipMemberValue(tag, buf); - typeMismatch = true; + if (exceptionProxy == null) exceptionProxy = exceptionProxy(tag); } } - return typeMismatch ? exceptionProxy(tag) : result; + return (exceptionProxy != null) ? exceptionProxy : result; } private static Object parseAnnotationArray(int length, @@ -756,19 +764,23 @@ ConstantPool constPool, Class> container) { Object[] result = (Object[]) Array.newInstance(annotationType, length); - boolean typeMismatch = false; - int tag = 0; + Object exceptionProxy = null; for (int i = 0; i < length; i++) { - tag = buf.get(); + int tag = buf.get(); if (tag == '@') { - result[i] = parseAnnotation(buf, constPool, container, true); + Object value = parseAnnotation(buf, constPool, container, true); + if (value instanceof ExceptionProxy) { + if (exceptionProxy == null) exceptionProxy = (ExceptionProxy) value; + } else { + result[i] = value; + } } else { skipMemberValue(tag, buf); - typeMismatch = true; + if (exceptionProxy == null) exceptionProxy = exceptionProxy(tag); } } - return typeMismatch ? exceptionProxy(tag) : result; + return (exceptionProxy != null) ? exceptionProxy : result; } /** diff -r 5fa19bad622d -r 9f4c08c444e8 test/jdk/java/lang/annotation/Missing/MissingArrayElement/EnumToCompileAgainst.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/annotation/Missing/MissingArrayElement/EnumToCompileAgainst.java Thu Jun 14 17:32:31 2018 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018, Google Inc. 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. + */ + +/** + * The enum that will be seen at compile-time. + * + *
The filename deliberately does not match, since we need to declare two versions of the enum + * for compile-time and runtime. + */ +enum Enum { + ONE, + TWO, + THREE +} diff -r 5fa19bad622d -r 9f4c08c444e8 test/jdk/java/lang/annotation/Missing/MissingArrayElement/EnumToRunAgainst.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/annotation/Missing/MissingArrayElement/EnumToRunAgainst.java Thu Jun 14 17:32:31 2018 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018, Google Inc. 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. + */ + +/** + * The enum that will be seen at runtime. + * + *
The filename deliberately does not match, since we need to declare two versions of the enum + * for compile-time and runtime. + */ +enum Enum { + ONE +} diff -r 5fa19bad622d -r 9f4c08c444e8 test/jdk/java/lang/annotation/Missing/MissingArrayElement/MissingAnnotation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/annotation/Missing/MissingArrayElement/MissingAnnotation.java Thu Jun 14 17:32:31 2018 -0700 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018, Google Inc. 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 static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; + +/** An annotation that will be missing at runtime. */ +@Retention(RUNTIME) +public @interface MissingAnnotation {} diff -r 5fa19bad622d -r 9f4c08c444e8 test/jdk/java/lang/annotation/Missing/MissingArrayElement/MissingAnnotationArrayElementTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/annotation/Missing/MissingArrayElement/MissingAnnotationArrayElementTest.java Thu Jun 14 17:32:31 2018 -0700 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018, Google Inc. 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 static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/* + * @test + * @bug 7183985 + * @summary getAnnotation() should throw NoClassDefFoundError when an annotation class is not + * present at runtime + * @compile MissingAnnotationArrayElementTest.java MissingAnnotation.java + * @clean MissingAnnotation + * @run main MissingAnnotationArrayElementTest + */ +public class MissingAnnotationArrayElementTest { + + @Retention(RUNTIME) + @interface AnnotationAnnotation { + MissingAnnotation[] value(); + } + + @AnnotationAnnotation({@MissingAnnotation, @MissingAnnotation}) + static class Test { + void f(@AnnotationAnnotation({@MissingAnnotation, @MissingAnnotation}) int x) {} + + Test(@AnnotationAnnotation({@MissingAnnotation, @MissingAnnotation}) int x) {} + } + + public static void main(String[] args) throws Exception { + // MissingAnnotation will be absent from the runtime classpath, causing a + // NoClassDefFoundError when AnnotationAnnotation is read (since the type of its value array + // references cannot be completed). + assertThrowsNoClassDefFoundError( + () -> Test.class.getAnnotation(AnnotationAnnotation.class)); + Method method = Test.class.getDeclaredMethod("f", int.class); + assertThrowsNoClassDefFoundError(method::getParameterAnnotations); + Constructor constructor = Test.class.getDeclaredConstructor(int.class); + assertThrowsNoClassDefFoundError(constructor::getParameterAnnotations); + } + + interface ThrowingRunnable { + void run() throws Exception; + } + + static void assertThrowsNoClassDefFoundError(ThrowingRunnable throwingRunnable) + throws Exception { + try { + throwingRunnable.run(); + throw new AssertionError("expected exception"); + } catch (NoClassDefFoundError expected) { + if (!expected.getMessage().contains("MissingAnnotation")) { + throw expected; + } + } + } +} diff -r 5fa19bad622d -r 9f4c08c444e8 test/jdk/java/lang/annotation/Missing/MissingArrayElement/MissingClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/annotation/Missing/MissingArrayElement/MissingClass.java Thu Jun 14 17:32:31 2018 -0700 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018, Google Inc. 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. + */ + +/** A class that will be missing at runtime. */ +public class MissingClass {} diff -r 5fa19bad622d -r 9f4c08c444e8 test/jdk/java/lang/annotation/Missing/MissingArrayElement/MissingClass2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/annotation/Missing/MissingArrayElement/MissingClass2.java Thu Jun 14 17:32:31 2018 -0700 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018, Google Inc. 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. + */ + +/** A class that will be missing at runtime. */ +public class MissingClass2 {} diff -r 5fa19bad622d -r 9f4c08c444e8 test/jdk/java/lang/annotation/Missing/MissingArrayElement/MissingClassArrayElementTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/annotation/Missing/MissingArrayElement/MissingClassArrayElementTest.java Thu Jun 14 17:32:31 2018 -0700 @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2018, Google Inc. 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 static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.util.Arrays; + +/* + * @test + * @bug 7183985 + * @summary getAnnotation() throws an ArrayStoreException when the annotation class not present + * @compile MissingClassArrayElementTest.java MissingClass.java MissingClass2.java + * @clean MissingClass MissingClass2 + * @run main MissingClassArrayElementTest + */ +public class MissingClassArrayElementTest { + + @Retention(RUNTIME) + @interface AnnotationAnnotation { + ClassArrayAnnotation[] value(); + } + + @Retention(RUNTIME) + @interface ClassArrayAnnotation { + Class>[] value(); + } + + @AnnotationAnnotation({ + @ClassArrayAnnotation({MissingClass.class}), + @ClassArrayAnnotation({MissingClass.class, String.class}), + @ClassArrayAnnotation({String.class, MissingClass.class}), + @ClassArrayAnnotation({MissingClass.class, MissingClass2.class}), + @ClassArrayAnnotation({String.class}) + }) + static class Test { + void f( + @AnnotationAnnotation({ + @ClassArrayAnnotation({MissingClass.class, MissingClass.class}), + @ClassArrayAnnotation({Float.class}) + }) + int x, + @AnnotationAnnotation({@ClassArrayAnnotation({Double.class})}) int y) {} + + Test( + @AnnotationAnnotation({ + @ClassArrayAnnotation({MissingClass.class, MissingClass.class}), + @ClassArrayAnnotation({Short.class}), + }) + int x, + @AnnotationAnnotation({@ClassArrayAnnotation({Character.class})}) int y) {} + } + + public static void main(String[] args) throws Exception { + classAnnotationTest(); + methodParameterAnnotationsTest(); + constructorParameterAnnotationsTest(); + } + + static void classAnnotationTest() throws Exception { + ClassArrayAnnotation[] outer = Test.class.getAnnotation(AnnotationAnnotation.class).value(); + assertMissing(outer[0]); + assertMissing(outer[1]); + assertMissing(outer[2]); + assertMissing(outer[3]); + assertArrayEquals(outer[4].value(), new Class>[] {String.class}); + } + + static void methodParameterAnnotationsTest() throws Exception { + AnnotationAnnotation[] methodParameterAnnotations = + Arrays.stream( + Test.class + .getDeclaredMethod("f", int.class, int.class) + .getParameterAnnotations()) + .map(x -> ((AnnotationAnnotation) x[0])) + .toArray(AnnotationAnnotation[]::new); + // The first parameter's annotation contains some well-formed values, and the second + // parameter's + // annotation is well-formed + assertArrayEquals( + methodParameterAnnotations[0].value()[1].value(), new Class>[] {Float.class}); + assertArrayEquals( + methodParameterAnnotations[1].value()[0].value(), new Class>[] {Double.class}); + // The first parameter's annotation contains a missing value + assertMissing(methodParameterAnnotations[0].value()[0]); + } + + static void constructorParameterAnnotationsTest() throws Exception { + AnnotationAnnotation[] constructorParameterAnnotations = + Arrays.stream( + Test.class + .getDeclaredConstructor(int.class, int.class) + .getParameterAnnotations()) + .map(x -> ((AnnotationAnnotation) x[0])) + .toArray(AnnotationAnnotation[]::new); + // The first parameter's annotation contains some well-formed values, and the second + // parameter's + // annotation is well-formed + assertArrayEquals( + constructorParameterAnnotations[0].value()[1].value(), + new Class>[] {Short.class}); + assertArrayEquals( + constructorParameterAnnotations[1].value()[0].value(), + new Class>[] {Character.class}); + // The first parameter's annotation contains a missing value + assertMissing(constructorParameterAnnotations[0].value()[0]); + } + + static void assertArrayEquals(Object[] actual, Object[] expected) { + if (!Arrays.equals(actual, expected)) { + throw new AssertionError( + "expected: " + Arrays.toString(expected) + ", was: " + Arrays.toString(actual)); + } + } + + static void assertMissing(ClassArrayAnnotation missing) { + try { + missing.value(); + throw new AssertionError("expected exception"); + } catch (TypeNotPresentException expected) { + if (!expected.typeName().equals("MissingClass")) { + throw new AssertionError( + "expected TypeNotPresentException: MissingClass", expected); + } + } + } +} diff -r 5fa19bad622d -r 9f4c08c444e8 test/jdk/java/lang/annotation/Missing/MissingArrayElement/MissingEnumArrayElementTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/annotation/Missing/MissingArrayElement/MissingEnumArrayElementTest.java Thu Jun 14 17:32:31 2018 -0700 @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2018, Google Inc. 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 static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.util.Arrays; + +/* + * @test + * @bug 7183985 + * @summary getAnnotation() throws an ArrayStoreException when the annotation class not present + * @compile MissingEnumArrayElementTest.java EnumToCompileAgainst.java + * @clean Enum + * @compile EnumToRunAgainst.java + * @run main MissingEnumArrayElementTest + */ +public class MissingEnumArrayElementTest { + + @Retention(RUNTIME) + @interface AnnotationAnnotation { + EnumArrayAnnotation[] value(); + } + + @Retention(RUNTIME) + @interface EnumArrayAnnotation { + Enum[] value(); + } + + @AnnotationAnnotation({ + @EnumArrayAnnotation({Enum.TWO}), + @EnumArrayAnnotation({Enum.ONE, Enum.TWO}), + @EnumArrayAnnotation({Enum.TWO, Enum.ONE}), + @EnumArrayAnnotation({Enum.TWO, Enum.THREE}), + @EnumArrayAnnotation({Enum.ONE}), + }) + static class Test { + void f( + @AnnotationAnnotation({ + @EnumArrayAnnotation({Enum.TWO, Enum.ONE}), + @EnumArrayAnnotation({Enum.ONE}) + }) + int x, + @AnnotationAnnotation({@EnumArrayAnnotation({Enum.ONE})}) int y) {} + + Test( + @AnnotationAnnotation({ + @EnumArrayAnnotation({Enum.TWO, Enum.ONE}), + @EnumArrayAnnotation({Enum.ONE}) + }) + int x, + @AnnotationAnnotation({@EnumArrayAnnotation({Enum.ONE})}) int y) {} + } + + public static void main(String[] args) throws Exception { + classAnnotationTest(); + methodParameterAnnotationsTest(); + constructorParameterAnnotationsTest(); + } + + static void classAnnotationTest() throws Exception { + EnumArrayAnnotation[] outer = Test.class.getAnnotation(AnnotationAnnotation.class).value(); + assertMissing(outer[0]); + assertMissing(outer[1]); + assertMissing(outer[2]); + assertMissing(outer[3]); + assertArrayEquals(outer[4].value(), new Enum[] {Enum.ONE}); + } + + static void methodParameterAnnotationsTest() throws Exception { + AnnotationAnnotation[] methodParameterAnnotations = + Arrays.stream( + Test.class + .getDeclaredMethod("f", int.class, int.class) + .getParameterAnnotations()) + .map(x -> ((AnnotationAnnotation) x[0])) + .toArray(AnnotationAnnotation[]::new); + // The first parameter's annotation contains some well-formed values, and the second + // parameter's + // annotation is well-formed + assertArrayEquals(methodParameterAnnotations[0].value()[1].value(), new Enum[] {Enum.ONE}); + assertArrayEquals(methodParameterAnnotations[1].value()[0].value(), new Enum[] {Enum.ONE}); + // The first parameter's annotation contains a missing value + assertMissing(methodParameterAnnotations[0].value()[0]); + } + + static void constructorParameterAnnotationsTest() throws Exception { + AnnotationAnnotation[] constructorParameterAnnotations = + Arrays.stream( + Test.class + .getDeclaredConstructor(int.class, int.class) + .getParameterAnnotations()) + .map(x -> ((AnnotationAnnotation) x[0])) + .toArray(AnnotationAnnotation[]::new); + // The first parameter's annotation contains some well-formed values, and the second + // parameter's + // annotation is well-formed + assertArrayEquals(constructorParameterAnnotations[0].value()[1].value(), new Enum[] {Enum.ONE}); + assertArrayEquals(constructorParameterAnnotations[1].value()[0].value(), new Enum[] {Enum.ONE}); + // The first parameter's annotation contains a missing value + assertMissing(constructorParameterAnnotations[0].value()[0]); + } + + static void assertArrayEquals(Object[] actual, Object[] expected) { + if (!Arrays.equals(actual, expected)) { + throw new AssertionError( + "expected: " + Arrays.toString(expected) + ", was: " + Arrays.toString(actual)); + } + } + + static void assertMissing(EnumArrayAnnotation annotation) throws Exception { + try { + annotation.value(); + throw new AssertionError("expected exception"); + } catch (EnumConstantNotPresentException expected) { + if (!expected.getMessage().equals("Enum.TWO")) { + throw new AssertionError( + "expected EnumConstantNotPresentException for Enum.TWO", expected); + } + } + } +}