diff --git a/src/ant.c b/src/ant.c index d1bb551..70b797b 100644 --- a/src/ant.c +++ b/src/ant.c @@ -7874,6 +7874,7 @@ jsval_t call_js_internal( js->tail_ctx = false; if (res != (jsval_t)T_TAILCALL) res = resolveprop(js, res); } else { + js->tail_ctx = false; res = js_eval(js, &fn[pf->body_start], pf->body_len); if (!is_err(res) && !(js->flags & F_RETURN)) res = js_mkundef(); } @@ -11713,7 +11714,8 @@ static jsval_t js_block_or_stmt_pos(struct js *js, jsoff_t *end_pos) { return res; } uint8_t stmt_tok = js->tok; - jsval_t res = resolveprop(js, js_stmt(js)); + jsval_t res = js_stmt(js); + if (res != (jsval_t)T_TAILCALL) res = resolveprop(js, res); bool is_block_stmt = ( stmt_tok == TOK_FUNC || stmt_tok == TOK_CLASS || stmt_tok == TOK_IF || stmt_tok == TOK_WHILE || diff --git a/tests/repro_bouncer_collectdecl.js b/tests/repro_bouncer_collectdecl.js new file mode 100644 index 0000000..5f5d357 --- /dev/null +++ b/tests/repro_bouncer_collectdecl.js @@ -0,0 +1,55 @@ +const bouncer = (f, ini) => { + let obj = ini; + while (obj.tag) obj = f(obj); + return obj.h0; +}; + +const Nil = { tag: 0 }; +const Cons = (h, t) => ({ tag: 1, h1: h, h2: t }); + +function mkList(n) { + let xs = Nil; + for (let i = n; i > 0; i--) xs = Cons(i, xs); + return xs; +} + +const collectBad = xs => bouncer(collectBadREC, { tag: 1, h0: xs }); +const collectBadREC = arg => { + if (arg.h0.tag === 1) { + return { tag: 0, h0: Cons(arg.h0.h1, collectBad(arg.h0.h2)) }; + } + return { tag: 0, h0: Nil }; +}; + +const collectGood = xs => reverse(bouncer(collectGoodREC, { tag: 1, h0: xs, h1: Nil })); +const collectGoodREC = arg => { + if (arg.h0.tag === 1) return { tag: 1, h0: arg.h0.h2, h1: Cons(arg.h0.h1, arg.h1) }; + return { tag: 0, h0: arg.h1 }; +}; + +function reverse(xs) { + let out = Nil; + while (xs.tag === 1) { + out = Cons(xs.h1, out); + xs = xs.h2; + } + return out; +} + +const n = Number(process.argv[2] || 5000); +const xs = mkList(n); + +let badOverflow = false; +try { + collectBad(xs); +} catch (_) { + badOverflow = true; +} + +if (!badOverflow) { + console.log('FAIL: expected collectBad to overflow'); + process.exit(1); +} + +collectGood(xs); +console.log('PASS'); diff --git a/tests/test_tco_curried.js b/tests/test_tco_curried.js index 5a9f260..69d04c3 100644 --- a/tests/test_tco_curried.js +++ b/tests/test_tco_curried.js @@ -106,7 +106,7 @@ const bind = (m, f) => const pure = v => MkM(tc => eta => ({ tag: 1, h2: { h2: tc, h3: v } })); let chain = pure(0); -for (let i = 0; i < 200; i++) { +for (let i = 0; i < 800; i++) { chain = bind(chain, v => pure(v + 1)); } const monadResult = chain.h1({})(0); diff --git a/tests/test_tco_monad.js b/tests/test_tco_monad.js index adcb03a..5dab835 100644 --- a/tests/test_tco_monad.js +++ b/tests/test_tco_monad.js @@ -1,17 +1,18 @@ // Monad-style bind chain (simplified newt pattern) -const MkM = (h1) => ({ tag: 0, h1: h1 }); +const MkM = h1 => ({ tag: 0, h1: h1 }); -const bind = (m, f) => MkM((tc) => (eta) => { - const sc = m.h1(tc)(eta); - if (sc.tag === 1) return f(sc.h2.h3).h1(sc.h2.h2)(eta); - return sc; -}); +const bind = (m, f) => + MkM(tc => eta => { + const sc = m.h1(tc)(eta); + if (sc.tag === 1) return f(sc.h2.h3).h1(sc.h2.h2)(eta); + return sc; + }); -const pure = (v) => MkM((tc) => (eta) => ({ tag: 1, h2: { h2: tc, h3: v } })); +const pure = v => MkM(tc => eta => ({ tag: 1, h2: { h2: tc, h3: v } })); let chain = pure(0); -for (let i = 0; i < 100; i++) { - chain = bind(chain, (v) => pure(v + 1)); +for (let i = 0; i < 800; i++) { + chain = bind(chain, v => pure(v + 1)); } const result = chain.h1({})(0); console.log('monad chain result:', result.h2.h3); // 100