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