I'm trying to use javassist
to modify the bytecode of some test classes at load time.
This is what I'm trying to do:
for (CtMethod ctm : ctc.getDeclaredMethods()) {
ctm.instrument(
new ExprEditor() {
public void edit(MethodCall m) throws CannotCompileException{
m.replace("{"
+ "try {"
+ "$_ = $proceed($$);"
+ "} catch(Exception e) {"
+ " System.err.println(e); "
+ " }"
+ "}"
);
}
});
}
}
}
ctc: an object of the class CtClass: the class being loaded (it comes from the method onLoad from my translator implmentation)
ctm: an method on the class ctc.
Basically, I'm trying to do something simple: for every method declared on the class im loading, I want to instrument that method, replacing every method call in that method, envolving in a try-catch.
Yes I know I have the function addCatch
, but that doesn't serve my end purposes, I need to use the expression editor here.
Here's the class I'm running to test this:
public class B {
public double bar(int x) {
System.out.println("Inside B.bar");
return (1 / x);
}
}
What I would is to transform this class into:
public class B {
public double bar(int x) {
try {
System.out.println("Inside B.bar");
} catch (Exception e) {
System.err.println(e);
}
return (1 / x);
}
}
(Yes, I know it's weird, but I want to get this working. However, everytime I try it, I get the following errors:
javassist.CannotCompileException: ... : inconsistent stack height -1
...
Caused by: javassist.bytecode.BadBytecode: ... inconsistent stack height -1
Expecting a stackmap frame at branch target 30
Exception Details:
Location:
test/Example.main([Ljava/lang/String;)V @21: aload_1
Reason:
Expected stackmap frame at this location.
Bytecode:
0000000: b200 10bb 0016 59b7 0018 bb00 1959 b700
0000010: 1b4d 4c0e 4a2b 2cb6 001c 4aa7 0010 3a05
0000020: b200 2e19 05b6 0031 a700 0329 494c 2b28
0000030: b600 2001 3a04 a700 103a 05b2 002e 1905
0000040: b600 31a7 0003 b1
Anyone know anything about this errors? Inconsistent stack height? I looked in google and nothing came up.
Inconsistent stack errors result from having Try/Catch blocks in an ExprEditor.MethodCall.replace(String) call:
https://issues.jboss.org/browse/JASSIST-210
The tutorial also mentions this in Section 4.2 ("It cannot be or contain a try-catch statement.")
This is because of the intricacies involved in Try/Catch mechanics, specifically:
"If an exception is thrown in the try block, then all the values in the stack are popped out."
The replace() function doesn't account for this and thus doesn't generate appropriate bytecode. The addCatch() method does.