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.Argument; import net.bytebuddy.implementation.bind.annotation.FieldProxy; import org.junit.Before; import org.junit.Test; import java.io.Serializable; import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; public class MethodDelegationFieldProxyTest { private static final String FOO = "foo", BAR = "bar", QUX = "qux"; @Before public void setUp() throws Exception { ExplicitStatic.foo = FOO; } @Test public void testExplicitFieldAccessSimplex() throws Exception { DynamicType.Loaded<Explicit> loaded = new ByteBuddy() .subclass(Explicit.class) .method(isDeclaredBy(Explicit.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(Get.class, Set.class)).to(Swap.class)) .make() .load(Explicit.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Explicit explicit = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(explicit.foo, is(FOO)); explicit.swap(); assertThat(explicit.foo, is(FOO + BAR)); } @Test public void testExplicitFieldAccessDuplex() throws Exception { DynamicType.Loaded<Explicit> loaded = new ByteBuddy() .subclass(Explicit.class) .method(isDeclaredBy(Explicit.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(GetSet.class)).to(SwapDuplex.class)) .make() .load(Explicit.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Explicit explicit = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(explicit.foo, is(FOO)); explicit.swap(); assertThat(explicit.foo, is(FOO + BAR)); } @Test public void testExplicitFieldAccessSerializableSimplex() throws Exception { DynamicType.Loaded<Explicit> loaded = new ByteBuddy() .subclass(Explicit.class) .method(isDeclaredBy(Explicit.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(Get.class, Set.class)).to(SwapSerializable.class)) .make() .load(Explicit.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Explicit explicit = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(explicit.foo, is(FOO)); explicit.swap(); assertThat(explicit.foo, is(FOO + BAR)); } @Test public void testExplicitFieldAccessSerializableDuplex() throws Exception { DynamicType.Loaded<Explicit> loaded = new ByteBuddy() .subclass(Explicit.class) .method(isDeclaredBy(Explicit.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(GetSet.class)).to(SwapSerializableDuplex.class)) .make() .load(Explicit.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Explicit explicit = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(explicit.foo, is(FOO)); explicit.swap(); assertThat(explicit.foo, is(FOO + BAR)); } @Test public void testExplicitFieldAccessStatic() throws Exception { DynamicType.Loaded<ExplicitStatic> loaded = new ByteBuddy() .subclass(ExplicitStatic.class) .method(isDeclaredBy(ExplicitStatic.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(Get.class, Set.class)).to(Swap.class)) .make() .load(ExplicitStatic.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); ExplicitStatic explicit = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(ExplicitStatic.foo, is(FOO)); explicit.swap(); assertThat(ExplicitStatic.foo, is(FOO + BAR)); } @Test public void testExplicitFieldAccessStaticDuplex() throws Exception { DynamicType.Loaded<ExplicitStatic> loaded = new ByteBuddy() .subclass(ExplicitStatic.class) .method(isDeclaredBy(ExplicitStatic.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(GetSet.class)).to(SwapDuplex.class)) .make() .load(ExplicitStatic.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); ExplicitStatic explicit = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(ExplicitStatic.foo, is(FOO)); explicit.swap(); assertThat(ExplicitStatic.foo, is(FOO + BAR)); } @Test public void testImplicitFieldGetterAccess() throws Exception { DynamicType.Loaded<ImplicitGetter> loaded = new ByteBuddy() .subclass(ImplicitGetter.class) .method(isDeclaredBy(ImplicitGetter.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(Get.class, Set.class)).to(GetInterceptor.class)) .make() .load(ImplicitGetter.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); ImplicitGetter implicitGetter = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(implicitGetter.foo, is(FOO)); assertThat(implicitGetter.getFoo(), is(FOO + BAR)); assertThat(implicitGetter.foo, is(FOO + BAR)); } @Test public void testImplicitFieldSetterAccess() throws Exception { DynamicType.Loaded<ImplicitSetter> loaded = new ByteBuddy() .subclass(ImplicitSetter.class) .method(isDeclaredBy(ImplicitSetter.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(Get.class, Set.class)).to(SetInterceptor.class)) .make() .load(ImplicitSetter.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); ImplicitSetter implicitSetter = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(implicitSetter.foo, is(FOO)); implicitSetter.setFoo(BAR); assertThat(implicitSetter.foo, is(FOO + BAR)); } @Test public void testImplicitFieldGetterAccessDuplex() throws Exception { DynamicType.Loaded<ImplicitGetter> loaded = new ByteBuddy() .subclass(ImplicitGetter.class) .method(isDeclaredBy(ImplicitGetter.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(GetSet.class)).to(GetInterceptorDuplex.class)) .make() .load(ImplicitGetter.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); ImplicitGetter implicitGetter = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(implicitGetter.foo, is(FOO)); assertThat(implicitGetter.getFoo(), is(FOO + BAR)); assertThat(implicitGetter.foo, is(FOO + BAR)); } @Test public void testImplicitFieldSetterAccessDuplex() throws Exception { DynamicType.Loaded<ImplicitSetter> loaded = new ByteBuddy() .subclass(ImplicitSetter.class) .method(isDeclaredBy(ImplicitSetter.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(GetSet.class)).to(SetInterceptorDuplex.class)) .make() .load(ImplicitSetter.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); ImplicitSetter implicitSetter = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(implicitSetter.foo, is(FOO)); implicitSetter.setFoo(BAR); assertThat(implicitSetter.foo, is(FOO + BAR)); } @Test public void testExplicitFieldAccessImplicitTarget() throws Exception { DynamicType.Loaded<ExplicitInherited> loaded = new ByteBuddy() .subclass(ExplicitInherited.class) .method(isDeclaredBy(ExplicitInherited.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(Get.class, Set.class)).to(Swap.class)) .make() .load(ExplicitInherited.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); ExplicitInherited explicitInherited = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(((Explicit) explicitInherited).foo, is(FOO)); assertThat(explicitInherited.foo, is(QUX)); explicitInherited.swap(); assertThat(((Explicit) explicitInherited).foo, is(FOO)); assertThat(explicitInherited.foo, is(QUX + BAR)); } @Test public void testExplicitFieldAccessExplicitTarget() throws Exception { DynamicType.Loaded<ExplicitInherited> loaded = new ByteBuddy() .subclass(ExplicitInherited.class) .method(isDeclaredBy(ExplicitInherited.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(Get.class, Set.class)).to(SwapInherited.class)) .make() .load(ExplicitInherited.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); ExplicitInherited explicitInherited = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(((Explicit) explicitInherited).foo, is(FOO)); assertThat(explicitInherited.foo, is(QUX)); explicitInherited.swap(); assertThat(((Explicit) explicitInherited).foo, is(FOO + BAR)); assertThat(explicitInherited.foo, is(QUX)); } @Test(expected = IllegalArgumentException.class) public void testFinalFieldSimplex() throws Exception { new ByteBuddy() .subclass(FinalField.class) .method(isDeclaredBy(FinalField.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(Get.class, Set.class)).to(Swap.class)) .make(); } @Test(expected = UnsupportedOperationException.class) public void testFinalFieldDuplex() throws Exception { DynamicType.Loaded<FinalField> loaded = new ByteBuddy() .subclass(FinalField.class) .method(isDeclaredBy(FinalField.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(GetSet.class)).to(SwapDuplex.class)) .make() .load(FinalField.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); loaded.getLoaded().getDeclaredConstructor().newInstance().method(); } @Test(expected = ClassCastException.class) public void testIncompatibleGetterTypeThrowsException() throws Exception { DynamicType.Loaded<Explicit> loaded = new ByteBuddy() .subclass(Explicit.class) .method(isDeclaredBy(Explicit.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(Get.class, Set.class)).to(GetterIncompatible.class)) .make() .load(Explicit.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Explicit explicit = loaded.getLoaded().getDeclaredConstructor().newInstance(); explicit.swap(); } @Test(expected = ClassCastException.class) public void testIncompatibleSetterTypeThrowsException() throws Exception { DynamicType.Loaded<Explicit> loaded = new ByteBuddy() .subclass(Explicit.class) .method(isDeclaredBy(Explicit.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(Get.class, Set.class)).to(SetterIncompatible.class)) .make() .load(Explicit.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Explicit explicit = loaded.getLoaded().getDeclaredConstructor().newInstance(); explicit.swap(); } @Test(expected = ClassCastException.class) public void testIncompatibleTypeThrowsExceptionGetDuplex() throws Exception { DynamicType.Loaded<Explicit> loaded = new ByteBuddy() .subclass(Explicit.class) .method(isDeclaredBy(Explicit.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(GetSet.class)).to(GetterIncompatibleDuplex.class)) .make() .load(Explicit.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Explicit explicit = loaded.getLoaded().getDeclaredConstructor().newInstance(); explicit.swap(); } @Test(expected = ClassCastException.class) public void testIncompatibleTypeThrowsExceptionSetDuplex() throws Exception { DynamicType.Loaded<Explicit> loaded = new ByteBuddy() .subclass(Explicit.class) .method(isDeclaredBy(Explicit.class)) .intercept(MethodDelegation.withDefaultConfiguration().withBinders(FieldProxy.Binder.install(GetSet.class)).to(SetterIncompatibleDuplex.class)) .make() .load(Explicit.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); Explicit explicit = loaded.getLoaded().getDeclaredConstructor().newInstance(); explicit.swap(); } @Test(expected = IllegalArgumentException.class) public void testGetterTypeDoesNotDeclareCorrectMethodThrowsException() throws Exception { FieldProxy.Binder.install(Serializable.class, Set.class); } @Test(expected = IllegalArgumentException.class) public void testSetterTypeDoesNotDeclareCorrectMethodThrowsException() throws Exception { FieldProxy.Binder.install(Get.class, Serializable.class); } @Test(expected = IllegalArgumentException.class) public void testGetterTypeDoesInheritFromOtherTypeThrowsException() throws Exception { FieldProxy.Binder.install(GetInherited.class, Set.class); } @Test(expected = IllegalArgumentException.class) public void testSetterTypeDoesInheritFromOtherTypeThrowsException() throws Exception { FieldProxy.Binder.install(Get.class, SetInherited.class); } @Test(expected = IllegalArgumentException.class) public void testGetterTypeNonPublicThrowsException() throws Exception { FieldProxy.Binder.install(GetPrivate.class, Set.class); } @Test(expected = IllegalArgumentException.class) public void testSetterTypeNonPublicThrowsException() throws Exception { FieldProxy.Binder.install(Get.class, SetPrivate.class); } @Test(expected = IllegalArgumentException.class) public void testSetterTypeIncorrectSignatureThrowsException() throws Exception { FieldProxy.Binder.install(GetIncorrect.class, Set.class); } @Test(expected = IllegalArgumentException.class) public void testGetterTypeIncorrectSignatureThrowsException() throws Exception { FieldProxy.Binder.install(Get.class, SetIncorrect.class); } @Test(expected = IllegalArgumentException.class) public void testGetterTypeNotInterfaceThrowsException() throws Exception { FieldProxy.Binder.install(Object.class, Set.class); } @Test(expected = IllegalArgumentException.class) public void testSetterTypeNotInterfaceThrowsException() throws Exception { FieldProxy.Binder.install(Get.class, Object.class); } @Test(expected = IllegalArgumentException.class) public void testDuplexTooManyMethodsThrowsException() throws Exception { FieldProxy.Binder.install(GetSetTooManyMethods.class); } @Test(expected = IllegalArgumentException.class) public void testDuplexNonPublicThrowsException() throws Exception { FieldProxy.Binder.install(GetSetNonPublic.class); } @Test(expected = IllegalArgumentException.class) public void testDuplexInheritedThrowsException() throws Exception { FieldProxy.Binder.install(GetSetInherited.class); } @Test(expected = IllegalArgumentException.class) public void testSetterTypeIncorrectSignatureDuplexThrowsException() throws Exception { FieldProxy.Binder.install(GetSetSetIncorrect.class); } @Test(expected = IllegalArgumentException.class) public void testGetterTypeIncorrectSignatureDuplexThrowsException() throws Exception { FieldProxy.Binder.install(GetSetGetIncorrect.class); } @Test(expected = IllegalArgumentException.class) public void testTypeNotInterfaceDuplexThrowsException() throws Exception { FieldProxy.Binder.install(Object.class); } public interface Get<T> { T get(); } public interface Set<T> { void set(T value); } public interface GetInherited<T> extends Get<T> { /* empty */ } public interface SetInherited<T> extends Set<T> { /* empty */ } private interface GetPrivate<T> { T get(); } private interface SetPrivate<T> { void set(T value); } public interface GetIncorrect<T> { T get(Object value); } public interface SetIncorrect<T> { Object set(T value); } public interface GetSet<T> { T get(); void set(T value); } public interface GetSetGetIncorrect<T> { String get(); void set(T value); } public interface GetSetSetIncorrect<T> { T get(); void set(String value); } interface GetSetNonPublic<T> { T get(); void set(T value); } public interface GetSetTooManyMethods<T> { T get(); void set(String value); void set(T value); } public interface GetSetInherited<T> extends GetSet<T> { /* empty */ } public static class Swap { public static void swap(@FieldProxy(FOO) Get<String> getter, @FieldProxy(FOO) Set<String> setter) { assertThat(getter, not(instanceOf(Serializable.class))); assertThat(setter, not(instanceOf(Serializable.class))); setter.set(getter.get() + BAR); } } public static class SwapDuplex { public static void swap(@FieldProxy(FOO) GetSet<String> accessor) { assertThat(accessor, not(instanceOf(Serializable.class))); accessor.set(accessor.get() + BAR); } } public static class Explicit { protected String foo = FOO; public void swap() { /* do nothing */ } } public static class ExplicitInherited extends Explicit { protected String foo = QUX; @Override public void swap() { /* do nothing */ } } public static class SwapInherited { public static void swap(@FieldProxy(value = FOO, declaringType = Explicit.class) Get<String> getter, @FieldProxy(value = FOO, declaringType = Explicit.class) Set<String> setter) { assertThat(getter, not(instanceOf(Serializable.class))); assertThat(setter, not(instanceOf(Serializable.class))); setter.set(getter.get() + BAR); } } public static class ExplicitStatic { protected static String foo; public void swap() { /* do nothing */ } } public static class ImplicitGetter { protected String foo = FOO; public String getFoo() { return null; } } public static class GetInterceptor { public static String get(@FieldProxy Get<String> getter, @FieldProxy Set<String> setter) { setter.set(getter.get() + BAR); return getter.get(); } } public static class SetInterceptor { public static void set(@Argument(0) String value, @FieldProxy Get<String> getter, @FieldProxy Set<String> setter) { setter.set(getter.get() + value); } } public static class GetInterceptorDuplex { public static String get(@FieldProxy GetSet<String> accessor) { accessor.set(accessor.get() + BAR); return accessor.get(); } } public static class SetInterceptorDuplex { public static void set(@Argument(0) String value, @FieldProxy GetSet<String> accessor) { accessor.set(accessor.get() + value); } } public static class ImplicitSetter { protected String foo = FOO; public void setFoo(String value) { /* do nothing */ } } public static class FinalField { protected final String foo = FOO; public void method() { /* do nothing */ } } public static class SwapSerializable { public static void swap(@FieldProxy(value = FOO, serializableProxy = true) Get<String> getter, @FieldProxy(value = FOO, serializableProxy = true) Set<String> setter) { assertThat(getter, instanceOf(Serializable.class)); assertThat(setter, instanceOf(Serializable.class)); setter.set(getter.get() + BAR); } } public static class SwapSerializableDuplex { public static void swap(@FieldProxy(value = FOO, serializableProxy = true) GetSet<String> accessor) { assertThat(accessor, instanceOf(Serializable.class)); accessor.set(accessor.get() + BAR); } } public static class GetterIncompatible { public static void swap(@FieldProxy(FOO) Get<Integer> getter, @FieldProxy(FOO) Set<String> setter) { Integer value = getter.get(); } } public static class SetterIncompatible { public static void swap(@FieldProxy(FOO) Get<String> getter, @FieldProxy(FOO) Set<Integer> setter) { setter.set(0); } } public static class GetterIncompatibleDuplex { public static void swap(@FieldProxy(FOO) GetSet<Integer> accessor) { Integer value = accessor.get(); } } public static class SetterIncompatibleDuplex { public static void swap(@FieldProxy(FOO) GetSet<Integer> accessor) { accessor.set(0); } } }