package net.bytebuddy.implementation; import net.bytebuddy.ByteBuddy; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.test.utility.CallTraceable; import org.hamcrest.CoreMatchers; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collection; import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @RunWith(Parameterized.class) public class FieldAccessorTest<T extends CallTraceable, S extends CallTraceable, U extends CallTraceable, V extends CallTraceable, X extends CallTraceable, Y extends CallTraceable> { private static final String FOO = "foo"; private static final String GET = "get", SET = "set"; private static final Object STATIC_FIELD = null; private static final String STRING_VALUE = "qux"; private static final boolean BOOLEAN_VALUE = true; private static final byte BYTE_VALUE = 42; private static final short SHORT_VALUE = 42; private static final char CHAR_VALUE = '@'; private static final int INT_VALUE = 42; private static final long LONG_VALUE = 42L; private static final float FLOAT_VALUE = 42f; private static final double DOUBLE_VALUE = 42d; private static final String STRING_DEFAULT_VALUE = "baz"; private static final boolean BOOLEAN_DEFAULT_VALUE = false; private static final byte BYTE_DEFAULT_VALUE = 0; private static final short SHORT_DEFAULT_VALUE = 0; private static final char CHAR_DEFAULT_VALUE = 0; private static final int INT_DEFAULT_VALUE = 0; private static final long LONG_DEFAULT_VALUE = 0L; private static final float FLOAT_DEFAULT_VALUE = 0f; private static final double DOUBLE_DEFAULT_VALUE = 0d; private final Object value; private final Class<T> instanceGetter; private final Class<S> instanceSetter; private final Class<U> staticGetter; private final Class<V> staticSetter; private final Class<?> propertyType; public FieldAccessorTest(Object value, Class<T> instanceGetter, Class<S> instanceSetter, Class<U> staticGetter, Class<V> staticSetter, Class<?> propertyType) { this.value = value; this.instanceGetter = instanceGetter; this.instanceSetter = instanceSetter; this.staticGetter = staticGetter; this.staticSetter = staticSetter; this.propertyType = propertyType; } @Parameterized.Parameters public static Collection<Object[]> data() { return Arrays.asList(new Object[][]{ {BOOLEAN_VALUE, BooleanInstanceGetter.class, BooleanInstanceSetter.class, BooleanClassGetter.class, BooleanClassSetter.class, boolean.class}, {BYTE_VALUE, ByteInstanceGetter.class, ByteInstanceSetter.class, ByteClassGetter.class, ByteClassSetter.class, byte.class}, {SHORT_VALUE, ShortInstanceGetter.class, ShortInstanceSetter.class, ShortClassGetter.class, ShortClassSetter.class, short.class}, {CHAR_VALUE, CharacterInstanceGetter.class, CharacterInstanceSetter.class, CharacterClassGetter.class, CharacterClassSetter.class, char.class}, {INT_VALUE, IntegerInstanceGetter.class, IntegerInstanceSetter.class, IntegerClassGetter.class, IntegerClassSetter.class, int.class}, {LONG_VALUE, LongInstanceGetter.class, LongInstanceSetter.class, LongClassGetter.class, LongClassSetter.class, long.class}, {FLOAT_VALUE, FloatInstanceGetter.class, FloatInstanceSetter.class, FloatClassGetter.class, FloatClassSetter.class, float.class}, {DOUBLE_VALUE, DoubleInstanceGetter.class, DoubleInstanceSetter.class, DoubleClassGetter.class, DoubleClassSetter.class, double.class}, {STRING_VALUE, ObjectInstanceGetter.class, ObjectInstanceSetter.class, ObjectClassGetter.class, ObjectClassSetter.class, Object.class} }); } @Test public void testInstanceGetterBeanProperty() throws Exception { testGetter(instanceGetter, FieldAccessor.ofBeanProperty()); } @Test public void testStaticGetterBeanProperty() throws Exception { testGetter(staticGetter, FieldAccessor.ofBeanProperty()); } @Test public void testInstanceGetterExplicit() throws Exception { testGetter(instanceGetter, FieldAccessor.ofField(FOO)); } @Test public void testStaticGetterExplicit() throws Exception { testGetter(staticGetter, FieldAccessor.ofField(FOO)); } @Test public void testInstanceGetterField() throws Exception { testGetter(instanceGetter, FieldAccessor.of(instanceGetter.getDeclaredField(FOO))); } @Test public void testStaticGetterField() throws Exception { testGetter(staticGetter, FieldAccessor.of(staticGetter.getDeclaredField(FOO))); } @Test public void testInstanceSetterBeanProperty() throws Exception { testSetter(instanceSetter, FieldAccessor.ofBeanProperty()); } @Test public void testStaticSetterBeanProperty() throws Exception { testSetter(staticSetter, FieldAccessor.ofBeanProperty()); } @Test public void testInstanceSetterExplicit() throws Exception { testSetter(instanceSetter, FieldAccessor.ofField(FOO)); } @Test public void testStaticSetterExplicit() throws Exception { testSetter(staticSetter, FieldAccessor.ofField(FOO)); } @Test public void testStaticSetterField() throws Exception { testSetter(staticSetter, FieldAccessor.of(staticSetter.getDeclaredField(FOO))); } @Test public void testInstanceSetterField() throws Exception { testSetter(instanceSetter, FieldAccessor.of(instanceSetter.getDeclaredField(FOO))); } @SuppressWarnings("unchecked") private <Z extends CallTraceable> void testGetter(Class<Z> target, Implementation implementation) throws Exception { DynamicType.Loaded<Z> loaded = new ByteBuddy() .subclass(target) .method(isDeclaredBy(target)) .intercept(implementation) .make() .load(target.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); assertThat(loaded.getLoadedAuxiliaryTypes().size(), is(0)); assertThat(loaded.getLoaded().getDeclaredMethods().length, is(1)); assertThat(loaded.getLoaded().getDeclaredFields().length, is(0)); Z instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(instance.getClass(), not(CoreMatchers.<Class<?>>is(target))); assertThat(instance, instanceOf(target)); Method getter = loaded.getLoaded() .getDeclaredMethod(GET + Character.toUpperCase(FOO.charAt(0)) + FOO.substring(1)); assertThat(getter.invoke(instance), is(value)); instance.assertZeroCalls(); assertFieldValue(target, instance); } @SuppressWarnings("unchecked") private <Z extends CallTraceable> void testSetter(Class<Z> target, Implementation implementation) throws Exception { DynamicType.Loaded<Z> loaded = new ByteBuddy() .subclass(target) .method(isDeclaredBy(target)) .intercept(implementation) .make() .load(target.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER); assertThat(loaded.getLoadedAuxiliaryTypes().size(), is(0)); assertThat(loaded.getLoaded().getDeclaredMethods().length, is(1)); assertThat(loaded.getLoaded().getDeclaredFields().length, is(0)); Z instance = loaded.getLoaded().getDeclaredConstructor().newInstance(); assertThat(instance.getClass(), not(CoreMatchers.<Class<?>>is(target))); assertThat(instance, instanceOf(target)); Method setter = loaded.getLoaded() .getDeclaredMethod(SET + Character.toUpperCase(FOO.charAt(0)) + FOO.substring(1), propertyType); assertThat(setter.invoke(instance, value), nullValue()); instance.assertZeroCalls(); assertFieldValue(target, instance); } private void assertFieldValue(Class<?> fieldHolder, Object instance) throws Exception { Field field = fieldHolder.getDeclaredField(FOO); boolean isStatic = (Modifier.STATIC & field.getModifiers()) != 0; Object fieldValue = isStatic ? field.get(STATIC_FIELD) : field.get(instance); assertThat(fieldValue, is(value)); } public static class BooleanInstanceGetter extends CallTraceable { protected boolean foo = BOOLEAN_VALUE; public boolean getFoo() { register(FOO); return BOOLEAN_DEFAULT_VALUE; } } public static class BooleanInstanceSetter extends CallTraceable { protected boolean foo = BOOLEAN_DEFAULT_VALUE; public void setFoo(boolean foo) { register(FOO, foo); } } public static class BooleanClassGetter extends CallTraceable { protected static boolean foo = BOOLEAN_VALUE; public boolean getFoo() { register(FOO); return BOOLEAN_DEFAULT_VALUE; } } public static class BooleanClassSetter extends CallTraceable { protected static boolean foo = BOOLEAN_DEFAULT_VALUE; public void setFoo(boolean foo) { register(FOO, foo); } } public static class ByteInstanceGetter extends CallTraceable { protected byte foo = BYTE_VALUE; public byte getFoo() { register(FOO); return BYTE_DEFAULT_VALUE; } } public static class ByteInstanceSetter extends CallTraceable { protected byte foo = BYTE_DEFAULT_VALUE; public void setFoo(byte foo) { register(FOO, foo); } } public static class ByteClassGetter extends CallTraceable { protected static byte foo = BYTE_VALUE; public byte getFoo() { register(FOO); return BYTE_DEFAULT_VALUE; } } public static class ByteClassSetter extends CallTraceable { protected static byte foo = BYTE_DEFAULT_VALUE; public void setFoo(byte foo) { register(FOO, foo); } } public static class ShortInstanceGetter extends CallTraceable { protected short foo = SHORT_VALUE; public short getFoo() { register(FOO); return SHORT_DEFAULT_VALUE; } } public static class ShortInstanceSetter extends CallTraceable { protected short foo = SHORT_DEFAULT_VALUE; public void setFoo(short foo) { register(FOO, foo); } } public static class ShortClassGetter extends CallTraceable { protected static short foo = SHORT_VALUE; public short getFoo() { register(FOO); return SHORT_DEFAULT_VALUE; } } public static class ShortClassSetter extends CallTraceable { protected static short foo = SHORT_DEFAULT_VALUE; public void setFoo(short foo) { register(FOO, foo); } } public static class IntegerInstanceGetter extends CallTraceable { protected int foo = INT_VALUE; public int getFoo() { register(FOO); return INT_DEFAULT_VALUE; } } public static class IntegerInstanceSetter extends CallTraceable { protected int foo = INT_DEFAULT_VALUE; public void setFoo(int foo) { register(FOO, foo); } } public static class IntegerClassGetter extends CallTraceable { protected static int foo = INT_VALUE; public int getFoo() { register(FOO); return INT_DEFAULT_VALUE; } } public static class IntegerClassSetter extends CallTraceable { protected static int foo = INT_DEFAULT_VALUE; public void setFoo(int foo) { register(FOO, foo); } } public static class CharacterInstanceGetter extends CallTraceable { protected char foo = CHAR_VALUE; public char getFoo() { register(FOO); return CHAR_DEFAULT_VALUE; } } public static class CharacterInstanceSetter extends CallTraceable { protected char foo = CHAR_DEFAULT_VALUE; public void setFoo(char foo) { register(FOO, foo); } } public static class CharacterClassGetter extends CallTraceable { protected static char foo = CHAR_VALUE; public char getFoo() { register(FOO); return CHAR_DEFAULT_VALUE; } } public static class CharacterClassSetter extends CallTraceable { protected static char foo = CHAR_DEFAULT_VALUE; public void setFoo(char foo) { register(FOO, foo); } } public static class LongInstanceGetter extends CallTraceable { protected long foo = LONG_VALUE; public long getFoo() { register(FOO); return LONG_DEFAULT_VALUE; } } public static class LongInstanceSetter extends CallTraceable { protected long foo = LONG_DEFAULT_VALUE; public void setFoo(long foo) { register(FOO, foo); } } public static class LongClassGetter extends CallTraceable { protected static long foo = LONG_VALUE; public long getFoo() { register(FOO); return LONG_DEFAULT_VALUE; } } public static class LongClassSetter extends CallTraceable { protected static long foo = LONG_DEFAULT_VALUE; public void setFoo(long foo) { register(FOO, foo); } } public static class FloatInstanceGetter extends CallTraceable { protected float foo = FLOAT_VALUE; public float getFoo() { register(FOO); return FLOAT_DEFAULT_VALUE; } } public static class FloatInstanceSetter extends CallTraceable { protected float foo = FLOAT_DEFAULT_VALUE; public void setFoo(float foo) { register(FOO, foo); } } public static class FloatClassGetter extends CallTraceable { protected static float foo = FLOAT_VALUE; public float getFoo() { register(FOO); return FLOAT_DEFAULT_VALUE; } } public static class FloatClassSetter extends CallTraceable { protected static float foo = FLOAT_DEFAULT_VALUE; public void setFoo(float foo) { register(FOO, foo); } } public static class DoubleInstanceGetter extends CallTraceable { protected double foo = DOUBLE_VALUE; public double getFoo() { register(FOO); return DOUBLE_DEFAULT_VALUE; } } public static class DoubleInstanceSetter extends CallTraceable { protected double foo = DOUBLE_DEFAULT_VALUE; public void setFoo(double foo) { register(FOO, foo); } } public static class DoubleClassGetter extends CallTraceable { protected static double foo = DOUBLE_VALUE; public double getFoo() { register(FOO); return DOUBLE_DEFAULT_VALUE; } } public static class DoubleClassSetter extends CallTraceable { protected static double foo = DOUBLE_DEFAULT_VALUE; public void setFoo(double foo) { register(FOO, foo); } } public static class ObjectInstanceGetter extends CallTraceable { protected Object foo = STRING_VALUE; public Object getFoo() { register(FOO); return STRING_DEFAULT_VALUE; } } public static class ObjectInstanceSetter extends CallTraceable { protected Object foo = STRING_DEFAULT_VALUE; public void setFoo(Object foo) { register(FOO, foo); } } public static class ObjectClassGetter extends CallTraceable { protected static Object foo = STRING_VALUE; public Object getFoo() { register(FOO); return STRING_DEFAULT_VALUE; } } public static class ObjectClassSetter extends CallTraceable { protected static Object foo = STRING_DEFAULT_VALUE; public void setFoo(Object foo) { register(FOO, foo); } } }