diff --git a/crates/oxide_vm/src/async_func.rs b/crates/oxide_vm/src/async_func.rs index f46b054e..5ba0b3b1 100644 --- a/crates/oxide_vm/src/async_func.rs +++ b/crates/oxide_vm/src/async_func.rs @@ -450,11 +450,14 @@ impl Vm { }; // 多层逃出:remaining 非空时逐层关闭(每层一轮结算),空了才执行完成 - // (或抛拒绝原因)。挂起信号由 register 置位,快照后返回续下一轮结算。 + // (或抛拒绝原因)。挂起信号由 register 置位,快照后返回续下一轮结算; + // 该信号由下一轮结算闭包消费、不经派发循环,须先清防陈旧信号泄漏进 + // 另一上下文的 dispatch。 while let Some(next_iter) = remaining.first().cloned() { remaining.remove(0); match self.register_async_escape_close(next_iter, completion, remaining.clone()) { Ok(true) => { + self.async_suspended = false; self.snapshot_async(unsafe { &mut *state_ptr })?; self.restore_async_flags(prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch); self.native_call_depth -= 1; diff --git a/crates/oxide_vm/src/async_generator.rs b/crates/oxide_vm/src/async_generator.rs index a33aa82e..1d6e3d99 100644 --- a/crates/oxide_vm/src/async_generator.rs +++ b/crates/oxide_vm/src/async_generator.rs @@ -861,11 +861,14 @@ impl Vm { }; // 多层逃出:remaining 非空时逐层关闭(每层一轮结算),空了才执行完成 - // (或抛拒绝原因)。挂起信号由 register 置位,快照后返回续下一轮结算。 + // (或抛拒绝原因)。挂起信号由 register 置位,快照后返回续下一轮结算; + // 该信号由下一轮结算闭包消费、不经派发循环,须先清防陈旧信号泄漏进 + // 另一上下文的 dispatch。 while let Some(next_iter) = remaining.first().cloned() { remaining.remove(0); match self.register_async_escape_close(next_iter, completion, remaining.clone()) { Ok(true) => { + self.async_gen_suspended = false; unsafe { (*state_ptr).phase = AsyncGenPhase::AwaitSuspended }; self.snapshot_async_generator(state_ptr)?; self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); diff --git a/crates/oxide_vm/tests/async_gen_delegation_tests.rs b/crates/oxide_vm/tests/async_gen_delegation_tests.rs index d8513048..a214df18 100644 --- a/crates/oxide_vm/tests/async_gen_delegation_tests.rs +++ b/crates/oxide_vm/tests/async_gen_delegation_tests.rs @@ -386,3 +386,26 @@ fn t20_async_gen_break_escape_continues() { .to_string(); assert_eq!(eval(&source), "\"body,close|after:false\""); } + +// T21:异步生成器多层逃出 + 中间夹一个普通异步函数的 return 恢复——多层结算循环 +// 置位的挂起信号不得泄漏进普通异步函数的 dispatch,普通异步函数不得被以 undefined +// 误完成(return 穿越 finally 命中派发循环检查点,不得因陈旧信号提前返回)。时序: +// g 的两轮结算(首轮逃出结算 + 多层结算循环)先于 f 的 await 恢复入队,f 恢复时体内 +// return 命中派发循环检查点。首轮结算入口已清信号,故陈旧信号必来自多层结算循环。 +#[test] +fn t21_async_gen_multi_layer_escape_no_signal_leak_into_async_func() { + let source = "let log=[];\ + const mk=(n)=>({[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:n,done:false})},return(){log.push('c'+n);return Promise.resolve({done:true})}}}});\ + async function* g() { for await (const a of mk(1)) { for await (const b of mk(2)) { log.push('body'); return 42; } } }\ + let resolveF;\ + const PF = new Promise((res) => { resolveF = res; });\ + async function f() { await PF;\ + log.push('f-body');\ + try { return 'f-done'; } finally { log.push('f-finally'); } }\ + const pG = (async () => { const it = g(); const r = await it.next(); return 'g:' + r.done + ':' + r.value; })();\ + const pF = f();\ + Promise.resolve().then(() => {}).then(() => resolveF('x'));\ + Promise.all([pF, pG]).then(([fv, gv]) => log.join(',') + '|' + fv + '|' + gv)" + .to_string(); + assert_eq!(eval(&source), "\"body,c2,c1,f-body,f-finally|f-done|g:true:42\""); +} diff --git a/crates/oxide_vm/tests/async_tests.rs b/crates/oxide_vm/tests/async_tests.rs index 449097e9..a117da29 100644 --- a/crates/oxide_vm/tests/async_tests.rs +++ b/crates/oxide_vm/tests/async_tests.rs @@ -379,6 +379,30 @@ fn for_await_of_escape_settlement_precedes_then() { assert_eq!(eval(&source), "\"body,after\""); } +#[test] +fn for_await_of_multi_layer_escape_no_signal_leak_into_async_gen() { + // 反向反例:普通异步函数多层逃出——多层结算循环置位的挂起信号不得泄漏进 + // 异步生成器的 dispatch,异步生成器不得被以 undefined 误完成。时序:f 的两轮 + // 结算(首轮逃出结算 + 多层结算循环)先于 g 的 await 恢复入队,g 恢复时体内 + // return 穿越 finally 命中派发循环检查点。首轮结算入口已清信号,故陈旧信号 + // 必来自多层结算循环。 + let source = "let log=[];\ + const mk=(n)=>({[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:n,done:false})},return(){log.push('c'+n);return Promise.resolve({done:true})}}}});\ + async function f() { for await (const a of mk(1)) { for await (const b of mk(2)) { log.push('f-body'); return 42; } } }\ + let resolveG;\ + const PG = new Promise((res) => { resolveG = res; });\ + async function* g() { await PG;\ + log.push('g-body');\ + try { return 'g-done'; } finally { log.push('g-finally'); } }\ + const pF = f();\ + const itG = g();\ + const pG1 = itG.next();\ + Promise.resolve().then(() => {}).then(() => resolveG('x'));\ + Promise.all([pF, pG1]).then(([fv, gv]) => log.join(',') + '|' + fv + '|' + gv.value + ':' + gv.done)" + .to_string(); + assert_eq!(eval(&source), "\"f-body,c2,c1,g-body,g-finally|42|g-done:true\""); +} + // ── class/object 生成器方法(异步) ── // class async 生成器方法:yield 顺序与 done 收敛。顶层 then 链驱动