package net.bytebuddy.implementation; import net.bytebuddy.ByteBuddy; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.implementation.bind.annotation.Origin; import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.test.utility.JavaVersionRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.MethodRule; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; public class MethodDelegationOriginTest { private static final String FOO = "foo", TYPE = "TYPE"; private static final String ORIGIN_METHOD_HANDLE = "net.bytebuddy.test.precompiled.OriginMethodHandle"; private static final String ORIGIN_METHOD_TYPE = "net.bytebuddy.test.precompiled.OriginMethodType"; private static final String ORIGIN_EXECUTABLE = "net.bytebuddy.test.precompiled.OriginExecutable"; private static final String ORIGIN_EXECUTABLE_CACHED = "net.bytebuddy.test.precompiled.OriginExecutableWithCache"; @Rule public MethodRule javaVersionRule = new JavaVersionRule(); @Test public void testOriginClass() throws Exception { DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .method(isDeclaredBy(Foo.class)) .intercept(MethodDelegation.to(OriginClass.class)) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Foo instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(instance.foo(), instanceOf(Class.class)); assertThat(instance.foo(), is((Object) Foo.class)); } @Test public void testOriginMethodWithoutCache() throws Exception { DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .method(isDeclaredBy(Foo.class)) .intercept(MethodDelegation.to(OriginMethod.class)) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Foo instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); Object method = instance.foo(); assertThat(method, instanceOf(Method.class)); assertThat(method, is((Object) Foo.class.getDeclaredMethod(FOO))); assertThat(method, not(sameInstance(instance.foo()))); } @Test public void testOriginMethodWithCache() throws Exception { DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .method(isDeclaredBy(Foo.class)) .intercept(MethodDelegation.to(OriginMethodWithCache.class)) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Foo instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); Object method = instance.foo(); assertThat(method, instanceOf(Method.class)); assertThat(method, is((Object) Foo.class.getDeclaredMethod(FOO))); assertThat(method, sameInstance(instance.foo())); } @Test @SuppressWarnings("unchecked") public void testOriginConstructorWithoutCache() throws Exception { OriginConstructor originConstructor = new OriginConstructor(); DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .constructor(ElementMatchers.any()) .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.to(originConstructor))) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(originConstructor.constructor, instanceOf(Constructor.class)); assertThat(originConstructor.constructor, is((Constructor) loaded.getLoaded().getDeclaredConstructor())); Constructor<?> previous = originConstructor.constructor; loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(originConstructor.constructor, instanceOf(Constructor.class)); assertThat(originConstructor.constructor, is((Constructor) loaded.getLoaded().getDeclaredConstructor())); assertThat(originConstructor.constructor, not(sameInstance((Constructor) previous))); } @Test @SuppressWarnings("unchecked") public void testOriginConstructorWithCache() throws Exception { OriginConstructorWithCache originConstructor = new OriginConstructorWithCache(); DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .constructor(ElementMatchers.any()) .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.to(originConstructor))) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(originConstructor.constructor, instanceOf(Constructor.class)); assertThat(originConstructor.constructor, is((Constructor) loaded.getLoaded().getDeclaredConstructor())); Constructor<?> previous = originConstructor.constructor; loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(originConstructor.constructor, instanceOf(Constructor.class)); assertThat(originConstructor.constructor, sameInstance((Constructor) previous)); } @Test @JavaVersionRule.Enforce(8) public void testOriginExecutableOnMethodWithoutCache() throws Exception { Object origin = Class.forName(ORIGIN_EXECUTABLE).getDeclaredConstructor().newInstance(); DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .method(isDeclaredBy(Foo.class)) .intercept(MethodDelegation.to(origin)) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Foo instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); Object method = instance.foo(); assertThat(method, instanceOf(Method.class)); assertThat(method, is((Object) Foo.class.getDeclaredMethod(FOO))); assertThat(method, not(sameInstance(instance.foo()))); } @Test @JavaVersionRule.Enforce(8) public void testOriginExecutableOnMethodWithCache() throws Exception { Object origin = Class.forName(ORIGIN_EXECUTABLE_CACHED).getDeclaredConstructor().newInstance(); DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .method(isDeclaredBy(Foo.class)) .intercept(MethodDelegation.to(origin)) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Foo instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); Object method = instance.foo(); assertThat(method, instanceOf(Method.class)); assertThat(method, is((Object) Foo.class.getDeclaredMethod(FOO))); assertThat(method, sameInstance(instance.foo())); } @Test @SuppressWarnings("unchecked") @JavaVersionRule.Enforce(8) public void testOriginExecutableConstructorWithoutCache() throws Exception { Object originConstructor = Class.forName(ORIGIN_EXECUTABLE).getDeclaredConstructor().newInstance(); Field constructor = Class.forName(ORIGIN_EXECUTABLE).getDeclaredField("executable"); DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .constructor(ElementMatchers.any()) .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.to(originConstructor))) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(constructor.get(originConstructor), instanceOf(Constructor.class)); assertThat(constructor.get(originConstructor), is((Object) loaded.getLoaded().getDeclaredConstructor())); Object previous = constructor.get(originConstructor); loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(constructor.get(originConstructor), instanceOf(Constructor.class)); assertThat(constructor.get(originConstructor), is((Object) loaded.getLoaded().getDeclaredConstructor())); assertThat(constructor.get(originConstructor), not(sameInstance(previous))); } @Test @SuppressWarnings("unchecked") @JavaVersionRule.Enforce(8) public void testOriginExecutableConstructorWithCache() throws Exception { Object originConstructor = Class.forName(ORIGIN_EXECUTABLE_CACHED).getDeclaredConstructor().newInstance(); Field constructor = Class.forName(ORIGIN_EXECUTABLE_CACHED).getDeclaredField("executable"); DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .constructor(ElementMatchers.any()) .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.to(originConstructor))) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(constructor.get(originConstructor), instanceOf(Constructor.class)); assertThat(constructor.get(originConstructor), is((Object) loaded.getLoaded().getDeclaredConstructor())); Object previous = constructor.get(originConstructor); loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(constructor.get(originConstructor), instanceOf(Constructor.class)); assertThat(constructor.get(originConstructor), sameInstance(previous)); } @Test public void testOriginString() throws Exception { DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .method(isDeclaredBy(Foo.class)) .intercept(MethodDelegation.to(OriginString.class)) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Foo instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(instance.foo(), instanceOf(String.class)); assertThat(instance.foo(), is((Object) Foo.class.getDeclaredMethod(FOO).toString())); } @Test @JavaVersionRule.Enforce(7) public void testOriginMethodHandle() throws Throwable { Class<?> originMethodHandle = Class.forName(ORIGIN_METHOD_HANDLE); DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .method(isDeclaredBy(Foo.class)) .intercept(MethodDelegation.to(originMethodHandle)) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Foo instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(instance.foo(), instanceOf((Class<?>) originMethodHandle.getDeclaredField(TYPE).get(null))); } @Test @JavaVersionRule.Enforce(7) public void testOriginMethodType() throws Throwable { Class<?> originMethodType = Class.forName(ORIGIN_METHOD_TYPE); DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .method(isDeclaredBy(Foo.class)) .intercept(MethodDelegation.to(originMethodType)) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Foo instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(instance.foo(), instanceOf((Class<?>) originMethodType.getDeclaredField(TYPE).get(null))); } @Test(expected = IllegalStateException.class) public void testOriginIllegal() throws Exception { new ByteBuddy() .subclass(Foo.class) .method(isDeclaredBy(Foo.class)) .intercept(MethodDelegation.to(OriginIllegal.class)) .make(); } public static class Foo { public Object foo() { return null; } } public static class OriginClass { public static Object foo(@Origin Class<?> type) { return type; } } public static class OriginMethod { public static Object foo(@Origin(cache = false) Method method) { return method; } } public static class OriginMethodWithCache { public static Object foo(@Origin(cache = true) Method method) { return method; } } public static class OriginConstructor { private Constructor<?> constructor; public void foo(@Origin(cache = false) Constructor<?> constructor) { this.constructor = constructor; } } public static class OriginConstructorWithCache { private Constructor<?> constructor; public void foo(@Origin(cache = true) Constructor<?> constructor) { this.constructor = constructor; } } public static class OriginString { public static Object foo(@Origin String string) { return string; } } public static class OriginIllegal { public static Object foo(@Origin Object object) { return object; } } }