Bytebuddy - Stackmanipulation 实现 lambda 调用
Bytebuddy - Stackmanipulation implement lambda call
我想用 bytebuddy stackmanipulation 生成以下方法。
public void test() {
this.process(() -> {
System.out.println("Im lambda call");
}
}
如果使用 javac 编译此类代码,它将生成:
- 名为 lambda$test$0 的“this”类型中没有任何参数的私有方法
- 在方法测试中会有调用动态指令
- 在测试方法中调用动态指令来调用lambda
如果我只有 ASM,我可以做这样的事情
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitInvokeDynamicInsn("run", "(LmyTest/Test;)Ljava/lang/Runnable;", 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;", false), new Object[]{Type.getType("()V"), new Handle(Opcodes.H_INVOKESPECIAL, "myTest/Test", "lambda$test[=11=]", "()V", false), Type.getType("()V")});
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, process ....)
我如何使用 bytebuddy 来完成此操作?我使用 bytebuddy 因为它对操作码的抽象比原始 asm 编写起来更愉快。
假设Runnable的内容(lambda$test$0的方法调用)已经生成
MethodDescription.InDefinedShape targetCall = thisType.getDeclaredMethods().filter(named("lambda$test[=12=]")).getOnly();
InvokeDynamic methodRef = InvokeDynamic.lambda(targetCall, new TypeDescription.ForLoadedType(Runnable.class)).withoutArguments();
StackManipulation.Size size = new StackManipulation.Compound(
stackManipulations...
methodRef... ??
).apply(mv, ctx);
这是我不知道该做什么的地方。我如何从 InvokeDynamic 获取 StackManipulation?
要为动态方法调用创建堆栈操作,而不是 InvokeDynamic
(更高级别 Implementation
),请使用
MethodInvocation.invoke(targetCall).dynamic("run",
Collections.singletonList(thisType),
TypeDescription.ForLoadedType.of(Runnable.class),
Collections.emptyList());
您可以将此 StackManipulation
与您的其余代码链接起来。
在这种情况下,ASM 可能不是最糟糕的选择,它是一个相当不错的字节码发射器,因此 Byte Buddy 不会尝试重新实现它。如果代码量最少,您可以将其包装在堆栈操作中并使用它实现该方法,如果这是您考虑的一个选项。
我想用 bytebuddy stackmanipulation 生成以下方法。
public void test() {
this.process(() -> {
System.out.println("Im lambda call");
}
}
如果使用 javac 编译此类代码,它将生成:
- 名为 lambda$test$0 的“this”类型中没有任何参数的私有方法
- 在方法测试中会有调用动态指令
- 在测试方法中调用动态指令来调用lambda
如果我只有 ASM,我可以做这样的事情
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitInvokeDynamicInsn("run", "(LmyTest/Test;)Ljava/lang/Runnable;", 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;", false), new Object[]{Type.getType("()V"), new Handle(Opcodes.H_INVOKESPECIAL, "myTest/Test", "lambda$test[=11=]", "()V", false), Type.getType("()V")});
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, process ....)
我如何使用 bytebuddy 来完成此操作?我使用 bytebuddy 因为它对操作码的抽象比原始 asm 编写起来更愉快。
假设Runnable的内容(lambda$test$0的方法调用)已经生成
MethodDescription.InDefinedShape targetCall = thisType.getDeclaredMethods().filter(named("lambda$test[=12=]")).getOnly();
InvokeDynamic methodRef = InvokeDynamic.lambda(targetCall, new TypeDescription.ForLoadedType(Runnable.class)).withoutArguments();
StackManipulation.Size size = new StackManipulation.Compound(
stackManipulations...
methodRef... ??
).apply(mv, ctx);
这是我不知道该做什么的地方。我如何从 InvokeDynamic 获取 StackManipulation?
要为动态方法调用创建堆栈操作,而不是 InvokeDynamic
(更高级别 Implementation
),请使用
MethodInvocation.invoke(targetCall).dynamic("run",
Collections.singletonList(thisType),
TypeDescription.ForLoadedType.of(Runnable.class),
Collections.emptyList());
您可以将此 StackManipulation
与您的其余代码链接起来。
在这种情况下,ASM 可能不是最糟糕的选择,它是一个相当不错的字节码发射器,因此 Byte Buddy 不会尝试重新实现它。如果代码量最少,您可以将其包装在堆栈操作中并使用它实现该方法,如果这是您考虑的一个选项。