我想向方法代碼添加說明。這些說明應在達到和離開方法之前執行。 爲了確保後面的指令總是在離開前執行,我想把它們放在finally塊中。 (我知道類AdviceAdapter
,但它並不能保證的退出代碼執行時被調用的方法拋出異常。)在try-finally塊中嵌入方法的現有代碼
我的問題是,在結果的指令順序錯誤。要被處理
方法:
@Test
public void original() {
assertTrue(true);
assertTrue(!(false));
}
期望的結果:(登錄X也可以發生在try塊的第一行)
@Test
public void desired() {
//some logging X
try {
assertTrue(true);
assertTrue(!(false));
}
finally {
//some logging Y
}
}
(所需結果的字節碼等於第E採用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)之後,但在返回之前我nstruction。
11-14需要是前03
請注意,返回也可能引發異常。 – Antimony
@Antimony:自身返回(第15行)不會導致異常,因爲它只是彈出並返回堆棧中的值。返回值的計算(可能會引發異常)在返回前發生在指令中,並且應該仍在try塊內。 (儘管測試用例通常是無效的方法。) – nrainer
通常,在監視器處於非法狀態的情況下,返回指令本身可能會引發異常。但這應該是一個問題。 – Antimony