我正在尝试修复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 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 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是“null”,对于groovy是“java/lang/CharSequence.length:()I”。这是导致链接问题中提到的ClassFormatError
的常量池条目。我正在尝试更改引导方法的输出,这是使用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();
}
是不是引导方法的参数需要不同?我不能很好地将java在常量表中的“null”与发送到ASM的类型和句柄联系起来。
下面是javap
的输出:
一个
1条答案
按热度按时间bpsygsoo1#
您是对的,这是Groovy编译器中的一个bug,是由于错误使用
tag
和isInterface
参数引起的。让我们从
isInterface
参数开始:当且仅当拥有者类别(也是
Handle
建构函式的参数)是界面时,这个参数才为true。让我们看一下Groovy的一些代码:
(从此处调用)
java.lang.invoke.LambdaMetafactory
不是接口,也永远不会是。所以传递给
Handle
构造函数的正确的东西是一个常量false
。接下来是目标方法本身的句柄:
您要创建的句柄是用于实现方法的-通常是同一个类中的私有方法。同样,如果所有者类是一个接口,则
isInterface
是true
。您必须使用
H_*
常量,该常量的名称必须与用于调用该方法的指令的名称相似。从设计的Angular 来看,可能有一个helper方法将任何
MethodNode
转换为Handle