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(基本类型 intClass 实例或 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.