Issue
Consider the following code that is meant to generate an invokedynamic
instruction using ASM:
// BOOTSTRAP = new Handle(->
// CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType methodType, Class<?> someClass)
mv.visitInvokeDynamicInsn("foo", "(I)I", BOOTSTRAP, Type.INT_TYPE);
When decompiling the generated class using ASMifier, the relevant line becomes
mv.visitInvokeDynamicInsn("foo", "(I)I", new Handle(/* SNIP (same as BOOTSTRAP) */),
Type.getType("LI;"));
¯¯¯¯¯
As you can see, the Type.INT_TYPE
has turned into a literal reference to a reference type named I
. As this doesn't exist, the JVM complains at runtime with java.lang.BootstrapMethodError: java.lang.NoClassDefFoundError: I
.
What I wanted to do instead was pass int.class
(the Class
instance for the primitive type int
, or the value of the Integer.TYPE
constant) to my bootstrap
method as the argument for someClass
. However, it seems like ASM did not properly understand or support this.
Can this be considered an ASM bug, and is there a workaround for this?
Solution
As Brett Kail pointed out, 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).
Since encoded static arguments to bootstrap methods require Class
objects to be encoded as CONSTANT_Class_info
, they do not support primitive types. See 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…
A work-around would be to add a convention, e.g. always encode the array type of the desired type and extract the element type in the bootstrap method. Or encode the desired type as the return type of a CONSTANT_MethodType_info
. The latter has the advantage of even supporting void.class
.
Answered By - Holger
Answer Checked By - Willingham (JavaFixing Volunteer)