I just found out I made invalid assumptions when I rewrote the synchronized block instrumentation code, and I hadn’t discovered my mistake until now, when I ran more general tests.
I assumed the structure of the monitorenter
code always looked like this:
aload\_0 // load something
dup
astore\_1 // store in local
monitorenter // lock
I assumed that the instruction immediately before the dup
would be sufficient by itself to put the object getting locked on the stack. However, that’s not the case, for example when a non-local variable is accessed:
aload\_0// load this
getstatic 0004 // load field
dup
astore\_1 // store in local
monitorenter // lock
Now, the two instructions before the dup
are necessary. It gets worse: If the object put on the stack is the result of a function call, making that call twice may produce different results:
synchronized(someFunc()) {
...
}
or in bytecode:
aload\_0// load this
invokevirtual 0018 // call some method
dup
astore\_1 // store in local
monitorenter // lock
Even if I were to duplicate aload_0; invokevirtual 0018
, the code would not do what I want. So apparently the real equivalent of what I want to do is this:
final Object temp = someFunc();
SynchronizedMonitor.tryEnterBlock(temp);
synchronized(temp) {
SynchronizedMonitor.enterBlock(temp);
try {
...
}
finally {
SynchronizedMonitor.leave(temp);
}
}
That boils down to bytecode like this (except more complicated because of javac’s ways):
aload\_0// load this
invokevirtual 0018 // call some method
astore\_1 // store return value in local temp
aload\_1 // restore from local temp
invokestatic (SynchronizedMonitor.tryEnter)
aload\_1 // restore from local temp
dup
astore\_2 // store in local 2
monitorenter // lock
aload\_1 // restore from local temp
invokestatic (SynchronizedMonitor.tryEnter)
...
aload\_2 // restore this from local 2
monitorexit // unlock
goto 26 // jump over finally handler
// handler for any exception (line 12)
astore\_3 // store exception in local 3
aload\_1 // restore from local temp
invokestatic (SynchronizedMonitor.leave)
aload\_2 // restore this from local 1
monitorexit // unlock
aload\_3 // restore exception from local 3
athrow // rethrow
...
So I have to introduce an additional local variable (which brings up the naming/equivalence issues again), store to it and reload from it several times. With several synchronized blocks in one method, it becomes more difficult to remember which local variable goes with which block. One thing that I probably can do is reuse the local variable already used by javac. In that case, local variables 1 and 2 would be replaced by a single local variable, or I could reorder the instructions and move the astore
up, optimizing an unnecessary astore
away. Both cases would not generate a one-to-one correspondence with javac, but it should be identical except for naming/equivalence again.
Now, if I weren’t trying to duplicate javac’s code, it would be as simple as this:
aload\_0// load this
invokevirtual 0018 // call some method
dup
astore\_2 // store in local 2
* dup
* dup
* invokestatic (SynchronizedMonitor.tryEnter)
monitorenter // lock
* invokestatic (SynchronizedMonitor.tryEnter)
...
aload\_2 // restore this from local 2
* dup
* invokestatic (SynchronizedMonitor.leave)
monitorexit // unlock
goto 26 // jump over finally handler
// handler for any exception (line 10)
astore\_3 // store exception in local 3
aload\_2 // restore this from local 1
* dup
* invokestatic (SynchronizedMonitor.leave)
monitorexit // unlock
aload\_3 // restore exception from local 3
athrow // rethrow
...
The beauty here is that the code is very tightly centered around monitorenter
and monitorexit
, and no new finally
handler has to be introduced. The inserted instructions have been marked with a *
.
Actually… now that I think of it, it’s likely that I don’t have to introduce a new code>finally handler anyway, because javac needs to make sure there is a hidden code>finally handler anyway to unlock and rethrow. But again, I wouldn’t get what javac generates, I wouldn’t get Java code that corresponds to it.
All of this is solvable, but I’m seriously beginning to doubt the value.