Annotated parameters in non-static inner-class constructor cause invalid class to be generated
Writing a class with an inner-class that has an annotated constructor parameter results in an invalid class being written that cannot be compiled against.
Consider this class:
public class Outer<T> {
public class Inner<I extends T> {
public Inner(String name, Class<I> type, @Nullable java.util.function.Consumer<? super I> configureAction) {
// Constructor
}
}
}
Using MethodWriter
to write such a structure, I get this in the resulting class (using javap -v
):
public Outer$Inner(java.lang.String, java.lang.Class<I>, java.util.function.Consumer<? super I>);
descriptor: (LOuter;Ljava/lang/String;Ljava/lang/Class;Ljava/util/function/Consumer;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=5, args_size=5
0: new #10 // class java/lang/Error
3: dup
4: invokespecial #13 // Method java/lang/Error."<init>":()V
7: athrow
Signature: #8 // (Ljava/lang/String;Ljava/lang/Class<TI;>;Ljava/util/function/Consumer<-TI;>;)V
RuntimeVisibleParameterAnnotations:
parameter 0:
parameter 1:
parameter 2:
0: #14()
Nullable
parameter 3:
Notice the last line: parameter 3
should not be there. IIUC, this is because non-static inner-class constructors cannot have their first parameter (the one pointing to the enclosing class instance) annotated, and thus this method should only have 3 RuntimeVisibleParameterAnnotations
entries.
This causes the Java compiler to fail when compiling against the class. E.g. when trying to compile this:
public class Main {
public static void main(String[] args) {
System.out.println("Hello " + Outer.Inner.class.getName());
}
}
The Java compiler fails with:
Main.java:3: error: cannot access Inner
System.out.println("Hello " + Outer.Inner.class.getName());
^
bad class file: ./Outer$Inner.class
bad RuntimeInvisibleParameterAnnotations attribute: Inner(String,Class<I>,Consumer<? super I>)
Please remove or make sure it appears in the correct subdirectory of the classpath.
where I,T are type-variables:
I extends T declared in class Inner
T extends Object declared in class Outer
Main.java:3: error: cannot access Byte
System.out.println("Hello " + Outer.Inner.class.getName());
^
bad class file: /modules/java.base/java/lang/Byte.class
bad RuntimeInvisibleParameterAnnotations attribute: toString(byte)
Please remove or make sure it appears in the correct subdirectory of the classpath.
Main.java:3: error: cannot access System
System.out.println("Hello " + Outer.Inner.class.getName());
^
bad class file: /modules/java.base/java/lang/System.class
bad RuntimeInvisibleParameterAnnotations attribute: registerNatives()
Please remove or make sure it appears in the correct subdirectory of the classpath.
I can work around this by explicitly setting calling MethodWriter.visitAnnotableParameterCount()
(and that's what I did in https://github.com/gradle/gradle/pull/31417). However, this is counter-intuitive and is not easy to discover. It would be much better if ASM did the right thing here.
Expected Behavior
Generate the right number of RuntimeVisibleParameterAnnotations
for non-static inner-class constructors by default.