references to non-static types from annotations
Alex Buckley
alex.buckley at oracle.com
Wed Apr 13 17:15:53 UTC 2016
On 4/8/2016 10:35 AM, Liam Miller-Cushon wrote:
> javac fails to reject references to non-static classes inside
> annotations, and instead produces invalid output.
>
> Example:
>
> class Test<T> {
> @Retention(RetentionPolicy.RUNTIME) @interface A { Class<?> value(); }
> @A(I.class) interface C {}
> class I {}
> public static void main(String[] args) {
> System.err.println(Arrays.toString(C.class.getAnnotations()));
> }
> }
>
> The reference to I.class in `@A(I.class) interface C {}` refers to the
> non-static type Test<T>.I, but the annotation is a static context.
To clarify, JLS 9.7.1 does not prohibit an annotation element in a
static context from referring to a non-static class. I don't see why
such a prohibition would be necessary, yet it sounds like you want javac
to reject @A(I.class).
The emission of an invalid return descriptor for class_info_index is
"just" a javac issue, due to not erasing the fully qualified generic
type of I. If the class literal does the erasure itself --
@A(Test.I.class) interface C {} -- then the program compiles and runs
without error.
Alex
> The compiler produces this attribute for the annotation:
>
> $ javap -v -private 'Test$C'
> ...
> #9 = Utf8 LTest$A;
> #10 = Utf8 value
> #13 = Utf8 LTest<TT;>.I;
> ...
> RuntimeVisibleAnnotations:
> 0: #9(#10=c#13)
>
> JVMS 4.7.16.1 requires a class_info_index to be the index of a Utf8_info
> representing a return descriptor, and `LTest<TT;>.I;` is not a legal
> return descriptor.
>
> The bytecode is rejected when read during reflection, and during
> subsequent compilations:
>
> $ javac Test.java && java Test
> Exception in thread "main" java.lang.ClassCastException:
> sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be
> cast to java.lang.Class
> at
> sun.reflect.annotation.AnnotationParser.toClass(AnnotationParser.java:448)
> at
> sun.reflect.annotation.AnnotationParser.parseSig(AnnotationParser.java:441)
>
> $ javac -implicit:none Other.java
> Other.java:1: error: cannot access C
> class Other { Class<?> clazz = Test.C.class; }
> ^
> bad class file: ./Test$C.class
> undeclared type variable: T
> Please remove or make sure it appears in the correct subdirectory
> of the classpath.
>
> Recording that a static context is being entered in HeaderPhase before
> calling annotateLater fixes the bug, and doesn't break any existing
> tests. The compatibility impact of the fix is minimal: I tried patching
> javac and building a large code base, and only found a single instance
> of the problem.
>
> If the proposed fix sounds reasonable, I'd be happy to clean up the
> patch for review.
>
> Thanks,
> Liam
More information about the compiler-dev
mailing list