Invoke method on a argument with a cast using Byte Buddy

Daniel Fernández :

I'm very new to Byte Buddy, and I'm trying to use it to create implementations of an interface that execute getter methods on objects. My interface looks like this:

public interface Executor {
    Object execute(final Object target);
}

And the idea is that if I have a class such as:

public class User {
   ...
   public String getName() { return this.name; }
   public String getSurname() { return this.surname; }
}

I need to be able to create one implementation of the Executor interface which execute(obj) method assumes obj is a User and calls its getName(), then another implementation which does the same for getSurname(), etc. Equivalent java code would therefore be:

public class MyHypotheticalByteBuddyExecutorImpl implements Executor {
    @Override
    Object execute(final Object target) {
        return ((User) target).getName();
    }
}

So the idea is to be able to create classes like the above for any combination of class + getter, like in this case User + getName().

I (think I) know how to make Byte Buddy create a class that almost does that:

final Method nameMethod = User.class.getMethod("getName", null);

final Class<?> myHypotheticalByteBuddyExecutorImpl =
    new ByteBuddy()
        .subclass(Object.class)
        .implement(Executor.class)
        .method(ElementMatchers.named("execute"))
            .intercept(MethodCall.invoke(nameMethod).onArgument(0))
        .make()
        .load(ByteBuddyTest.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
        .getLoaded();

...but then Byte Buddy rightly throws an Exception saying that I cannot execute method getName() on an Object. I'm assuming therefore that I'm lacking the ((User) target) cast:

Exception in thread "main" java.lang.IllegalStateException: Cannot invoke public java.lang.String com.example.User.getName() on class java.lang.Object
    at net.bytebuddy.implementation.MethodCall$TargetHandler$ForMethodParameter$Resolved.toStackManipulation(MethodCall.java:2527)
    at net.bytebuddy.implementation.MethodCall$Appender.toStackManipulation(MethodCall.java:3541)
    at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:3502)
    ...

I believe this could be defined as a StackManipulation (I might be totally wrong), something like:

final StackManipulation typeCasting =
    TypeCasting.to(TypeDescription.ForLoadedType.of(User.class));

But I cannot find anywhere in the Byte Buddy API how I can apply this cast (or any other code I might need for casting) to the argument of the execute(Object) method before executing the getter.

How can I implement this?

Rafael Winterhalter :

This should work by using dynamic typing which you can configure by:

MethodCall.invoke(nameMethod)
  .onArgument(0)
  .withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC);

The stack manipulation is used for creating custom byte code, I do not think that this is what you want to do here.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=158275&siteId=1