Skip to content

Commit 407928c

Browse files
hyperpolymathclaude
andcommitted
fix(codegen-deno): async-context IIFE wrappers (Refs #122)
Synthesised class methods are `async` and an associated cross-method call lowers to `(await this.m(...))`. But the expression-position IIFE wrappers (block / try / match / let / return) were always plain `(() => { ... })()` — so a contained `await` produced `SyntaxError: Unexpected reserved word` (hit by ubicity's `loadAllExperiences`: a try whose body `await this.ensureDirectories()`). Add `ctx.in_async` (set while emitting a synthesised `async` method body) and an `iife` helper: in async context the wrapper is `(await (async () => { ... })())`, otherwise the plain form. Applied to gen_block_expr / gen_try / gen_match / ExprLet / ExprReturn / ExprUnsafe. Free functions, constructors and module top-level stay non-async (they never emit an awaited associated call). Verified: affinescript `dune build` clean, `dune runtest` 214 tests / same 2 pre-existing E2E Node-CJS vscode failures (zero regressions), 5 Deno-ESM harnesses green; the regenerated ubicity src/storage.js now parses and `deno task test` is **44 passed / 0 failed** (the full suite, incl. the .test.js variants that import the migrated storage.js via mapper/privacy/export). Refs #122 (follow-up to #123/#125; completes the ubicity#30 consumer). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 1045d1a commit 407928c

1 file changed

Lines changed: 29 additions & 12 deletions

File tree

lib/codegen_deno.ml

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ type codegen_ctx = {
6363
definition shadows a same-named host intrinsic ({!deno_builtins}),
6464
so e.g. a user `fn len(xs: IntList)` is NOT lowered to `.length`. *)
6565
local_fns : (string, unit) Hashtbl.t;
66+
(* True while emitting a synthesised `async` method body. The
67+
expression-position IIFE wrappers (block/try/match/let/return) must
68+
then be `async` and awaited, because they may contain an awaited
69+
associated call (`(await this.m(...))`) — `await` inside a plain
70+
`(() => {...})()` is a SyntaxError ("Unexpected reserved word"). *)
71+
in_async : bool;
6672
}
6773

6874
let create_ctx symbols = {
@@ -73,8 +79,16 @@ let create_ctx symbols = {
7379
self_name = None;
7480
assoc = Hashtbl.create 32;
7581
local_fns = Hashtbl.create 64;
82+
in_async = false;
7683
}
7784

85+
(* Expression-position IIFE wrapper. In an async method body it must be
86+
`(await (async () => { ... })())` so a contained `await` is legal;
87+
elsewhere a plain `(() => { ... })()`. *)
88+
let iife ctx (inner : string) : string =
89+
if ctx.in_async then "(await (async () => { " ^ inner ^ " })())"
90+
else "(() => { " ^ inner ^ " })()"
91+
7892
let emit ctx str = Buffer.add_string ctx.output str
7993

8094
let emit_line ctx str =
@@ -321,11 +335,11 @@ let rec gen_expr ctx (expr : expr) : string =
321335
let kw = if el_mut then "let" else "const" in
322336
(match el_body with
323337
| Some body ->
324-
"((() => { " ^ kw ^ " " ^ pat_str ^ " = " ^ val_str ^ "; return "
325-
^ gen_expr ctx body ^ "; })())"
338+
iife ctx (kw ^ " " ^ pat_str ^ " = " ^ val_str ^ "; return "
339+
^ gen_expr ctx body ^ ";")
326340
| None ->
327-
"((() => { " ^ kw ^ " " ^ pat_str ^ " = " ^ val_str
328-
^ "; return Unit; })())")
341+
iife ctx (kw ^ " " ^ pat_str ^ " = " ^ val_str
342+
^ "; return Unit;"))
329343
| ExprTuple exprs | ExprArray exprs ->
330344
"[" ^ String.concat ", " (List.map (gen_expr ctx) exprs) ^ "]"
331345
| ExprIndex (arr, idx) ->
@@ -349,8 +363,8 @@ let rec gen_expr ctx (expr : expr) : string =
349363
| ExprMatch { em_scrutinee; em_arms } ->
350364
gen_match ctx em_scrutinee em_arms
351365
| ExprBlock block -> gen_block_expr ctx block
352-
| ExprReturn (Some e) -> "(() => { return " ^ gen_expr ctx e ^ "; })()"
353-
| ExprReturn None -> "(() => { return Unit; })()"
366+
| ExprReturn (Some e) -> iife ctx ("return " ^ gen_expr ctx e ^ ";")
367+
| ExprReturn None -> iife ctx "return Unit;"
354368
| ExprLambda { elam_params; elam_body; elam_ret_ty = _ } ->
355369
let ps = List.map (fun (p : param) -> mangle p.p_name.name) elam_params in
356370
"((" ^ String.concat ", " ps ^ ") => " ^ gen_expr ctx elam_body ^ ")"
@@ -367,7 +381,7 @@ let rec gen_expr ctx (expr : expr) : string =
367381
| ExprResume (Some e) -> gen_expr ctx e
368382
| ExprResume None -> "Unit"
369383
| ExprUnsafe _ ->
370-
"(() => { throw new Error('unsafe op not supported in Deno-ESM backend'); })()"
384+
iife ctx "throw new Error('unsafe op not supported in Deno-ESM backend');"
371385

372386
and gen_literal (lit : literal) : string =
373387
match lit with
@@ -423,7 +437,11 @@ and gen_match ctx scrutinee arms =
423437
in
424438
prefix ^ " " ^ gen_arms rest
425439
in
426-
"((" ^ scrut_var ^ ") => { " ^ gen_arms arms ^ " })(" ^ scrutinee_str ^ ")"
440+
if ctx.in_async then
441+
"(await (async (" ^ scrut_var ^ ") => { " ^ gen_arms arms ^ " })("
442+
^ scrutinee_str ^ "))"
443+
else
444+
"((" ^ scrut_var ^ ") => { " ^ gen_arms arms ^ " })(" ^ scrutinee_str ^ ")"
427445

428446
and gen_pattern_test scrut pat =
429447
match pat with
@@ -485,7 +503,7 @@ and gen_block_expr ctx block =
485503
| Some e -> "return " ^ gen_expr ctx e ^ ";"
486504
| None -> "return Unit;"
487505
in
488-
"(() => { " ^ Buffer.contents body ^ result ^ " })()"
506+
iife ctx (Buffer.contents body ^ result)
489507

490508
and gen_try ctx body catch finally =
491509
let body_str = gen_block_expr ctx body in
@@ -502,8 +520,7 @@ and gen_try ctx body catch finally =
502520
| None -> ""
503521
| Some blk -> " finally { " ^ gen_block_expr ctx blk ^ "; }"
504522
in
505-
"(() => { try { return " ^ body_str ^ "; } " ^ catch_str ^ finally_str
506-
^ " })()"
523+
iife ctx ("try { return " ^ body_str ^ "; } " ^ catch_str ^ finally_str)
507524

508525
(* Unwrap span markers to inspect an expression's real head. *)
509526
and unspan = function
@@ -695,7 +712,7 @@ let gen_method ctx ~(recv_name : string) ~(js_name : string)
695712
let rest_params = match fd.fd_params with _ :: t -> t | [] -> [] in
696713
let params =
697714
List.map (fun (p : param) -> mangle p.p_name.name) rest_params in
698-
let ctx_m = { ctx with self_name = Some recv_name } in
715+
let ctx_m = { ctx with self_name = Some recv_name; in_async = true } in
699716
emit_line ctx_m
700717
(Printf.sprintf "async %s(%s) {" (mangle js_name)
701718
(String.concat ", " params));

0 commit comments

Comments
 (0)