diff --git a/crates/oxide_vm/src/async_generator.rs b/crates/oxide_vm/src/async_generator.rs index 7f1cd05..6faeebe 100644 --- a/crates/oxide_vm/src/async_generator.rs +++ b/crates/oxide_vm/src/async_generator.rs @@ -448,7 +448,8 @@ impl Vm { let state = unsafe { &mut *state_ptr }; state.current.take() }; - unsafe { (*state_ptr).phase = AsyncGenPhase::YieldSuspended }; + // 委托让出同样在 unwrap 前不可恢复(AsyncGeneratorYield 先 Await)。 + unsafe { (*state_ptr).phase = AsyncGenPhase::AwaitSuspended }; self.snapshot_async_generator(state_ptr)?; self.restore_inline_state(saved); if let Some(req) = request { @@ -529,7 +530,10 @@ impl Vm { let state = unsafe { &mut *state_ptr }; state.current.take() }; - unsafe { (*state_ptr).phase = AsyncGenPhase::YieldSuspended }; + // yield 值 unwrap 前视为 await 挂起(不可由新 next 恢复):AsyncGeneratorYield + // 先 Await(value),unwrap 完成前排队请求不得恢复生成器——否则被拒 yield 值 + // 尚未结算,排队 next 会错误地继续执行。 + unsafe { (*state_ptr).phase = AsyncGenPhase::AwaitSuspended }; self.snapshot_async_generator(state_ptr)?; self.restore_inline_state(saved); if let Some(req) = request { @@ -779,14 +783,25 @@ fn async_gen_yield_unwrap_closure(vm: &mut Vm, args: &[u8]) -> NativeResult { let obj = unsafe { &*ctx.as_js_object_ptr() }; if obj.is_async_generator_obj() { let state = vm.async_gen_state_ptr(ctx); - // 仅在仍挂起在 yield 点(未被新的 next 恢复)时关闭。 - if unsafe { (*state).phase } == AsyncGenPhase::YieldSuspended { + // 仅在仍挂起在 yield 点(unwrap 前 AwaitSuspended,未被新 next 恢复)时关闭。 + if unsafe { (*state).phase } == AsyncGenPhase::AwaitSuspended { unsafe { (*state).phase = AsyncGenPhase::Completed }; unsafe { (*state).result = JsValue::undefined() }; } } } } else { + // yield 值 unwrap 完成:恢复可让出状态(async_generator_start 在 AwaitSuspended + // 下不启动,须先转回 YieldSuspended 才能恢复执行继续 yield/完成)。 + if ctx.is_object() { + let obj = unsafe { &*ctx.as_js_object_ptr() }; + if obj.is_async_generator_obj() { + let state = vm.async_gen_state_ptr(ctx); + if unsafe { (*state).phase } == AsyncGenPhase::AwaitSuspended { + unsafe { (*state).phase = AsyncGenPhase::YieldSuspended }; + } + } + } let result = if raw { value } else { make_iter_result(vm, value, false) }; let _ = vm.resolve_promise(promise, result); } diff --git a/crates/oxide_vm/tests/async_tests.rs b/crates/oxide_vm/tests/async_tests.rs index 7f3b200..e4e436b 100644 --- a/crates/oxide_vm/tests/async_tests.rs +++ b/crates/oxide_vm/tests/async_tests.rs @@ -116,3 +116,46 @@ fn async_constructor_name() { fn async_class_method() { assert_eq!(eval("class A { async m(){ return await 7 } } new A().m()"), "7"); } + +#[test] +fn async_gen_rejected_yield_queued_next_gets_done() { + // P6:yield 值被拒 → 生成器 completed,unwrap 前排队的 next 得 {done:true}, + // 不得错误继续执行后续 yield。 + assert_eq!( + eval( + "async function* g() { yield Promise.reject('e1'); yield 'never'; } \ + async function run() { const it = g(); const p1 = it.next(); const p2 = it.next(); \ + let r1, r2; try { await p1; r1 = 'p1-ok'; } catch (e) { r1 = 'p1-rej:' + e; } \ + try { const v = await p2; r2 = 'p2:' + v.value + ':' + v.done; } catch (e) { r2 = 'p2-rej:' + e; } \ + return r1 + '|' + r2; } run()" + ), + "\"p1-rej:e1|p2:undefined:true\"" + ); +} + +#[test] +fn async_gen_normal_yield_awaits_value() { + // yield 一个 resolved promise:unwrap 完成后让出展开值;next(arg) 作为 yield 表达式值。 + assert_eq!( + eval( + "async function* g() { var v = yield Promise.resolve(9); yield v; } \ + async function run() { const it = g(); const a = (await it.next()).value; \ + const b = (await it.next(100)).value; return a + ',' + b; } run()" + ), + "\"9,100\"" + ); +} + +#[test] +fn async_gen_yield_then_next_continues() { + // 正常 yield 值(非 promise)后 next 继续执行到完成。 + assert_eq!( + eval( + "async function* g() { yield 1; yield 2; } \ + async function run() { const it = g(); const a = (await it.next()).value; \ + const b = (await it.next()).value; const c = (await it.next()).done; \ + return a + ',' + b + ',' + c; } run()" + ), + "\"1,2,true\"" + ); +}