2

我想向方法代碼添加說明。這些說明應在達到和離開方法之前執行。 爲了確保後面的指令總是在離開前執行,我想把它們放在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

+0

請注意,返回也可能引發異常。 – Antimony

+0

@Antimony:自身返回(第15行)不會導致異常,因爲它只是彈出並返回堆棧中的值。返回值的計算(可能會引發異常)在返回前發生在指令中,並且應該仍在try塊內。 (儘管測試用例通常是無效的方法。) – nrainer

+0

通常,在監視器處於非法狀態的情況下,返回指令本身可能會引發異常。但這應該是一個問題。 – Antimony

回答

0

警告:如果一個方法只包含一個返回指令該解決方案唯一的工作(例如:它不工作,如果它只是拋出一個異常)。 參見:Embed the existing code of a method in a try-finally block (2)


我發現這個問題: 調用super.visitCode當現有的代碼不是在visitCode方法插入。這個方法在超類中是空的。這清楚地表明現有的代碼是在其他點添加的。

解決方案: 我調用我的方法before(添加的代碼,這需要在開始新線)在visitCode方法。如果操作碼是返回語句,我在visitVarInsn中調用after

@Override 
public void visitCode() 
{ 
    before(); 
} 

@Override 
public void visitInsn(int opcode) 
{ 
    if (OpcodesUtil.isXRETURN(opcode)) 
    { 
     after(); 
    } 

    super.visitInsn(opcode); 
} 

(該AdviceAdapter工作過了,但是也有一些問題,確保每一個ClassReaderaccept方法被調用,EXPAND_FRAMES。此外,它可能會勸告多個退出點,收盤正好一次嘗試時不工作塊)。

0

您只需把JUnit標註@Before@After在你的方法應該和之前的測試方法後調用。

+0

感謝您的回答。我知道,但那不是我想要達到的。其他'@ Before'或'@ After'(或'@ BeforeClass','@ AfterClass')方法可能已經存在,然後在我之前執行。 – nrainer

相關問題