Change private class-fields with Advice.FieldValue and inline = true with IndyBootstrap

I would like to do the following:

@Advice.OnMethodExit(suppress = Throwable.class, inline = true)
        public static void onExit(
            @Advice.Enter @Nullable Object abstractSpanObj,
            @Advice.This Object thiz,
            @Advice.FieldValue(value = "localState", readOnly = false) Map<IOLocal<?>, Object> localState
        )

As far as I understand, you can only set readOnly = false on a private field of a class when the Advice is inlined. The problem is that it cannot be inlined due to the IndyBootstrap.

How can we change private fields of classes with this approach? Or is this a limitation of the ElasticAPM intrumentation?

It seems to be related to: Customizable advice dispatcher · Issue #830 · raphw/byte-buddy · GitHub

You are right, in Indy plugins, the way to assign values to fields is done this way: apm-agent-java/InstrumentationTest.java at b0dbda275335adf618d1e4c8fc94a53ce19f6764 · elastic/apm-agent-java · GitHub

Im not sure on how to use it. I tried:

        @AssignTo(fields = @AssignTo.Field(index = 3, value = "localState"))
        @Advice.OnMethodExit(suppress = Throwable.class, inline = false)
        public static void onExit(
            @Advice.Enter @Nullable Object abstractSpanObj,
            @Advice.This Object thiz,
            @Advice.Argument(3) Object localStateObj
        )

I assigned it to index 3 as the method Im trying to instrument already has 3 arguments but then I get an exception that index 3 isnt defined.

How can I retrieve the assigned field from the annotation?

The advice method returns the value that should be assigned to the filed. The linked tests show two forms:

  1. If you only want to assign to a field, your advice method will return the value to assign and you don't need to specify index
  2. If you want to assign to multiple things, your advice method needs to return an Object[] and the index specifies what value from the returned array needs to be assigned to the corresponding field.

I hope this helps

Im still quite confused on the usage tbh :slight_smile:

class MyInstrumentation extends ElasticApmInstrumentation {
 
        @AssignTo.Field("privateString")
        @Advice.OnMethodEnter
        public static String getPrivateString(@Advice.Argument(0) String s) {
            return s;
        }

        @Advice.OnMethodExit(suppress = Throwable.class, inline = false)
        public static void onExit(
            @Advice.Enter @Nullable Object abstractSpanObj,
            @Advice.This Object thiz
        ) { getPrivateString() }

}

How would you change the value of this private field during runtime with this approach?

I think I figured it out, let me know if this makes sense:

class MyInstrumentation extends ElasticApmInstrumentation {
 
        @AssignTo.Field("localState")
        @Advice.OnMethodExit(suppress = Throwable.class, inline = false)
        public static Map<IOLocal<?>, Object> onExit(
            @Advice.Enter @Nullable Object abstractSpanObj,
            @Advice.This Object thiz,
            @Advice.FieldValue(value = "localState") Map<IOLocal<?>, Object> localState
        ) { return localState.updated("foo", "bar" }

}

Yes, assuming that the localState field's type is Map<IOLocal<?>, Object> and this is also what is being returned from localState.updated("foo", "bar").
If it works, then it's probably right :slight_smile:

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.