考虑以下用于使用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反编译生成的类时,相关的行变为
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
(基本类型Class
的{{1}}实例或int
常量的值)传递给Integer.TYPE
1}}方法作为bootstrap
的参数。但是,似乎ASM没有正确理解或支持这一点。
这可以被视为ASM错误,是否有针对此的解决方法?
答案 0 :(得分:2)
我不相信可以将原始类型作为引导方法参数传递,因为无法在类文件中对它们进行编码。根据JVM规范,类参数表示为CONSTANT_Class_info,它只能表示internal form中的名称,而不能表示descriptor。
编辑:proposal支持基本类型常量:
这个最小的原型采用这个答案,使用分号
;
(ASCII 十进制代码59)作为转义字符。因此,类型int.class
现在可以使用UTF8获取void.class
类文件常量 字符串“;I
”和“;V
”。从那以后,分号的选择很自然 类名不能包含分号(除非它是数组类型), 和描述符语法通常在类中的分号后面找到 文件。
答案 1 :(得分:2)
作为Brett Kail pointed out,不可能为基本类型编码Class
常量。在源代码中使用类似int.class
的文字时,编译器会将其编码为字段java.lang.Integer.TYPE
的读取操作,该字段包含所需的Class
对象。对于注释,有可能,因为注释值被编码为指向包含返回描述符而不是CONSTANT_Utf8_info
的{{1}}(请参阅JVM spec §4.7.16.1)。
由于引导方法的编码静态参数需要将CONSTANT_Class_info
个对象编码为Class
,因此它们不支持基本类型。见JVM spec §4.7.23:
bootstrap_arguments数组中的每个条目都必须是constant_pool表的有效索引。该索引处的constant_pool条目必须是
CONSTANT_Class_info
,CONSTANT_String_info
,CONSTANT_Class_info
,CONSTANT_Integer_info
,CONSTANT_Long_info
,CONSTANT_Float_info
,CONSTANT_Double_info
,或CONSTANT_MethodHandle_info
结构......
解决方法是添加一个约定,例如始终编码所需类型的数组类型,并在bootstrap方法中提取元素类型。或者将所需类型编码为CONSTANT_MethodType_info
的返回类型。后者具有甚至支持CONSTANT_MethodType_info
的优势。