raphw / byte-buddy

Runtime code generation for the Java virtual machine.
https://bytebuddy.net
Apache License 2.0
6.23k stars 804 forks source link

Having trouble delegating added instance method to static method #1641

Closed jimshowalter closed 4 months ago

jimshowalter commented 4 months ago

It fails with "Exception in thread "main" java.lang.IllegalArgumentException: None of [public static java.lang.Object org.foo.bytebuddysave.SaveInterceptor.interceptInstanceSave(java.lang.reflect.Method,org.foo.bytebuddysave.Saver,org.foo.bytebuddysave.Flags,java.lang.Object)] allows for delegation from public synchronized org.foo.bytebuddysave.Entity2 org.foo.bytebuddysave.Entity2$Setter$Tracking$ByteBuddy$Subclass._instance$Save$Method(org.foo.bytebuddysave.Saver,org.foo.bytebuddysave.Flags)".

This is where it's trying to configure the method:

  public static Class<?> setterTrackingEntityClass(Class<?> entityClass) throws NoSuchMethodException {
    return new ByteBuddy()
        .with(MODIFIED_FIELDS_TRACKER_SUBCLASS_NAMING_STRATEGY)
        .subclass(entityClass, ConstructorStrategy.Default.NO_CONSTRUCTORS)
        .defineField(
            MODIFIED_FIELDS_TRACKER_FIELD_NAME,
            TypeDescription.Generic.Builder.parameterizedType(Set.class, String.class).build(),
            Visibility.PRIVATE,
            FieldManifestation.FINAL)
        .defineConstructor(Visibility.PUBLIC)
        .intercept(
            MethodCall.invoke(entityClass.getConstructor())
                .onSuper()
                .andThen(
                    FieldAccessor.ofField(MODIFIED_FIELDS_TRACKER_FIELD_NAME)
                        .setsValue(new HashSet<String>())))
        .method(ElementMatchers.isSetter())
        .intercept(MethodDelegation.to(SetterInterceptor.class).andThen(SuperMethodCall.INSTANCE))
        .transform(Transformer.ForMethod.withModifiers(SynchronizationState.SYNCHRONIZED))
        .defineMethod(INSTANCE_SAVE_METHOD_NAME, entityClass, Modifier.PUBLIC)
        .withParameters(Saver.class, Flags.class)
        .intercept(MethodDelegation.to(SaveInterceptor.class))
        .transform(Transformer.ForMethod.withModifiers(SynchronizationState.SYNCHRONIZED))
        .make()
        .load(entityClass.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
        .getLoaded();
  }

And this is the class it's trying to delegate to:

package org.foo.bytebuddysave;

import java.lang.reflect.Method;
import net.bytebuddy.implementation.bind.annotation.Argument;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.This;

public class SaveInterceptor {

  public static Object interceptInstanceSave(
      @Origin Method saveMethod,
      @Argument(0) Saver saver,
      @Argument(1) Flags flags,
      @This Object entity) {
    System.out.println(
        "Successful interception of ByteBuddy-generated instance-level synchronized save method");
    return EntityModificationUtils.saveImpl(entity, saver, flags);
  }
}

To reproduce the error, unzip the attached file and run AddSynchronizedInstanceLevelSaveMethod.

byte-buddy-instance-method-problem.zip

jimshowalter commented 4 months ago

Changing the signature to public static Object interceptInstanceSave(@Origin Method saveMethod, @AllArguments Object[] args, @This Object entity) doesn't help.

jimshowalter commented 4 months ago

It needed @RuntimeType.