如何 bootstrap 与 ObjectWeb2 ASM 的接口方法引用
How to bootstrap interface method reference with ObjectWeb2 ASM
我正在尝试修复 Groovy 中对接口方法引用的元工厂调用:https://issues.apache.org/jira/browse/GROOVY-9853
给出小Java程序
public class J {
public static void main(String[] args) {
java.util.function.ToIntFunction<CharSequence> f = CharSequence::length;
f.applyAsInt("");
}
}
编译器编写这个bootstrap方法:
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
给定一个非常相似的Groovy程序
class G {
@groovy.transform.CompileStatic
static main(args) {
java.util.function.ToIntFunction<CharSequence> f = CharSequence::length
f.applyAsInt("")
}
}
groovy 编译器编写此 bootstrap 方法:
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
第二个常量池条目对于 java 为“空”,对于 groovy 为“java/lang/CharSequence.length:()I”。这是导致链接问题中提到的 ClassFormatError
的常量池条目。我正在尝试更改 bootstrap 方法输出,这是使用 ASM 完成的,如下所示:
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();
}
bootstrap 方法参数是否需要不同?我不太清楚 java 在常量 table 中的“null”与根据类型和句柄发送给 ASM 的内容之间的联系。
这里是 javap
输出:
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
你是对的,这是Groovy编译器的一个错误,由错误使用tag
和isInterface
参数引起。
让我们从isInterface
参数开始:
当且仅当所有者 class(也是 Handle
构造函数的参数)是一个接口时,此参数才为真。
让我们来看看一些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
);
}
(从 here 调用)
java.lang.invoke.LambdaMetafactory
不是接口。永远不会。
所以传递给 Handle
构造函数的正确的东西是常量 false
.
接下来是handle for the target method itself:
createBootstrapMethodArguments(createMethodDescriptor(abstractMethod), H_INVOKEVIRTUAL, lambdaClass, lambdaMethod, expression.isSerializable())
您要创建的句柄用于实现方法 - 通常是同一 class 中的私有方法。同样,如果所有者 class 是一个接口,isInterface
就是 true
。
您必须使用 H_*
常量,其名称与调用该方法的指令相似。
从设计的角度来看,可能有一个辅助方法可以将任何 MethodNode
转换为 Handle
我正在尝试修复 Groovy 中对接口方法引用的元工厂调用:https://issues.apache.org/jira/browse/GROOVY-9853
给出小Java程序
public class J {
public static void main(String[] args) {
java.util.function.ToIntFunction<CharSequence> f = CharSequence::length;
f.applyAsInt("");
}
}
编译器编写这个bootstrap方法:
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
给定一个非常相似的Groovy程序
class G {
@groovy.transform.CompileStatic
static main(args) {
java.util.function.ToIntFunction<CharSequence> f = CharSequence::length
f.applyAsInt("")
}
}
groovy 编译器编写此 bootstrap 方法:
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
第二个常量池条目对于 java 为“空”,对于 groovy 为“java/lang/CharSequence.length:()I”。这是导致链接问题中提到的 ClassFormatError
的常量池条目。我正在尝试更改 bootstrap 方法输出,这是使用 ASM 完成的,如下所示:
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();
}
bootstrap 方法参数是否需要不同?我不太清楚 java 在常量 table 中的“null”与根据类型和句柄发送给 ASM 的内容之间的联系。
这里是 javap
输出:
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
你是对的,这是Groovy编译器的一个错误,由错误使用tag
和isInterface
参数引起。
让我们从isInterface
参数开始:
当且仅当所有者 class(也是 Handle
构造函数的参数)是一个接口时,此参数才为真。
让我们来看看一些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 ); }
(从 here 调用)
java.lang.invoke.LambdaMetafactory
不是接口。永远不会。
所以传递给 Handle
构造函数的正确的东西是常量 false
.
接下来是handle for the target method itself:
createBootstrapMethodArguments(createMethodDescriptor(abstractMethod), H_INVOKEVIRTUAL, lambdaClass, lambdaMethod, expression.isSerializable())
您要创建的句柄用于实现方法 - 通常是同一 class 中的私有方法。同样,如果所有者 class 是一个接口,isInterface
就是 true
。
您必须使用 H_*
常量,其名称与调用该方法的指令相似。
从设计的角度来看,可能有一个辅助方法可以将任何 MethodNode
转换为 Handle