byte-buddy icon indicating copy to clipboard operation
byte-buddy copied to clipboard

Advice method contains asynchronization code will throw IllegalAccessError

Open MrLiuzy opened this issue 1 year ago • 1 comments

version 1.12.19 recurrence this issue by running code below :

import java.util.Arrays;
import java.util.concurrent.CompletableFuture;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.matcher.ElementMatchers;

public class ByteBuddyAdviceTest {

    public static class Target {
        public void sayHello(String hello) {
            System.out.println(hello);
        }
    }
    
    public static class TargetAgent {
        
        @Advice.OnMethodExit
        public static void methodExit(@Advice.AllArguments Object[] args) {
            System.err.println("方法退出, 参数:" + Arrays.toString(args));
            // success
            otherMethod(args);
            // Exception in thread "main" java.lang.BootstrapMethodError: java.lang.IllegalAccessError: tried to access method ...
            CompletableFuture.runAsync(()->{});
        }
        
        static void otherMethod(Object[] args) {
            CompletableFuture.runAsync(() -> System.out.println("otherMethod"));
        }
    }
    
    public static void main(String[] args) {
        ByteBuddyAgent.install();
        // @formatter:off
        new ByteBuddy()
            .redefine(Target.class)
            .visit(Advice.to(TargetAgent.class).on(ElementMatchers.named("sayHello")))
            .make()
            .load(Target.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent())
            ;
        // @formatter:on
        new Target().sayHello("hello byte buddy");
    }
}

MrLiuzy avatar Apr 09 '24 01:04 MrLiuzy

This is because of the lambda expression. It's compiled to a method of its own which is not visible after inlining.

You should avoid lambdas and rather create explicit classes that you need to inject into the targeted class loader using ClassInjector. Then thereafter you can instantiate the callback explicitly and provide it where you now place the lambda.

raphw avatar Apr 09 '24 05:04 raphw