In every language I know that supports finally, once you enter the try, the finally block always executes, no matter how you exit. In monkeyc, it only executes on fall-through exit from the try or catch.
So (sorry about the weirdness, I just wanted to cover all the ways you can exit from a try/catch block other than fall through).
do { switch (x) { case 1: try { break; } finally { // never executes System.println("finally - break"); } break; case 2: try { continue; } finally { // never executes System.println("finally - continue"); } break; case 3: try { return; } finally { // never executes System.println("finally - return"); } break; case 4: try { throw new Exception(); } catch (ex) { return; // or break or continue } finally { // never executes System.println("finally - non-fallthrough catch"); } break; case 5: try { try { throw new Exception(); } catch (ex) { throw ex; } finally { // never executes System.println("finally - catch throws"); } } catch (ex) { // does execute System.println("caught"); } break; } } while (false);
The documentation isn't 100% clear about this. I see
An optional
finally
statement can be placed at the end of a try-catch block, which will execute regardless of whether an exception has been thrown
which seems to say that it should always execute; but isn't quite explicit about when an exception *isn't* thrown. But note that "case 5" above demonstrates that even throwing from a catch block doesn't execute the finally block, which certainly seems to contradict what the documentation implies.
There's also a comment in a code sample:
finally {
// Execute this after the preceding try and catch statements are completed
}
which again really suggests that it should execute when the try or catch blocks exit, no matter how they do so.
Looking at how finally is implemented in the bytecode (via the "-g" option to monkeyc), it looks like the compiler just inserts a jsr to the finally block on the exit paths from the try and catch blocks - but forgets to do so ahead of branches out of (or returns from) the try or catch blocks. So most of these are easy to fix. The only problem is case 5.
Obviously, in case 5 as written, it would be easy to add a jsr to the finally handler just before the throw - because you know that the throw is going to throw(!). But in general, it would be a function call that *might* throw; so to handle this properly there has to be a compiler generated try/catch inside the catch block, that redirects to the finally handler.