Search code examples
javabyte-buddy

Bytebuddy - Stackmanipulation implement lambda call


I would like to generate follwing method with bytebuddy stackmanipulation.

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

if such code would be compiled with javac it would produce:

  1. private method in the "this" type called lambda$test$0 without any arguments
  2. within the method test there would be invoke dynamic instruction
  3. invoke dynamic instruction in the test method to invoke the lambda

If i had just ASM i could do something like this

            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$0", "()V", false), Type.getType("()V")});
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, process ....)

 

How would i do this with just bytebuddy? Im using bytebuddy as its abstraction over opcodes is just much more pleasant to write than raw asm.

Let`s assume that the content of Runnable (method call of lambda$test$0) has already been generated

            MethodDescription.InDefinedShape targetCall = thisType.getDeclaredMethods().filter(named("lambda$test$0")).getOnly();
            InvokeDynamic methodRef = InvokeDynamic.lambda(targetCall, new TypeDescription.ForLoadedType(Runnable.class)).withoutArguments();


        StackManipulation.Size size = new StackManipulation.Compound(
            stackManipulations...
            methodRef... ??
        ).apply(mv, ctx);

This is where i dont know what to do. How can i get StackManipulation from InvokeDynamic?


Solution

  • To create a stack manipulation for a dynamic method invocation, instead of InvokeDynamic (a more high level Implementation), use

    MethodInvocation.invoke(targetCall).dynamic("run",
      Collections.singletonList(thisType),
      TypeDescription.ForLoadedType.of(Runnable.class),
      Collections.emptyList());
    

    You can chain this StackManipulation with the rest of your code.

    In this case, ASM might however not be the worst option, it is a rather good byte code emitter and Byte Buddy does therefore not attempt to reimplement it. If it is the least amount of code, you can wrap it in a stack manipulation and implement the method using it, too, if this is an option you consider.