Встроить существующий код метода в блок try-finally

Я хочу добавить инструкции в код методов. Эти инструкции должны выполняться после достижения и перед выходом из метода. Чтобы убедиться, что последние инструкции всегда выполняются перед выходом, я хочу поместить их в блок 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.


person nrainer    schedule 25.06.2013    source источник
comment
Обратите внимание, что return также может вызвать исключение.   -  person Antimony    schedule 25.06.2013
comment
@Antimony: Сам возврат (строка 15) не может вызвать исключение, поскольку он просто появляется и возвращает значение в стеке. Вычисление возвращаемого значения (которое может вызвать исключение) происходит в инструкциях перед возвратом, и это все еще должно быть в блоке try. (Хотя тестовые случаи обычно являются недействительными методами.)   -  person nrainer    schedule 25.06.2013
comment
в общем, сама инструкция возврата может генерировать исключение в случае, когда монитор находится в недопустимом состоянии. Но это должно быть проблемой здесь.   -  person Antimony    schedule 26.06.2013
comment
Хорошо, верно. Вы поняли, почему инструкции расположены в неправильном порядке?   -  person nrainer    schedule 26.06.2013


Ответы (2)


Я не уверен, где ошибка в вашем подходе. Но я добился чего-то подобного после проб и ошибок с помощью AdviceAdapter.

Видеть

http://code.google.com/p/pitestrunner/source/browse/pitest/src/main/java/org/pitest/coverage/codeassist/CoverageMethodVisitor.java

person henry    schedule 26.06.2013
comment
Спасибо, я попытался сравнить ваше решение со своим. Соответствующие инструкции байт-кода были почти такими же, но теперь я нашел проблему. - person nrainer; 27.06.2013

Вы можете просто поместить аннотации JUnit @Before и @After в свои методы, которые должны вызываться до и после вашего тестового метода.

person Lukas Eichler    schedule 25.06.2013
comment
Спасибо за Ваш ответ. Я знаю это, но это не то, чего я хочу достичь. Другие методы @Before или @After (или @BeforeClass, @AfterClass) могут уже существовать и выполняться перед моим. - person nrainer; 25.06.2013