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.RuntimeType; import net.bytebuddy.implementation.bind.annotation.SuperCall; import net.bytebuddy.test.utility.CallTraceable; import net.bytebuddy.test.utility.JavaVersionRule; import org.hamcrest.CoreMatchers; import org.junit.Rule; import org.junit.Test; import org.junit.rules.MethodRule; import java.io.Serializable; import java.lang.reflect.Method; import java.util.concurrent.Callable; import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; public class MethodDelegationSuperCallTest { private static final String SINGLE_DEFAULT_METHOD = "net.bytebuddy.test.precompiled.SingleDefaultMethodInterface"; private static final String CONFLICTING_INTERFACE = "net.bytebuddy.test.precompiled.SingleDefaultMethodConflictingInterface"; private static final String FOO = "foo", BAR = "bar"; @Rule public MethodRule javaVersionRule = new JavaVersionRule(); @Test public void testRunnableSuperCall() throws Exception { DynamicType.Loaded<Foo> loaded = new ByteBuddy() .subclass(Foo.class) .method(isDeclaredBy(Foo.class)) .intercept(MethodDelegation.to(RunnableClass.class)) .make() .load(Foo.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Foo instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(instance.value, is(BAR)); instance.foo(); assertThat(instance.value, is(FOO)); } @Test public void testCallableSuperCall() throws Exception { DynamicType.Loaded<Bar> loaded = new ByteBuddy() .subclass(Bar.class) .method(isDeclaredBy(Bar.class)) .intercept(MethodDelegation.to(CallableClass.class)) .make() .load(Bar.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Bar instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(instance.bar(), is(FOO)); } @Test public void testVoidToNonVoidSuperCall() throws Exception { DynamicType.Loaded<VoidTest> loaded = new ByteBuddy() .subclass(VoidTest.class) .method(isDeclaredBy(VoidTest.class)) .intercept(MethodDelegation.to(NonVoidTarget.class)) .make() .load(VoidTest.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); VoidTest instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); instance.foo(); instance.assertOnlyCall(FOO); } @Test public void testRuntimeTypeSuperCall() throws Exception { DynamicType.Loaded<RuntimeTypeTest> loaded = new ByteBuddy() .subclass(RuntimeTypeTest.class) .method(isDeclaredBy(RuntimeTypeTest.class)) .intercept(MethodDelegation.to(RuntimeTypeTarget.class)) .make() .load(RuntimeTypeTest.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); RuntimeTypeTest instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(instance.foo(), is(FOO)); } @Test public void testSerializableProxy() throws Exception { DynamicType.Loaded<Bar> loaded = new ByteBuddy() .subclass(Bar.class) .method(isDeclaredBy(Bar.class)) .intercept(MethodDelegation.to(SerializationCheck.class)) .make() .load(Bar.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Bar instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(instance.bar(), is(FOO)); } @Test @JavaVersionRule.Enforce(8) public void testDefaultMethodFallback() throws Exception { DynamicType.Loaded<?> loaded = new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD)) .intercept(MethodDelegation.to(NonVoidTarget.class)) .make() .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Object instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); Method method = loaded.getLoaded().getMethod(FOO); assertThat(method.invoke(instance), is((Object) FOO)); } @Test(expected = IllegalArgumentException.class) @JavaVersionRule.Enforce(8) public void testDefaultMethodFallbackDisabled() throws Exception { new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD)) .intercept(MethodDelegation.to(NoFallback.class)) .make(); } @Test(expected = IllegalArgumentException.class) @JavaVersionRule.Enforce(8) public void testDefaultMethodFallbackAmbiguous() throws Exception { new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD), Class.forName(CONFLICTING_INTERFACE)) .intercept(MethodDelegation.to(NonVoidTarget.class)) .make(); } @Test(expected = IllegalArgumentException.class) public void testAbstractMethodNonBindable() throws Exception { new ByteBuddy() .subclass(Qux.class) .method(isDeclaredBy(Qux.class)) .intercept(MethodDelegation.to(CallableClass.class)) .make(); } @Test(expected = IllegalStateException.class) public void testWrongTypeThrowsException() throws Exception { new ByteBuddy() .subclass(Bar.class) .method(isDeclaredBy(Bar.class)) .intercept(MethodDelegation.to(IllegalAnnotation.class)) .make(); } public static class Foo { public String value = BAR; public void foo() { value = FOO; } } public static class RunnableClass { public static void foo(@SuperCall Runnable runnable) { assertThat(runnable, CoreMatchers.not(instanceOf(Serializable.class))); runnable.run(); } } public static class Bar { public String bar() { return FOO; } } public static class CallableClass { public static String bar(@SuperCall Callable<String> callable) throws Exception { assertThat(callable, CoreMatchers.not(instanceOf(Serializable.class))); return callable.call(); } } public abstract static class Qux { public abstract String bar(); } public static class VoidTest extends CallTraceable { public void foo() { register(FOO); } } public static class NonVoidTarget { public static Object foo(@SuperCall Callable<?> zuper) throws Exception { return zuper.call(); } } public static class RuntimeTypeTest { public String foo() { return FOO; } } public static class RuntimeTypeTarget { @RuntimeType public static Object foo(@SuperCall Callable<?> zuper) throws Exception { return zuper.call(); } } public static class IllegalAnnotation { public static String bar(@SuperCall String value) throws Exception { return value; } } public static class SerializationCheck { public static String bar(@SuperCall(serializableProxy = true) Callable<String> callable) throws Exception { assertThat(callable, instanceOf(Serializable.class)); return callable.call(); } } @SuppressWarnings("unused") public static class NoFallback { @RuntimeType public static Object foo(@SuperCall(fallbackToDefault = false) Callable<?> zuper) throws Exception { return null; } } }