Я использую Javassist (3.25.0-GA) и Java 8 с пользовательским агентом для преобразования байт-кода и добавления операторов печати в существующие предложения catch{}
. Это работает для простых случаев, но имеет проблему с скомпилированным байт-кодом синтаксиса try-with-resources
.
Вот основной пример того, что я пытаюсь сделать, и результаты, когда он работает правильно на стандартном try/catch
блоков:
// before byte code manipulation
public void methodWithCatchClause() {
try {
throwsAnException();
} catch (Exception ex) {
handleException(ex);
}
}
// after byte code manipulation
public void methodWithCatchClause() {
try {
throwsAnException();
} catch (Exception ex) {
System.out.println("CATCH CLAUSE!"); // added by Javassist
handleException(ex);
}
}
Логика, которую я использую для преобразования байт-кода, основана на другом сообщении SO [0]:
// from https://stackoverflow.com/questions/51738034/javassist-insert-a-method-at-the-beginning-of-catch-block
ControlFlow cf = new ControlFlow(ctMethod); // ctMethod == methodWithCatchClause()
for (ControlFlow.Block block : cf.basicBlocks()) {
ControlFlow.Catcher catchers[] = block.catchers();
for (int i = 0; i < catchers.length; i++) {
ControlFlow.Catcher catcher = catchers[i];
ControlFlow.Block catcherBlock = catcher.block();
int position = catcherBlock.position();
int lineNumber = ctMethod.getMethodInfo().getLineNumber(position);
ctMethod.insertAt(lineNumber + 1, "System.out.println(\"CATCH CLAUSE!\");");
}
}
Но этот код не работаетв сочетании с синтаксисом try-with-resources
. В качестве конкретного примера этот код:
public void tryWithResources() {
try (TestAutoClosable test = new TestAutoClosable()) {
test.doStuff();
} catch (Exception ex) {
handleException(ex);
}
}
Превращается в это после генерации кода:
public void tryWithResources() {
try {
TestAutoClosable test = new TestAutoClosable();
Throwable var2 = null;
try {
System.out.println("CATCH CLAUSE!");
test.doStuff();
} catch (Throwable var12) {
var2 = var12;
throw var12;
} finally {
if (test != null) {
if (var2 != null) {
try {
test.close();
} catch (Throwable var11) {
var2.addSuppressed(var11);
}
} else {
test.close();
}
}
}
} catch (Exception var14) {
System.out.println("CATCH CLAUSE!");
System.out.println("CATCH CLAUSE!");
System.out.println("CATCH CLAUSE!");
// this goes on for 15 more entries...
this.handleException(var14);
}
}
Это, конечно, вызывает "CATCH CLAUSE!"печататься несколько раз в нечетных местах. Возможно, было бы полезно упомянуть, что пустые предложения catch, независимо от синтаксиса try/catch
, ломаются аналогичным образом (может быть, основная причина связана?).
Я бы ожидал чего-то более близкого к этому в качестве конечного результата:
public void tryWithResources() {
try {
TestAutoClosable test = new TestAutoClosable();
Throwable var2 = null;
try {
test.noop();
} catch (Throwable var12) {
System.out.println("CATCH CLAUSE!");
var2 = var12;
throw var12;
} finally {
if (test != null) {
if (var2 != null) {
try {
test.close();
} catch (Throwable var11) {
var2.addSuppressed(var11);
}
} else {
test.close();
}
}
}
} catch (Exception var14) {
this.handleException(var14);
}
}
Я пытаюсь выяснить, есть ли у меня простая ошибка в моем коде или мой подход совершенно неправильный. Буду признателен за любую помощь в этом вопросе. Заранее спасибо.
[0] Javassist: вставьте метод в начале блока catch