I’m using Javassist (3.25.0-GA) and Java 8 with a custom Agent to transform bytecode and add print statements to existing catch{}
clauses. This works for simple cases, but has a problem with the compiled bytecode of the try-with-resources
syntax.
Here is a basic example of what I'm trying to do and the results when it works correctly on standard try/catch
blocks:
// 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);
}
}
The logic I'm using to transform the bytecode is inspired by another SO post [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!\");");
}
}
But this code breaks in conjunction with the try-with-resources
syntax. As a concrete example this code:
public void tryWithResources() {
try (TestAutoClosable test = new TestAutoClosable()) {
test.doStuff();
} catch (Exception ex) {
handleException(ex);
}
}
Turns into this after code generation:
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);
}
}
This of course is causing "CATCH CLAUSE!" to be printed multiple times in odd places. It might be helpful to mention that empty catch clauses, regardless of try/catch
syntax, break in a similar fashion (maybe the underlying cause is related?).
I would expect something closer to this as the end result:
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);
}
}
I'm trying to figure out if I have a simple error in my code or if my approach is entirely wrong. I would appreciate any help with the matter. Thanks in advance.
[0] Javassist: insert a method at the beginning of catch block