Search code examples
javareflectionbyte-buddy

Is there a way to call a variable length parameter method with a field using Methodcall in bytebuddy intercept method?


I am trying to return some fields as a list using bytebuddy. I called Arrays.asList() using MethodCall, but it cannot handle variable length parameters. Are there any other approaches?

public class TestClass {
    int a;
    int b;
    
    public List<?> getFields(){
        throw new RuntimeException("Bytebuddy not work.");
    }
}

new ByteBuddy()
                .rebase(TypePool.Default.ofSystemLoader().describe("com.test.TestClass").resolve(), 
                        ClassFileLocator.ForClassLoader.ofSystemLoader())
                .method(ElementMatchers.named("getFields"))
                .intercept(MethodCall.invoke(Arrays.class.getMethod("asList", Object[].class))
                        .withField("a", "b"))       
                .make()
                .load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                .getLoaded();
        
        TestClass testTarget = new TestClass();
        LOG.info("result : {}", testTarget.getFields());

error:

Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.IllegalStateException: public static java.util.List java.util.Arrays.asList(java.lang.Object[]) does not accept 2 arguments at net.bytebuddy.implementation.MethodCall$Appender.toStackManipulation(MethodCall.java:3537) at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:3506) at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:708) at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:693) at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:600) at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor.onVisitEnd(TypeWriter.java:5022) at net.bytebuddy.utility.visitor.MetadataAwareClassVisitor.visitEnd(MetadataAwareClassVisitor.java:323) at net.bytebuddy.jar.asm.ClassReader.accept(ClassReader.java:722) at net.bytebuddy.jar.asm.ClassReader.accept(ClassReader.java:401) at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:3827) at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:2166) at net.bytebuddy.dynamic.scaffold.inline.RebaseDynamicTypeBuilder.make(RebaseDynamicTypeBuilder.java:252) at net.bytebuddy.dynamic.scaffold.inline.AbstractInliningDynamicTypeBuilder.make(AbstractInliningDynamicTypeBuilder.java:123) at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3595) at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:3819) at


Solution

  • In this case of a more complex method body, I would recommend you to write the code in Java and use either a MethodDelegation or an Advice to address the issue in code and then link your method.

    Alternatively, you can use a StackManipulation and construct the method from single blocks of code. In this case, you would need to construct an array first and then supply this array to the method as varargs does not exist on the byte code level.