diff --git a/crates/oxide_builtins/src/iterator.rs b/crates/oxide_builtins/src/iterator.rs index 7b5c2eb2..1ff30d3b 100644 --- a/crates/oxide_builtins/src/iterator.rs +++ b/crates/oxide_builtins/src/iterator.rs @@ -155,6 +155,386 @@ pub fn iterator_dispose(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } +// ── 终端方法共享工具(forEach/every/some/find/reduce/toArray 共用)── + +/// 终端方法的统一前置校验:`this` 非对象 → TypeError;回调不可调用 → 关底层后抛 +/// TypeError(2024 规范更新:参数校验失败也执行 IteratorClose,且此时不读 next)。 +/// +/// # 步骤 +/// 1. `this` 非对象 → TypeError(原始值直接抛,不读 next)。 +/// 2. 回调不可调用 → 以 `{Iterator: this, NextMethod: undefined}` 构造记录并 +/// IteratorClose(延迟 GetMethod return 并调用),随后抛 TypeError。 +/// 3. GetIteratorDirect:读 `next` 一次并缓存(getter 抛错透传原值)。 +/// +/// # 返回值 +/// - `Ok((iterated, next))`:可进入循环;`Err` 为透传的异常值。 +pub(crate) fn validate_terminal_and_get_direct( + vm: &mut H, this_val: JsValue, callback: JsValue, +) -> Result<(JsValue, JsValue), JsValue> { + if !this_val.is_object() { + return Err(crate::error::create_type_error(vm, "Iterator.prototype method called on non-object")); + } + if !is_callable(callback) { + // 校验失败仍关底层:NextMethod 未读,IteratorClose 只经 return 方法关闭。 + let err = crate::error::create_type_error(vm, "Iterator.prototype method requires a callable callback"); + let _ = iterator_close_record(vm, this_val, Some(err)); + return Err(err); + } + get_iterator_direct(vm, this_val) +} + +/// GetIteratorDirect:读 `next` 一次并缓存,返回 `(iterated, next)` 对。 +/// +/// # 边界与前提 +/// - `this_val` 必须是对象(调用方已校验)。 +/// - `next` getter 抛错时透传原异常值。 +pub(crate) fn get_iterator_direct(vm: &mut H, this_val: JsValue) -> Result<(JsValue, JsValue), JsValue> { + let iter_obj = unsafe { &*this_val.as_js_object_ptr() }; + let next_si = vm.kernel_core().perm_interner().intern("next").0; + let next = vm.ordinary_get(iter_obj, next_si, this_val).map_err(|e| engine_error(vm, &e))?; + Ok((this_val, next)) +} + +/// IteratorStepValue:调缓存 next 取一步,读结果对象 `done`/`value`。 +/// +/// # 返回值 +/// - `Ok(Some(value))`:有元素产出; +/// - `Ok(None)`:迭代完成(done=true,不读 value getter); +/// - `Err`:next 抛错 / 结果非对象 / done/value getter 抛错(透传原值,不关底层)。 +pub(crate) fn iterator_record_step( + vm: &mut H, next: JsValue, iterated: JsValue, +) -> Result, JsValue> { + let result = vm.call_function_sync(next, iterated, &[]).map_err(|e| engine_error(vm, &e))?; + if !result.is_object() { + return Err(crate::error::create_type_error(vm, "iterator result is not an object")); + } + let result_obj = unsafe { &*result.as_js_object_ptr() }; + let done_si = vm.kernel_core().perm_interner().intern("done").0; + let done = vm.ordinary_get(result_obj, done_si, result).map_err(|e| engine_error(vm, &e))?; + if oxide_runtime_api::to_boolean(done) { + return Ok(None); + } + let value_si = vm.kernel_core().perm_interner().intern("value").0; + let value = vm + .ordinary_get(result_obj, value_si, result) + .map_err(|e| engine_error(vm, &e))?; + Ok(Some(value)) +} + +/// IteratorClose:延迟 GetMethod(iterator, "return") 并调用,按 completion 形态决定 +/// 错误胜者(throw → 原错误胜出;正常 → return 相关错误胜出)。 +/// +/// # 参数 +/// - `completion_err`:`Some(v)` 表示在途异常(completion 为 throw,v 为原异常值); +/// `None` 表示正常完成。 +/// +/// # 返回值 +/// - 正常完成时 `Ok(())` 表示关闭成功,`Err` 为 return 相关错误; +/// - throw completion 时恒返回 `Err(v)`(原异常胜出,return 错误被吞)。 +pub(crate) fn iterator_close_record( + vm: &mut H, iterated: JsValue, completion_err: Option, +) -> Result<(), JsValue> { + let iter_obj = unsafe { &*iterated.as_js_object_ptr() }; + let return_si = vm.kernel_core().perm_interner().intern("return").0; + let return_fn = match vm.ordinary_get(iter_obj, return_si, iterated) { + Ok(f) if is_callable(f) => f, + Ok(_) => { + // 无 return 方法(或不可调用):GetMethod 返回 undefined,直接收尾。 + return match completion_err { + Some(v) => Err(v), + None => Ok(()), + }; + } + Err(err) => { + let exc = engine_error(vm, &err); + return match completion_err { + Some(v) => Err(v), + None => Err(exc), + }; + } + }; + match vm.call_function_sync(return_fn, iterated, &[]) { + Ok(result) => match completion_err { + // throw completion:原异常胜出,不检查 return 结果形态。 + Some(v) => Err(v), + // 正常完成:return 结果须为对象,否则 TypeError。 + None => { + if !result.is_object() { + return Err(crate::error::create_type_error(vm, "iterator return() result is not an object")); + } + Ok(()) + } + }, + Err(err) => { + let exc = engine_error(vm, &err); + match completion_err { + Some(v) => Err(v), + None => Err(exc), + } + } + } +} + +/// 计数器转 Number 值:int 区间用 int 表示,越界回落 float(规范 `𝔽(counter)`)。 +fn counter_number(counter: i64) -> JsValue { + if counter >= i32::MIN as i64 && counter <= i32::MAX as i64 { + JsValue::int(counter as i32) + } else { + JsValue::float(counter as f64) + } +} + +// ── 终端 6 方法 ── + +/// `%Iterator.prototype%.forEach(procedure)`:消费全部元素,逐个调用 procedure。 +/// +/// # 步骤 +/// 1. 校验 this/回调并取底层迭代器(共享前置)。 +/// 2. 循环取元素,每个元素 `Call(procedure, undefined, «value, counter»)`。 +/// 3. 回调抛错 → IteratorClose 后透传原值;耗尽返回 undefined。 +pub fn iterator_for_each(vm: &mut H, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let callback = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let (iterated, next) = match validate_terminal_and_get_direct(vm, this_val, callback) { + Ok(pair) => pair, + Err(v) => return NativeResult::Err(v), + }; + let mut counter: i64 = 0; + loop { + let value = match iterator_record_step(vm, next, iterated) { + Ok(Some(v)) => v, + Ok(None) => return NativeResult::Ok(JsValue::undefined()), + Err(v) => return NativeResult::Err(v), + }; + let cb_args = [value, counter_number(counter)]; + if let Err(err) = vm.call_function_sync(callback, JsValue::undefined(), &cb_args) { + let exc = engine_error(vm, &err); + let _ = iterator_close_record(vm, iterated, Some(exc)); + return NativeResult::Err(exc); + } + counter += 1; + } +} + +/// `%Iterator.prototype%.every(predicate)`:谓词全真返回 true,首个 falsy 关底层返 false。 +/// +/// # 步骤 +/// 1. 校验 this/回调并取底层迭代器(共享前置)。 +/// 2. 循环取元素,每个元素 `Call(predicate, undefined, «value, counter»)`。 +/// 3. 谓词 falsy → IteratorClose(正常完成,return 错误胜出)后返回 false;耗尽返回 true。 +pub fn iterator_every(vm: &mut H, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let callback = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let (iterated, next) = match validate_terminal_and_get_direct(vm, this_val, callback) { + Ok(pair) => pair, + Err(v) => return NativeResult::Err(v), + }; + let mut counter: i64 = 0; + loop { + let value = match iterator_record_step(vm, next, iterated) { + Ok(Some(v)) => v, + Ok(None) => return NativeResult::Ok(JsValue::bool(true)), + Err(v) => return NativeResult::Err(v), + }; + let cb_args = [value, counter_number(counter)]; + let result = match vm.call_function_sync(callback, JsValue::undefined(), &cb_args) { + Ok(r) => r, + Err(err) => { + let exc = engine_error(vm, &err); + let _ = iterator_close_record(vm, iterated, Some(exc)); + return NativeResult::Err(exc); + } + }; + if !oxide_runtime_api::to_boolean(result) { + return match iterator_close_record(vm, iterated, None) { + Ok(()) => NativeResult::Ok(JsValue::bool(false)), + Err(v) => NativeResult::Err(v), + }; + } + counter += 1; + } +} + +/// `%Iterator.prototype%.some(predicate)`:谓词首个 truthy 关底层返回 true,耗尽返 false。 +/// +/// # 步骤 +/// 1. 校验 this/回调并取底层迭代器(共享前置)。 +/// 2. 循环取元素,每个元素 `Call(predicate, undefined, «value, counter»)`。 +/// 3. 谓词 truthy → IteratorClose(正常完成,return 错误胜出)后返回 true;耗尽返回 false。 +pub fn iterator_some(vm: &mut H, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let callback = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let (iterated, next) = match validate_terminal_and_get_direct(vm, this_val, callback) { + Ok(pair) => pair, + Err(v) => return NativeResult::Err(v), + }; + let mut counter: i64 = 0; + loop { + let value = match iterator_record_step(vm, next, iterated) { + Ok(Some(v)) => v, + Ok(None) => return NativeResult::Ok(JsValue::bool(false)), + Err(v) => return NativeResult::Err(v), + }; + let cb_args = [value, counter_number(counter)]; + let result = match vm.call_function_sync(callback, JsValue::undefined(), &cb_args) { + Ok(r) => r, + Err(err) => { + let exc = engine_error(vm, &err); + let _ = iterator_close_record(vm, iterated, Some(exc)); + return NativeResult::Err(exc); + } + }; + if oxide_runtime_api::to_boolean(result) { + return match iterator_close_record(vm, iterated, None) { + Ok(()) => NativeResult::Ok(JsValue::bool(true)), + Err(v) => NativeResult::Err(v), + }; + } + counter += 1; + } +} + +/// `%Iterator.prototype%.find(predicate)`:谓词首个 truthy 关底层返回对应 value,耗尽 +/// 返回 undefined。 +/// +/// # 步骤 +/// 1. 校验 this/回调并取底层迭代器(共享前置)。 +/// 2. 循环取元素,每个元素 `Call(predicate, undefined, «value, counter»)`。 +/// 3. 谓词 truthy → IteratorClose(正常完成,return 错误胜出)后返回 value;耗尽 undefined。 +pub fn iterator_find(vm: &mut H, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let callback = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let (iterated, next) = match validate_terminal_and_get_direct(vm, this_val, callback) { + Ok(pair) => pair, + Err(v) => return NativeResult::Err(v), + }; + let mut counter: i64 = 0; + loop { + let value = match iterator_record_step(vm, next, iterated) { + Ok(Some(v)) => v, + Ok(None) => return NativeResult::Ok(JsValue::undefined()), + Err(v) => return NativeResult::Err(v), + }; + let cb_args = [value, counter_number(counter)]; + let result = match vm.call_function_sync(callback, JsValue::undefined(), &cb_args) { + Ok(r) => r, + Err(err) => { + let exc = engine_error(vm, &err); + let _ = iterator_close_record(vm, iterated, Some(exc)); + return NativeResult::Err(exc); + } + }; + if oxide_runtime_api::to_boolean(result) { + return match iterator_close_record(vm, iterated, None) { + Ok(()) => NativeResult::Ok(value), + Err(v) => NativeResult::Err(v), + }; + } + counter += 1; + } +} + +/// `%Iterator.prototype%.reduce(reducer, initialValue?)`:归约全部元素。 +/// +/// # 步骤 +/// 1. 校验 this/回调并取底层迭代器(共享前置)。 +/// 2. 无 initialValue:首元素作 accumulator(首步即 done → TypeError,不关底层); +/// 有 initialValue:直接作 accumulator。 +/// 3. 循环取元素,`Call(reducer, undefined, «accumulator, value, counter»)` 结果续作 +/// accumulator;回调抛错 → IteratorClose 后透传原值。 +/// 4. 耗尽返回 accumulator。 +pub fn iterator_reduce(vm: &mut H, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + let callback = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let has_initial = args.len() > 2; + let (iterated, next) = match validate_terminal_and_get_direct(vm, this_val, callback) { + Ok(pair) => pair, + Err(v) => return NativeResult::Err(v), + }; + // 无初始值:首元素直接作 accumulator(首步 done 抛 TypeError,规范不关底层)。 + let (mut accumulator, mut counter): (JsValue, i64) = if has_initial { + (vm.reg(args[2]), 0) + } else { + match iterator_record_step(vm, next, iterated) { + Ok(Some(first)) => (first, 1), + Ok(None) => { + return NativeResult::Err(crate::error::create_type_error( + vm, + "Reduce of empty iterator with no initial value", + )) + } + Err(v) => return NativeResult::Err(v), + } + }; + loop { + let value = match iterator_record_step(vm, next, iterated) { + Ok(Some(v)) => v, + Ok(None) => return NativeResult::Ok(accumulator), + Err(v) => return NativeResult::Err(v), + }; + let cb_args = [accumulator, value, counter_number(counter)]; + match vm.call_function_sync(callback, JsValue::undefined(), &cb_args) { + Ok(result) => accumulator = result, + Err(err) => { + let exc = engine_error(vm, &err); + let _ = iterator_close_record(vm, iterated, Some(exc)); + return NativeResult::Err(exc); + } + } + counter += 1; + } +} + +/// `%Iterator.prototype%.toArray()`:消费全部元素,返回普通数组。 +/// +/// # 步骤 +/// 1. `this` 非对象 → TypeError;GetIteratorDirect 取底层迭代器。 +/// 2. 循环收集元素到列表,耗尽后构造数组返回。 +pub fn iterator_to_array(vm: &mut H, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + if !this_val.is_object() { + return NativeResult::Err(crate::error::create_type_error( + vm, + "Iterator.prototype method called on non-object", + )); + } + let (iterated, next) = match get_iterator_direct(vm, this_val) { + Ok(pair) => pair, + Err(v) => return NativeResult::Err(v), + }; + let mut items: Vec = Vec::new(); + loop { + match iterator_record_step(vm, next, iterated) { + Ok(Some(value)) => items.push(value), + Ok(None) => return NativeResult::Ok(make_array_from_list(vm, &items)), + Err(v) => return NativeResult::Err(v), + } + } +} + +/// CreateArrayFromList:按元素列表构造普通数组(Array.prototype 为原型)。 +fn make_array_from_list(vm: &mut H, items: &[JsValue]) -> JsValue { + let array_proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject; + let arr = vm.alloc_object(JsObject::new_array( + EMPTY_SHAPE_ID, + JsValue::from_js_object(array_proto), + items.len().min(oxide_types::object::MAX_DENSE_PROPS), + vm.epoch().bump(), + )); + // 逐元素写入数组元素区,最后统一 set_prop_count(new_array 预分配不足时自动扩容)。 + for (i, item) in items.iter().enumerate() { + // SAFETY: arr 是当前 epoch 新分配数组对象,元素区已就绪。 + unsafe { + (*arr).set_prop_at(i, *item); + } + } + // SAFETY: 与 new_array 同 epoch,写入后按实际元素数设 prop_count。 + unsafe { + (*arr).set_prop_count(items.len()); + } + JsValue::from_js_object(arr) +} + /// `%IteratorPrototype%[@@iterator]`:返回 this(迭代器对象自迭代)。 /// /// 挂在 `%IteratorPrototype%` 上,所有集合迭代器经原型链继承,保证 diff --git a/crates/oxide_vm/src/bindings/mod.rs b/crates/oxide_vm/src/bindings/mod.rs index b72a10c0..c87689bf 100644 --- a/crates/oxide_vm/src/bindings/mod.rs +++ b/crates/oxide_vm/src/bindings/mod.rs @@ -438,6 +438,26 @@ fn bind_iterator_protos(core: &Arc, session: &KernelSession) { ); } + // %IteratorPrototype% 的 6 个终端方法(就地消费底层迭代器,无 wrapper): + // forEach/every/some/find/reduce 带回调校验(失败也关底层),toArray 无参。 + // length:前 5 个为 1,toArray 为 0(规范 length/name 描述符由 bind_method 设置)。 + let si_for_each = core.perm_interner().intern("forEach").0; + if core.shape_forge().lookup_position(iter_proto.shape_id(), si_for_each).is_none() { + apply_binding_table( + world, + iter_proto, + core, + &[ + ("forEach", oxide_builtins::iterator::iterator_for_each:: as *const (), 1), + ("every", oxide_builtins::iterator::iterator_every:: as *const (), 1), + ("some", oxide_builtins::iterator::iterator_some:: as *const (), 1), + ("find", oxide_builtins::iterator::iterator_find:: as *const (), 1), + ("reduce", oxide_builtins::iterator::iterator_reduce:: as *const (), 1), + ("toArray", oxide_builtins::iterator::iterator_to_array:: as *const (), 0), + ], + ); + } + // %ArrayIteratorPrototype% 服务 Array/TA 两族;Map/Set 共用按 `__mode__` // 分发的实现;%RegExpStringIteratorPrototype% 供 matchAll。 bind_iterator_proto_next( diff --git a/crates/oxide_vm/tests/builtins_iterator_tests.rs b/crates/oxide_vm/tests/builtins_iterator_tests.rs index 172c9e91..634bc05b 100644 --- a/crates/oxide_vm/tests/builtins_iterator_tests.rs +++ b/crates/oxide_vm/tests/builtins_iterator_tests.rs @@ -636,3 +636,380 @@ fn iterator_prototype_rebound_after_full_reset() { .unwrap(); assert!(r.is_undefined()); } + +// ── 终端 6 方法(forEach/every/some/find/reduce/toArray)── + +#[test] +fn iterator_terminal_for_each_basic() { + // forEach 耗尽全部元素,回调收 (value, counter),返回 undefined。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var out = []; \ + var r = Iterator.from([10, 20, 30]).forEach(function (v, i) { out.push(v + i); }); \ + out.join(',') + '|' + (r === undefined)", + ) + .unwrap(); + assert_eq!(to_str(&vm, result), "10,21,32|true"); + let bool_cases = [ + // 普通数组迭代器(非 from 包装)同样可消费。 + ("var out = 0; [1, 2, 3].values().forEach(function (v) { out += v; }); out === 6", true), + // 空迭代立即返回 undefined。 + ("Iterator.from([]).forEach(function () {}) === undefined", true), + // 回调 this 为 undefined(规范 Call(procedure, undefined, ...))。 + ( + "var captured; Iterator.from([1]).forEach(function () { captured = this; }); \ + captured === undefined", + true, + ), + ]; + for (src, expected) in bool_cases { + let result = eval(&mut vm, src).unwrap(); + assert_eq!(result.as_bool(), expected, "for {}", src); + } +} + +#[test] +fn iterator_terminal_every_some_find_basic() { + // every/some/find 正常路径:全真/短路/命中值与未命中。 + let mut vm = Vm::new(); + let bool_cases = [ + ("Iterator.from([1, 2, 3]).every(function (v) { return v > 0; }) === true", true), + ("Iterator.from([1, 2, 3]).every(function (v) { return v > 1; }) === false", true), + ("Iterator.from([1, 2, 3]).some(function (v) { return v > 2; }) === true", true), + ("Iterator.from([1, 2, 3]).some(function (v) { return v > 9; }) === false", true), + ("Iterator.from([1, 2, 3]).find(function (v) { return v > 1; }) === 2", true), + ("Iterator.from([1, 2, 3]).find(function (v) { return v > 9; }) === undefined", true), + // 空迭代:every/some 恒为相反端点值,find 为 undefined。 + ("Iterator.from([]).every(function () { return false; }) === true", true), + ("Iterator.from([]).some(function () { return true; }) === false", true), + ("Iterator.from([]).find(function () { return true; }) === undefined", true), + // 谓词回调收 (value, counter)。 + ( + "var pairs = []; \ + Iterator.from(['a', 'b']).every(function (v, i) { pairs.push(v + i); return true; }); \ + pairs.join(',') === 'a0,b1'", + true, + ), + ]; + for (src, expected) in bool_cases { + let result = eval(&mut vm, src).unwrap(); + assert_eq!(result.as_bool(), expected, "for {}", src); + } +} + +#[test] +fn iterator_terminal_short_circuit_closes() { + // 短路路径关底层:only 消费到命中点,return 方法被调用。 + let mut vm = Vm::new(); + let bool_cases = [ + // some 命中后不再消费剩余元素,且底层 return 被调用。 + ( + "var reads = 0; var closed = false; \ + class CI extends Iterator { \ + next() { reads++; return reads === 1 ? { done: false, value: 1 } : { done: true, value: undefined }; } \ + return() { closed = true; return {}; } \ + } \ + var r = new CI().some(function () { return true; }); \ + r === true && closed === true && reads === 1", + true, + ), + // every 首个 falsy 即短路关底层。 + ( + "var reads = 0; var closed = false; \ + class CI extends Iterator { \ + next() { reads++; return reads === 1 ? { done: false, value: 1 } : { done: true, value: undefined }; } \ + return() { closed = true; return {}; } \ + } \ + var r = new CI().every(function () { return false; }); \ + r === false && closed === true && reads === 1", + true, + ), + // find 命中返回对应 value 且关底层。 + ( + "var closed = false; \ + class CI extends Iterator { \ + next() { return { done: false, value: 7 }; } \ + return() { closed = true; return {}; } \ + } \ + var r = new CI().find(function () { return true; }); \ + r === 7 && closed === true", + true, + ), + // 自然耗尽(done=true)不调 return。 + ( + "var closed = false; \ + class CI extends Iterator { \ + next() { return { done: true, value: undefined }; } \ + return() { closed = true; return {}; } \ + } \ + new CI().every(function () { return true; }) === true && closed === false", + true, + ), + // 短路 close 时 return getter 抛错 → 该错误胜出(正常完成形态)。 + ( + "class CI extends Iterator { \ + next() { return { done: false, value: 1 }; } \ + get return() { throw new RangeError('close'); } \ + } \ + try { new CI().some(function () { return true; }); false } \ + catch (e) { e instanceof RangeError }", + true, + ), + ]; + for (src, expected) in bool_cases { + let result = eval(&mut vm, src).unwrap(); + assert_eq!(result.as_bool(), expected, "for {}", src); + } +} + +#[test] +fn iterator_terminal_reduce_basic() { + // reduce:有/无初始值、单元素、空迭代 + 初始值、counter 语义。 + let mut vm = Vm::new(); + let bool_cases = [ + ("Iterator.from([1, 2, 3]).reduce(function (a, b) { return a + b; }) === 6", true), + ("Iterator.from([1, 2, 3]).reduce(function (a, b) { return a + b; }, 10) === 16", true), + ("Iterator.from([1]).reduce(function (a, b) { return a + b; }) === 1", true), + ("Iterator.from([]).reduce(function (a, b) { return a + b; }, 99) === 99", true), + // 无初始值:首元素作 accumulator,counter 从 1 起。 + ( + "var pairs = []; \ + Iterator.from(['a', 'b', 'c']).reduce(function (acc, v, i) { pairs.push(i); return acc; }); \ + pairs.join(',') === '1,2'", + true, + ), + // 有初始值:counter 从 0 起。 + ( + "var pairs = []; \ + Iterator.from(['a', 'b']).reduce(function (acc, v, i) { pairs.push(i); return acc; }, 'x'); \ + pairs.join(',') === '0,1'", + true, + ), + // accumulator 可为任意类型(含对象引用延续)。 + ( + "var acc = []; \ + Iterator.from([1, 2]).reduce(function (a, v) { a.push(v); return a; }, acc) === acc \ + && acc.join(',') === '1,2'", + true, + ), + ]; + for (src, expected) in bool_cases { + let result = eval(&mut vm, src).unwrap(); + assert_eq!(result.as_bool(), expected, "for {}", src); + } +} + +#[test] +fn iterator_terminal_reduce_empty_no_initial_throws() { + // 空迭代无初始值 → TypeError,且不关底层(规范字面:首步 done 直接抛)。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var closed = false; \ + class CI extends Iterator { \ + next() { return { done: true, value: undefined }; } \ + return() { closed = true; return {}; } \ + } \ + var ok = false; \ + try { new CI().reduce(function (a, b) { return a + b; }); } \ + catch (e) { ok = e instanceof TypeError; } \ + ok && closed === false", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn iterator_terminal_to_array() { + // toArray 消费全部元素返回普通数组。 + let mut vm = Vm::new(); + let bool_cases = [ + ( + "var a = Iterator.from([1, 2, 3]).toArray(); \ + a instanceof Array && a.length === 3 && a[0] === 1 && a[2] === 3", + true, + ), + ( + "var a = Iterator.from((function* () { yield 'x'; yield 'y'; })()).toArray(); \ + a instanceof Array && a.length === 2 && a[0] === 'x' && a[1] === 'y'", + true, + ), + ( + "Iterator.from([]).toArray() instanceof Array && Iterator.from([]).toArray().length === 0", + true, + ), + // 数组迭代器(Array.prototype.values)同样可收集。 + ("var a = [1, 2].values().toArray(); a instanceof Array && a.length === 2", true), + ]; + for (src, expected) in bool_cases { + let result = eval(&mut vm, src).unwrap(); + assert_eq!(result.as_bool(), expected, "for {}", src); + } +} + +#[test] +fn iterator_terminal_callback_error_passthrough() { + // 回调抛错:原值透传(任意类型不二次包装),且关底层。 + let mut vm = Vm::new(); + let bool_cases = [ + // 非 Error 原值 42 透传。 + ( + "try { Iterator.from([1]).forEach(function () { throw 42; }); 'no' } \ + catch (e) { e === 42 }", + true, + ), + // Error 对象身份保留(不重包)。 + ( + "var sentinel = new RangeError('boom'); \ + try { Iterator.from([1]).every(function () { throw sentinel; }); 'no' } \ + catch (e) { e === sentinel }", + true, + ), + // 回调抛错后底层 return 被调用,原错误仍胜出。 + ( + "var closed = false; \ + class CI extends Iterator { \ + next() { return { done: false, value: 1 }; } \ + return() { closed = true; throw new RangeError('close'); } \ + } \ + var ok = false; \ + try { new CI().find(function () { throw 42; }); } \ + catch (e) { ok = e === 42; } \ + ok && closed === true", + true, + ), + ]; + for (src, expected) in bool_cases { + let result = eval(&mut vm, src).unwrap(); + assert_eq!(result.as_bool(), expected, "for {}", src); + } +} + +#[test] +fn iterator_terminal_callback_validation_closes() { + // 回调不可调用:抛 TypeError 且关底层,且不读 next(2024 规范更新)。 + let mut vm = Vm::new(); + let bool_cases = [ + // 无参调用(回调为 undefined)也关底层。 + ( + "var closed = false; \ + class CI extends Iterator { \ + next() { return { done: true, value: undefined }; } \ + return() { closed = true; return {}; } \ + } \ + var ok = false; \ + try { new CI().forEach(); } catch (e) { ok = e instanceof TypeError; } \ + ok && closed === true", + true, + ), + // 非可调用对象同样关底层;next getter 不被读取。 + ( + "var closed = false; var read = false; \ + var it = Object.create(Iterator.prototype); \ + Object.defineProperty(it, 'next', { get: function () { read = true; return function () {}; } }); \ + it.return = function () { closed = true; return {}; }; \ + var ok = false; \ + try { it.forEach({}); } catch (e) { ok = e instanceof TypeError; } \ + ok && closed === true && read === false", + true, + ), + // 回调校验失败关底层对 every/some/find/reduce 一致生效。 + ( + "var closed = false; \ + class CI extends Iterator { \ + next() { return { done: true, value: undefined }; } \ + return() { closed = true; return {}; } \ + } \ + var all = false; \ + try { new CI().some(null); } catch (e) { all = e instanceof TypeError; } \ + all && closed === true", + true, + ), + ]; + for (src, expected) in bool_cases { + let result = eval(&mut vm, src).unwrap(); + assert_eq!(result.as_bool(), expected, "for {}", src); + } +} + +#[test] +fn iterator_terminal_reentrancy_guard() { + // 终端方法消费期间底层生成器被重入(body 执行中再取 next):生成器重入守卫 + // 抛 TypeError,经终端方法干净透传(不吞错、不损坏生成器状态)。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "var caught = null; \ + var gen = (function* () { \ + yield 1; \ + try { Iterator.from(gen).forEach(function (v) { gen.next(); }); } \ + catch (e) { caught = e; } \ + })(); \ + gen.next(); gen.next(); \ + caught !== null && caught instanceof TypeError", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn iterator_terminal_method_shape() { + // 6 方法 length/name 与绑定位置(挂 %IteratorPrototype% 原型)。 + let mut vm = Vm::new(); + let bool_cases = [ + ( + "Iterator.prototype.forEach.length === 1 && Iterator.prototype.every.length === 1 \ + && Iterator.prototype.some.length === 1 && Iterator.prototype.find.length === 1 \ + && Iterator.prototype.reduce.length === 1 && Iterator.prototype.toArray.length === 0", + true, + ), + ( + "Iterator.prototype.forEach.name === 'forEach' && Iterator.prototype.every.name === 'every' \ + && Iterator.prototype.some.name === 'some' && Iterator.prototype.find.name === 'find' \ + && Iterator.prototype.reduce.name === 'reduce' && Iterator.prototype.toArray.name === 'toArray'", + true, + ), + // 方法经原型链对普通对象可用(this 为任意对象即可)。 + ( + "var it = { next: function () { return { done: true, value: undefined }; } }; \ + Iterator.prototype.toArray.call(it) instanceof Array", + true, + ), + // this 非对象 → TypeError。 + ( + "try { Iterator.prototype.forEach.call(1, function () {}); false } \ + catch (e) { e instanceof TypeError }", + true, + ), + ]; + for (src, expected) in bool_cases { + let result = eval(&mut vm, src).unwrap(); + assert_eq!(result.as_bool(), expected, "for {}", src); + } +} + +#[test] +fn iterator_terminal_rebound_after_full_reset() { + // dirty reset 后 6 终端方法随 %IteratorPrototype% 重建重绑,功能完好。 + let mut vm = Vm::new(); + unsafe { &mut *(vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject) }.bump_generation(); + vm.full_reset(); + let bool_cases = [ + ("Iterator.from([1, 2, 3]).forEach(function () {}) === undefined", true), + ( + "var out = []; \ + Iterator.from(['a', 'b']).forEach(function (v) { out.push(v); }); out.join('') === 'ab'", + true, + ), + ("Iterator.from([1, 2]).reduce(function (a, b) { return a + b; }, 0) === 3", true), + ("Iterator.from([1, 2]).toArray().length === 2", true), + ("Iterator.from([1]).some(function () { return true; }) === true", true), + ("Iterator.from([1]).every(function () { return true; }) === true", true), + ("Iterator.from([1]).find(function () { return true; }) === 1", true), + ]; + for (src, expected) in bool_cases { + let result = eval(&mut vm, src).unwrap(); + assert_eq!(result.as_bool(), expected, "for {}", src); + } +}