From c4689893d13f09cd754d4f18dc6db74eb0adaeb9 Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Mon, 11 Apr 2016 17:21:36 +0200 Subject: [PATCH] Added tests and documentation to exception table sensitive method visitor. --- .../main/java/net/bytebuddy/asm/Advice.java | 2 +- .../ExceptionTableSensitiveMethodVisitor.java | 169 +++++++++++++++--- ...eptionTableSensitiveMethodVisitorTest.java | 100 +++++++++++ 3 files changed, 246 insertions(+), 25 deletions(-) create mode 100644 byte-buddy-dep/src/test/java/net/bytebuddy/utility/ExceptionTableSensitiveMethodVisitorTest.java diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/asm/Advice.java b/byte-buddy-dep/src/main/java/net/bytebuddy/asm/Advice.java index b08d8fefe52..6406c33b9ea 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/asm/Advice.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/asm/Advice.java @@ -1117,7 +1117,7 @@ protected AdviceVisitor(MethodVisitor methodVisitor, } @Override - protected void onFirstCodeInstruction() { + protected void onAfterExceptionTable() { methodEnter.prepare(); onUserPrepare(); methodExit.prepare(); diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/utility/ExceptionTableSensitiveMethodVisitor.java b/byte-buddy-dep/src/main/java/net/bytebuddy/utility/ExceptionTableSensitiveMethodVisitor.java index fbfbfee8c63..90c2e27d436 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/utility/ExceptionTableSensitiveMethodVisitor.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/utility/ExceptionTableSensitiveMethodVisitor.java @@ -4,23 +4,43 @@ import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; +/** + * A {@link MethodVisitor} that adds a callback after visiting the exception table of a method. + */ public abstract class ExceptionTableSensitiveMethodVisitor extends MethodVisitor { + /** + * {@code true} if the exception table callback was already triggered. + */ private boolean trigger; + /** + * Creates an exception table sensitive method visitor. + * + * @param api The ASM API version. + * @param methodVisitor The delegating method visitor. + */ protected ExceptionTableSensitiveMethodVisitor(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); trigger = true; } + /** + * Considers if the end of the exception table was reached. + */ private void considerEndOfExceptionTable() { if (trigger) { trigger = false; - onFirstCodeInstruction(); + onAfterExceptionTable(); } } - protected abstract void onFirstCodeInstruction(); + /** + * Invoked after the exception table was visited. Typically, the exception table is visited by ASM at the beginning + * of a method. It is however possible that a user adds exception table entries at a later point. Normally, this is + * however not meaningful use of ASM. + */ + protected abstract void onAfterExceptionTable(); @Override public final void visitLabel(Label label) { @@ -28,6 +48,12 @@ public final void visitLabel(Label label) { onVisitLabel(label); } + /** + * Visits a label. + * + * @param label The visited label. + * @see MethodVisitor#visitLabel(Label) + */ protected void onVisitLabel(Label label) { super.visitLabel(label); } @@ -38,6 +64,12 @@ public final void visitIntInsn(int opcode, int operand) { onVisitIntInsn(opcode, operand); } + /** + * Visits an integer opcode. + * + * @param opcode The visited opcode. + * @param operand The visited operand. + */ protected void onVisitIntInsn(int opcode, int operand) { super.visitIntInsn(opcode, operand); } @@ -48,8 +80,14 @@ public final void visitVarInsn(int opcode, int var) { onVisitVarInsn(opcode, var); } - protected void onVisitVarInsn(int opcode, int var) { - super.visitVarInsn(opcode, var); + /** + * Visits an variable instruction. + * + * @param opcode The visited opcode. + * @param offset The visited offset. + */ + protected void onVisitVarInsn(int opcode, int offset) { + super.visitVarInsn(opcode, offset); } @Override @@ -58,6 +96,12 @@ public final void visitTypeInsn(int opcode, String type) { onVisitTypeInsn(opcode, type); } + /** + * Visits a type instruction. + * + * @param opcode The visited opcode. + * @param type The type name. + */ protected void onVisitTypeInsn(int opcode, String type) { super.visitTypeInsn(opcode, type); } @@ -68,8 +112,16 @@ public final void visitFieldInsn(int opcode, String owner, String name, String d onVisitFieldInsn(opcode, owner, name, desc); } - protected void onVisitFieldInsn(int opcode, String owner, String name, String desc) { - super.visitFieldInsn(opcode, owner, name, desc); + /** + * Visits a field instruction. + * + * @param opcode The visited opcode. + * @param owner The field's owner. + * @param name The field's name. + * @param descriptor The field's descriptor. + */ + protected void onVisitFieldInsn(int opcode, String owner, String name, String descriptor) { + super.visitFieldInsn(opcode, owner, name, descriptor); } @Override @@ -79,21 +131,39 @@ public final void visitMethodInsn(int opcode, String owner, String name, String onVisitMethodInsn(opcode, owner, name, desc); } + /** + * Visits a method instruction. + * + * @param opcode The visited opcode. + * @param owner The method's owner. + * @param name The method's internal name. + * @param descriptor The method's descriptor. + * @deprecated Use {@link ExceptionTableSensitiveMethodVisitor#onVisitMethodInsn(int, String, String, String, boolean)} instead. + */ @Deprecated @SuppressWarnings("deprecation") - protected void onVisitMethodInsn(int opcode, String owner, String name, String desc) { + protected void onVisitMethodInsn(int opcode, String owner, String name, String descriptor) { considerEndOfExceptionTable(); - super.visitMethodInsn(opcode, owner, name, desc); + super.visitMethodInsn(opcode, owner, name, descriptor); } @Override - public final void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + public final void visitMethodInsn(int opcode, String owner, String name, String desc, boolean iFace) { considerEndOfExceptionTable(); - onVisitMethodInsn(opcode, owner, name, desc, itf); + onVisitMethodInsn(opcode, owner, name, desc, iFace); } - protected void onVisitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { - super.visitMethodInsn(opcode, owner, name, desc, itf); + /** + * Visits a method instruction. + * + * @param opcode The visited opcode. + * @param owner The method's owner. + * @param name The method's internal name. + * @param descriptor The method's descriptor. + * @param iFace {@code true} if the method belongs to an interface. + */ + protected void onVisitMethodInsn(int opcode, String owner, String name, String descriptor, boolean iFace) { + super.visitMethodInsn(opcode, owner, name, descriptor, iFace); } @Override @@ -102,8 +172,16 @@ public final void visitInvokeDynamicInsn(String name, String desc, Handle bsm, O onVisitInvokeDynamicInsn(name, desc, bsm, bsmArgs); } - protected void onVisitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { - super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + /** + * Visits an invoke dynamic instruction. + * + * @param name The name of the method. + * @param descriptor The descriptor of the method. + * @param handle The bootstrap method handle. + * @param argument The bootstrap method arguments. + */ + protected void onVisitInvokeDynamicInsn(String name, String descriptor, Handle handle, Object... argument) { + super.visitInvokeDynamicInsn(name, descriptor, handle, argument); } @Override @@ -112,6 +190,12 @@ public final void visitJumpInsn(int opcode, Label label) { onVisitJumpInsn(opcode, label); } + /** + * Visits a jump instruction. + * + * @param opcode The visited opcode. + * @param label The visited label. + */ protected void onVisitJumpInsn(int opcode, Label label) { super.visitJumpInsn(opcode, label); } @@ -122,8 +206,13 @@ public final void visitLdcInsn(Object cst) { onVisitLdcInsn(cst); } - protected void onVisitLdcInsn(Object cst) { - super.visitLdcInsn(cst); + /** + * Visits a constant pool access instruction. + * + * @param constant The constant pool value. + */ + protected void onVisitLdcInsn(Object constant) { + super.visitLdcInsn(constant); } @Override @@ -132,8 +221,14 @@ public final void visitIincInsn(int var, int increment) { onVisitIincInsn(var, increment); } - protected void onVisitIincInsn(int var, int increment) { - super.visitIincInsn(var, increment); + /** + * Visits an increment instruction. + * + * @param offset The offset of the accessed variable. + * @param increment The value with which to increment. + */ + protected void onVisitIincInsn(int offset, int increment) { + super.visitIincInsn(offset, increment); } @Override @@ -142,8 +237,16 @@ public final void visitTableSwitchInsn(int min, int max, Label dflt, Label... la onVisitTableSwitchInsn(min, max, dflt, labels); } - protected void onVisitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { - super.visitTableSwitchInsn(min, max, dflt, labels); + /** + * Visits a table switch instruction. + * + * @param min The minimum index. + * @param max The maximum index. + * @param defaultTarget A label indicating the default value. + * @param label Labels indicating the jump targets. + */ + protected void onVisitTableSwitchInsn(int min, int max, Label defaultTarget, Label... label) { + super.visitTableSwitchInsn(min, max, defaultTarget, label); } @Override @@ -152,8 +255,15 @@ public final void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) onVisitLookupSwitchInsn(dflt, keys, labels); } - protected void onVisitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { - super.visitLookupSwitchInsn(dflt, keys, labels); + /** + * Visits a lookup switch instruction. + * + * @param defaultTarget The default option. + * @param keys The key values. + * @param key The targets for each key. + */ + protected void onVisitLookupSwitchInsn(Label defaultTarget, int[] keys, Label[] key) { + super.visitLookupSwitchInsn(defaultTarget, keys, key); } @Override @@ -162,8 +272,14 @@ public final void visitMultiANewArrayInsn(String desc, int dims) { onVisitMultiANewArrayInsn(desc, dims); } - protected void onVisitMultiANewArrayInsn(String desc, int dims) { - super.visitMultiANewArrayInsn(desc, dims); + /** + * Visits an instruction for creating a multidimensional array. + * + * @param descriptor The type descriptor of the array's component type. + * @param dimensions The dimensions of the array. + */ + protected void onVisitMultiANewArrayInsn(String descriptor, int dimensions) { + super.visitMultiANewArrayInsn(descriptor, dimensions); } @Override @@ -172,6 +288,11 @@ public final void visitInsn(int opcode) { onVisitInsn(opcode); } + /** + * Visits a simple instruction. + * + * @param opcode The opcode of the instruction. + */ protected void onVisitInsn(int opcode) { super.visitInsn(opcode); } diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/utility/ExceptionTableSensitiveMethodVisitorTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/utility/ExceptionTableSensitiveMethodVisitorTest.java new file mode 100644 index 00000000000..ededb891f44 --- /dev/null +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/utility/ExceptionTableSensitiveMethodVisitorTest.java @@ -0,0 +1,100 @@ +package net.bytebuddy.utility; + +import net.bytebuddy.test.utility.MockitoRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.mockito.Mock; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; + +import static org.mockito.Mockito.*; + +@RunWith(Parameterized.class) +public class ExceptionTableSensitiveMethodVisitorTest { + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[][]{ + {Opcodes.ASM5, "visitLabel", new Class[]{Label.class}, new Object[]{new Label()}}, + {Opcodes.ASM5, "visitIntInsn", new Class[]{int.class, int.class}, new Object[]{0, 0}}, + {Opcodes.ASM5, "visitVarInsn", new Class[]{int.class, int.class}, new Object[]{0, 0}}, + {Opcodes.ASM5, "visitTypeInsn", new Class[]{int.class, String.class}, new Object[]{0, ""}}, + {Opcodes.ASM5, "visitFieldInsn", new Class[]{int.class, String.class, String.class, String.class}, new Object[]{0, "", "", ""}}, + {Opcodes.ASM4, "visitMethodInsn", new Class[]{int.class, String.class, String.class, String.class}, new Object[]{0, "", "", ""}}, + {Opcodes.ASM5, "visitMethodInsn", new Class[]{int.class, String.class, String.class, String.class, boolean.class}, new Object[]{0, "", "", "", false}}, + {Opcodes.ASM5, "visitInvokeDynamicInsn", new Class[]{String.class, String.class, Handle.class, Object[].class}, new Object[]{"", "", new Handle(0, "", "", "", false), new Object[0]}}, + {Opcodes.ASM5, "visitJumpInsn", new Class[]{int.class, Label.class}, new Object[]{0, new Label()}}, + {Opcodes.ASM5, "visitLdcInsn", new Class[]{Object.class}, new Object[]{new Object()}}, + {Opcodes.ASM5, "visitIincInsn", new Class[]{int.class, int.class}, new Object[]{0, 0}}, + {Opcodes.ASM5, "visitTableSwitchInsn", new Class[]{int.class, int.class, Label.class, Label[].class}, new Object[]{0, 0, new Label(), new Label[0]}}, + {Opcodes.ASM5, "visitLookupSwitchInsn", new Class[]{Label.class, int[].class, Label[].class}, new Object[]{new Label(), new int[0], new Label[0]}}, + {Opcodes.ASM5, "visitMultiANewArrayInsn", new Class[]{String.class, int.class}, new Object[]{"", 0}}, + {Opcodes.ASM5, "visitInsn", new Class[]{int.class}, new Object[]{0}}, + }); + } + + @Rule + public TestRule mockitoRule = new MockitoRule(this); + + @Mock + private MethodVisitor methodVisitor; + + private final int api; + + private final String name; + + private final Class[] type; + + private final Object[] argument; + + public ExceptionTableSensitiveMethodVisitorTest(int api, String name, Class[] type, Object[] argument) { + this.api = api; + this.name = name; + this.type = type; + this.argument = argument; + } + + @Test + public void testCallback() throws Exception { + Method method = MethodVisitor.class.getDeclaredMethod(name, type); + PseudoVisitor pseudoVisitor = new PseudoVisitor(api, methodVisitor); + method.invoke(pseudoVisitor, argument); + method.invoke(pseudoVisitor, argument); + method.invoke(verify(methodVisitor, times(2)), argument); + verifyNoMoreInteractions(methodVisitor); + pseudoVisitor.check(); + } + + private static class PseudoVisitor extends ExceptionTableSensitiveMethodVisitor { + + private boolean called; + + public PseudoVisitor(int api, MethodVisitor methodVisitor) { + super(api, methodVisitor); + } + + @Override + protected void onAfterExceptionTable() { + if (called) { + throw new AssertionError(); + } + called = true; + verifyZeroInteractions(mv); + } + + protected void check() { + if (!called) { + throw new AssertionError(); + } + } + } +} \ No newline at end of file