package net.bytebuddy.asm; import net.bytebuddy.ByteBuddy; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.dynamic.scaffold.InstrumentedType; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.bytecode.ByteCodeAppender; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import java.util.Arrays; import java.util.Collection; import static net.bytebuddy.matcher.ElementMatchers.named; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @RunWith(Parameterized.class) public class AdviceExchangedDuplicationTest { private static final String FOO = "foo"; private static final int NUMERIC_VALUE = 42; @Parameterized.Parameters public static Collection<Object[]> data() { return Arrays.asList( new Object[][]{ {Opcodes.DUP_X1, int.class, int.class, NUMERIC_VALUE, NUMERIC_VALUE}, {Opcodes.DUP_X2, int.class, long.class, NUMERIC_VALUE, (long) NUMERIC_VALUE}, {Opcodes.DUP2_X1, long.class, int.class, (long) NUMERIC_VALUE, NUMERIC_VALUE}, {Opcodes.DUP2_X2, long.class, long.class, (long) NUMERIC_VALUE, (long) NUMERIC_VALUE} } ); } private final int duplication; private final Class<?> valueType, ignoredValueType; private final Object value, ignoredValue; public AdviceExchangedDuplicationTest(int duplication, Class<?> valueType, Class<?> ignoredValueType, Object value, Object ignoredValue) { this.duplication = duplication; this.valueType = valueType; this.ignoredValueType = ignoredValueType; this.value = value; this.ignoredValue = ignoredValue; } @Test public void testAdvice() throws Exception { Class<?> type = new ByteBuddy() .subclass(Object.class) .defineMethod(FOO, valueType, Visibility.PUBLIC) .intercept(new DuplicationImplementation()) .make() .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER_PERSISTENT) .getLoaded(); Class<?> redefined = new ByteBuddy() .redefine(type) .visit(Advice.to(AdviceExchangedDuplicationTest.class).on(named(FOO))) .make() .load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER) .getLoaded(); assertThat(redefined.getDeclaredMethod(FOO).invoke(redefined.getDeclaredConstructor().newInstance()), is(value)); } @Advice.OnMethodExit @SuppressWarnings("unused") private static void exit() { /* empty */ } private class DuplicationImplementation implements Implementation, ByteCodeAppender { @Override public ByteCodeAppender appender(Target implementationTarget) { return this; } @Override public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; } @Override public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { methodVisitor.visitLdcInsn(ignoredValue); methodVisitor.visitLdcInsn(value); methodVisitor.visitInsn(duplication); methodVisitor.visitInsn(Type.getType(valueType).getSize() == 2 ? Opcodes.POP2 : Opcodes.POP); methodVisitor.visitInsn(Type.getType(ignoredValueType).getSize() == 2 ? Opcodes.POP2 : Opcodes.POP); methodVisitor.visitInsn(Type.getType(valueType).getOpcode(Opcodes.IRETURN)); return new Size(Type.getType(valueType).getSize() * 2 + Type.getType(ignoredValueType).getSize(), instrumentedMethod.getStackSize()); } } }