When I looked at the code to instrument synchronized blocks a few days ago, I noticed that athrow
was not being treated as a “return instruction”, so I… fixed that. It seemed to be very similar to the ?return
instructions: The method would exit, perhaps execute a finally
handler, and that’s it. I shouldn’t have done that. While throwing an exception often is a rather quick way to exit a method, it’s not guaranteed that the method will be exited, whereas for ?return
it is.
The result was that I introduced the “leave + unlock” block in front of each athrow
opcode. If an exception was thrown, caught, and rethrown, the monitor would actually be unlocked several times, and the JVM doesn’t allow that. It throws an IllegalMonitorStateException, something that should happen when “a thread has attempted to wait on an object’s monitor or to notify other threads waiting on an object’s monitor without owning the specified monitor”, according to the Java API documentation. Unlocking an unlocked monitor isn’t mentioned there, but that probably makes sense: In Java, that can never happen. Using bytecode, it can.
So I… unfixed that. I also made several smaller bugfixes, added a few flags to some supporting tools, and ran some more tests. The stack issues are gone.