Closed Simn closed 5 years ago
Somewhat related to this: We need the finally
functionality to ensure that our locks are released in the case of exceptions.
The documentation mentions that subroutines were used for that in older java versions: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.10.2.5
To implement the try-finally construct, a compiler for the Java programming language that generates class files with version number 50.0 or below may use the exception-handling facilities together with two special instructions: jsr ("jump to subroutine") and ret ("return from subroutine").
What it doesn't seem to mention is what to do in versions > 50.0...
Without subroutines, we're gonna have to duplicate some code.
What we need here is a block-exit detection. Given a Haxe code block, it can be exited these ways:
return
throw
or something down the call-stack)break
and continue
if we're in a loopreturn
This should be straightforward: Emit the block-exit code, then emit the return instruction. I don't think there's anything else we have to do.
This requires wrapping the block in the Java equivalent of try block catch(e:Dynamic) { block-exit code; throw e; }
.
break
and continue
This is similar to return
, but we have to detect if we're actually leaving our block. That should be a matter of distinguishing finally-in-loop and loop-in-finally.
If we can establish the restriction that finally blocks must be Void
, all we have to do is put the block-exit code at the end.
We also have to remember that this stuff can be nested (in theory).
Let's assume that we have java.Lib.finally(f:Void -> Void, fFinally:Void ->Void)
. We would do the following:
StackFinally fFinally
to a block_exit
stack.f
which catches Dynamic
, executes fFinally
and rethrows the exception.f
:
StackLoop
to the block_exit
stack.return
instruction, generate all StackFinally
code on the block_exit
stack.break
or continue
, generate StackFinally
from the block_exit
stack until we find a StackLoop
item.f
, generate fFinally
.I implemented this as an expression filter, but I'm not gonna use it because I need finer control over it. Posting it here in case we can use it in Haxe core:
module FinallyHandler = struct
type finally_stack =
| StackFinally of texpr
| StackLoop
let transform_finally com e e_finally =
let rec loop stack e = match e.eexpr with
| TReturn e1 ->
let e1 = Option.map (loop stack) e1 in
let el = ExtList.List.filter_map (function
| StackFinally e -> Some e
| StackLoop -> None
) stack in
mk (TBlock (el @ [{e with eexpr = TReturn e1}])) e.etype e.epos
| TBreak | TContinue ->
let rec loop stack = match stack with
| [] -> []
| StackLoop :: _ -> []
| StackFinally e :: stack -> e :: loop stack
in
let el = loop stack in
mk (TBlock (el @ [e])) e.etype e.epos
| TWhile(e1,e2,flag) ->
let e1 = loop stack e1 in
let e2 = loop (StackLoop :: stack) e2 in
{e with eexpr = TWhile(e1,e2,flag)}
| TFor(v,e1,e2) ->
let e1 = loop stack e1 in
let e2 = loop (StackLoop :: stack) e2 in
{e with eexpr = TFor(v,e1,e2)}
| _ ->
Type.map_expr (loop stack) e
in
let e = loop [StackFinally e_finally] e in
let e = mk (TBlock [
e;
e_finally;
]) e.etype e.epos in
let v = alloc_var VGenerated "tmp" t_dynamic null_pos in
let e_local = mk (TLocal v) v.v_type v.v_pos in
let e_catch = mk (TBlock [
e_finally;
mk (TThrow e_local) t_dynamic null_pos
]) e_finally.etype e_finally.epos in
mk (TTry(e,[v,e_catch])) e.etype e.epos
end
There are two instructions for this:
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.monitorenter https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.monitorexit
Sounds surprisingly straightforward actually.