Skip to content

Commit 8eceedc

Browse files
committed
some initial work on exception support, add some major TODOs
1 parent e5d2aea commit 8eceedc

File tree

9 files changed

+84
-33
lines changed

9 files changed

+84
-33
lines changed

src/optimization/analyzer.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1098,7 +1098,7 @@ module Run = struct
10981098
(* lose Coroutine<T> type here *)
10991099
(match cf.cf_type with
11001100
| TAbstract ({ a_path = [],"Coroutine" }, [TFun (args, ret)]) ->
1101-
let args = args @ [("",false,tfun [ret] ctx.com.basic.tvoid)] in
1101+
let args = args @ [("",false,tfun [ret; t_dynamic] ctx.com.basic.tvoid)] in
11021102
cf.cf_type <- TFun (args, ctx.com.basic.tvoid);
11031103
| _ -> ())
11041104
| _ -> ()

src/optimization/analyzerTexprTransformer.ml

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ let rec func ctx bb tf t p =
4545
let bb_root = create_node (BKFunctionBegin tf) tf.tf_expr.etype tf.tf_expr.epos in
4646
let bb_exit = create_node BKFunctionEnd tf.tf_expr.etype tf.tf_expr.epos in
4747
let coroutine = match follow t with
48-
| TAbstract ({a_path=[],"Coroutine"}, _) -> Some (alloc_var VGenerated "_hx_result" t_dynamic p)
48+
| TAbstract ({a_path=[],"Coroutine"}, _) -> Some (
49+
alloc_var VGenerated "_hx_result" t_dynamic p,
50+
alloc_var VGenerated "_hx_error" t_dynamic p
51+
)
4952
| _ -> None
5053
in
5154
add_function g tf t p bb_root coroutine;
@@ -329,7 +332,7 @@ let rec func ctx bb tf t p =
329332
| _ -> false
330333
in
331334
(match coroutine with
332-
| Some vresult when is_coroutine efun ->
335+
| Some (vresult,_) when is_coroutine efun ->
333336
let bb_next = create_node BKNormal e1.etype e1.epos in
334337
add_cfg_edge bb bb_next CFGGoto;
335338
let syntax_edge = SESuspend (
@@ -745,13 +748,14 @@ and block_to_texpr ctx bb =
745748
let e = mk (TBlock el) bb.bb_type bb.bb_pos in
746749
e
747750

748-
and block_to_texpr_coroutine ctx bb vcontinuation vresult p =
751+
and block_to_texpr_coroutine ctx bb vcontinuation vresult verror p =
749752
assert(bb.bb_closed);
750753

751754
let open Texpr.Builder in
752755
let com = ctx.com in
753756

754757
declare_var ctx.graph vresult bb;
758+
declare_var ctx.graph verror bb;
755759

756760
let vstate = alloc_var VGenerated "_hx_state" com.basic.tint p in
757761
declare_var ctx.graph vstate bb;
@@ -769,7 +773,11 @@ and block_to_texpr_coroutine ctx bb vcontinuation vresult p =
769773

770774
let mk_continuation_call eresult p =
771775
let econtinuation = make_local vcontinuation p in
772-
mk (TCall (econtinuation, [eresult])) com.basic.tvoid p
776+
mk (TCall (econtinuation, [eresult; make_null t_dynamic p])) com.basic.tvoid p
777+
in
778+
let mk_continuation_call_error eerror p =
779+
let econtinuation = make_local vcontinuation p in
780+
mk (TCall (econtinuation, [make_null t_dynamic p; eerror])) com.basic.tvoid p
773781
in
774782

775783
(* TODO: maybe merge this into block_to_texpr somehow, and only introduce new states when there is a suspension point *)
@@ -832,8 +840,8 @@ and block_to_texpr_coroutine ctx bb vcontinuation vresult p =
832840
mk_case (current_el @ el @ [esetstate; ecallcontinuation; ereturn]) :: statecases
833841
| TermNone ->
834842
mk_case (current_el @ el @ [set_state back_state_id]) :: statecases
835-
| TermThrow (_,p) ->
836-
Error.error "throw is currently not supported in coroutines" p
843+
| TermThrow (e,p) ->
844+
mk_case (current_el @ el @ [set_state (-1); mk_continuation_call_error e p; ereturn]) :: statecases
837845
| TermCondBranch _ ->
838846
die "unexpected TermCondBranch" __LOC__)
839847

@@ -982,10 +990,25 @@ and block_to_texpr_coroutine ctx bb vcontinuation vresult p =
982990
let eswitch = mk (TSwitch (estate, statecases, Some ethrow)) com.basic.tvoid p in
983991
let eloop = mk (TWhile (make_bool com.basic true p, eswitch, DoWhile)) com.basic.tvoid p in
984992

993+
(* TODO: this has to be much more complicated, unfortunately, we need a try/catch around the state machine to catch errors from
994+
synchronous throws and then we need to propagate properly and then we need to support try/catch inside coroutines etc etc.
995+
maybe while implementing support for all this, we can as well look into adding COROUTINE_SUSPEND markers and separate coroutine
996+
and non-coroutine worlds a bit more *)
997+
let eerror = make_local verror p in
998+
let eif = mk (TIf (
999+
mk (TBinop (
1000+
OpNotEq,
1001+
eerror,
1002+
make_null verror.v_type p (* TODO: throw null should work *)
1003+
)) com.basic.tbool p,
1004+
mk_continuation_call_error eerror p,
1005+
Some eloop
1006+
)) com.basic.tvoid p in
1007+
9851008
let estatemachine_def = mk (TFunction {
986-
tf_args = [(vresult,None)];
1009+
tf_args = [(vresult,None); (verror,None)];
9871010
tf_type = com.basic.tvoid;
988-
tf_expr = eloop;
1011+
tf_expr = eif;
9891012
}) tstatemachine p in
9901013

9911014
let state_var = mk (TVar (vstate, Some (make_int com.basic 0 p))) com.basic.tvoid p in
@@ -1001,10 +1024,10 @@ and func ctx i =
10011024
let bb,t,p,tf,coroutine = Hashtbl.find ctx.graph.g_functions i in
10021025
let e,tf_args,tf_type =
10031026
match coroutine with
1004-
| Some vresult ->
1027+
| Some (vresult,verror) ->
10051028
let vcontinuation = alloc_var VGenerated "_hx_continuation" (tfun [t_dynamic] ctx.com.basic.tvoid) p in
10061029
declare_var ctx.graph vcontinuation bb;
1007-
let e = block_to_texpr_coroutine ctx bb vcontinuation vresult p in
1030+
let e = block_to_texpr_coroutine ctx bb vcontinuation vresult verror p in
10081031
let tf_args = tf.tf_args @ [(vcontinuation,None)] in
10091032
e, tf_args, tf.tf_type
10101033
| None ->

src/optimization/analyzerTypes.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ end
229229
module Graph = struct
230230
open BasicBlock
231231

232-
type tfunc_info = BasicBlock.t * Type.t * pos * tfunc * tvar option
232+
type tfunc_info = BasicBlock.t * Type.t * pos * tfunc * (tvar * tvar) option
233233
type texpr_lookup = BasicBlock.t * texpr_lookup_target
234234
type var_write = BasicBlock.t list
235235
type 'a itbl = (int,'a) Hashtbl.t

