Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Intercepting default constructor with Byte Buddy

I'm trying to intercept constructor calls with Byte Buddy, this is my sample code:

package t;

import static net.bytebuddy.dynamic.loading.ClassLoadingStrategy.Default.INJECTION;
import static net.bytebuddy.implementation.MethodDelegation.to;
import static net.bytebuddy.matcher.ElementMatchers.any;
import java.util.concurrent.Callable;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;

public class Bar {
  public static void main (String[] args) {
    new ByteBuddy ().subclass (Object.class)
      .name ("t.Foo").constructor (any ()).intercept (to (new Object () {

        @RuntimeType
        public Object construct (@SuperCall Callable<Object> z) throws Exception {
          System.out.println ("CALLING XTOR");
          return z.call ();
        }
      })).make ().load (Bar.class.getClassLoader (), INJECTION).getLoaded ();
  }
}

Which yields the following exception:

Exception in thread "main" java.lang.IllegalStateException: Error invoking java.lang.ClassLoader#findClass
  at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$Resolved.loadClass(ClassInjector.java:392)
  at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection.inject(ClassInjector.java:201)
  at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default$InjectionDispatcher.load(ClassLoadingStrategy.java:199)
  at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default.load(ClassLoadingStrategy.java:114)
  at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:3302)
  at t.Bar.main(Bar.java:21)
Caused by: java.lang.ClassFormatError: Bad method name at constant pool index 19 in class file t/Foo$auxiliary$EalKkAhD
  at java.lang.ClassLoader.defineClass1(Native Method)
  at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:497)
  at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$Resolved.loadClass(ClassInjector.java:388)
  ... 5 more

I saw this related question where I do however get a collision exception (as if the constructor was defined twice).

like image 244
Lev Kuznetsov Avatar asked Jan 20 '16 18:01

Lev Kuznetsov


1 Answers

You cannot create a handler for a super method invocation for a constructor. The JVM's verifier ensures that any constructor calls another constructor once and only once. This call must be hard-coded into the method that invokes the constructor.

Note that this restriction holds for any Java byte code and even for scenarios where one is dealing with MethodHandles. Constructors are the big exception.

To make your exampel work, you need to invoke the super constructor either before or after the delegation. Note that you cannot access any properties that are defined by this from the interceptor (e.g. by @This or by @SuperCall, it is however possible to read the arguments) if you intercept before calling another constructor.

For your code example, simply chain the interceptor with a SuperCall such that the invocation becomes hard-coded as shown in the following example code:

new ByteBuddy().subclass(Object.class)
               .name("t.Foo")
               .constructor(any()).intercept(to(new Object() {
                 public void construct() throws Exception {
                   System.out.println("CALLING XTOR");
                 }
               }).andThen(SuperMethodCall.INSTANCE)) // This makes the difference!
               .make()
               .load(Bar.class.getClassLoader(), INJECTION)
               .getLoaded()

I agree that Byte Buddy should be more informative about this problem. I will add proper runtime handling for this scenario (i.e. the method is no longer considered for binding).

like image 85
Rafael Winterhalter Avatar answered Nov 19 '22 04:11

Rafael Winterhalter