Is there a more efficient way for the interweaved code to access the `Method` object?
For the Advice example below,
@Advice.OnMethodEnter
public static boolean onEnter(final @Advice.Origin Method method) {
Context ctx = new Context(method);
}
The interweaved code calls getMethod to retrieve the Method object.
public void close() {
Context var1 = new Context(DruidDataSource.class.getMethod("close"));
I think it's not high efficient, each time the target method is called, the getMethod will be invoked. And If we want to get the Method object both in the OnMethodEnter and OnMethodExit, the generated code will call such method twice.
And if the Advice is like this:
@Advice.OnMethodEnter
public static boolean onEnter(final @Advice.Origin Method method) {
Context ctx = new Context(method, method.getDeclaringClass());
}
We can see that the injected code calls the getMethod twice:
public void close() {
Context var1 = new Context(DruidDataSource.class.getMethod("close"),
DruidDataSource.class.getMethod("close").getDeclaringClass());
I also checked the interweaved code of MethodDelegation way. I found that for any Method object, it's cached in a static object which is initialized in the static initializer, which is more efficient than the Advice way.
public class DruidDataSource {
static {
cachedValue$L47wuwfW$kqgv4v0 = DruidDataSource.class.getMethod("close");
}
public void close() {
....
delegate$jgtt6t1.intercept(cachedValue$L47wuwfW$kqgv4v0);
....
}
}
So, for the Advice, is it able to interweave the code for the Method object as the way MethodDelegation does?
You can use caching in advice, too. The origin annotation has a cached property that you can set. As advice is often used for retransformation, and since retransformation does not support adding fields, this is normally disabled.
You can otherwise use the origin on a string, unless you need to process method objects in a more complex manner.
Thank you @raphw for your response. In my case, I really need to process the method objects.
But I didn't see this cached property on Advice.Origin annotation. Do I miss something?
The net.bytebuddy.implementation.bind.annotation.Origin has a cache property.
You are right, I must have planned but never implemented this.
You could however simply create your own offset mapping and use MethodConstant.of(instrumentedType).cached() as the resolved value. You can do this via Advice.withCustomBinding().bind(MyAnnotation.class, ...). Simply have a look at the binding for Origin to see how this can be done.
OK, I will try it. Very appreciate your response.
@raphw It works by defining a customer mapping as below.
public class TargetMethodMapping implements Advice.OffsetMapping {
@Override
public Target resolve(TypeDescription instrumentedType,
MethodDescription instrumentedMethod,
Assigner assigner,
Advice.ArgumentHandler argumentHandler,
Sort sort) {
return new Target.ForStackManipulation(MethodConstant.of(instrumentedMethod.asDefined()).cached());
}
}
The interweaved code shows that the method objects are cached in static variables which are initialized in static initializer.
static {
cachedValue$fbnROjZ8$kqgv4v0 = DruidDataSource.class.getMethod("close");
Thank you again.