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.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 FixedValueConstantPoolTypesTest<T extends CallTraceable> {
private static final String FOO = "foo", BAR = "bar";
private static final String STRING_VALUE = "foo";
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 Void NULL_VALUE = null;
private static final String STRING_DEFAULT_VALUE = "bar";
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 fixedValue;
private final Class<T> helperClass;
public FixedValueConstantPoolTypesTest(Object fixedValue, Class<T> helperClass) {
this.fixedValue = fixedValue;
this.helperClass = helperClass;
}
@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{STRING_VALUE, StringTarget.class},
{BOOLEAN_VALUE, BooleanTarget.class},
{BYTE_VALUE, ByteTarget.class},
{SHORT_VALUE, ShortTarget.class},
{CHAR_VALUE, CharTarget.class},
{INT_VALUE, IntTarget.class},
{LONG_VALUE, LongTarget.class},
{FLOAT_VALUE, FloatTarget.class},
{DOUBLE_VALUE, DoubleTarget.class}
});
}
@Test
public void testConstantPool() throws Exception {
DynamicType.Loaded<T> loaded = new ByteBuddy()
.subclass(helperClass)
.method(isDeclaredBy(helperClass))
.intercept(FixedValue.value(fixedValue))
.make()
.load(helperClass.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER);
assertThat(loaded.getLoadedAuxiliaryTypes().size(), is(0));
assertThat(loaded.getLoaded().getDeclaredMethods().length, is(2));
assertThat(loaded.getLoaded().getDeclaredFields().length, is(0));
T instance = loaded.getLoaded().getDeclaredConstructor().newInstance();
assertThat(instance.getClass(), not(CoreMatchers.<Class<?>>is(StringTarget.class)));
assertThat(instance, instanceOf(helperClass));
assertThat(loaded.getLoaded().getDeclaredMethod(FOO).invoke(instance), is(fixedValue));
assertThat(loaded.getLoaded().getDeclaredMethod(BAR).invoke(instance), is(fixedValue));
instance.assertZeroCalls();
}
@Test
public void testStaticField() throws Exception {
DynamicType.Loaded<T> loaded = new ByteBuddy()
.subclass(helperClass)
.method(isDeclaredBy(helperClass))
.intercept(FixedValue.reference(fixedValue))
.make()
.load(helperClass.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER);
assertThat(loaded.getLoadedAuxiliaryTypes().size(), is(0));
assertThat(loaded.getLoaded().getDeclaredMethods().length, is(2));
assertThat(loaded.getLoaded().getDeclaredFields().length, is(fixedValue == null ? 0 : 1));
T instance = loaded.getLoaded().getDeclaredConstructor().newInstance();
assertThat(instance.getClass(), not(CoreMatchers.<Class<?>>is(StringTarget.class)));
assertThat(instance, instanceOf(helperClass));
assertThat(loaded.getLoaded().getDeclaredMethod(FOO).invoke(instance), is(fixedValue));
assertThat(loaded.getLoaded().getDeclaredMethod(BAR).invoke(instance), is(fixedValue));
instance.assertZeroCalls();
}
@SuppressWarnings("unused")
public static class StringTarget extends CallTraceable {
public String foo() {
register(FOO);
return STRING_DEFAULT_VALUE;
}
public Object bar() {
register(BAR);
return STRING_DEFAULT_VALUE;
}
}
@SuppressWarnings("unused")
public static class BooleanTarget extends CallTraceable {
public boolean foo() {
register(FOO);
return BOOLEAN_DEFAULT_VALUE;
}
public Boolean bar() {
register(BAR);
return BOOLEAN_DEFAULT_VALUE;
}
}
@SuppressWarnings("unused")
public static class ByteTarget extends CallTraceable {
public byte foo() {
register(FOO);
return BYTE_DEFAULT_VALUE;
}
public Byte bar() {
register(BAR);
return BYTE_DEFAULT_VALUE;
}
}
@SuppressWarnings("unused")
public static class ShortTarget extends CallTraceable {
public short foo() {
register(FOO);
return SHORT_DEFAULT_VALUE;
}
public Short bar() {
register(BAR);
return SHORT_DEFAULT_VALUE;
}
}
@SuppressWarnings("unused")
public static class CharTarget extends CallTraceable {
public char foo() {
register(FOO);
return CHAR_DEFAULT_VALUE;
}
public Character bar() {
register(BAR);
return CHAR_DEFAULT_VALUE;
}
}
@SuppressWarnings("unused")
public static class IntTarget extends CallTraceable {
public int foo() {
register(FOO);
return INT_DEFAULT_VALUE;
}
public Integer bar() {
register(BAR);
return INT_DEFAULT_VALUE;
}
}
@SuppressWarnings("unused")
public static class LongTarget extends CallTraceable {
public long foo() {
register(FOO);
return LONG_DEFAULT_VALUE;
}
public Long bar() {
register(BAR);
return LONG_DEFAULT_VALUE;
}
}
@SuppressWarnings("unused")
public static class FloatTarget extends CallTraceable {
public float foo() {
register(FOO);
return FLOAT_DEFAULT_VALUE;
}
public Float bar() {
register(BAR);
return FLOAT_DEFAULT_VALUE;
}
}
@SuppressWarnings("unused")
public static class DoubleTarget extends CallTraceable {
public double foo() {
register(FOO);
return DOUBLE_DEFAULT_VALUE;
}
public Double bar() {
register(BAR);
return DOUBLE_DEFAULT_VALUE;
}
}
}