ASM 无法将 'Type.INT_TYPE' 作为常量 Bootstrap 方法参数传递
ASM cannot pass 'Type.INT_TYPE' as constant Bootstrap Method Argument
考虑以下旨在使用 ASM 生成 invokedynamic
指令的代码:
// BOOTSTRAP = new Handle(->
// CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType methodType, Class<?> someClass)
mv.visitInvokeDynamicInsn("foo", "(I)I", BOOTSTRAP, Type.INT_TYPE);
使用ASMifier反编译生成的class时,相关行变为
mv.visitInvokeDynamicInsn("foo", "(I)I", new Handle(/* SNIP (same as BOOTSTRAP) */),
Type.getType("LI;"));
¯¯¯¯¯
如您所见,Type.INT_TYPE
已变成对名为 I
的引用类型的文字引用。由于这不存在,JVM 在运行时抱怨 java.lang.BootstrapMethodError: java.lang.NoClassDefFoundError: I
.
我想做的是将 int.class
(基本类型 int
的 Class
实例或 Integer.TYPE
常量的值)传递给我bootstrap
方法作为 someClass
的参数。但是,ASM 似乎没有正确理解或支持这一点。
这可以被视为 ASM 错误吗?是否有解决方法?
我认为不可能将基本类型作为 bootstrap 方法参数传递,因为无法在 class 文件中对它们进行编码。根据 JVM 规范,class 个参数表示为 CONSTANT_Class_info, which can only represent names in internal form, not as a descriptor.
编辑:有一个proposal支持原始类型常量:
This minimal prototype adopts this answer, using semicolon ;
(ASCII
decimal code 59) as the escape character. Thus, the types int.class
and void.class
may now be obtained class-file constants with the UTF8
strings ";I
" and ";V
". The choice of semicolon is natural here, since
a class name cannot contain a semicolon (unless it is an array type),
and descriptor syntax is often found following semicolons in class
files.
如, it is impossible to encode a Class
constant for a primitive type. When you use a literal like int.class
in source code, the compiler will encode it as read operation of the field java.lang.Integer.TYPE
, which contains the desired Class
object. For annotations, it is possible, because the annotation value is encoded to point to a CONSTANT_Utf8_info
containing a return descriptor rather than a CONSTANT_Class_info
(see JVM spec §4.7.16.1).
由于 bootstrap 方法的编码静态参数要求 Class
对象被编码为 CONSTANT_Class_info
,因此它们不支持原始类型。见 JVM spec §4.7.23:
Each entry in the bootstrap_arguments array must be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_String_info
, CONSTANT_Class_info
, CONSTANT_Integer_info
, CONSTANT_Long_info
, CONSTANT_Float_info
, CONSTANT_Double_info
, CONSTANT_MethodHandle_info
, or CONSTANT_MethodType_info
structure…
解决方法是添加约定,例如始终对所需类型的数组类型进行编码,并在 bootstrap 方法中提取元素类型。或者将所需类型编码为 CONSTANT_MethodType_info
的 return 类型。后者的优势甚至支持 void.class
.
考虑以下旨在使用 ASM 生成 invokedynamic
指令的代码:
// BOOTSTRAP = new Handle(->
// CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType methodType, Class<?> someClass)
mv.visitInvokeDynamicInsn("foo", "(I)I", BOOTSTRAP, Type.INT_TYPE);
使用ASMifier反编译生成的class时,相关行变为
mv.visitInvokeDynamicInsn("foo", "(I)I", new Handle(/* SNIP (same as BOOTSTRAP) */),
Type.getType("LI;"));
¯¯¯¯¯
如您所见,Type.INT_TYPE
已变成对名为 I
的引用类型的文字引用。由于这不存在,JVM 在运行时抱怨 java.lang.BootstrapMethodError: java.lang.NoClassDefFoundError: I
.
我想做的是将 int.class
(基本类型 int
的 Class
实例或 Integer.TYPE
常量的值)传递给我bootstrap
方法作为 someClass
的参数。但是,ASM 似乎没有正确理解或支持这一点。
这可以被视为 ASM 错误吗?是否有解决方法?
我认为不可能将基本类型作为 bootstrap 方法参数传递,因为无法在 class 文件中对它们进行编码。根据 JVM 规范,class 个参数表示为 CONSTANT_Class_info, which can only represent names in internal form, not as a descriptor.
编辑:有一个proposal支持原始类型常量:
This minimal prototype adopts this answer, using semicolon
;
(ASCII decimal code 59) as the escape character. Thus, the typesint.class
andvoid.class
may now be obtained class-file constants with the UTF8 strings ";I
" and ";V
". The choice of semicolon is natural here, since a class name cannot contain a semicolon (unless it is an array type), and descriptor syntax is often found following semicolons in class files.
如Class
constant for a primitive type. When you use a literal like int.class
in source code, the compiler will encode it as read operation of the field java.lang.Integer.TYPE
, which contains the desired Class
object. For annotations, it is possible, because the annotation value is encoded to point to a CONSTANT_Utf8_info
containing a return descriptor rather than a CONSTANT_Class_info
(see JVM spec §4.7.16.1).
由于 bootstrap 方法的编码静态参数要求 Class
对象被编码为 CONSTANT_Class_info
,因此它们不支持原始类型。见 JVM spec §4.7.23:
Each entry in the bootstrap_arguments array must be a valid index into the constant_pool table. The constant_pool entry at that index must be a
CONSTANT_String_info
,CONSTANT_Class_info
,CONSTANT_Integer_info
,CONSTANT_Long_info
,CONSTANT_Float_info
,CONSTANT_Double_info
,CONSTANT_MethodHandle_info
, orCONSTANT_MethodType_info
structure…
解决方法是添加约定,例如始终对所需类型的数组类型进行编码,并在 bootstrap 方法中提取元素类型。或者将所需类型编码为 CONSTANT_MethodType_info
的 return 类型。后者的优势甚至支持 void.class
.