如何将 InvokeDynamic 作为参数传递给 ByteBuddy 中的 MethodCall

问题描述

我想生成一个类的字节码,该类将方法引用作为参数传递给另一个方法。 例如:

public class GeneratedClass {
    public GeneratedClass() {
        Test.foo((Function)Test::getId)
    }
}

使用 ByteBuddy 我可以生成一个带有自定义构造函数的类,并创建一个 InvokeDynamic 来表示 Test::getId,但问题是我无法将 InvokeDynamic 作为参数传递给我的MethodCall。我目前的实现如下:

var fooMethod = Test.class.getmethod("foo",Function.class);
InvokeDynamic methodRef = InvokeDynamic.lambda(Test.class.getmethod("getId"),Function.class)
        .withoutArguments();
new ByteBuddy()
        .subclass(Object.class,ConstructorStrategy.Default.NO_CONSTRUCTORS)
        .name("GeneratedClass")
        .defineConstructor(Visibility.PUBLIC)
        .intercept(
                MethodCall.invoke(fooMethod)
                .with((Object)null) \\I want to pass the methodRef instead of null
                .andThen(methodRef)
        ).make()
        .saveIn(new File("target"));

生成以下内容

public class GeneratedClass {
    public GeneratedClass() {
        Test.foo((Function)null);
        Test::getId;
    }
}

解决方法

截至今天,DSL 不支持此功能,但您可以提供自定义 StackManipulation 作为 with 的参数。在您的情况下,您需要为此解决 MethodInvocation

通过一个小技巧,你可以在今天实现它,但创建一个辅助方法:

builder = builder
  .defineMethod("mylambda",Function.class,Visibility.PRIVATE,Ownership.STATIC)
  .intercept(methodRef)

然后您可以使用 MethodCall 调用此方法并将其作为参数传递。