1

考虑以下旨在invokedynamic使用 ASM 生成指令的代码:

// 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.classClass原始类型的实例int,或Integer.TYPE常量的值)传递给我的bootstrap方法作为someClass. 但是,ASM 似乎没有正确理解或支持这一点。

这是否可以被视为 ASM 错误,是否有解决方法?

4

2 回答 2

3

我不相信可以将原始类型作为引导方法参数传递,因为不可能在类文件中对它们进行编码。根据 JVM 规范,类参数表示为CONSTANT_Class_info,它只能表示内部形式的名称,而不是描述符

编辑:有一个支持原始类型常量的建议:

这个最小的原型采用了这个答案,使用分号;(ASCII 十进制代码 59)作为转义字符。因此,现在可以使用 UTF8 字符串 " " 和 " " 获得类型int.class 和类文件常量。分号的选择在这里很自然,因为类名不能包含分号(除非它是数组类型),并且描述符语法经常出现在类文件中的分号后面。void.class;I;V

于 2016-08-12T22:41:40.417 回答
2

正如Brett Kail 指出的那样,不可能Class为原始类型编码常量。当您在源代码中使用类似文字int.class时,编译器会将其编码为 field 的读取操作java.lang.Integer.TYPE,其中包含所需的Class对象。对于注释,这是可能的,因为注释值被编码为指向CONSTANT_Utf8_info包含返回描述符的 a而不是 a CONSTANT_Class_info(参见JVM 规范 §4.7.16.1)。

由于引导方法的编码静态参数需要Class将对象编码为CONSTANT_Class_info,因此它们不支持原始类型。请参阅JVM 规范 §4.7.23

bootstrap_arguments 数组中的每个条目都必须是 constant_pool 表的有效索引。该索引处的 constant_pool 条目必须是CONSTANT_String_info, CONSTANT_Class_info, CONSTANT_Integer_info, CONSTANT_Long_info, CONSTANT_Float_info, CONSTANT_Double_info, CONSTANT_MethodHandle_info, 或CONSTANT_MethodType_info结构...</p>

一种解决方法是添加一个约定,例如始终对所需类型的数组类型进行编码并在引导方法中提取元素类型。或者将所需类型编码为 a 的返回类型CONSTANT_MethodType_info。后者的优势是连支持void.class

于 2016-08-22T14:29:14.807 回答