package net.bytebuddy.implementation; import net.bytebuddy.ByteBuddy; import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.implementation.bind.annotation.DefaultCall; 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 org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; public class MethodDelegationDefaultCallTest { private static final String FOO = "foo", QUX = "qux"; 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 PREFERRING_INTERCEPTOR = "net.bytebuddy.test.precompiled.SingleDefaultMethodPreferringInterceptor"; private static final String CONFLICTING_PREFERRING_INTERCEPTOR = "net.bytebuddy.test.precompiled.SingleDefaultMethodConflictingPreferringInterceptor"; @Rule public MethodRule javaVersionRule = new JavaVersionRule(); @Test @JavaVersionRule.Enforce(8) public void testRunnableDefaultCall() throws Exception { DynamicType.Loaded<?> loaded = new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD)) .intercept(MethodDelegation.to(RunnableClass.class)) .make() .load(Class.forName(SINGLE_DEFAULT_METHOD).getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Object instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); Method method = loaded.getLoaded().getMethod(FOO); assertThat(method.invoke(instance), is((Object) QUX)); } @Test @JavaVersionRule.Enforce(8) public void testCallableDefaultCall() throws Exception { DynamicType.Loaded<?> loaded = new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD)) .intercept(MethodDelegation.to(CallableClass.class)) .make() .load(Class.forName(SINGLE_DEFAULT_METHOD).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 testImplicitAmbiguousDefaultCallIsBoundToFirst() throws Exception { new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD), Class.forName(CONFLICTING_INTERFACE)) .defineMethod(FOO, Object.class, Visibility.PUBLIC) .intercept(MethodDelegation.to(CallableClass.class)) .make(); } @Test @JavaVersionRule.Enforce(8) public void testExplicitDefaultCall() throws Exception { DynamicType.Loaded<?> loaded = new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD), Class.forName(CONFLICTING_INTERFACE)) .intercept(MethodDelegation.to(Class.forName(PREFERRING_INTERCEPTOR))) .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 @JavaVersionRule.Enforce(8) public void testExplicitDefaultCallToOtherInterface() throws Exception { DynamicType.Loaded<?> loaded = new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD), Class.forName(CONFLICTING_INTERFACE)) .intercept(MethodDelegation.to(Class.forName(CONFLICTING_PREFERRING_INTERCEPTOR))) .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) QUX)); } @Test(expected = IllegalStateException.class) @JavaVersionRule.Enforce(8) public void testIllegalDefaultCallThrowsException() throws Exception { new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD)) .intercept(MethodDelegation.to(IllegalAnnotation.class)) .make(); } @Test @JavaVersionRule.Enforce(8) public void testSerializableProxy() throws Exception { DynamicType.Loaded<?> loaded = new ByteBuddy() .subclass(Object.class) .implement(Class.forName(SINGLE_DEFAULT_METHOD)) .intercept(MethodDelegation.to(SerializationCheck.class)) .make() .load(Class.forName(SINGLE_DEFAULT_METHOD).getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Object instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); Method method = loaded.getLoaded().getMethod(FOO); assertThat(method.invoke(instance), is((Object) FOO)); } public static class RunnableClass { public static Object foo(@DefaultCall Runnable runnable) { assertThat(runnable, CoreMatchers.not(instanceOf(Serializable.class))); runnable.run(); return QUX; } } public static class CallableClass { public static String bar(@DefaultCall Callable<String> callable) throws Exception { assertThat(callable, CoreMatchers.not(instanceOf(Serializable.class))); return callable.call(); } } public static class IllegalAnnotation { public static String bar(@DefaultCall String value) throws Exception { return value; } } public static class SerializationCheck { public static String bar(@DefaultCall(serializableProxy = true) Callable<String> callable) throws Exception { assertThat(callable, instanceOf(Serializable.class)); return callable.call(); } } }