Я хочу добавить инструкции в код методов. Эти инструкции должны быть выполнены после достижения и до выхода из метода. Чтобы убедиться, что последние инструкции всегда выполняются перед отъездом, я хочу поместить их в блок 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
}
}
(Байт-код желаемого результата равен байт-коду е следующий код 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), но перед возвратом i nstruction.
11-14 должны быть перед 03.
Обратите внимание, что для возврата также возможно исключение. – Antimony
@ Антитела: само возвращение (строка 15) не может вызвать исключение, поскольку оно просто появляется и возвращает значение в стеке. Вычисление возвращаемого значения (которое может вызвать исключение) происходит в инструкциях перед возвратом, и это все равно должно быть в блоке try. (Хотя тестовые примеры обычно являются недействительными.) – nrainer
, как правило, сама инструкция возврата может выдать исключение в случае, когда монитор находится в незаконном состоянии. Но это должно быть проблемой. – Antimony