Hi,
I am using ASM to wrap method call into try-finally block in order to log the
start and end events of that method. For example, I am transforming "foo()"
into "try { logFooStart(); foo(); } finally { logFooEnd(); }". Now I am running
into a java.lang.VerifyError when the method call to wrap is a `this`
constructor.
Here is my Test class:
package instrumentation;
public class Test {
public Test(Test t) { this(); }
public Test() { }
public static void main(String[] args) {
new Test(null);
}
}
My code to insert the try-finally block looks rough like this:
/* Wrap the method call instruction into a try-finally block with logging code.
*
* LOG_INVOKE_METHOD
* L0:
* visitMethodInsn(opcode, owner, name, desc, itf)
* LOG_FINISH_METHOD
* goto L2
* L1:
* LOG_FINISH_METHOD
* throw exception
* L2:
* ...
*/
mv.push(locId) // push an int to the stack
invokeRtnMethod(LOG_INVOKE_METHOD); // my code to log event
Label l0 = mv.mark();
mv.visitMethodInsn(opcode, owner, name, desc, itf);
mv.push(locId);
invokeRtnMethod(LOG_FINISH_METHOD);
Label l2 = mv.newLabel();
mv.goTo(l2);
Label l1 = mv.mark();
mv.catchException(l0, l1, null);
mv.push(locId);
invokeRtnMethod(LOG_FINISH_METHOD);
mv.throwException();
mv.mark(l2);
`mv` is a GeneratorAdaptor and the underlying ClassWriter is constructed with
flag COMPUTE_FRAMES.
Here is the bytecode of the transformed Test constructor:
public instrumentation.Test(instrumentation.Test);
descriptor: (Linstrumentation/Test;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: sipush 137
4: invokestatic #30 // Method
com/runtimeverification/rvpredict/runtime/RVPredictRuntime.logInvokeMethod:(I)V
7: invokespecial #8 // Method "<init>":()V
10: sipush 137
13: invokestatic #33 // Method
com/runtimeverification/rvpredict/runtime/RVPredictRuntime.logFinishMethod:(I)V
16: goto 26
19: sipush 137
22: invokestatic #33 // Method
com/runtimeverification/rvpredict/runtime/RVPredictRuntime.logFinishMethod:(I)V
25: athrow
26: return
Exception table:
from to target type
7 19 19 any
LocalVariableTable:
Start Length Slot Name Signature
0 27 0 this Linstrumentation/Test;
0 27 1 t Linstrumentation/Test;
LineNumberTable:
line 6: 0
line 7: 26
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 19
locals = [ top, class instrumentation/Test ]
stack = [ class java/lang/Throwable ]
frame_type = 255 /* full_frame */
offset_delta = 6
locals = [ class instrumentation/Test, class instrumentation/Test ]
stack = []
And here is the java.lang.VerifyError I get:
Exception in thread "main" java.lang.VerifyError: Stack map does not match the
one at exception handler 19
Exception Details:
Location:
instrumentation/Test.<init>(Linstrumentation/Test;)V @19: sipush
Reason:
Current frame's flags are not assignable to stack map frame's.
Current Frame:
bci: @7
flags: { flagThisUninit }
locals: { uninitializedThis, 'instrumentation/Test' }
stack: { 'java/lang/Throwable' }
Stackmap Frame:
bci: @19
flags: { }
locals: { top, 'instrumentation/Test' }
stack: { 'java/lang/Throwable' }
Bytecode:
0x0000000: 2a11 0089 b800 1eb7 0008 1100 89b8 0021
0x0000010: a700 0a11 0089 b800 21bf b1
Exception Handler Table:
bci [7, 19] => handler: 19
Stackmap Table:
full_frame(@19,{Top,Object[#1]},{Object[#35]})
full_frame(@26,{Object[#1],Object[#1]},{})
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at
sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
at
sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
It would appear that `flagThisUninit` is not correctly computed for the
stackmap of the finally-block. Is it a bug of the ASM or am I just doing
invalid transformation?
Thanks!
Yilong