diff --git a/crates/oxide_builtins/src/iterator.rs b/crates/oxide_builtins/src/iterator.rs index 6fabba9..c5fcf96 100644 --- a/crates/oxide_builtins/src/iterator.rs +++ b/crates/oxide_builtins/src/iterator.rs @@ -3,7 +3,7 @@ use oxide_types::object::JsObject; use oxide_types::private_key::make_well_known_symbol_key; use oxide_types::value::JsValue; -use oxide_runtime_api::{NativeResult, VmHost}; +use oxide_runtime_api::{to_object, NativeResult, VmHost}; const INNER_PROP: &str = "__inner__"; const INDEX_PROP: &str = "__index__"; @@ -26,7 +26,17 @@ pub fn iterator_from(vm: &mut H, args: &[u8]) -> NativeResult { /// 为任意值创建统一迭代器包装对象:String/Array/Map/Set 直接支持索引遍历, /// 其它对象则要求提供可调用的 `next`。不可迭代时返回 TypeError。 pub fn make_iterator_for_value(vm: &mut H, value: JsValue) -> Result { - match try_make_iterator(vm, value) { + match try_make_iterator_inner(vm, value, true) { + Ok(Some(iterator)) => Ok(iterator), + Ok(None) => Err(crate::error::create_type_error(vm, "value is not iterable")), + Err(err) => Err(err), + } +} + +/// 同 [`make_iterator_for_value`],但包装器不绑定 `return` 方法(`yield*` 委托专用): +/// 委托转发对内层迭代器延迟 GetMethod,避免创建包装器时访问内层 return getter。 +pub fn make_iterator_for_value_without_return(vm: &mut H, value: JsValue) -> Result { + match try_make_iterator_inner(vm, value, false) { Ok(Some(iterator)) => Ok(iterator), Ok(None) => Err(crate::error::create_type_error(vm, "value is not iterable")), Err(err) => Err(err), @@ -38,13 +48,17 @@ pub fn make_iterator_for_value(vm: &mut H, value: JsValue) -> Result< /// # 步骤 /// 1. 经迭代协议取内层迭代器(String/Array/Map/Set 直接作为内层,其余对象调用 /// `@@iterator` 或回退可调用的 `next`)。 -/// 2. 包装成统一迭代器对象(带 `next` 与 `return`),供调用方逐个取元素。 +/// 2. 包装成统一迭代器对象(带 `next` 与可选 `return`),供调用方逐个取元素。 /// /// # 返回值 /// - `Ok(Some(iterator))`:可迭代,返回包装器; /// - `Ok(None)`:不可迭代(调用方回退 array-like 路径); /// - `Err`:`@@iterator` getter/call 抛错,透传原异常值。 -pub(crate) fn try_make_iterator(vm: &mut H, value: JsValue) -> Result, JsValue> { +/// - `bind_return` 控制是否暴露 `return` 方法(for-of/解构的 IteratorClose 需要, +/// `yield*` 委托不需要且须避免创建时访问内层 return getter)。 +pub(crate) fn try_make_iterator_inner( + vm: &mut H, value: JsValue, bind_return: bool, +) -> Result, JsValue> { let inner = match get_iterator(vm, value) { Ok(Some(inner)) => inner, Ok(None) => return Ok(None), @@ -65,12 +79,10 @@ pub(crate) fn try_make_iterator(vm: &mut H, value: JsValue) -> Result let next_fn = make_native_function(vm, "next", iterator_wrapper_next:: as *const (), 0); vm.set_or_create_prop_value(wrapper_obj, next_si, next_fn); - // 把 IteratorClose 转发给内层迭代器,使 for-of 异常退出时可清理。 - // 仅当内层提供可调用的 return 方法时包装器才暴露 return:内建集合 - // (数组/字符串等索引迭代)无 return 方法,此时 GetMethod 应返回 undefined - // (IteratorClose 跳过),否则 return() 结果 undefined 会被误判为非对象报错。 + // for-of/解构的 IteratorClose 需要 return 方法:条件暴露(内层有可调用 return 时)。 + // `yield*` 委托(bind_return=false)不绑定,转发时对内层延迟 GetMethod。 let return_si = vm.kernel_core().perm_interner().intern("return").0; - if inner.is_object() { + if bind_return && inner.is_object() { let inner_obj = unsafe { &*inner.as_js_object_ptr() }; if let Ok(return_fn) = vm.ordinary_get(inner_obj, return_si, inner) { if is_callable(return_fn) { @@ -94,13 +106,16 @@ fn iterator_wrapper_return(vm: &mut H, args: &[u8]) -> NativeResult { Ok(inner) if inner.is_object() => inner, _ => return NativeResult::Ok(JsValue::undefined()), }; + // 延迟 GetMethod:内层无 return 方法时返回 undefined(IteratorClose 跳过)。 let inner_obj = unsafe { &*inner.as_js_object_ptr() }; let return_si = vm.kernel_core().perm_interner().intern("return").0; let return_fn = match vm.ordinary_get(inner_obj, return_si, inner) { Ok(f) if is_callable(f) => f, _ => return NativeResult::Ok(JsValue::undefined()), }; - match vm.call_function_sync(return_fn, inner, &[]) { + // 转发调用实参(`yield*` 委托的 return(v) 语义),缺省为 undefined。 + let arg = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + match vm.call_function_sync(return_fn, inner, &[arg]) { Ok(result) => NativeResult::Ok(result), Err(err) => match vm.take_uncaught_value() { Some(original) => NativeResult::Err(original), @@ -136,9 +151,17 @@ pub fn iterator_wrapper_next(vm: &mut H, args: &[u8]) -> NativeResult let next_si = vm.kernel_core().perm_interner().intern("next").0; let next = match vm.ordinary_get(inner_obj, next_si, inner) { Ok(next) => next, - Err(err) => return NativeResult::Err(crate::error::create_type_error(vm, &err)), + Err(err) => { + // GetMethod 的 next getter 抛错:透传原异常,不重新包装成 TypeError。 + return match vm.take_uncaught_value() { + Some(original) => NativeResult::Err(original), + None => NativeResult::Err(crate::error::create_type_error(vm, &err)), + }; + } }; - return match vm.call_function_sync(next, inner, &[]) { + // 转发调用实参(`yield*` 委托的 next(v) 语义),缺省为 undefined。 + let arg = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + return match vm.call_function_sync(next, inner, &[arg]) { Ok(result) => NativeResult::Ok(result), // 透传原始抛出的值(任意类型)而非重新包装成 TypeError, // 使外围 try/catch 能看到真正的错误。 @@ -203,44 +226,53 @@ fn get_iterator(vm: &mut H, value: JsValue) -> Result return Ok(Some(value)); } - if value.is_object() { - let obj = unsafe { &*value.as_js_object_ptr() }; - // 迭代协议:GetIterator 先取 value[Symbol.iterator] 并调用。 - let sym_iter_si = make_well_known_symbol_key(0); - let method = match vm.ordinary_get(obj, sym_iter_si, value) { - Ok(m) => m, + // 非字符串 primitive(boolean/number/symbol/bigint):GetIterator 先 ToObject, + // 再走迭代协议(如 `yield* true` 委托 Boolean.prototype[Symbol.iterator])。 + // null/undefined 的 ToObject 失败按不可迭代处理。 + let obj_value = if value.is_object() { + value + } else { + match to_object(value, vm) { + Ok(obj) => obj, + Err(_) => return Ok(None), + } + }; + let obj = unsafe { &*obj_value.as_js_object_ptr() }; + // 迭代协议:GetIterator 先取 value[Symbol.iterator] 并调用。 + let sym_iter_si = make_well_known_symbol_key(0); + let method = match vm.ordinary_get(obj, sym_iter_si, obj_value) { + Ok(m) => m, + Err(err) => { + // GetMethod 取 @@iterator 时 getter 抛出:透传原值,不落入鸭子回退。 + let exc = vm + .take_uncaught_value() + .unwrap_or_else(|| crate::error::create_type_error(vm, &err)); + return Err(exc); + } + }; + if is_callable(method) { + let iterator = match vm.call_function_sync(method, obj_value, &[]) { + Ok(it) => it, Err(err) => { - // GetMethod 取 @@iterator 时 getter 抛出:透传原值,不落入鸭子回退。 let exc = vm .take_uncaught_value() .unwrap_or_else(|| crate::error::create_type_error(vm, &err)); return Err(exc); } }; - if is_callable(method) { - let iterator = match vm.call_function_sync(method, value, &[]) { - Ok(it) => it, - Err(err) => { - let exc = vm - .take_uncaught_value() - .unwrap_or_else(|| crate::error::create_type_error(vm, &err)); - return Err(exc); - } - }; - if !iterator.is_object() { - return Err(crate::error::create_type_error( - vm, - "Result of the Symbol.iterator method is not an object", - )); - } - return Ok(Some(iterator)); + if !iterator.is_object() { + return Err(crate::error::create_type_error( + vm, + "Result of the Symbol.iterator method is not an object", + )); } - // 鸭子回退:对象自身有可调用 next(Map/Set 迭代器包装等既有用法)。 - let next_si = vm.kernel_core().perm_interner().intern("next").0; - if let Ok(next) = vm.ordinary_get(obj, next_si, value) { - if is_callable(next) { - return Ok(Some(value)); - } + return Ok(Some(iterator)); + } + // 鸭子回退:对象自身有可调用 next(Map/Set 迭代器包装等既有用法)。 + let next_si = vm.kernel_core().perm_interner().intern("next").0; + if let Ok(next) = vm.ordinary_get(obj, next_si, obj_value) { + if is_callable(next) { + return Ok(Some(obj_value)); } } @@ -366,7 +398,7 @@ fn is_typed_array_value(value: JsValue) -> bool { } /// 判断值是否为可调用对象(native 或字节码函数)。 -pub(crate) fn is_callable(value: JsValue) -> bool { +pub fn is_callable(value: JsValue) -> bool { if !value.is_object() { return false; } diff --git a/crates/oxide_bytecode/src/opcode.rs b/crates/oxide_bytecode/src/opcode.rs index de8847a..d6f5524 100644 --- a/crates/oxide_bytecode/src/opcode.rs +++ b/crates/oxide_bytecode/src/opcode.rs @@ -197,10 +197,12 @@ define_opcodes! { INIT_PRIVATE = 0x74 => "INIT_PRIVATE", PRIVATE_BRAND_IN = 0x75 => "PRIVATE_BRAND_IN", - // ── 生成器 (0x76-0x77) ── + // ── 生成器 (0x76-0x78) ── YIELD = 0x76 => "YIELD", // rd=被让出的值(结果经 reg 0 交付,见 contract.rs) // 生成器 body 起点标记:调用时参数初始化完成后挂起于此,首次 next() 从这继续。 SUSPEND_BODY = 0x77 => "SUSPEND_BODY", + // `yield*` 委托:rd=内层可迭代对象;委托完成值经 reg 0 交付(与 YIELD 同协议)。 + YIELD_STAR = 0x78 => "YIELD_STAR", // ── 位运算 (0x80-0x8F) ── BIT_AND = 0x80 => "BIT_AND", diff --git a/crates/oxide_emit/src/expr/yield_expr.rs b/crates/oxide_emit/src/expr/yield_expr.rs index 8f787e4..ae3a657 100644 --- a/crates/oxide_emit/src/expr/yield_expr.rs +++ b/crates/oxide_emit/src/expr/yield_expr.rs @@ -9,9 +9,15 @@ use oxide_parser::YieldExpression; impl Emitter { pub(crate) fn emit_yield_expression(&self, ye: &YieldExpression, ctx: &mut CompileCtx) -> Result { - // 未支持:`yield*` 委托迭代,后续按 IteratorYield 协议实现。 + // `yield*` 委托:求值内层可迭代对象后发 YIELD_STAR,运行时转发 next/return/throw。 + // 委托完成值(内层 done 的 value)经 reg 0 交付,与普通 `yield` 同协议。 if ye.delegate { - return Err("yield* not yet supported".into()); + let arg = ye.argument.as_ref().ok_or_else(|| "yield* requires an argument".to_string())?; + let inner_reg = self.emit_expression(arg, ctx)?; + ctx.inst(Inst::yield_star(Operand::Reg(inner_reg))); + let result_reg = ctx.alloc_reg(); + ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::None, Operand::None)); + return Ok(result_reg); } // 求值被让出的值(`yield` 无参数时让出 undefined)。 let value_reg = if let Some(arg) = &ye.argument { diff --git a/crates/oxide_ir/src/contract.rs b/crates/oxide_ir/src/contract.rs index 5bd9e2c..499a203 100644 --- a/crates/oxide_ir/src/contract.rs +++ b/crates/oxide_ir/src/contract.rs @@ -19,8 +19,8 @@ impl Inst { match self.op { // CALL 系:结果隐式写 reg 0,rd 是 callee 的 use OpCode::CALL | OpCode::CALL_NATIVE | OpCode::CALL_SPREAD => Some(0), - // YIELD:恢复时 next(v) 的 v 经 reg 0 交付(与 CALL 同协议) - OpCode::YIELD => Some(0), + // YIELD/YIELD_STAR:恢复时 next(v) 的 v / 委托完成值经 reg 0 交付(与 CALL 同协议) + OpCode::YIELD | OpCode::YIELD_STAR => Some(0), // GET_PROP 系:结果写 a/b 槽而非 rd OpCode::GET_PROP | OpCode::IC_GET_PROP => reg_of(&self.a), OpCode::GET_PROP_DYNAMIC => reg_of(&self.b), @@ -228,8 +228,8 @@ impl Inst { } // HALT:隐式读 reg 0(顶层返回值) OpCode::HALT => uses.push(0), - // RETURN/THROW/YIELD:读 rd(None→0) - OpCode::RETURN | OpCode::THROW | OpCode::YIELD => push_operand(&mut uses, &self.rd), + // RETURN/THROW/YIELD/YIELD_STAR:读 rd(None→0) + OpCode::RETURN | OpCode::THROW | OpCode::YIELD | OpCode::YIELD_STAR => push_operand(&mut uses, &self.rd), // 加载族:a 槽是 Const/Imm 立即数,无寄存器 use OpCode::LOAD_CONST | OpCode::CREATE_CLOSURE diff --git a/crates/oxide_ir/src/inst.rs b/crates/oxide_ir/src/inst.rs index 07c384d..7cd7160 100644 --- a/crates/oxide_ir/src/inst.rs +++ b/crates/oxide_ir/src/inst.rs @@ -273,6 +273,12 @@ impl Inst { Self::new(OpCode::YIELD, src, Operand::None, Operand::None) } + /// `yield*` 委托:`rd` 为内层可迭代对象。运行时取迭代器并转发 next/return/throw, + /// 委托完成值(内层 done 的 value)经 reg 0 交付,与 `yield_value` 同协议。 + pub fn yield_star(src: Operand) -> Self { + Self::new(OpCode::YIELD_STAR, src, Operand::None, Operand::None) + } + /// 生成器 body 起点标记:调用时参数初始化完成后挂起于此,首次 `next()` 继续。 pub fn suspend_body() -> Self { Self::new(OpCode::SUSPEND_BODY, Operand::None, Operand::None, Operand::None) diff --git a/crates/oxide_vm/src/generator.rs b/crates/oxide_vm/src/generator.rs index 0635f97..be2916e 100644 --- a/crates/oxide_vm/src/generator.rs +++ b/crates/oxide_vm/src/generator.rs @@ -8,10 +8,10 @@ use std::sync::Arc; -use oxide_builtins::iterator::make_iter_result; +use oxide_builtins::iterator::{is_callable, make_iter_result}; use oxide_bytecode::opcode; use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{to_boolean, NativeResult}; use oxide_types::mem::P; use oxide_types::object::{Cell, JsObject, NativeFnPtr}; use oxide_types::value::JsValue; @@ -62,6 +62,8 @@ pub(crate) struct GeneratorState { pub for_in_iters: Vec<*mut ForInIter<'static>>, pub for_of_iters: Vec, pub last_for_of_result: JsValue, + /// `yield*` 委托中的内层迭代器:Some = 挂起在委托点,恢复时转发 next/return/throw。 + pub delegated_iterator: Option, pub saved_bytecode_stack: Vec>, pub saved_immutables_stack: Vec<*const [JsValue]>, /// 在途异常/完成(throw 穿越 finally 挂起时保留,恢复后继续展开)。 @@ -86,12 +88,37 @@ pub(crate) enum GeneratorResumeMode { pub(crate) enum GeneratorStep { /// 让出:`value` 为 `yield` 的值,生成器仍可继续。 Suspended { value: JsValue }, + /// `yield*` 委托让出:`value` 为内层迭代器的原始结果对象,外层 next() 原样返回 + /// (透传内层 done/value 字段,spec GeneratorYield 语义)。 + SuspendedRaw { value: JsValue }, /// 完成:`value` 为返回值。 Completed { value: JsValue }, /// 未捕获异常逃逸:`value` 为原异常值,调用方须重新抛出。 Thrown { value: JsValue }, } +/// `yield*` 首次推进(YIELD_STAR dispatch)的结局。 +pub(crate) enum YieldStarOutcome { + /// 内层未 done:挂起外层,让出内层原始结果对象(原样透传)。 + Suspend(JsValue), + /// 内层 done:委托完成,value 为委托值(写 reg 0 继续外层)。 + Continue(JsValue), + /// 异常已展开到外层 catch/finally:继续 dispatch。 + Unwind, +} + +/// `yield*` 委托转发(生成器恢复时)的结局。 +enum DelegateOutcome { + /// 内层未 done:保持委托挂起,让出 value。 + Suspend { value: JsValue }, + /// 内层消化 next/throw 后 done:委托结束,外层继续,value 为委托值。 + Continue { value: JsValue }, + /// 内层消化 return 后 done / 无 return 方法:委托结束,外层完成,value 为返回值。 + Complete { value: JsValue }, + /// 内层调用抛错且已展开到外层 catch/finally:委托终止,继续 dispatch。 + Unwind, +} + impl Vm { /// 调用生成器函数返回的迭代器对象:创建生成器实例、挂状态并执行调用时参数初始化。 /// @@ -124,6 +151,7 @@ impl Vm { for_in_iters: Vec::new(), for_of_iters: Vec::new(), last_for_of_result: JsValue::undefined(), + delegated_iterator: None, saved_bytecode_stack: Vec::new(), saved_immutables_stack: Vec::new(), exception_value: None, @@ -313,6 +341,7 @@ impl Vm { self.iters.for_in_iters.clear(); self.iters.for_of_iters.clear(); self.iters.last_for_of_result = JsValue::undefined(); + self.delegated_iterator = None; self.spill_stack.clear(); self.save_stack.clear(); self.saved_bytecode_stack.clear(); @@ -362,6 +391,7 @@ impl Vm { self.iters.for_in_iters = std::mem::take(&mut state.for_in_iters); self.iters.for_of_iters = std::mem::take(&mut state.for_of_iters); self.iters.last_for_of_result = state.last_for_of_result; + self.delegated_iterator = state.delegated_iterator.take(); self.saved_bytecode_stack = std::mem::take(&mut state.saved_bytecode_stack); self.saved_immutables_stack = std::mem::take(&mut state.saved_immutables_stack); self.exception_value = state.exception_value.take(); @@ -371,8 +401,64 @@ impl Vm { self.inline_callee = None; state.phase = GeneratorPhase::Running; - // 注入模式:next(v) 写 reg 0;throw(e) 恢复异常上下文;return(v) 完成穿越。 - match mode { + // `yield*` 委托恢复:把 next/return/throw 请求转发给内层迭代器,按内层 + // 结局决定挂起透传 / 外层继续 / 外层完成 / 异常传播。 + // 委托分支处置完毕后直接进入 dispatch,不再执行注入模式(reg 0 已就位)。 + if self.delegated_iterator.is_some() { + let forwarded = self.delegate_forward(mode); + match forwarded { + Err(e) => { + // 委托期间异常逃逸(unwind 失败,已弹出全部帧):标记完成并交付 Thrown。 + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_error(self, &e)); + self.restore_inline_state(saved); + self.native_call_depth -= 1; + let state = unsafe { &mut *state_ptr }; + state.phase = GeneratorPhase::Completed; + state.result = JsValue::undefined(); + return Ok(GeneratorStep::Thrown { value: exc }); + } + Ok(DelegateOutcome::Suspend { value }) => { + let state = unsafe { &mut *state_ptr }; + state.phase = GeneratorPhase::Suspended; + self.snapshot_generator(state)?; + self.restore_inline_state(saved); + self.native_call_depth -= 1; + // 委托让出原样透传内层结果对象,不二次包装。 + return Ok(GeneratorStep::SuspendedRaw { value }); + } + Ok(DelegateOutcome::Continue { value }) => { + // 委托结束(内层消化 next/throw 后 done):yield* 表达式值为 + // value,外层继续执行。 + self.regs[0] = value; + } + Ok(DelegateOutcome::Complete { value }) => { + // 委托结束(内层消化 return 后 done 或无 return 方法):外层完成, + // 交付值为内层结果,穿越自身 finally。 + let prev_dispatch = self.generator_dispatch; + self.generator_dispatch = true; + let completed = self.complete_generator_return(value); + self.generator_dispatch = prev_dispatch; + if let Some(completed) = completed? { + self.restore_inline_state(saved); + self.native_call_depth -= 1; + let state = unsafe { &mut *state_ptr }; + state.phase = GeneratorPhase::Completed; + state.result = completed; + return Ok(GeneratorStep::Completed { value: completed }); + } + // 进入 finally 穿越:继续 dispatch。 + } + Ok(DelegateOutcome::Unwind) => { + // 内层调用抛错且已展开到外层 catch/finally:委托终止,继续 dispatch。 + self.delegated_iterator = None; + } + } + } else { + // 注入模式:next(v) 写 reg 0;throw(e) 恢复异常上下文;return(v) 完成穿越。 + match mode { GeneratorResumeMode::Next(arg) => { self.regs[0] = arg; } @@ -403,6 +489,7 @@ impl Vm { return Ok(GeneratorStep::Completed { value: completed }); } } + } } } @@ -415,8 +502,13 @@ impl Vm { // YIELD 让出:快照挂起状态。 if let Some(value) = self.generator_suspended.take() { let state = unsafe { &mut *state_ptr }; + // 委托挂起时 value 是内层原始结果对象,原样透传;普通 yield 才二次包装。 + let delegating = self.delegated_iterator.is_some(); self.snapshot_generator(state)?; self.restore_inline_state(saved); + if delegating { + return Ok(GeneratorStep::SuspendedRaw { value }); + } return Ok(GeneratorStep::Suspended { value }); } @@ -443,6 +535,216 @@ impl Vm { } } + /// `yield*` 委托首次进入:GetIterator 取内层迭代器并推进一步。 + /// + /// # 步骤 + /// 1. 经迭代协议把内层值包装为迭代器(不可迭代抛 TypeError)。 + /// 2. 调内层 next(undefined) 得 {value, done}。 + /// 3. done → 委托完成值交付(外层继续);未 done → 挂起,委托迭代器存入 + /// `self.delegated_iterator`。 + /// + /// # 副作用 + /// - 内层 next 同步执行,可能压入/弹出调用帧。 + /// - 挂起时设置 `delegated_iterator`,由 snapshot_generator 存入生成器状态。 + pub(crate) fn dispatch_yield_star(&mut self, rd: usize) -> Result { + let inner = self.regs[rd]; + let iterator = match oxide_builtins::iterator::make_iterator_for_value_without_return(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")); + }; + if !result.is_object() { + return self.yield_star_raise(self.error_message_text("TypeError", "iterator result is not an object")); + } + let result_obj = unsafe { &*result.as_js_object_ptr() }; + let done_si = self.kernel_core.perm_interner().intern("done").0; + let value_si = self.kernel_core.perm_interner().intern("value").0; + let done = to_boolean(match self.ordinary_get(result_obj, done_si, result) { + Ok(v) => v, + Err(e) => return self.yield_star_raise(e), + }); + // done 时才读取 value(委托完成值);done=false 直接让出原始结果对象。 + if done { + let value = match self.ordinary_get(result_obj, value_si, result) { + Ok(v) => v, + Err(e) => return self.yield_star_raise(e), + }; + Ok(YieldStarOutcome::Continue(value)) + } else { + self.delegated_iterator = Some(iterator); + // 挂起让出内层原始结果对象:外层 next() 原样透传(done 字段保持内层值)。 + Ok(YieldStarOutcome::Suspend(result)) + } + } + + /// `yield*` 委托单步转发:把外层恢复请求转发给内层迭代器。 + /// + /// # 步骤 + /// 1. next 走统一包装器(数组/字符串按索引、对象委托自身 next,方法内置不提前绑定); + /// return/throw 对内层迭代器延迟 GetMethod(wrapper 不应在创建时访问内层方法)。 + /// 2. 方法缺失时按语义兜底:next → TypeError;return → 外层直接完成(值为请求值); + /// throw → 先 IteratorClose(调内层 return,其抛错以该错为准)再补抛原异常。 + /// 3. 调内层方法得结果对象,读 done/value 判定委托结局。 + /// + /// # 副作用 + /// - 内层调用抛错时经 unwind 展开(可被外层 catch/finally 捕获,返回 Unwind)。 + /// - 委托结束时清空 `self.delegated_iterator`,挂起透传时保留。 + fn delegate_forward(&mut self, mode: GeneratorResumeMode) -> Result { + let iterator = match self.delegated_iterator { + Some(it) => it, + None => return Ok(DelegateOutcome::Unwind), + }; + let (method, arg) = match mode { + GeneratorResumeMode::Next(v) => ("next", v), + GeneratorResumeMode::Return(v) => ("return", v), + GeneratorResumeMode::Throw(e) => ("throw", e), + }; + // return/throw 的 receiver 与 GetMethod 目标都是内层迭代器(包装器统一 next)。 + let inner = if matches!(mode, GeneratorResumeMode::Next(_)) { + iterator + } else { + self.iterator_inner(iterator) + }; + let iter_obj = unsafe { &*inner.as_js_object_ptr() }; + let method_si = self.kernel_core.perm_interner().intern(method).0; + let method_fn = match self.ordinary_get(iter_obj, method_si, inner) { + Ok(f) => f, + Err(e) => return self.delegate_raise(e), + }; + let inner_result = if is_callable(method_fn) { + match self.call_function_sync(method_fn, inner, &[arg]) { + Ok(r) => r, + Err(e) => return self.delegate_raise(e), + } + } else { + match mode { + GeneratorResumeMode::Next(_) => { + return self.delegate_raise(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(_e) => { + // 内层无 throw 方法:先 IteratorClose(GetMethod 内层 return,getter 抛错 + // 传播、调用抛错传播、结果非对象抛 TypeError),再抛 TypeError(协议违规)。 + let ret_si = self.kernel_core.perm_interner().intern("return").0; + let ret_fn = match self.ordinary_get(iter_obj, ret_si, inner) { + Ok(f) => f, + Err(e) => return self.delegate_raise(e), + }; + if is_callable(ret_fn) { + let close_result = match self.call_function_sync(ret_fn, inner, &[]) { + Ok(r) => r, + Err(e) => return self.delegate_raise(e), + }; + if !close_result.is_object() { + return self.delegate_raise( + self.error_message_text("TypeError", "IteratorResult is not an object"), + ); + } + } + self.delegated_iterator = None; + return self.delegate_raise(self.error_message_text( + "TypeError", + "yield* protocol violation: iterator does not have a throw method", + )); + } + } + }; + if !inner_result.is_object() { + return self.delegate_raise(self.error_message_text("TypeError", "iterator result is not an object")); + } + let result_obj = unsafe { &*inner_result.as_js_object_ptr() }; + let done_si = self.kernel_core.perm_interner().intern("done").0; + let value_si = self.kernel_core.perm_interner().intern("value").0; + let done = to_boolean(match self.ordinary_get(result_obj, done_si, inner_result) { + Ok(v) => v, + Err(e) => return self.delegate_raise(e), + }); + // done 时才读取 value(委托完成值);done=false 直接让出原始结果对象。 + if done { + let value = match self.ordinary_get(result_obj, value_si, inner_result) { + Ok(v) => v, + Err(e) => return self.delegate_raise(e), + }; + // 委托结束:next/throw 被内层消化后外层继续,return 被内层消化后外层完成。 + self.delegated_iterator = None; + match mode { + GeneratorResumeMode::Next(_) | GeneratorResumeMode::Throw(_) => Ok(DelegateOutcome::Continue { value }), + GeneratorResumeMode::Return(_) => Ok(DelegateOutcome::Complete { value }), + } + } else { + // 转发期间内层生成器的恢复会覆盖 `self.delegated_iterator`,须重新存回 + // 委托迭代器,使外层挂起快照保留委托状态。让出内层原始结果对象。 + self.delegated_iterator = Some(iterator); + Ok(DelegateOutcome::Suspend { value: inner_result }) + } + } + + /// 从统一包装器中取内层迭代器(`__inner__` 槽),兜底返回包装器自身。 + fn iterator_inner(&mut self, wrapper: JsValue) -> JsValue { + if !wrapper.is_object() { + return wrapper; + } + let obj = unsafe { &*wrapper.as_js_object_ptr() }; + let inner_si = self.kernel_core.perm_interner().intern("__inner__").0; + match self.ordinary_get(obj, inner_si, wrapper) { + Ok(v) if !v.is_undefined() => v, + _ => wrapper, + } + } + + /// 委托期间的异常注入:恢复原始异常值并经 unwind 展开。 + /// + /// # 返回值 + /// - `Ok(Unwind)`:异常被外层 catch/finally 捕获,可继续 dispatch; + /// - `Err`:异常逃逸出外层(unwind 失败),调用方按 Thrown 交付。 + fn delegate_raise(&mut self, msg: String) -> Result { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_type_error(self, &msg)); + self.exception_value = Some(exc); + self.pending_error_kind = Some(self.thrown_error_kind(exc)); + match self.unwind() { + Ok(()) => Ok(DelegateOutcome::Unwind), + Err(e) => Err(e), + } + } + + /// `yield*` 首次推进的异常注入:与 [`delegate_raise`] 同路径,结局为 + /// [`YieldStarOutcome`] 的 Unwind。 + fn yield_star_raise(&mut self, msg: String) -> Result { + let exc = self + .last_uncaught_value + .take() + .unwrap_or_else(|| oxide_builtins::error::create_type_error(self, &msg)); + self.exception_value = Some(exc); + self.pending_error_kind = Some(self.thrown_error_kind(exc)); + match self.unwind() { + Ok(()) => Ok(YieldStarOutcome::Unwind), + Err(e) => Err(e), + } + } + /// 把当前 VM 执行状态(生成器 body 刚在嵌套 dispatch 中让出)快照进状态盒。 /// /// 生成器帧弹出存 `frame`,其余栈段整段搬入(嵌套循环期间这些栈只含生成器数据)。 @@ -469,6 +771,7 @@ impl Vm { state.for_in_iters = std::mem::take(&mut self.iters.for_in_iters); state.for_of_iters = std::mem::take(&mut self.iters.for_of_iters); state.last_for_of_result = self.iters.last_for_of_result; + state.delegated_iterator = self.delegated_iterator.take(); state.saved_bytecode_stack = std::mem::take(&mut self.saved_bytecode_stack); state.saved_immutables_stack = std::mem::take(&mut self.saved_immutables_stack); state.exception_value = self.exception_value.take(); @@ -535,8 +838,10 @@ pub(crate) fn generator_symbol_iterator(vm: &mut Vm, args: &[u8]) -> NativeResul } /// 把恢复结局折叠为迭代器结果 `{value, done}` 或传播异常。 +/// 委托让出(SuspendedRaw)原样透传内层结果对象,不做二次包装。 fn generator_step_result(vm: &mut Vm, step: Result) -> NativeResult { match step { + Ok(GeneratorStep::SuspendedRaw { value }) => NativeResult::Ok(value), Ok(GeneratorStep::Suspended { value }) | Ok(GeneratorStep::Completed { value }) => { let done = matches!(step, Ok(GeneratorStep::Completed { .. })); NativeResult::Ok(make_iter_result(vm, value, done)) @@ -684,6 +989,9 @@ pub(crate) fn generator_native_edges(obj: &JsObject) -> Vec { } edges.extend(state.for_of_iters.iter().copied().filter(|v| v.is_object())); push(state.last_for_of_result, &mut edges); + if let Some(iter) = state.delegated_iterator { + push(iter, &mut edges); + } edges } @@ -726,6 +1034,7 @@ pub(crate) fn rewrite_generator_native(obj: &JsObject, mut rewrite: impl FnMut(J *v = rewrite(*v); } state.last_for_of_result = rewrite(state.last_for_of_result); + state.delegated_iterator = state.delegated_iterator.map(&mut rewrite); } /// 释放生成器状态盒(对象被 GC 回收时),返回释放字节数。 diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index a03fa17..641f1d6 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -285,6 +285,9 @@ pub struct Vm { /// 生成器体 `dispatch()` 让出时的信号:YIELD 置 Some(让出值),恢复方( /// generator 内嵌 dispatch 循环)取走并判定挂起。None = 正常返回/异常。 pub(crate) generator_suspended: Option, + /// `yield*` 委托中的内层迭代器:YIELD_STAR 让出时置入,恢复时转发 next/return/throw + /// 后按结局清空。随生成器挂起/恢复经 GeneratorState 传递(snapshot/rewrite 共管)。 + pub(crate) delegated_iterator: Option, /// 当前是否处于生成器内嵌 dispatch 循环:生成器帧弹出且 frames 清空时, /// `do_return` 据此把结果交付给恢复方(而非当作普通顶层返回继续执行)。 pub(crate) generator_dispatch: bool, @@ -1511,6 +1514,23 @@ impl Vm { return Ok(JsValue::undefined()); } + OpCode::YIELD_STAR => { + // `yield*` 委托:取内层迭代器并推进一步。 + // 未 done → 挂起让出(存委托迭代器);done → 委托完成值写 reg 0 继续外层; + // unwind 捕获到异常 → 继续 dispatch(已展开到 catch/finally)。 + match self.dispatch_yield_star(rd)? { + crate::generator::YieldStarOutcome::Suspend(value) => { + self.generator_suspended = Some(value); + self.profiling.set_instruction_count(steps); + return Ok(JsValue::undefined()); + } + crate::generator::YieldStarOutcome::Continue(value) => { + self.regs[0] = value; + } + crate::generator::YieldStarOutcome::Unwind => {} + } + } + OpCode::SUSPEND_BODY => { // 生成器调用时参数初始化结束:挂起在 body 起点;正常 next() 恢复时直接穿过。 if self.generator_init_step { diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index 5c53038..56eea3c 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -54,6 +54,7 @@ impl Vm { accessor_frame_target_reg: None, inline_callee: None, generator_suspended: None, + delegated_iterator: None, generator_dispatch: false, generator_init_step: false, generator_body_started: false, @@ -127,6 +128,7 @@ impl Vm { accessor_frame_target_reg: None, inline_callee: None, generator_suspended: None, + delegated_iterator: None, generator_dispatch: false, generator_init_step: false, generator_body_started: false, @@ -251,6 +253,7 @@ impl Vm { self.pending_error_kind = None; self.pending_completion = None; self.generator_suspended = None; + self.delegated_iterator = None; self.generator_dispatch = false; self.generator_init_step = false; self.generator_body_started = false; diff --git a/crates/oxide_vm/tests/generator_yield_star_tests.rs b/crates/oxide_vm/tests/generator_yield_star_tests.rs new file mode 100644 index 0000000..21258ef --- /dev/null +++ b/crates/oxide_vm/tests/generator_yield_star_tests.rs @@ -0,0 +1,111 @@ +//! `yield*` 委托语法运行时测试:委托数组/字符串/生成器,next/return/throw 转发, +//! 委托完成值传递与空委托场景。 + +use oxide_compiler::compiler::Compiler; +use oxide_parser::Allocator; +use oxide_vm::vm::Vm; + +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(&module) { + Ok(result) => { + if result.is_string() { + vm.lookup_str(result).unwrap_or_default() + } else { + format!("{result}") + } + } + Err(e) => format!("vm error: {e}"), + } +} + +// 委托数组:逐元素让出。 +#[test] +fn yield_star_array() { + assert_eq!(eval("function* g(){ yield* [1,2,3] } Array.from(g()).join(',')"), "1,2,3"); +} + +// 委托字符串:按码元让出。 +#[test] +fn yield_star_string() { + assert_eq!(eval("function* g(){ yield* 'ab' } Array.from(g()).join(',')"), "a,b"); +} + +// 委托完成值作为 yield* 表达式结果,外层继续执行。 +#[test] +fn yield_star_completion_value() { + assert_eq!( + eval("function* inner(){ yield 1; yield 2; return 3 } function* outer(){ var r = yield* inner(); yield r } var it=outer(); [it.next().value, it.next().value, it.next().value, it.next().done].join(',')"), + "1,2,3,true" + ); +} + +// 委托夹在普通 yield 之间,保持顺序。 +#[test] +fn yield_star_mixed_with_plain_yield() { + assert_eq!( + eval("function* g(){ yield 0; yield* [1,2]; yield 3 } Array.from(g()).join(',')"), + "0,1,2,3" + ); +} + +// 嵌套委托(yield* 内再 yield*)。 +#[test] +fn yield_star_nested() { + assert_eq!( + eval("function* a(){ yield 1 } function* b(){ yield* a() } function* c(){ yield* b() } Array.from(c()).join(',')"), + "1" + ); +} + +// 空委托:立即继续外层。 +#[test] +fn yield_star_empty() { + assert_eq!(eval("function* g(){ yield* [] ; yield 5 } Array.from(g()).join(',')"), "5"); +} + +// 外层 return() 转发给内层:内层完成值交付。 +#[test] +fn yield_star_return_forward() { + assert_eq!( + eval("function* inner(){ yield 1 } function* outer(){ yield* inner() } var it=outer(); it.next(); it.return(9).value"), + "9" + ); +} + +// 外层 throw() 转发给内层:内层消化后外层继续。 +#[test] +fn yield_star_throw_forward() { + assert_eq!( + eval("function* inner(){ try { yield 1 } catch(e) { return 'handled:'+e } } function* outer(){ var r = yield* inner(); yield 'got: ' + r } var it=outer(); var a=it.next(); var b=it.throw('x'); [a.value, b.value].join(',')"), + "1,got: handled:x" + ); +} + +// 委托不可迭代值:经外层 catch 捕获。 +#[test] +fn yield_star_not_iterable_throws() { + assert_eq!( + eval("function* outer(){ try { yield* 42 } catch(e) { yield 'err' } } Array.from(outer()).join(',')"), + "err" + ); +} + +// 内层无 return 方法:外层 return() 直接完成(值为请求值)。 +#[test] +fn yield_star_inner_without_return() { + assert_eq!( + eval("var inner = { [Symbol.iterator]() { return this; }, next() { return {value:1, done:false}; } }; function* outer(){ yield* inner; yield 5 } var it=outer(); var a=it.next(); var b=it.return(9); [a.value, b.value, b.done].join(',')"), + "1,9,true" + ); +} +