Using Byte Buddy's advice API, is it possible to return from the instrumented method without actually executing it?
One use case would be to implement a cache and to return the cached value, if present, instead of computing the value again.
@Advice.OnMethodEnter
public static Object returnCachedValue(@Advice.Argument(0) String query) {
if (cache.containsKey(query)) {
// should "abort" method call
return cache.get(query);
}
}
I know that this code sample above just creates a local variable which I can get in a @Advice.OnMethodExit
method. But is there a way to abort the method call on an explicit return
? If yes, is this also possible for void
methods?
No, this is not possible, a return value can only be set from exit advice. But it can be emulated by skipping the original method in case that a value already exists and by setting this value from the exit advice in case that the enter advice defines a value:
class MyAdvice {
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
public static Object returnCachedValue(@Advice.Argument(0) String query) {
if (cache.containsKey(query)) {
return cache.get(query);
} else {
return null;
}
}
@Advice.OnMethodExit
public static void processCachedValue(
@Advice.Argument(0) String query,
@Advice.Return(readOnly = false, typing = DYNAMIC) Object returned,
@Advice.Enter Object enter) {
if (enter != null) {
returned = enter;
} else {
cache.put(query, returned);
}
}
}
Of course, this does not work if the cached value is null
. To avoid this, you could wrap the value in some instance to make sure that the enter value is never null
. Doing so would also allow to use the above pattern to void
methods.
This might look inconvenient to program but the idea of advice is that Byte Buddy can use the advice class as a template and inline the byte code without much work to avoid a runtime overhead.