package net.bytebuddy.asm;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.modifier.Ownership;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.test.utility.JavaVersionRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.MethodRule;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
public class AdviceInconsistentFrameTest {
private static final String FOO = "foo", BAR = "bar";
@Rule
public MethodRule javaVersionRule = new JavaVersionRule();
@Test(expected = IllegalStateException.class)
@JavaVersionRule.Enforce(7)
public void testFrameTooShort() throws Exception {
Class<?> type = new ByteBuddy()
.subclass(Object.class)
.defineMethod(FOO, String.class, Visibility.PUBLIC)
.intercept(new TooShortMethod())
.make()
.load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
.getLoaded();
assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) BAR));
new ByteBuddy()
.redefine(type)
.visit(Advice.to(TrivialAdvice.class).on(named(FOO)))
.make();
}
@Test(expected = IllegalStateException.class)
@JavaVersionRule.Enforce(7)
public void testFrameInconsistentThisParameter() throws Exception {
Class<?> type = new ByteBuddy()
.subclass(Object.class)
.defineMethod(FOO, String.class, Visibility.PUBLIC)
.intercept(new InconsistentThisReferenceMethod())
.make()
.load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
.getLoaded();
assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) BAR));
new ByteBuddy()
.redefine(type)
.visit(Advice.to(TrivialAdvice.class).on(named(FOO)))
.make();
}
@Test(expected = IllegalStateException.class)
@JavaVersionRule.Enforce(7)
public void testFrameInconsistentParameter() throws Exception {
Class<?> type = new ByteBuddy()
.subclass(Object.class)
.defineMethod(FOO, String.class, Visibility.PUBLIC, Ownership.STATIC)
.withParameters(Void.class)
.intercept(new InconsistentParameterReferenceMethod())
.make()
.load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
.getLoaded();
assertThat(type.getDeclaredMethod(FOO, Void.class).invoke(null, (Object) null), is((Object) BAR));
new ByteBuddy()
.redefine(type)
.visit(Advice.to(TrivialAdvice.class).on(named(FOO)))
.make();
}
@SuppressWarnings("all")
private static class TrivialAdvice {
@Advice.OnMethodEnter
private static void exit() {
/* empty */
}
}
private static class TooShortMethod 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.visitFrame(Opcodes.F_FULL, 0, new Object[0], 0, new Object[0]);
methodVisitor.visitLdcInsn(BAR);
methodVisitor.visitInsn(Opcodes.ARETURN);
return new Size(1, 2);
}
}
private static class InconsistentThisReferenceMethod 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.visitFrame(Opcodes.F_FULL, 1, new Object[] {TypeDescription.OBJECT.getInternalName()}, 0, new Object[0]);
methodVisitor.visitLdcInsn(BAR);
methodVisitor.visitInsn(Opcodes.ARETURN);
return new Size(1, 2);
}
}
private static class InconsistentParameterReferenceMethod 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.visitFrame(Opcodes.F_FULL, 1, new Object[] {TypeDescription.OBJECT.getInternalName()}, 0, new Object[0]);
methodVisitor.visitLdcInsn(BAR);
methodVisitor.visitInsn(Opcodes.ARETURN);
return new Size(1, 2);
}
}
}