mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 实现 yield* 委托语法(内层迭代器 next/return/throw 转发)
This commit is contained in:
@@ -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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// 为任意值创建统一迭代器包装对象:String/Array/Map/Set 直接支持索引遍历,
|
||||
/// 其它对象则要求提供可调用的 `next`。不可迭代时返回 TypeError。
|
||||
pub fn make_iterator_for_value<H: VmHost>(vm: &mut H, value: JsValue) -> Result<JsValue, JsValue> {
|
||||
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<H: VmHost>(vm: &mut H, value: JsValue) -> Result<JsValue, JsValue> {
|
||||
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<H: VmHost>(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<H: VmHost>(vm: &mut H, value: JsValue) -> Result<Option<JsValue>, JsValue> {
|
||||
/// - `bind_return` 控制是否暴露 `return` 方法(for-of/解构的 IteratorClose 需要,
|
||||
/// `yield*` 委托不需要且须避免创建时访问内层 return getter)。
|
||||
pub(crate) fn try_make_iterator_inner<H: VmHost>(
|
||||
vm: &mut H, value: JsValue, bind_return: bool,
|
||||
) -> Result<Option<JsValue>, 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<H: VmHost>(vm: &mut H, value: JsValue) -> Result
|
||||
let next_fn = make_native_function(vm, "next", iterator_wrapper_next::<H> 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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, value: JsValue) -> Result<Option<JsValue>
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -9,9 +9,15 @@ use oxide_parser::YieldExpression;
|
||||
|
||||
impl Emitter {
|
||||
pub(crate) fn emit_yield_expression(&self, ye: &YieldExpression, ctx: &mut CompileCtx) -> Result<u32, String> {
|
||||
// 未支持:`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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<JsValue>,
|
||||
pub last_for_of_result: JsValue,
|
||||
/// `yield*` 委托中的内层迭代器:Some = 挂起在委托点,恢复时转发 next/return/throw。
|
||||
pub delegated_iterator: Option<JsValue>,
|
||||
pub saved_bytecode_stack: Vec<Vec<opcode::Instr>>,
|
||||
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<YieldStarOutcome, String> {
|
||||
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<DelegateOutcome, String> {
|
||||
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<DelegateOutcome, String> {
|
||||
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<YieldStarOutcome, String> {
|
||||
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<GeneratorStep, String>) -> 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<JsValue> {
|
||||
}
|
||||
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 回收时),返回释放字节数。
|
||||
|
||||
@@ -285,6 +285,9 @@ pub struct Vm {
|
||||
/// 生成器体 `dispatch()` 让出时的信号:YIELD 置 Some(让出值),恢复方(
|
||||
/// generator 内嵌 dispatch 循环)取走并判定挂起。None = 正常返回/异常。
|
||||
pub(crate) generator_suspended: Option<JsValue>,
|
||||
/// `yield*` 委托中的内层迭代器:YIELD_STAR 让出时置入,恢复时转发 next/return/throw
|
||||
/// 后按结局清空。随生成器挂起/恢复经 GeneratorState 传递(snapshot/rewrite 共管)。
|
||||
pub(crate) delegated_iterator: Option<JsValue>,
|
||||
/// 当前是否处于生成器内嵌 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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user