瀏覽代碼

deal with `this` properly

Simon Krajewski 1 年之前
父節點
當前提交
b8d8e6fb2d

+ 1 - 0
src/coro/coro.ml

@@ -25,4 +25,5 @@ let fun_to_coro ctx e tf =
 let create_coro_context com meta = {
 	com;
 	coro_debug = Meta.has (Meta.Custom ":coroutine.debug") meta;
+	vthis = None;
 }

+ 21 - 0
src/coro/coroFromTexpr.ml

@@ -33,7 +33,25 @@ let expr_to_coro ctx (vresult,verror) cb_root e =
 		make_block (Some(e.etype,e.epos))
 	in
 	let cb_unreachable = make_block None in
+	let replace_this e =
+		let v = match ctx.vthis with
+			| Some v ->
+				v
+			| None ->
+				let v = alloc_var VGenerated (Printf.sprintf "%sthis" Typecore.gen_local_prefix) e.etype e.epos in
+				ctx.vthis <- Some v;
+				v
+		in
+		Builder.make_local v e.epos
+	in
 	let rec loop cb ret e = match e.eexpr with
+		(* special cases *)
+		| TConst TThis ->
+			let ev = replace_this e in
+			cb,ev
+		| TField(({eexpr = TConst TThis} as e1),fa) ->
+			let e1 = replace_this e1 in
+			cb,{e with eexpr = TField(e1,fa)}
 		(* simple values *)
 		| TConst _ | TLocal _ | TTypeExpr _ | TIdent _ ->
 			cb,e
@@ -103,6 +121,9 @@ let expr_to_coro ctx (vresult,verror) cb_root e =
 		| TVar(v,None) ->
 			add_expr cb e;
 			cb,e_no_value
+		| TVar(v,Some {eexpr = TConst TThis}) ->
+			ctx.vthis <- Some v;
+			cb,e_no_value
 		| TVar(v,Some e1) ->
 			add_expr cb {e with eexpr = TVar(v,None)};
 			let cb,e1 = loop_assign cb (RLocal v) e1 in

+ 8 - 0
src/coro/coroToTexpr.ml

@@ -359,6 +359,14 @@ let block_to_texpr_coroutine ctx bb vcontinuation vresult verror p =
 	let excstate_var = mk (TVar (vexcstate, Some (make_int com.basic rethrow_state_id p))) com.basic.tvoid p in
 	let shared_vars = List.map (fun v -> mk (TVar (v,Some (Texpr.Builder.default_value v.v_type v.v_pos))) com.basic.tvoid null_pos) decls in
 	let shared_vars = List.rev (excstate_var :: state_var :: shared_vars) in
+	let shared_vars = match ctx.vthis with
+		| None ->
+			shared_vars
+		| Some v ->
+			let e_this = mk (TConst TThis) v.v_type v.v_pos in
+			let e_var = mk (TVar(v,Some e_this)) com.basic.tvoid null_pos in
+			e_var :: shared_vars
+	in
 
 	mk (TBlock (shared_vars @ [
 		mk (TVar (vstatemachine, None)) com.basic.tvoid p;

+ 1 - 0
src/coro/coroTypes.ml

@@ -5,6 +5,7 @@ open Type
 type some_ctx = {
 	com : Common.context;
 	coro_debug : bool;
+	mutable vthis : tvar option;
 }
 
 type coro_block = {

+ 1 - 0
tests/misc/coroutines/src/Main.hx

@@ -1,6 +1,7 @@
 function main() {
 	utest.UTest.run([
 		new TestBasic(),
+		new TestTricky(),
 		new TestControlFlow(),
 		new TestGenerator(),
 		#if js

+ 33 - 0
tests/misc/coroutines/src/TestTricky.hx

@@ -0,0 +1,33 @@
+class CoroFile {
+	public final file:String;
+
+	public function new(file) {
+		this.file = file;
+	}
+
+	@:coroutine public function write() {
+		return file;
+	}
+
+	@:coroutine public function almostWrite() {
+		return () -> file;
+	}
+}
+
+class TestTricky extends utest.Test {
+	function testCapturedThis(async:Async) {
+		var file = new CoroFile("value");
+		file.write.start((result, _) -> {
+			Assert.equals("value", result);
+			async.done();
+		});
+	}
+
+	function testPreviouslyCapturedThis(async:Async) {
+		var file = new CoroFile("value");
+		file.almostWrite.start((result, _) -> {
+			Assert.equals("value", result());
+			async.done();
+		});
+	}
+}