Bytebuddy - Stackmanipulation 实现 lambda 调用

Bytebuddy - Stackmanipulation implement lambda call

我想用 bytebuddy stackmanipulation 生成以下方法。

public void test() {
     this.process(() -> {
          System.out.println("Im lambda call");
     }
}

如果使用 javac 编译此类代码,它将生成:

  1. 名为 lambda$test$0 的“this”类型中没有任何参数的私有方法
  2. 在方法测试中会有调用动态指令
  3. 在测试方法中调用动态指令来调用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 不会尝试重新实现它。如果代码量最少,您可以将其包装在堆栈操作中并使用它实现该方法,如果这是您考虑的一个选项。