From ac13b1a2857221383205344fd216930995e9e311 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 27 Sep 2026 18:06:36 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BC=82=E6=AD=A5=E7=94=9F=E6=88=90?= =?UTF-8?q?=E5=99=A8=20yield*=20=E8=B5=B0=E5=BC=82=E6=AD=A5=E8=BF=AD?= =?UTF-8?q?=E4=BB=A3=E5=8D=8F=E8=AE=AE=E6=8C=89=E8=A7=84=E8=8C=83=E7=BB=93?= =?UTF-8?q?=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/async_generator.rs | 394 ++++++++++++++++-- crates/oxide_vm/src/generator.rs | 73 ++++ crates/oxide_vm/src/vm/dispatch_loop.rs | 7 +- .../tests/async_gen_delegation_tests.rs | 326 +++++++++++++++ 4 files changed, 772 insertions(+), 28 deletions(-) create mode 100644 crates/oxide_vm/tests/async_gen_delegation_tests.rs diff --git a/crates/oxide_vm/src/async_generator.rs b/crates/oxide_vm/src/async_generator.rs index 5a0a6ce2..fd2b6087 100644 --- a/crates/oxide_vm/src/async_generator.rs +++ b/crates/oxide_vm/src/async_generator.rs @@ -14,7 +14,7 @@ use std::collections::VecDeque; -use oxide_builtins::iterator::make_iter_result; +use oxide_builtins::iterator::{is_callable, make_iter_result}; use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_runtime_api::{NativeResult, VmHost}; use oxide_types::object::{Cell, JsObject, NativeFnPtr, PropAttributes}; @@ -46,6 +46,19 @@ pub(crate) struct AsyncGenRequest { pub reject: JsValue, } +/// `yield*` 委托协议步(异步生成器):内层 next/return/throw 的 promise 结算后, +/// 步进取续闭包按步推进协议状态机。纯枚举(零 JsValue 字段,零 GC 面): +/// 结算值经反应实参传递,跨微任务存活由 job 队列 GC 根保证。 +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum DelegateStep { + /// 等内层 next(v) 或 throw(e) 的 promise 结算(两者结算逻辑同构)。 + Inner, + /// 等内层 return(v) 的 promise 结算(外层 return 请求)。 + Return, + /// 等内层 return()(close)的 promise 结算(外层 throw 请求,内层无 throw 方法)。 + Close, +} + /// 异步生成器挂起时的完整执行上下文快照 + 请求队列。 /// /// 存在异步生成器对象 `native_data`(`Box`),跨 next()/微任务存活;其中所有 @@ -64,6 +77,8 @@ pub(crate) struct AsyncGeneratorState { pub queue: VecDeque, /// 正在处理的请求:yield 让出 / await 挂起 / 完成时结算。 pub current: Option, + /// 委托协议步:步进取续闭包消费后恢复;恢复入口恒为 None(纯枚举,零 GC 面)。 + pub delegate_pending: Option, /// 挂起时的执行上下文(regs/pc/bytecode/各栈段/迭代器/在途异常)。 pub suspended: crate::suspended::SuspendedFrame, } @@ -74,6 +89,8 @@ const AG_REJECT_PROP: &str = "__oxide_async_gen_reject__"; const AG_YIELD_PROMISE_PROP: &str = "__oxide_async_gen_yield_promise__"; /// yield 值 unwrap 恢复闭包上区分委托透传(raw)的属性名。 const AG_YIELD_RAW_PROP: &str = "__oxide_async_gen_yield_raw__"; +/// 委托步进取续闭包上存协议步的属性名。 +const AG_STEP_PROP: &str = "__oxide_async_gen_delegate_step__"; impl Vm { /// 调用异步生成器函数返回的迭代器对象:创建异步生成器实例、挂状态并执行 @@ -96,6 +113,7 @@ impl Vm { result: JsValue::undefined(), queue: VecDeque::new(), current: None, + delegate_pending: None, suspended: crate::suspended::SuspendedFrame::new_empty(), }); obj_ref.set_native_data(Box::into_raw(state) as *mut u8); @@ -185,7 +203,7 @@ impl Vm { } /// 取出异步生成器对象的状态指针(调用方须先校验 `is_async_generator_obj`)。 - fn async_gen_state_ptr(&self, gen_val: JsValue) -> *mut AsyncGeneratorState { + pub(crate) fn async_gen_state_ptr(&self, gen_val: JsValue) -> *mut AsyncGeneratorState { unsafe { (*gen_val.as_js_object_ptr()).native_data() as *mut AsyncGeneratorState } } @@ -324,9 +342,12 @@ impl Vm { return Err("async generator suspended across runs is no longer valid".into()); } state.phase = AsyncGenPhase::Running; + // 入口不变量:步进取续闭包先消费协议步再调本函数。 + debug_assert!(state.delegate_pending.is_none()); } - // 委托恢复:把 next/return/throw 请求转发给内层迭代器。 + // 委托恢复:把 next/return/throw 请求转发给内层异步迭代器,登记步进取续 + // 闭包等 promise 结算后经快照分支挂起(AwaitSuspended)。 if self.delegated_iterator.is_some() { let mode = { let state = unsafe { &mut *state_ptr }; @@ -336,7 +357,7 @@ impl Vm { .map(|c| c.mode) .unwrap_or(GeneratorResumeMode::Next(JsValue::undefined())) }; - let forwarded = self.delegate_forward(mode); + let forwarded = self.delegate_forward_async(mode, gen_val); match forwarded { Err(e) => { let exc = self @@ -346,29 +367,14 @@ impl Vm { self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); return self.finish_async_gen_exc(state_ptr, saved, gen_val, exc); } - Ok(DelegateOutcome::Suspend { value }) => { - self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); - // AsyncGeneratorYield:委托让出值经 promise unwrap 后原样透传。 - let value_promise = if self.is_promise_value(value) { - value - } else { - let (p, _, _) = self.new_promise_capability(); - let _ = self.resolve_promise(p, value); - p - }; - let request = { - let state = unsafe { &mut *state_ptr }; - state.current.take() - }; - // 委托让出同样在 unwrap 前不可恢复(AsyncGeneratorYield 先 Await)。 + Ok(DelegateOutcome::Suspend { .. }) => { + // 协议步等待(delegate_pending 已置):内层 promise 结算前 body 不得 + // 继续,快照挂起状态后返回,等步进取续闭包(微任务)恢复。本分支在 + // dispatch 之前返回,调度标志须在此恢复(dispatch 后的恢复不会执行)。 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); self.restore_inline_state(saved); - if let Some(req) = request { - let fulfill_fn = self.make_async_gen_yield_unwrap_fn(gen_val, req.promise, true, false); - let reject_fn = self.make_async_gen_yield_unwrap_fn(gen_val, req.promise, true, true); - let _ = self.perform_promise_then(value_promise, fulfill_fn, reject_fn); - } return Ok(()); } Ok(DelegateOutcome::Continue { value }) => { @@ -428,7 +434,6 @@ impl Vm { // YIELD 让出:快照挂起状态,让出值经 promise unwrap 后结算当前请求。 if let Some(value) = self.generator_suspended.take() { - let delegating = self.delegated_iterator.is_some(); // AsyncGeneratorYield:让出值 PromiseResolve 包装后 await 展开 // (yield 一个 rejected promise 时 next() 的 promise 被 reject)。 let value_promise = if self.is_promise_value(value) { @@ -449,8 +454,8 @@ impl Vm { self.snapshot_async_generator(state_ptr)?; self.restore_inline_state(saved); if let Some(req) = request { - let fulfill_fn = self.make_async_gen_yield_unwrap_fn(gen_val, req.promise, delegating, false); - let reject_fn = self.make_async_gen_yield_unwrap_fn(gen_val, req.promise, delegating, true); + let fulfill_fn = self.make_async_gen_yield_unwrap_fn(gen_val, req.promise, false, false); + let reject_fn = self.make_async_gen_yield_unwrap_fn(gen_val, req.promise, false, true); let _ = self.perform_promise_then(value_promise, fulfill_fn, reject_fn); } return Ok(()); @@ -478,6 +483,95 @@ impl Vm { } } + /// `yield*` 委托单步转发(异步生成器):把外层请求转发给内层异步迭代器, + /// 登记步进取续闭包等 promise 结算。 + /// + /// # 步骤 + /// 1. Next/Throw:取内层 next/throw 方法并以请求值调用;无 throw 方法时回退 + /// 内层 return()(无参 close),再无 return 方法时抛 TypeError 入 body。 + /// 2. Return:取内层 return 方法;缺失时以请求值直接完成(Complete); + /// 可调用时调用。 + /// 3. 调用结果 promise 直通或 PromiseResolve 包装,登记步进取续闭包 + /// (Inner/Return/Close),置 `delegate_pending`。 + /// + /// # 返回值 + /// - `Ok(Suspend)`:协议步等待(`delegate_pending` 已置,调用方经快照分支挂起); + /// - `Ok(Complete)`:内层无 return 方法,外层以请求值直接完成; + /// - `Ok(Unwind)`:TypeError 已抛入 body(内层无 throw/return 方法),继续 dispatch; + /// - `Err`:内层调用同步抛错,调用方走既有 Err 通道。 + fn delegate_forward_async( + &mut self, mode: GeneratorResumeMode, gen_val: JsValue, + ) -> Result { + let iterator = match self.delegated_iterator { + Some(it) => it, + None => return Ok(DelegateOutcome::Unwind), + }; + let (method, arg, mut step) = match mode { + GeneratorResumeMode::Next(v) => ("next", v, DelegateStep::Inner), + GeneratorResumeMode::Throw(e) => ("throw", e, DelegateStep::Inner), + GeneratorResumeMode::Return(v) => ("return", v, DelegateStep::Return), + }; + let iter_obj = unsafe { &*iterator.as_js_object_ptr() }; + let method_si = self.kernel_core.perm_interner().intern(method).0; + let method_fn = self.ordinary_get(iter_obj, method_si, iterator)?; + let inner_result = if is_callable(method_fn) { + self.call_function_sync(method_fn, iterator, &[arg])? + } else { + match mode { + GeneratorResumeMode::Next(_) => { + return Err(self.error_message_text("TypeError", "iterator.next is not callable")); + } + GeneratorResumeMode::Return(v) => { + // 内层无 return 方法:外层直接完成,值为请求值。 + self.delegated_iterator = None; + return Ok(DelegateOutcome::Complete { value: v }); + } + GeneratorResumeMode::Throw(_) => { + // 内层无 throw 方法:回退内层 return()(无参 close),步为 Close。 + let ret_si = self.kernel_core.perm_interner().intern("return").0; + let ret_fn = self.ordinary_get(iter_obj, ret_si, iterator)?; + if !is_callable(ret_fn) { + // 内层亦无 return 方法:close 为 no-op,抛 TypeError 入 body(协议违规)。 + self.delegated_iterator = None; + let exc = oxide_builtins::error::create_type_error( + self, + "yield* protocol violation: iterator does not have a throw method", + ); + self.exception_value = Some(exc); + self.pending_error_kind = Some(self.thrown_error_kind(exc)); + return match self.unwind() { + Ok(()) => Ok(DelegateOutcome::Unwind), + Err(e) => Err(e), + }; + } + // 步为 Close:close 成功后抛 TypeError 入 body,拒绝则抛拒绝原因入 body。 + step = DelegateStep::Close; + self.call_function_sync(ret_fn, iterator, &[])? + } + } + }; + // Await:结果 promise 直通,非 promise 经 PromiseResolve 包装。 + let result_promise = if self.is_promise_value(inner_result) { + inner_result + } else { + match self.promise_resolve(inner_result) { + Ok(p) => p, + Err(exc) => { + self.last_uncaught_value = Some(exc); + return Err(String::new()); + } + } + }; + // 登记步进取续闭包;先 Call 再置 pending(同步抛错不悬挂)。 + let fulfill_fn = self.make_delegate_step_closure(gen_val, step, false); + let reject_fn = self.make_delegate_step_closure(gen_val, step, true); + let _ = self.perform_promise_then(result_promise, fulfill_fn, reject_fn); + self.delegated_iterator = Some(iterator); + let state = unsafe { &mut *self.async_gen_state_ptr(gen_val) }; + state.delegate_pending = Some(step); + Ok(DelegateOutcome::Suspend { value: JsValue::undefined() }) + } + /// 恢复嵌套 dispatch 覆盖的 VM 标志(dispatch 前提前返回的路径用)。 fn restore_async_gen_flags( &mut self, prev_ctx: Option, prev_gen_ctx: Option, prev_gd: bool, prev_ad: bool, @@ -599,6 +693,26 @@ impl Vm { self.add_fn_name_length(obj, "", 1); JsValue::from_js_object(ptr) } + + /// 构造委托步进取续闭包:携带目标异步生成器上下文、协议步与 fulfill/reject 角色。 + pub(crate) fn make_delegate_step_closure(&mut self, ctx: JsValue, step: DelegateStep, reject_role: bool) -> JsValue { + let fn_proto = self.session.builtin_world().fn_proto_val(); + let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); + func.set_function(true); + // SAFETY: async_gen_delegate_step_closure 是 NativeFn 函数项。 + func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(async_gen_delegate_step_closure as *const ()) })); + func.set_native_arg_count(1); + let ptr = self.alloc_object(func); + let obj = unsafe { &mut *ptr }; + let ctx_si = self.kernel_core.perm_interner().intern(crate::async_func::ASYNC_CTX_PROP).0; + self.set_or_create_prop_value(obj, ctx_si, ctx); + let step_si = self.kernel_core.perm_interner().intern(AG_STEP_PROP).0; + self.set_or_create_prop_value(obj, step_si, JsValue::int(step as i32)); + let role_si = self.kernel_core.perm_interner().intern(AG_REJECT_PROP).0; + self.set_or_create_prop_value(obj, role_si, JsValue::bool(reject_role)); + self.add_fn_name_length(obj, "", 1); + JsValue::from_js_object(ptr) + } } /// await 恢复闭包:读自身 prop 的异步生成器上下文与角色,恢复异步生成器继续执行。 @@ -695,6 +809,231 @@ fn async_gen_yield_unwrap_closure(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } +/// 委托步进取续闭包:内层 next/return/throw 的 promise 结算时推进委托协议一步。 +/// +/// 读自身 prop 的协议步与角色,校验状态盒 `delegate_pending` 匹配(防陈旧闭包), +/// 再按步分支(规范:`yield*` 交付值不做二次 Await,失败一律抛入 body 可 catch): +/// - Inner:结算值判 done——done 时委托值注入 body 恢复;未 done 时委托值原样 +/// 让出(`{value, done:false}` 结算当前请求);非对象或拒绝原因抛入 body。 +/// - Return:done 时以委托值完成生成器(return completion);未 done 时委托值 +/// 原样让出;拒绝原因 / 非对象抛入 body。 +/// - Close:close 结算后抛 TypeError 入 body(协议违规);close 拒绝则抛拒绝原因。 +fn async_gen_delegate_step_closure(vm: &mut Vm, args: &[u8]) -> NativeResult { + let callee = vm.reg(254); + if !callee.is_object() { + return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "delegate step handler is invalid")); + } + let callee_obj = unsafe { &*callee.as_js_object_ptr() }; + let ctx_si = vm.kernel_core.perm_interner().intern(crate::async_func::ASYNC_CTX_PROP).0; + let ctx = vm.resolve_property(callee_obj, ctx_si).unwrap_or(JsValue::undefined()); + let step_si = vm.kernel_core.perm_interner().intern(AG_STEP_PROP).0; + let step = match vm.resolve_property(callee_obj, step_si) { + Some(v) if v.is_int() => v.as_int(), + _ => return NativeResult::Ok(JsValue::undefined()), + }; + let step = match step { + 0 => DelegateStep::Inner, + 1 => DelegateStep::Return, + 2 => DelegateStep::Close, + _ => return NativeResult::Ok(JsValue::undefined()), + }; + let role_si = vm.kernel_core.perm_interner().intern(AG_REJECT_PROP).0; + let is_reject = vm + .resolve_property(callee_obj, role_si) + .is_some_and(oxide_runtime_api::to_boolean); + let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + if !ctx.is_object() { + return NativeResult::Ok(JsValue::undefined()); + } + let obj = unsafe { &*ctx.as_js_object_ptr() }; + if !obj.is_async_generator_obj() { + return NativeResult::Ok(JsValue::undefined()); + } + let state_ptr = vm.async_gen_state_ptr(ctx); + // 陈旧防御:状态盒的在途协议步必须与本闭包一致。 + if unsafe { (*state_ptr).delegate_pending } != Some(step) { + return NativeResult::Ok(JsValue::undefined()); + } + match step { + DelegateStep::Inner => { + // 失败子路径:拒绝原因 / 非对象结算值 / done 或 value getter 抛错, + // 均抛入 body(body 的 try/catch 可捕获)。 + let failure = if is_reject { + Some(value) + } else if !value.is_object() { + Some(oxide_builtins::error::create_type_error(vm, "iterator result is not an object")) + } else { + None + }; + if let Some(exc) = failure { + throw_delegate_into_body(vm, ctx, state_ptr, exc); + return NativeResult::Ok(JsValue::undefined()); + } + let result_obj = unsafe { &*value.as_js_object_ptr() }; + let done_si = vm.kernel_core.perm_interner().intern("done").0; + let done = match vm.ordinary_get(result_obj, done_si, value) { + Ok(d) => oxide_runtime_api::to_boolean(d), + Err(e) => { + let exc = vm.last_uncaught_value.take().unwrap_or_else(|| { + oxide_builtins::error::create_from_text(vm, &e) + }); + throw_delegate_into_body(vm, ctx, state_ptr, exc); + return NativeResult::Ok(JsValue::undefined()); + } + }; + let value_si = vm.kernel_core.perm_interner().intern("value").0; + let inner_value = match vm.ordinary_get(result_obj, value_si, value) { + Ok(v) => v, + Err(e) => { + let exc = vm.last_uncaught_value.take().unwrap_or_else(|| { + oxide_builtins::error::create_from_text(vm, &e) + }); + throw_delegate_into_body(vm, ctx, state_ptr, exc); + return NativeResult::Ok(JsValue::undefined()); + } + }; + if done { + // 委托 done:委托值注入 body,body 从 yield* 续点继续。清挂起帧的 + // 委托迭代器(restore_into 会把它写回 VM,不清则恢复后再走 S0 转发)。 + let state = unsafe { &mut *state_ptr }; + if let Some(req) = state.current.as_mut() { + req.mode = GeneratorResumeMode::Next(inner_value); + } + state.delegate_pending = None; + state.suspended.delegated_iterator = None; + match vm.resume_async_generator(ctx) { + Ok(()) => NativeResult::Ok(JsValue::undefined()), + Err(e) => NativeResult::Err(oxide_builtins::error::create_from_text(vm, &e)), + } + } else { + // 委托未 done:交付值原样让出(规范 yield* 不对值 Await),委托持续。 + delegate_yield_raw(vm, ctx, state_ptr, inner_value); + NativeResult::Ok(JsValue::undefined()) + } + } + DelegateStep::Return => { + // return 分支:拒绝原因 / 非对象结算值 / getter 抛错均抛入 body; + // done:true 以委托值完成生成器;done:false 原样让出委托值。 + let (done, inner_value) = match read_delegate_result(vm, value) { + DelegateResult::Failure(exc) => { + throw_delegate_into_body(vm, ctx, state_ptr, exc); + return NativeResult::Ok(JsValue::undefined()); + } + DelegateResult::Ok(done, inner_value) => (done, inner_value), + }; + if done { + // 以委托值完成(return completion,finally 穿越):改写请求模式为 + // Return(委托值) 后恢复,注入路径走 complete_generator_return。 + let state = unsafe { &mut *state_ptr }; + if let Some(req) = state.current.as_mut() { + req.mode = GeneratorResumeMode::Return(inner_value); + } + state.delegate_pending = None; + state.suspended.delegated_iterator = None; + match vm.resume_async_generator(ctx) { + Ok(()) => NativeResult::Ok(JsValue::undefined()), + Err(e) => NativeResult::Err(oxide_builtins::error::create_from_text(vm, &e)), + } + } else { + delegate_yield_raw(vm, ctx, state_ptr, inner_value); + NativeResult::Ok(JsValue::undefined()) + } + } + DelegateStep::Close => { + // close 分支(内层无 throw 方法):close 结算后抛 TypeError 入 body + // (协议违规);close 拒绝则抛拒绝原因入 body。 + let exc = if is_reject { + value + } else { + oxide_builtins::error::create_type_error( + vm, + "yield* protocol violation: iterator does not have a throw method", + ) + }; + throw_delegate_into_body(vm, ctx, state_ptr, exc); + NativeResult::Ok(JsValue::undefined()) + } + } +} + +/// 委托结算值读取:done 与 value 的 getter 抛错经 `last_uncaught_value` 取原值。 +enum DelegateResult { + /// 结算值读取成功(done 标志与委托值)。 + Ok(bool, JsValue), + /// 结算值读取失败(须抛入 body 的异常值)。 + Failure(JsValue), +} + +fn read_delegate_result(vm: &mut Vm, value: JsValue) -> DelegateResult { + if !value.is_object() { + return DelegateResult::Failure(oxide_builtins::error::create_type_error( + vm, + "iterator result is not an object", + )); + } + let result_obj = unsafe { &*value.as_js_object_ptr() }; + let done_si = vm.kernel_core.perm_interner().intern("done").0; + let done = match vm.ordinary_get(result_obj, done_si, value) { + Ok(d) => oxide_runtime_api::to_boolean(d), + Err(e) => { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_from_text(vm, &e)); + return DelegateResult::Failure(exc); + } + }; + let value_si = vm.kernel_core.perm_interner().intern("value").0; + let inner_value = match vm.ordinary_get(result_obj, value_si, value) { + Ok(v) => v, + Err(e) => { + let exc = vm + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_from_text(vm, &e)); + return DelegateResult::Failure(exc); + } + }; + DelegateResult::Ok(done, inner_value) +} + +/// 委托让出:内层值原样交付(规范 `yield*` 不对交付值 Await),以 `{value, done:false}` +/// 结算当前请求、恢复可让出状态、驱动队列。 +/// +/// 微任务上下文专用:不恢复生成器执行(生成器已让出,等待下一个 next/return/throw)。 +fn delegate_yield_raw(vm: &mut Vm, ctx: JsValue, state_ptr: *mut AsyncGeneratorState, value: JsValue) { + { + let state = unsafe { &mut *state_ptr }; + state.delegate_pending = None; + if unsafe { (*state_ptr).phase } == AsyncGenPhase::AwaitSuspended { + unsafe { (*state_ptr).phase = AsyncGenPhase::YieldSuspended }; + } + } + let request = unsafe { (*state_ptr).current.take() }; + if let Some(req) = request { + let result = make_iter_result(vm, value, false); + let _ = vm.resolve_promise(req.promise, result); + } + if !unsafe { (*state_ptr).queue.is_empty() } { + let _ = vm.async_generator_start(ctx); + } +} + +/// 把委托协议异常抛入 body:当前请求改写为 Throw(exc)、清协议状态后恢复 +/// (body 的 catch 可捕获;无 catch 时既有 finish 通道 reject 请求并完成)。 +fn throw_delegate_into_body(vm: &mut Vm, ctx: JsValue, state_ptr: *mut AsyncGeneratorState, exc: JsValue) { + { + let state = unsafe { &mut *state_ptr }; + if let Some(req) = state.current.as_mut() { + req.mode = GeneratorResumeMode::Throw(exc); + } + state.delegate_pending = None; + // 清挂起帧的委托迭代器(restore_into 会把它写回 VM,不清则恢复后再走 S0 转发)。 + state.suspended.delegated_iterator = None; + } + let _ = vm.resume_async_generator(ctx); +} + /// `%AsyncGeneratorPrototype%.next`:入队 next 请求并返回其结果 Promise。 pub(crate) fn async_generator_next(vm: &mut Vm, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); @@ -1188,6 +1527,7 @@ pub(crate) fn clone_async_generator_native_with_rewrite( resolve: rewrite(req.resolve), reject: rewrite(req.reject), }), + delegate_pending: state.delegate_pending, suspended: state.suspended.clone_with_rewrite(rewrite), }; new.set_native_data(Box::into_raw(Box::new(cloned)) as *mut u8); diff --git a/crates/oxide_vm/src/generator.rs b/crates/oxide_vm/src/generator.rs index e58a57c4..19cefe17 100644 --- a/crates/oxide_vm/src/generator.rs +++ b/crates/oxide_vm/src/generator.rs @@ -77,6 +77,9 @@ pub(crate) enum YieldStarOutcome { Continue(JsValue), /// 异常已展开到外层 catch/finally:继续 dispatch。 Unwind, + /// 异步生成器委托:内层迭代已登记微任务队列,外层挂起让出 + /// (数据经 async_gen_suspended 信号与状态盒 delegate_pending 传递)。 + AsyncSuspend, } /// `yield*` 委托转发(生成器恢复时)的结局。 @@ -429,6 +432,11 @@ impl Vm { /// - 内层 next 同步执行,可能压入/弹出调用帧。 /// - 挂起时设置 `delegated_iterator`,由 snapshot_generator 存入生成器状态。 pub(crate) fn dispatch_yield_star(&mut self, rd: usize) -> Result { + // 异步生成器委托走异步迭代协议(GetAsyncIterator + promise 结算), + // 同步路径原样保留。 + if self.async_gen_dispatch { + return self.dispatch_yield_star_async(rd); + } let inner = self.regs[rd]; let iterator = match oxide_builtins::iterator::make_iterator_for_value_without_return(self, inner) { Ok(it) => it, @@ -475,6 +483,71 @@ impl Vm { } } + /// `yield*` 异步委托首次推进:GetAsyncIterator 取内层异步迭代器并推进一次。 + /// + /// # 步骤 + /// 1. 经 GetAsyncIterator 取内层迭代器(`@@asyncIterator` 优先,缺失回退同步 + /// 迭代器包 AsyncFromSyncIterator)。 + /// 2. 调内层 next(undefined) 得结果 R0。 + /// 3. Await 结算:R0 为 promise 时直通,否则 PromiseResolve 包装。 + /// 4. 登记步进取续闭包(Inner 步)到结果 promise,置 + /// `delegated_iterator` 与 `delegate_pending` 并挂起。 + /// + /// # 边界 + /// - 任一步同步抛错经 `yield_star_raise` 展开(可被 body catch); + /// 顺序约束为先 Call 再置 pending 与挂起标志,同步抛错不悬挂。 + /// + /// # 副作用 + /// - 内层 next 同步执行,可能压入/弹出调用帧。 + /// - 登记步进取续闭包(微任务队列 GC 根持有)。 + /// - 置 `delegated_iterator`、`delegate_pending` 与 `async_gen_suspended`。 + pub(crate) fn dispatch_yield_star_async(&mut self, rd: usize) -> Result { + let inner = self.regs[rd]; + let iterator = match crate::async_from_sync::make_async_iterator(self, inner) { + Ok(it) => it, + Err(exc) => { + self.last_uncaught_value = Some(exc); + return self.yield_star_raise(String::new()); + } + }; + let iter_obj = unsafe { &*iterator.as_js_object_ptr() }; + let next_si = self.kernel_core.perm_interner().intern("next").0; + let next_fn = match self.ordinary_get(iter_obj, next_si, iterator) { + Ok(f) => f, + Err(e) => return self.yield_star_raise(e), + }; + let result = if is_callable(next_fn) { + match self.call_function_sync(next_fn, iterator, &[JsValue::undefined()]) { + Ok(r) => r, + Err(e) => return self.yield_star_raise(e), + } + } else { + return self.yield_star_raise(self.error_message_text("TypeError", "iterator.next is not callable")); + }; + // Await:结果 promise 直通,非 promise 经 PromiseResolve 包装。 + let result_promise = if self.is_promise_value(result) { + result + } else { + match self.promise_resolve(result) { + Ok(p) => p, + Err(exc) => { + self.last_uncaught_value = Some(exc); + return self.yield_star_raise(String::new()); + } + } + }; + // 登记步进取续闭包(Inner 步);先 Call 再置 pending 与挂起标志。 + let gen_val = self.async_gen_context.expect("async yield* outside async generator"); + let fulfill_fn = self.make_delegate_step_closure(gen_val, crate::async_generator::DelegateStep::Inner, false); + let reject_fn = self.make_delegate_step_closure(gen_val, crate::async_generator::DelegateStep::Inner, true); + let _ = self.perform_promise_then(result_promise, fulfill_fn, reject_fn); + self.delegated_iterator = Some(iterator); + let state = unsafe { &mut *self.async_gen_state_ptr(gen_val) }; + state.delegate_pending = Some(crate::async_generator::DelegateStep::Inner); + self.async_gen_suspended = true; + Ok(YieldStarOutcome::AsyncSuspend) + } + /// `yield*` 委托单步转发:把外层恢复请求转发给内层迭代器。 /// /// # 步骤 diff --git a/crates/oxide_vm/src/vm/dispatch_loop.rs b/crates/oxide_vm/src/vm/dispatch_loop.rs index 19f25aa1..8ed9a963 100644 --- a/crates/oxide_vm/src/vm/dispatch_loop.rs +++ b/crates/oxide_vm/src/vm/dispatch_loop.rs @@ -830,7 +830,8 @@ impl Vm { OpCode::YIELD_STAR => { // `yield*` 委托:取内层迭代器并推进一步。 // 未 done → 挂起让出(存委托迭代器);done → 委托完成值写 reg 0 继续外层; - // unwind 捕获到异常 → 继续 dispatch(已展开到 catch/finally)。 + // unwind 捕获到异常 → 继续 dispatch(已展开到 catch/finally); + // 异步委托 → 内层迭代已登记微任务队列,内嵌 dispatch 返回。 match self.dispatch_yield_star(rd)? { crate::generator::YieldStarOutcome::Suspend(value) => { self.generator_suspended = Some(value); @@ -841,6 +842,10 @@ impl Vm { self.regs[0] = value; } crate::generator::YieldStarOutcome::Unwind => {} + crate::generator::YieldStarOutcome::AsyncSuspend => { + self.profiling.set_instruction_count(steps); + return Ok(JsValue::undefined()); + } } } diff --git a/crates/oxide_vm/tests/async_gen_delegation_tests.rs b/crates/oxide_vm/tests/async_gen_delegation_tests.rs new file mode 100644 index 00000000..fe6db4fe --- /dev/null +++ b/crates/oxide_vm/tests/async_gen_delegation_tests.rs @@ -0,0 +1,326 @@ +//! 异步生成器 `yield*` 委托协议运行时测试:GetAsyncIterator 取迭代器、 +//! 内层 next/return/throw 的 promise 结算、交付值二次 Await 展开、 +//! return/throw 转发与失败路径(拒绝替换、TypeError 抛入 body 可 catch)。 + +use std::sync::Arc; + +use oxide_compiler::compiler::Compiler; +use oxide_parser::Allocator; +use oxide_vm::promise::promise_settled_value; +use oxide_vm::vm::Vm; + +/// 执行源码并格式化顶层结果;Promise 结果取其 drain 后的结算值。 +fn eval(source: &str) -> String { + let allocator = Allocator::default(); + let program = match oxide_parser::parse(&allocator, source) { + Ok(p) => p, + Err(e) => return format!("parse error: {}", e[0].message), + }; + let module = match Compiler::new().compile(&program) { + Ok(m) => m, + Err(e) => return format!("compile error: {e}"), + }; + let mut vm = Vm::new(); + match vm.run(&Arc::new(module)) { + Ok(result) => format_value(&vm, result), + Err(e) => format!("vm error: {e}"), + } +} + +fn format_value(vm: &Vm, val: oxide_vm::JsValue) -> String { + if val.is_string() { + format!("\"{}\"", vm.lookup_str(val).unwrap_or_default()) + } else if val.is_object() { + let obj = unsafe { &*val.as_js_object_ptr() }; + if obj.is_promise_obj() { + match promise_settled_value(obj) { + Some((true, v)) => format_value(vm, v), + Some((false, v)) => format!("", format_value(vm, v)), + None => "".to_string(), + } + } else if obj.is_array() { + "[array]".to_string() + } else { + "[object]".to_string() + } + } else { + format!("{val}") + } +} + +// T1:基本委托——首个 next() 交付数字 7(非 IteratorResult 对象、非 promise), +// 第二个 next() 委托完成。 +#[test] +fn t1_basic_async_iterable() { + assert_eq!( + eval( + "async function* g() { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; return Promise.resolve({ value: 7, done: false }); } \ + return Promise.resolve({ done: true }); }, \ + return() { return Promise.resolve({ done: true }); } }; } }; } \ + (async function run() { const it = g(); const r1 = await it.next(); \ + const r2 = await it.next(); \ + return [r1.value, typeof r1.value, r1.done, r2.value, r2.done].join(','); })()" + ), + "\"7,number,false,,true\"" + ); +} + +// T2:多元素异步可迭代(顺序 promise 链),逐元素交付后 done。 +#[test] +fn t2_multi_element_async_iterable() { + assert_eq!( + eval( + "async function* g() { yield* { [Symbol.asyncIterator]() { \ + const items = [1, 2]; let i = 0; return { \ + next() { if (i < items.length) { return Promise.resolve({ value: items[i++], done: false }); } \ + return Promise.resolve({ done: true }); } }; } }; } \ + (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; })()" + ), + "\"1,2,true\"" + ); +} + +// T3:同步可迭代进异步生成器——AsyncFromSyncIterator 回退路径同语义。 +#[test] +fn t3_sync_iterable_fallback() { + assert_eq!( + eval( + "async function* g() { yield* [1, 2, 3]; } \ + (async function run() { const it = g(); \ + const a = (await it.next()).value; const b = (await it.next()).value; \ + const c = (await it.next()).value; const d = (await it.next()).done; \ + return a + ',' + b + ',' + c + ',' + d; })()" + ), + "\"1,2,3,true\"" + ); +} + +// T4:二次 await——内层 next 交付 {value: Promise.resolve(5), done: false}, +// next() 交付展开后的 5。 +#[test] +fn t4_double_await_unwraps_value() { + assert_eq!( + eval( + "async function* g() { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; \ + return Promise.resolve({ value: Promise.resolve(5), done: false }); } \ + return Promise.resolve({ done: true }); } }; } }; } \ + (async function run() { const it = g(); return (await it.next()).value; })()" + ), + "5" + ); +} + +// T5:交付值为拒绝 promise——yield* 不对值 Await,值原样让出,next() 以 +// {value: 拒绝promise, done: false} 结算(不拒绝)。 +#[test] +fn t5_rejected_yield_value_yielded_as_is() { + assert_eq!( + eval( + "async function* g() { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; \ + return Promise.resolve({ value: Promise.reject('e5'), done: false }); } \ + return Promise.resolve({ done: true }); } }; } }; } \ + (async function run() { const it = g(); const r1 = await it.next(); \ + return r1.done + ':' + (r1.value instanceof Promise); })()" + ), + "\"false:true\"" + ); +} + +// T6:内层 next 拒绝且 body catch——拒绝原因抛入委托 body,可被捕获。 +#[test] +fn t6_inner_next_reject_thrown_into_body() { + assert_eq!( + eval( + "async function* g() { try { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; return Promise.reject('e6'); } \ + return Promise.resolve({ done: true }); } }; } }; } \ + catch (e) { return 'caught:' + e; } yield 'never'; } \ + (async function run() { const it = g(); const r = await it.next(); \ + return r.value + ':' + r.done; })()" + ), + "\"caught:e6:true\"" + ); +} + +// T7:内层结果非对象——TypeError 抛入委托 body,可被捕获。 +#[test] +fn t7_non_object_result_thrown_into_body() { + assert_eq!( + eval( + "async function* g() { try { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; return Promise.resolve(42); } \ + return Promise.resolve({ done: true }); } }; } }; } \ + catch (e) { return 'caught:' + e.name; } } \ + (async function run() { const it = g(); const r = await it.next(); \ + return r.value + ':' + r.done; })()" + ), + "\"caught:TypeError:true\"" + ); +} + +// T8:.return(v) 转发——内层有 return 方法时以请求值调用,done 时生成器以 +// 委托值(内层 return 结果值)完成(非请求值)。 +#[test] +fn t8_return_forwarded_completes_with_inner_value() { + assert_eq!( + eval( + "var log = []; \ + async function* g() { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; return Promise.resolve({ value: 1, done: false }); } \ + return Promise.resolve({ done: true }); }, \ + return(v) { log.push('ret:' + v); return Promise.resolve({ value: 'inner', done: true }); } }; } }; } \ + (async function run() { const it = g(); await it.next(); \ + const r = await it.return(9); return r.value + ':' + r.done + ':' + log.join(','); })()" + ), + "\"inner:true:ret:9\"" + ); +} + +// T9:.return(v) 内层无 return 方法——同步完成路径,直接以请求值完成。 +#[test] +fn t9_return_without_inner_return() { + assert_eq!( + eval( + "async function* g() { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; return Promise.resolve({ value: 1, done: false }); } \ + return Promise.resolve({ done: true }); } }; } }; } \ + (async function run() { const it = g(); await it.next(); \ + const r = await it.return(9); return r.value + ':' + r.done; })()" + ), + "\"9:true\"" + ); +} + +// T10:.throw(e) 转发——内层有 throw 方法时以 e 调用,未 done 时交付内层值。 +#[test] +fn t10_throw_forwarded_to_inner() { + assert_eq!( + eval( + "async function* g() { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; return Promise.resolve({ value: 1, done: false }); } \ + return Promise.resolve({ done: true }); }, \ + throw(e) { return Promise.resolve({ value: 'x:' + e, done: false }); } }; } }; } \ + (async function run() { const it = g(); await it.next(); \ + const r = await it.throw('e10'); return r.value + ':' + r.done; })()" + ), + "\"x:e10:false\"" + ); +} + +// T11:.throw(e) 内层无 throw 方法——close 成功后抛 TypeError 入 body(协议违规, +// 非原异常),可 catch。 +#[test] +fn t11_throw_without_inner_throw_close_then_typeerror() { + assert_eq!( + eval( + "async function* g() { try { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; return Promise.resolve({ value: 1, done: false }); } \ + return Promise.resolve({ done: true }); }, \ + return() { return Promise.resolve({ done: true }); } }; } }; } \ + catch (e) { return 'caught:' + e.name; } } \ + (async function run() { const it = g(); await it.next(); \ + const r = await it.throw('e11'); return r.value + ':' + r.done; })()" + ), + "\"caught:TypeError:true\"" + ); +} + +// T12:close 拒绝——.throw(e) 内层无 throw 方法且内层 return() 拒绝时,拒绝原因 +// 抛入 body(可 catch),生成器以 catch 返回值完成,throw 请求以该值结算。 +#[test] +fn t12_close_rejection_thrown_into_body() { + assert_eq!( + eval( + "async function* g() { try { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; return Promise.resolve({ value: 1, done: false }); } \ + return Promise.resolve({ done: true }); }, \ + return() { return Promise.reject('r12'); } }; } }; } \ + catch (e) { return 'caught:' + e; } } \ + (async function run() { const it = g(); await it.next(); \ + const r = await it.throw('e12'); return r.value + ':' + r.done; })()" + ), + "\"caught:r12:true\"" + ); +} + +// T13:return 分支 done:false——内层 return 结果非 done 时,委托值原样让出 +// (return 请求以 {value: 9, done: false} 结算)。 +#[test] +fn t13_return_not_done_yields_inner_value() { + assert_eq!( + eval( + "async function* g() { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; return Promise.resolve({ value: 1, done: false }); } \ + return Promise.resolve({ done: true }); }, \ + return(v) { return Promise.resolve({ value: 9, done: false }); } }; } }; } \ + (async function run() { const it = g(); await it.next(); \ + const r = await it.return(5); return r.value + ':' + r.done; })()" + ), + "\"9:false\"" + ); +} + +// T14:微任务时序——内层结算先于外层 next() 结算(push 序数组断言交错)。 +#[test] +fn t14_microtask_ordering() { + assert_eq!( + eval( + "var order = []; \ + async function* g() { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; \ + return new Promise(r => { order.push('inner'); r({ value: 1, done: false }); }); } \ + return Promise.resolve({ done: true }); } }; } }; } \ + (async function run() { const it = g(); const p = it.next(); \ + order.push('req'); const r = await p; order.push('outer'); \ + return order.join(','); })()" + ), + "\"inner,req,outer\"" + ); +} + +// 边界:委托不可迭代值——TypeError 抛入委托 body,可被捕获。 +#[test] +fn t15_yield_star_non_iterable_thrown_into_body() { + assert_eq!( + eval( + "async function* g() { try { yield* 42; } catch (e) { return 'caught:' + e.name; } } \ + (async function run() { const it = g(); const r = await it.next(); \ + return r.value + ':' + r.done; })()" + ), + "\"caught:TypeError:true\"" + ); +} + +// 边界:内层 next 同步抛错(非 promise 拒绝)——异常抛入委托 body,可被捕获。 +#[test] +fn t16_inner_next_sync_throw_thrown_into_body() { + assert_eq!( + eval( + "async function* g() { try { yield* { [Symbol.asyncIterator]() { \ + let first = true; return { \ + next() { if (first) { first = false; throw 'sync-e'; } \ + return Promise.resolve({ done: true }); } }; } }; } \ + catch (e) { return 'caught:' + e; } } \ + (async function run() { const it = g(); const r = await it.next(); \ + return r.value + ':' + r.done; })()" + ), + "\"caught:sync-e:true\"" + ); +}