I am trying to fix the metafactory call for an interface method reference in Groovy: https://issues.apache.org/jira/browse/GROOVY-9853
Given small Java program
public class J {
public static void main(String[] args) {
java.util.function.ToIntFunction<CharSequence> f = CharSequence::length;
f.applyAsInt("");
}
}
the compiler writes this bootstrap method:
Bootstrap methods:
0 : # 58 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#59 (Ljava/lang/Object;)I
#66 null
#68 (Ljava/lang/CharSequence;)I
Given a very similar Groovy program
class G {
@groovy.transform.CompileStatic
static main(args) {
java.util.function.ToIntFunction<CharSequence> f = CharSequence::length
f.applyAsInt("")
}
}
the groovy compiler writes this bootstrap method:
Bootstrap methods:
0 : # 47 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#31 (Ljava/lang/Object;)I
#38 java/lang/CharSequence.length:()I
#40 (Ljava/lang/CharSequence;)I
The second constant pool entry is "null" for java and "java/lang/CharSequence.length:()I" for groovy. And this is the constant pool entry that is causing the ClassFormatError
mentioned in the linked issue. I'm trying to change the bootstrap method output, which is done using ASM like this:
methodVisitor.visitInvokeDynamicInsn(
"applyAsInt",
"()Ljava/util/function/ToIntFunction;",
new Handle(
Opcodes.H_INVOKESTATIC,
"java/lang/invoke/LambdaMetafactory",
"metafactory",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;",
isInterface // false because enclosing class "G" is not an interface
),
createBootstrapMethodArguments(
"()Ljava/util/function/ToIntFunction;",
Opcodes.H_INVOKEVIRTUAL,
typeOrTargetRefType, // CharSequence
methodRefMethod, // CharSequence#length
false
) // 3 arguments: [Type("(Ljava/lang/Object;)I"), Handle("java/lang/CharSequence.length()I (5 itf)"), Type("(Ljava/lang/CharSequence;)I")]
);
Object[] createBootstrapMethodArguments(String abstractMethodDesc, int insn, ClassNode methodOwnerClassNode, MethodNode methodNode, boolean serializable) {
List<Object> arguments = new ArrayList<>(5);
arguments.add(Type.getType(abstractMethodDesc));
arguments.add(new Handle(
insn,
BytecodeHelper.getClassInternalName(methodOwnerClassNode.getName()),
methodNode.getName(),
BytecodeHelper.getMethodDescriptor(methodNode),
methodOwnerClassNode.isInterface()));
arguments.add(Type.getType(BytecodeHelper.getMethodDescriptor(methodNode.getReturnType(),
(Parameter[]) methodNode.getNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE))));
if (serializable) {
arguments.add(5);
arguments.add(0);
}
return arguments.toArray();
}
Is it bootstrap method arguments that needs to be different? I can't quite make the connection between the "null" that java has in the constant table and what is sent to ASM in terms of Types and Handles.
Here is the javap
output:
class J
minor version: 0
major version: 52
flags: (0x0020) ACC_SUPER
this_class: #1 // J
super_class: #3 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 3
Constant pool:
#1 = Class #2 // J
#2 = Utf8 J
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Methodref #3.#9 // java/lang/Object."<init>":()V
#9 = NameAndType #5:#6 // "<init>":()V
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 LJ;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = InvokeDynamic #0:#17 // #0:applyAsInt:()Ljava/util/function/ToIntFunction;
#17 = NameAndType #18:#19 // applyAsInt:()Ljava/util/function/ToIntFunction;
#18 = Utf8 applyAsInt
#19 = Utf8 ()Ljava/util/function/ToIntFunction;
#20 = String #21 //
#21 = Utf8
#22 = InterfaceMethodref #23.#25 // java/util/function/ToIntFunction.applyAsInt:(Ljava/lang/Object;)I
#23 = Class #24 // java/util/function/ToIntFunction
#24 = Utf8 java/util/function/ToIntFunction
#25 = NameAndType #18:#26 // applyAsInt:(Ljava/lang/Object;)I
#26 = Utf8 (Ljava/lang/Object;)I
#27 = Utf8 args
#28 = Utf8 [Ljava/lang/String;
#29 = Utf8 f
#30 = Utf8 Ljava/util/function/ToIntFunction;
#31 = Utf8 LocalVariableTypeTable
#32 = Utf8 Ljava/util/function/ToIntFunction<Ljava/lang/CharSequence;>;
#33 = Utf8 MethodParameters
#34 = Utf8 SourceFile
#35 = Utf8 Temporary.java
#36 = Utf8 BootstrapMethods
#37 = Methodref #38.#40 // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#38 = Class #39 // java/lang/invoke/LambdaMetafactory
#39 = Utf8 java/lang/invoke/LambdaMetafactory
#40 = NameAndType #41:#42 // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#41 = Utf8 metafactory
#42 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#43 = MethodHandle 6:#37 // REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#44 = MethodType #26 // (Ljava/lang/Object;)I
#45 = InterfaceMethodref #46.#48 // java/lang/CharSequence.length:()I
#46 = Class #47 // java/lang/CharSequence
#47 = Utf8 java/lang/CharSequence
#48 = NameAndType #49:#50 // length:()I
#49 = Utf8 length
#50 = Utf8 ()I
#51 = MethodHandle 9:#45 // REF_invokeInterface java/lang/CharSequence.length:()I
#52 = Utf8 (Ljava/lang/CharSequence;)I
#53 = MethodType #52 // (Ljava/lang/CharSequence;)I
#54 = Utf8 InnerClasses
#55 = Class #56 // java/lang/invoke/MethodHandles$Lookup
#56 = Utf8 java/lang/invoke/MethodHandles$Lookup
#57 = Class #58 // java/lang/invoke/MethodHandles
#58 = Utf8 java/lang/invoke/MethodHandles
#59 = Utf8 Lookup
{
J();
descriptor: ()V
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LJ;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: invokedynamic #16, 0 // InvokeDynamic #0:applyAsInt:()Ljava/util/function/ToIntFunction;
5: astore_1
6: aload_1
7: ldc #20 // String
9: invokeinterface #22, 2 // InterfaceMethod java/util/function/ToIntFunction.applyAsInt:(Ljava/lang/Object;)I
14: pop
15: return
LineNumberTable:
line 5: 0
line 6: 6
line 25: 15
LocalVariableTable:
Start Length Slot Name Signature
0 16 0 args [Ljava/lang/String;
6 10 1 f Ljava/util/function/ToIntFunction;
LocalVariableTypeTable:
Start Length Slot Name Signature
6 10 1 f Ljava/util/function/ToIntFunction<Ljava/lang/CharSequence;>;
MethodParameters:
Name Flags
args
}
BootstrapMethods:
0: #43 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#44 (Ljava/lang/Object;)I
#51 REF_invokeInterface java/lang/CharSequence.length:()I
#53 (Ljava/lang/CharSequence;)I
InnerClasses:
public static final #59= #55 of #57; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
public class Groovy9853 implements groovy.lang.GroovyObject
minor version: 0
major version: 52
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #2 // Groovy9853
super_class: #4 // java/lang/Object
interfaces: 1, fields: 3, methods: 6, attributes: 2
Constant pool:
#1 = Utf8 Groovy9853
#2 = Class #1 // Groovy9853
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 groovy/lang/GroovyObject
#6 = Class #5 // groovy/lang/GroovyObject
#7 = Utf8 Scratch.groovy
#8 = Utf8 $staticClassInfo
#9 = Utf8 Lorg/codehaus/groovy/reflection/ClassInfo;
#10 = Utf8 __$stMC
#11 = Utf8 Z
#12 = Utf8 metaClass
#13 = Utf8 Lgroovy/lang/MetaClass;
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Lgroovy/transform/Generated;
#17 = NameAndType #14:#15 // "<init>":()V
#18 = Methodref #4.#17 // java/lang/Object."<init>":()V
#19 = Utf8 $getStaticMetaClass
#20 = Utf8 ()Lgroovy/lang/MetaClass;
#21 = NameAndType #19:#20 // $getStaticMetaClass:()Lgroovy/lang/MetaClass;
#22 = Methodref #2.#21 // Groovy9853.$getStaticMetaClass:()Lgroovy/lang/MetaClass;
#23 = NameAndType #12:#13 // metaClass:Lgroovy/lang/MetaClass;
#24 = Fieldref #2.#23 // Groovy9853.metaClass:Lgroovy/lang/MetaClass;
#25 = Utf8 this
#26 = Utf8 LGroovy9853;
#27 = Utf8 main
#28 = Utf8 ([Ljava/lang/String;)V
#29 = Utf8 args
#30 = Utf8 (Ljava/lang/Object;)I
#31 = MethodType #30 // (Ljava/lang/Object;)I
#32 = Utf8 java/lang/CharSequence
#33 = Class #32 // java/lang/CharSequence
#34 = Utf8 length
#35 = Utf8 ()I
#36 = NameAndType #34:#35 // length:()I
#37 = InterfaceMethodref #33.#36 // java/lang/CharSequence.length:()I
#38 = MethodHandle 5:#37 // REF_invokeVirtual java/lang/CharSequence.length:()I
#39 = Utf8 (Ljava/lang/CharSequence;)I
#40 = MethodType #39 // (Ljava/lang/CharSequence;)I
#41 = Utf8 java/lang/invoke/LambdaMetafactory
#42 = Class #41 // java/lang/invoke/LambdaMetafactory
#43 = Utf8 metafactory
#44 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#45 = NameAndType #43:#44 // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#46 = Methodref #42.#45 // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#47 = MethodHandle 6:#46 // REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#48 = Utf8 applyAsInt
#49 = Utf8 ()Ljava/util/function/ToIntFunction;
#50 = NameAndType #48:#49 // applyAsInt:()Ljava/util/function/ToIntFunction;
#51 = InvokeDynamic #0:#50 // #0:applyAsInt:()Ljava/util/function/ToIntFunction;
#52 = Utf8
#53 = String #52 //
#54 = Utf8 java/util/function/ToIntFunction
#55 = Class #54 // java/util/function/ToIntFunction
#56 = NameAndType #48:#30 // applyAsInt:(Ljava/lang/Object;)I
#57 = InterfaceMethodref #55.#56 // java/util/function/ToIntFunction.applyAsInt:(Ljava/lang/Object;)I
#58 = Utf8 [Ljava/lang/String;
#59 = Utf8 f
#60 = Utf8 Ljava/util/function/ToIntFunction;
#61 = Utf8 getClass
#62 = Utf8 ()Ljava/lang/Class;
#63 = NameAndType #61:#62 // getClass:()Ljava/lang/Class;
#64 = Methodref #4.#63 // java/lang/Object.getClass:()Ljava/lang/Class;
#65 = Utf8 org/codehaus/groovy/runtime/ScriptBytecodeAdapter
#66 = Class #65 // org/codehaus/groovy/runtime/ScriptBytecodeAdapter
#67 = Utf8 initMetaClass
#68 = Utf8 (Ljava/lang/Object;)Lgroovy/lang/MetaClass;
#69 = NameAndType #67:#68 // initMetaClass:(Ljava/lang/Object;)Lgroovy/lang/MetaClass;
#70 = Methodref #66.#69 // org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass:(Ljava/lang/Object;)Lgroovy/lang/MetaClass;
#71 = NameAndType #8:#9 // $staticClassInfo:Lorg/codehaus/groovy/reflection/ClassInfo;
#72 = Fieldref #2.#71 // Groovy9853.$staticClassInfo:Lorg/codehaus/groovy/reflection/ClassInfo;
#73 = Utf8 org/codehaus/groovy/reflection/ClassInfo
#74 = Class #73 // org/codehaus/groovy/reflection/ClassInfo
#75 = Utf8 getClassInfo
#76 = Utf8 (Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo;
#77 = NameAndType #75:#76 // getClassInfo:(Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo;
#78 = Methodref #74.#77 // org/codehaus/groovy/reflection/ClassInfo.getClassInfo:(Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo;
#79 = Utf8 getMetaClass
#80 = NameAndType #79:#20 // getMetaClass:()Lgroovy/lang/MetaClass;
#81 = Methodref #74.#80 // org/codehaus/groovy/reflection/ClassInfo.getMetaClass:()Lgroovy/lang/MetaClass;
#82 = Utf8 Lgroovy/transform/Internal;
#83 = Utf8 Ljava/beans/Transient;
#84 = Utf8 groovy/lang/MetaClass
#85 = Class #84 // groovy/lang/MetaClass
#86 = Utf8 setMetaClass
#87 = Utf8 (Lgroovy/lang/MetaClass;)V
#88 = Utf8 mc
#89 = Utf8 $getLookup
#90 = Utf8 ()Ljava/lang/invoke/MethodHandles$Lookup;
#91 = Utf8 java/lang/invoke/MethodHandles
#92 = Class #91 // java/lang/invoke/MethodHandles
#93 = Utf8 lookup
#94 = NameAndType #93:#90 // lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
#95 = Methodref #92.#94 // java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
#96 = Utf8 Code
#97 = Utf8 LocalVariableTable
#98 = Utf8 RuntimeVisibleAnnotations
#99 = Utf8 LineNumberTable
#100 = Utf8 MethodParameters
#101 = Utf8 StackMapTable
#102 = Utf8 SourceFile
#103 = Utf8 BootstrapMethods
{
private static org.codehaus.groovy.reflection.ClassInfo $staticClassInfo;
descriptor: Lorg/codehaus/groovy/reflection/ClassInfo;
flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
public static transient boolean __$stMC;
descriptor: Z
flags: (0x1089) ACC_PUBLIC, ACC_STATIC, ACC_TRANSIENT, ACC_SYNTHETIC
private transient groovy.lang.MetaClass metaClass;
descriptor: Lgroovy/lang/MetaClass;
flags: (0x1082) ACC_PRIVATE, ACC_TRANSIENT, ACC_SYNTHETIC
public Groovy9853();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=1
0: aload_0
1: invokespecial #18 // Method java/lang/Object."<init>":()V
4: aload_0
5: invokevirtual #22 // Method $getStaticMetaClass:()Lgroovy/lang/MetaClass;
8: astore_1
9: aload_1
10: aload_0
11: swap
12: putfield #24 // Field metaClass:Lgroovy/lang/MetaClass;
15: aload_1
16: pop
17: return
LocalVariableTable:
Start Length Slot Name Signature
0 17 0 this LGroovy9853;
RuntimeVisibleAnnotations:
0: #16()
groovy.transform.Generated
public static void main(java.lang.String...);
descriptor: ([Ljava/lang/String;)V
flags: (0x0089) ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
Code:
stack=2, locals=2, args_size=1
0: invokedynamic #51, 0 // InvokeDynamic #0:applyAsInt:()Ljava/util/function/ToIntFunction;
5: astore_1
6: aload_1
7: pop
8: aload_1
9: ldc #53 // String
11: invokeinterface #57, 2 // InterfaceMethod java/util/function/ToIntFunction.applyAsInt:(Ljava/lang/Object;)I
16: pop
17: return
LineNumberTable:
line 7: 0
line 8: 8
line 9: 17
LocalVariableTable:
Start Length Slot Name Signature
0 17 0 args [Ljava/lang/String;
6 11 1 f Ljava/util/function/ToIntFunction;
MethodParameters:
Name Flags
args
protected groovy.lang.MetaClass $getStaticMetaClass();
descriptor: ()Lgroovy/lang/MetaClass;
flags: (0x1004) ACC_PROTECTED, ACC_SYNTHETIC
Code:
stack=2, locals=2, args_size=1
0: aload_0
1: invokevirtual #64 // Method java/lang/Object.getClass:()Ljava/lang/Class;
4: ldc #2 // class Groovy9853
6: if_acmpeq 14
9: aload_0
10: invokestatic #70 // Method org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass:(Ljava/lang/Object;)Lgroovy/lang/MetaClass;
13: areturn
14: getstatic #72 // Field $staticClassInfo:Lorg/codehaus/groovy/reflection/ClassInfo;
17: astore_1
18: aload_1
19: ifnonnull 34
22: aload_0
23: invokevirtual #64 // Method java/lang/Object.getClass:()Ljava/lang/Class;
26: invokestatic #78 // Method org/codehaus/groovy/reflection/ClassInfo.getClassInfo:(Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo;
29: dup
30: astore_1
31: putstatic #72 // Field $staticClassInfo:Lorg/codehaus/groovy/reflection/ClassInfo;
34: aload_1
35: invokevirtual #81 // Method org/codehaus/groovy/reflection/ClassInfo.getMetaClass:()Lgroovy/lang/MetaClass;
38: areturn
StackMapTable: number_of_entries = 2
frame_type = 14 /* same */
frame_type = 252 /* append */
offset_delta = 19
locals = [ class org/codehaus/groovy/reflection/ClassInfo ]
public groovy.lang.MetaClass getMetaClass();
descriptor: ()Lgroovy/lang/MetaClass;
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: getfield #24 // Field metaClass:Lgroovy/lang/MetaClass;
4: dup
5: ifnull 9
8: areturn
9: pop
10: aload_0
11: dup
12: invokevirtual #22 // Method $getStaticMetaClass:()Lgroovy/lang/MetaClass;
15: putfield #24 // Field metaClass:Lgroovy/lang/MetaClass;
18: aload_0
19: getfield #24 // Field metaClass:Lgroovy/lang/MetaClass;
22: areturn
StackMapTable: number_of_entries = 1
frame_type = 73 /* same_locals_1_stack_item */
stack = [ class groovy/lang/MetaClass ]
RuntimeVisibleAnnotations:
0: #16()
groovy.transform.Generated
1: #82()
groovy.transform.Internal
2: #83()
java.beans.Transient
public void setMetaClass(groovy.lang.MetaClass);
descriptor: (Lgroovy/lang/MetaClass;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #24 // Field metaClass:Lgroovy/lang/MetaClass;
5: return
RuntimeVisibleAnnotations:
0: #16()
groovy.transform.Generated
1: #82()
groovy.transform.Internal
MethodParameters:
Name Flags
mc
public static java.lang.invoke.MethodHandles$Lookup $getLookup();
descriptor: ()Ljava/lang/invoke/MethodHandles$Lookup;
flags: (0x1009) ACC_PUBLIC, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=1, locals=0, args_size=0
0: invokestatic #95 // Method java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
3: areturn
}
BootstrapMethods:
0: #47 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#31 (Ljava/lang/Object;)I
#38 REF_invokeVirtual java/lang/CharSequence.length:()I
#40 (Ljava/lang/CharSequence;)I
CodePudding user response:
You are right, this is a bug in the Groovy compiler, caused by the wrong use of the tag
and isInterface
parameter.
Let's start with the isInterface
parameter:
This parameter is true if and only if the owner class (also a parameter of the Handle
constructor) is an interface.
Let's take a look at some of Groovy's code:
default Handle createBootstrapMethod(boolean isInterface, boolean serializable) { if (serializable) { return new Handle( Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "altMetafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;", isInterface ); } return new Handle( Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", isInterface ); }
(Called from here)
java.lang.invoke.LambdaMetafactory
is not an interface. And will never be.
So the right thing to pass to the Handle
constructor is a constant false
.
Next is the handle for the target method itself:
createBootstrapMethodArguments(createMethodDescriptor(abstractMethod), H_INVOKEVIRTUAL, lambdaClass, lambdaMethod, expression.isSerializable())
The Handle that you want to create is for the implementation method - often a private method in the same class. Again, isInterface
is true
if the owner class is an interface.
You have to use the H_*
constant with a similar name as to which instruction you would use to invoke the method.
From a design perspective, it might be possible to have a helper method that turns any MethodNode
into an Handle