【发布时间】:2013-06-25 07:52:41
【问题描述】:
我想在方法代码中添加说明。这些指令应该在到达方法之后和离开方法之前执行。
为了确保在离开之前总是执行后面的指令,我想将它们放在 finally 块中。
(我知道 AdviceAdapter 类,但是当被调用的方法抛出异常时,它不能确保退出代码的执行。)
我的问题是结果中的指令顺序错误。
要处理的方法:
@Test
public void original() {
assertTrue(true);
assertTrue(!(false));
}
期望的结果:
@Test
public void desired() {
//some logging X
try {
assertTrue(true);
assertTrue(!(false));
}
finally {
//some logging Y
}
}
(记录 X 也可以发生在 try 块的第一行。)
(期望结果的字节码等于下面Java代码的字节码:)
@Test
public void desired() {
//some logging X
try {
assertTrue(true);
assertTrue(!(false));
//some logging Y
}
catch (Throwable t) {
//some logging Y
throw t;
}
}
我使用 ASM 处理方法的代码:
@Override
public void visitCode() {
before();
super.visitCode();
after();
}
private void before() {
insertInstructionToSetMode(LoggingMode.TESTING);
this.l0 = new Label();
this.l1 = new Label();
visitLabel(l0);
}
private void after() {
visitTryCatchBlock(l0, l1, l1, null);
Label l2 = new Label();
visitJumpInsn(GOTO, l2);
visitLabel(this.l1);
visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"});
visitVarInsn(ASTORE, 1);
insertInstructionToSetMode(LoggingMode.FRAMING);
visitVarInsn(ALOAD, 1);
visitInsn(ATHROW);
visitLabel(l2);
visitFrame(Opcodes.F_SAME, 0, null, 0, null);
insertInstructionToSetMode(LoggingMode.FRAMING);
}
private void insertInstructionToSetMode(LoggingMode mode) {
String modeValue = (mode == LoggingMode.TESTING ? FIELD_NAME_TESTING : FIELD_NAME_FRAMING);
visitFieldInsn(Opcodes.GETSTATIC, CP_LOGGING_MODE, modeValue, FIELD_DESC_LOGGING_MODE);
visitMethodInsn(INVOKESTATIC, CP_INVOCATION_LOGGER, METHOD_NAME_SET_MODE, METHOD_DESC_SET_MODE);
}
生成的字节码(指令顺序错误):
// logging X
01 getstatic instrumentation/LoggingMode/TESTING Linstrumentation/LoggingMode;
02 invokestatic instrumentation/InvocationLogger/setMode(Linstrumentation/LoggingMode;)V
// successfully passed the try block
03 goto 9
// catch block for the finally behaviour
04 astore_1
05 getstatic instrumentation/LoggingMode/FRAMING Linstrumentation/LoggingMode;
06 invokestatic instrumentation/InvocationLogger/setMode(Linstrumentation/LoggingMode;)V
07 aload_1
08 athrow
// logging Y
09 getstatic instrumentation/LoggingMode/FRAMING Linstrumentation/LoggingMode;
10 invokestatic instrumentation/InvocationLogger/setMode(Linstrumentation/LoggingMode;)V
// original code
11 iconst_1
12 invokestatic org/junit/Assert/assertTrue(Z)V
13 iconst_1
14 invokestatic org/junit/Assert/assertTrue(Z)V
15 return
01-02 可以,但是 09-10 需要在原始代码(14)之后,但在返回指令之前。 11-14 需要在 03 之前。
【问题讨论】:
-
请注意,return 也有可能抛出异常。
-
@Antimony:返回本身(第 15 行)不会导致异常,因为它只是弹出并返回堆栈上的值。返回值的计算(可能引发异常)发生在返回之前的指令中,并且应该仍然在 try 块中。 (不过,测试用例通常是 void 方法。)
-
一般来说,在监视器处于非法状态的情况下,返回指令本身会抛出异常。但这应该是一个问题。
-
好的,没错。您知道为什么说明的顺序错误吗?
标签: java bytecode instrumentation java-bytecode-asm bytecode-manipulation