ASM无法通过' Type.INT_TYPE'作为常量Bootstrap方法参数

时间:2016-08-06 22:01:29

标签: java java-bytecode-asm invokedynamic jvm-bytecode

考虑以下用于使用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错误,是否有针对此的解决方法?

2 个答案:

答案 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_infoCONSTANT_String_infoCONSTANT_Class_infoCONSTANT_Integer_infoCONSTANT_Long_infoCONSTANT_Float_infoCONSTANT_Double_info ,或CONSTANT_MethodHandle_info结构......

解决方法是添加一个约定,例如始终编​​码所需类型的数组类型,并在bootstrap方法中提取元素类型。或者将所需类型编码为CONSTANT_MethodType_info的返回类型。后者具有甚至支持CONSTANT_MethodType_info的优势。