raphw / byte-buddy

Runtime code generation for the Java virtual machine.
https://bytebuddy.net
Apache License 2.0
6.23k stars 804 forks source link

Advice method contains asynchronization code will throw IllegalAccessError #1614

Closed MrLiuzy closed 6 days ago

MrLiuzy commented 5 months ago

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");
    }
}
raphw commented 5 months ago

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.