src/typing/typer.ml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1585,10 +1585,10 @@ and type_call ?(mode=MGet) ctx e el (with_type:WithType.t) inline p =
15851585
build_call ~mode ctx e el with_type p;
15861586
in
15871587
let create_coroutine e args ret p =
1588-
let args = args @ [("_hx_continuation",false,(tfun [ret] ctx.com.basic.tvoid))] in
1588+
let args = args @ [("_hx_continuation",false,(tfun [ret; t_dynamic] ctx.com.basic.tvoid))] in
15891589
let ret = ctx.com.basic.tvoid in
15901590
let el, _ = unify_call_args ctx el args ret p false false false in
1591-
mk (TCall (e, el)) (tfun [t_dynamic] ctx.com.basic.tvoid) p
1591+
mk (TCall (e, el)) (tfun [t_dynamic; t_dynamic] ctx.com.basic.tvoid) p
15921592
in
15931593
match e, el with
15941594
| (EConst (Ident "trace"),p) , e :: el ->
@@ -1628,7 +1628,8 @@ and type_call ?(mode=MGet) ctx e el (with_type:WithType.t) inline p =
16281628
(match follow e.etype with
16291629
| TAbstract ({ a_path = [],"Coroutine" }, [TFun (args, ret)]) ->
16301630
let ecoro = create_coroutine e args ret p in
1631-
mk (TCall (ecoro, [Builder.make_null t_dynamic p])) ctx.com.basic.tvoid p
1631+
let enull = Builder.make_null t_dynamic p in
1632+
mk (TCall (ecoro, [enull; enull])) ctx.com.basic.tvoid p
16321633
| _ -> def ())
16331634
| (EField (e,"create"),_), args ->
16341635
let e = type_expr ctx e WithType.value in

