The instrumentation should actually be pretty easy. I just looked at the bytecode of this class:
public class Test {
public static boolean isOldThread() {
return true;
}
public static void setOldThread() { }
public static void main(String[] args) {
if (isOldThread()) {
// some code
}
else {
setOldThread();
}
}
}
and the main method contains these instructions:
invokestatic Test.isOldThread()Z
ifeq 5
// some code
goto 6
invokestatic Test.setOldThread()V
return
The invokestatic Test.isOldThread()Z
puts the return value on the stack. The ifeq
jumps over the then block if the return value was false
; the goto
in line 4 jumps over the else block if it was true
and the then block has already been executed.
This just needs to be turned into
invokestatic java/lang/Thread.currentThread()Ljava/lang/Thread;
getfield java/lang/Thread.$$$oldThread$$$
ifeq 6
// some code
goto 9
iconst_1
invokestatic java/lang/Thread.currentThread()Ljava/lang/Thread;
putfield java/lang/Thread.$$$oldThread$$$
return
The old line 1 gets replaced by the new lines 1 and 2, which get the current thread, and then get its $$$oldThread$$$
field.
The old line 5 gets replaced by the new lines 6 to 8, which put true
on the stack, get the current thread, and then set its $$$oldThread$$$
field.
monitorenter
and monitorexit
are even easier to handle.
// put object ref on stack
invokestatic edu/rice/cs/cunit/SyncPointBuffer/monitorEnter
just has to be changed into
// put object ref on stack
monitorenter
Exactly the same applies for monitorexit
. The value on top of the stack isn’t used as first parameter anymore, now it’s directly consumed by the monitorenter
or monitorexit
instruction.