Java 8 type-use annotation on local variable in a method overriding a parameterized one breaks ClassReader
The following code breaks ClassReader (processing Impl.class):
abstract class Base<T> {
abstract T m();
}
class Impl extends Base<Void> {
Void m() {
@TypeUseAnnotation Object o = new Object();
return null;
}
}
@Target({ ElementType.TYPE_USE })
@interface TypeUseAnnotation {}
Running something like:
new ClassReader(Impl.class.getName()).accept(new ClassWriter(0), 0);
yields:
java.lang.ArrayIndexOutOfBoundsException: 8
at org.objectweb.asm.ClassReader.readLabel(Unknown Source)
at org.objectweb.asm.ClassReader.a(Unknown Source)
at org.objectweb.asm.ClassReader.a(Unknown Source)
at org.objectweb.asm.ClassReader.b(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at com.example.Test.test(Test.java:10)
this could be an issue with the bytecode produced by javac (tested on oracle
javac 1.8.0_131 and 1.8.0_152-ea):
$ javap -p -v -c Impl.class
...
class com.example.Impl extends com.example.Base<java.lang.Void>
minor version: 0
major version: 52
flags: ACC_SUPER
Constant pool:
#1 = Methodref #6.#25 // com/example/Base."<init>":()V
#2 = Class #26 // java/lang/Object
#3 = Methodref #2.#25 // java/lang/Object."<init>":()V
#4 = Methodref #5.#27 // com/example/Impl.m:()Ljava/lang/Void;
#5 = Class #28 // com/example/Impl
#6 = Class #29 // com/example/Base
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/example/Impl;
#14 = Utf8 m
#15 = Utf8 ()Ljava/lang/Void;
#16 = Utf8 o
#17 = Utf8 Ljava/lang/Object;
#18 = Utf8 RuntimeInvisibleTypeAnnotations
#19 = Utf8 Lcom/example/TypeUseAnnotation;
#20 = Utf8 ()Ljava/lang/Object;
#21 = Utf8 Signature
#22 = Utf8 Lcom/example/Base<Ljava/lang/Void;>;
#23 = Utf8 SourceFile
#24 = Utf8 Impl.java
#25 = NameAndType #7:#8 // "<init>":()V
#26 = Utf8 java/lang/Object
#27 = NameAndType #14:#15 // m:()Ljava/lang/Void;
#28 = Utf8 com/example/Impl
#29 = Utf8 com/example/Base
{
com.example.Impl();
descriptor: ()V
flags:
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method
com/example/Base."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/example/Impl;
java.lang.Void m();
descriptor: ()Ljava/lang/Void;
flags:
Code:
stack=2, locals=2, args_size=1
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #3 // Method
java/lang/Object."<init>":()V
7: astore_1
8: aconst_null
9: areturn
LineNumberTable:
line 6: 0
line 7: 8
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/example/Impl;
8 2 1 o Ljava/lang/Object;
RuntimeInvisibleTypeAnnotations:
0: #19(): LOCAL_VARIABLE, {start_pc=8, length=2, index=1}
java.lang.Object m();
descriptor: ()Ljava/lang/Object;
flags: ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokevirtual #4 // Method m:()Ljava/lang/Void;
4: areturn
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/example/Impl;
RuntimeInvisibleTypeAnnotations:
0: #19(): LOCAL_VARIABLE, {start_pc=8, length=2, index=1}
}
Signature: #22 // Lcom/example/Base<Ljava/lang/Void;>;
SourceFile: "Impl.java"