std/StdTypes.hx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,10 @@ abstract Coroutine<T> {
182182
for resuming coroutine execution.
183183
**/
184184
@:coroutine
185-
public static extern function suspend<T>(f:(cont:T->Void)->Void):T;
185+
public static extern function suspend<T>(f:(cont:(T,Null<Dynamic>)->Void)->Void):T;
186186

187187
static function __init__():Void {
188-
js.Syntax.code("{0} = {1}", Coroutine.suspend, cast function(f, cont) return _ -> f(cont));
188+
js.Syntax.code("{0} = {1}", Coroutine.suspend, cast function(f, cont) return (_,_) -> f(cont));
189189
}
190190
#end
191191
}
Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,47 @@
11
class TestBasic extends utest.Test {
22
function testSimpleStart(async:Async) {
3-
simple.start(42, result -> {
3+
simple.start(42, (result,error) -> {
44
Assert.equals(42, result);
55
async.done();
66
});
77
}
88

99
function testSimpleCreate(async:Async) {
10-
var cont = simple.create(42, result -> {
10+
var cont = simple.create(42, (result,error) -> {
1111
Assert.equals(42, result);
1212
async.done();
1313
});
14-
cont(null);
14+
cont(null, null);
15+
}
16+
17+
function testErrorDirect(async:Async) {
18+
error.start((result, error) -> {
19+
// TODO: Exceptions.filter is currently run before coroutine processor
20+
// so we get wrapped exception here... think what we want to do with this
21+
var error:haxe.Exception = error;
22+
Assert.equals("nope", error.message);
23+
async.done();
24+
});
25+
}
26+
27+
function testErrorPropagation(async:Async) {
28+
@:coroutine function propagate() {
29+
error();
30+
}
31+
propagate.start((result, error) -> {
32+
// TODO: Exceptions.filter is currently run before coroutine processor
33+
// so we get wrapped exception here... think what we want to do with this
34+
var error:haxe.Exception = error;
35+
Assert.equals("nope", error.message);
36+
async.done();
37+
});
1538
}
1639

1740
@:coroutine static function simple(arg:Int):Int {
1841
return arg;
1942
}
20-
}
43+
44+
@:coroutine static function error() {
45+
throw "nope";
46+
}
47+
}

tests/misc/coroutines/src/TestControlFlow.hx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class TestControlFlow extends utest.Test {
44
if (x) return 1;
55
return 2;
66
}
7-
mapCalls.start([true, false], f, result -> {
7+
mapCalls.start([true, false], f, (result,error) -> {
88
Assert.same([1, 2], result);
99
async.done();
1010
});
@@ -20,7 +20,7 @@ class TestControlFlow extends utest.Test {
2020
v = 2;
2121
}
2222
@:coroutine function f2(x) { f(x); return v; }
23-
mapCalls.start([true, false], f2, result -> {
23+
mapCalls.start([true, false], f2, (result,error) -> {
2424
Assert.same([1, 2], result);
2525
async.done();
2626
});
@@ -30,7 +30,7 @@ class TestControlFlow extends utest.Test {
3030
@:coroutine function f(x) {
3131
return if (x) 1 else 2;
3232
}
33-
mapCalls.start([true, false], f, result -> {
33+
mapCalls.start([true, false], f, (result,error) -> {
3434
Assert.same([1, 2], result);
3535
async.done();
3636
});
@@ -45,7 +45,7 @@ class TestControlFlow extends utest.Test {
4545
}
4646
return "d";
4747
}
48-
mapCalls.start([1, 2, 3, 4], f, result -> {
48+
mapCalls.start([1, 2, 3, 4], f, (result,error) -> {
4949
Assert.same(["a", "b", "c", "d"], result);
5050
async.done();
5151
});
@@ -61,7 +61,7 @@ class TestControlFlow extends utest.Test {
6161
}
6262
return "e";
6363
}
64-
mapCalls.start([1, 2, 3, 4], f, result -> {
64+
mapCalls.start([1, 2, 3, 4], f, (result,error) -> {
6565
Assert.same(["a", "b", "c", "d"], result);
6666
async.done();
6767
});
@@ -79,7 +79,7 @@ class TestControlFlow extends utest.Test {
7979
}
8080
return results;
8181
}
82-
mapCalls.start([0, 1, 2], f, result -> {
82+
mapCalls.start([0, 1, 2], f, (result,error) -> {
8383
Assert.same([
8484
[0,1,2,3,4,5,6,7,8,9],
8585
[0,1,2,3,4],

tests/misc/coroutines/src/TestGenerator.hx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ private function sequence<T>(f:Coroutine<Yield<T>->Void>):Iterator<T> {
4444

4545
var nextStep = null;
4646

47-
function finish(_) {
47+
function finish(_, _) {
4848
finished = true;
4949
}
5050

@@ -56,14 +56,14 @@ private function sequence<T>(f:Coroutine<Yield<T>->Void>):Iterator<T> {
5656
function hasNext():Bool {
5757
if (nextStep == null) {
5858
nextStep = f.create(yield, finish);
59-
nextStep(null);
59+
nextStep(null, null);
6060
}
6161
return !finished;
6262
}
6363

6464
function next():T {
6565
var value = nextValue;
66-
nextStep(null);
66+
nextStep(null, null);
6767
return value;
6868
}
6969

tests/misc/coroutines/src/TestJsPromise.hx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import js.lib.Promise;
22

33
@:coroutine
44
private function await<T>(p:Promise<T>):T {
5-
return Coroutine.suspend(cont -> p.then(cont));
5+
return Coroutine.suspend(cont -> p.then(r -> cont(r, null), e -> cont(null, e)));
66
}
77

88
private function promise<T>(c:Coroutine<()->T>):Promise<T> {
9-
return new Promise((resolve,_) -> c.start(resolve));
9+
return new Promise((resolve,reject) -> c.start((result, error) -> if (error != null) reject(error) else resolve(result)));
1010
}
1111

1212
class TestJsPromise extends utest.Test {
@@ -18,7 +18,7 @@ class TestJsPromise extends utest.Test {
1818
return x + 1;
1919
}
2020

21-
awaiting.start(result -> {
21+
awaiting.start((result,error) -> {
2222
Assert.equals(42, result);
2323
async.done();
2424
});

0 commit comments

Comments
 (0)