javabytecodebyte-buddybytecode-manipulation

How to augment methods during ByteBuddy transformation?


Context

I am implementing byte code transformations with ByteBuddy and the process of manipulation is a multi step process. Because of that, the manipulation has to be able to:

  1. augment originally existing methods
  2. create new methods entirely
  3. augment a method that was introduced via 2.

For 1. I used an @OnMethodExit advice applied via:

Builder<?> builder = builder.visit(Advice.to(Helper.class)
  .on(ElementMatchers.hasMethodNamed(name));

with Helper the augmentation code for the method (effectively setting a field's value). When creating new methods, I build them as follows:

Builder<?> builder = builder.defineMethod(…)
  .intercept(MethodDelegation.to(OtherHelper.class));
  .…;

OtherHelper consumes the runtime instance via a static method taking @This Object object as argument.

The problem

In short: I don't see the former transformation applied if it follows the latter. The actual execution order is as follows:

  1. My type gets processed and a method added via MethodDelegation.….
  2. In a subsequent step I find that newly introduced method and try to augment the implementation generation through Advice.to(…) using an @OnMethodExit advice.
  3. The resulting code has the behavior of step 1 but is lacking the behavior of step 2.

I am assuming I invalidly combine the two parts of the implementation. Any ideas? A hunch: does the ElementMatcher matching the augmentation by name not see the method introduced using ….defineMethod(…) yet? The name is coming from some method inspection I start from builder.toTypeDescription() which actually makes me assume that the to-be-created method is already visible to the builder as otherwise it wouldn't be found in that step in the first place.


Solution

  • Can you share a reconstruction of your example? In a simple example, I observe the expected behavior:

    public class Bar {
      public static void main(String[] args) throws Exception {
        Class<?> type = new ByteBuddy().subclass(Object.class)
          .visit(Advice.to(Bar.class).on(named("m")))
          .defineMethod("m", void.class, Visibility.PUBLIC)
          .intercept(MethodDelegation.to(Bar.class))
          .make()
          .load(Bar.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
          .getLoaded();
    
        type.getMethod("m").invoke(type.getConstructor().newInstance());
      }
    
      @BindingPriority(2)
      public static void delegation() {
        System.out.println("Delegation!");
      }
    
      @Advice.OnMethodEnter
      public static void enter() {
        System.out.println("Advice!");
      }
    }
    

    This example prints both Advice! and Delegation!.