lundi 22 mai 2017

ByteBuddy: newly defined fields not visible through reflection

I use ByteBuddy in an Agent to add a tracking variable to each Runnable in a test program:

new AgentBuilder.Default()
.with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED)
.type(ElementMatchers.isSubTypeOf(Runnable.class)
                .and(ElementMatchers.not(ElementMatchers.isInterface())))
                .and(ElementMatchers.not(ElementMatchers.isAbstract()))
            .transform((builder, typeDescription, classLoader, module) -> builder
                .defineField("foo", String.class)
                .constructor(ElementMatchers.any())
                .intercept(Advice.to(TestRunnableConstructorInterceptor.class))
                .method(ElementMatchers.named("run"))
                .intercept(Advice.to(TestRunnableRunInterceptor.class))
            )

With my Interceptor classes looking like this:

public static class TestRunnableConstructorInterceptor {
    @Advice.OnMethodExit
    public static void intercept(@Advice.This Object thiz, @Advice.FieldValue(value="foo",readOnly=false) String foo) throws Exception {
        foo = "baz"; // this sets the value successfully
    }
}

public static class TestRunnableRunInterceptor {
    @Advice.OnMethodEnter
    public static void intercept(@Advice.This Object thiz, @Advice.FieldValue("foo") String foo) throws Exception {
        System.out.println(foo); //prints  "baz"

        thiz.getClass().getField("foo"); // java.lang.NoSuchFieldException
    }
}

I can see that ByteBuddy is passing through the newly defined field via the FieldValue annotation, but reflectively the variable is not visible - perhaps because the reflection is being applied to the original class, and not the 'rebased' class?

Is this the expected behavior? Is there a way to access this new field via reflection?

Could this be something to do with the Runnables being lambdas? I'm using Advice rather than MethodDelegation because if I try to use MethodDelegation on Runnable#run I get errors like this (from my interception Listener)

Failed to transform java.util.concurrent.ThreadPoolExecutor$Worker$auxiliary$8cXEOSRS$auxiliary$7BgjnLbO (before loading) + Exception: java.lang.IllegalStateException: Cannot resolve type description for java.util.concurrent.ThreadPoolExecutor$Worker$auxiliary$8cXEOSRS Cannot resolve type description for java.util.concurrent.ThreadPoolExecutor$Worker$auxiliary$8cXEOSRSnet.bytebuddy.pool.TypePool$Resolution$Illegal.resolve(TypePool.java:134)





Aucun commentaire:

Enregistrer un commentaire