mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 实现 Iterator.concat 按序拼接迭代器
This commit is contained in:
@@ -20,6 +20,10 @@ const ZIPS_PROP: &str = "__zips__";
|
||||
const OPEN_PROP: &str = "__open__";
|
||||
const RESULTS_PROP: &str = "__results__";
|
||||
const PADDING_PROP: &str = "__padding__";
|
||||
const ITEMS_PROP: &str = "__items__";
|
||||
const SEG_PROP: &str = "__seg__";
|
||||
const CURRENT_PROP: &str = "__current__";
|
||||
const CURRENT_NEXT_PROP: &str = "__current_next__";
|
||||
|
||||
/// `Iterator` 构造逻辑:不是构造函数,`new Iterator()` / `Iterator()` 均抛 TypeError;
|
||||
/// subclass `super()` 路径放行并返回 undefined(原型由调用方按 newTarget 设置)。
|
||||
@@ -331,6 +335,7 @@ pub(crate) enum IteratorHelperKind {
|
||||
Drop = 3,
|
||||
FlatMap = 4,
|
||||
Zip = 5,
|
||||
Concat = 6,
|
||||
}
|
||||
|
||||
impl IteratorHelperKind {
|
||||
@@ -342,6 +347,7 @@ impl IteratorHelperKind {
|
||||
3 => Self::Drop,
|
||||
4 => Self::FlatMap,
|
||||
5 => Self::Zip,
|
||||
6 => Self::Concat,
|
||||
_ => Self::Map,
|
||||
}
|
||||
}
|
||||
@@ -483,6 +489,31 @@ fn get_iterator_strict<H: VmHost>(vm: &mut H, what: &str, value: JsValue) -> Res
|
||||
get_iterator_direct(vm, iter)
|
||||
}
|
||||
|
||||
/// GetMethod 只读:`@@iterator` getter 抛错透传原值,方法为 undefined/null/
|
||||
/// 其它不可调用值一律 TypeError(规范 GetMethod 步 4 同落 TypeError),返回
|
||||
/// 方法闭包本身,不调用(concat 构造期只缓存方法,懒开由各段首次 next 触发)。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `value` 必须是对象(调用方已校验)。
|
||||
///
|
||||
/// # 返回
|
||||
/// - `Ok(method)`:可调用方法闭包;`Err` 为透传异常值或 TypeError。
|
||||
fn get_iterator_method<H: VmHost>(vm: &mut H, value: JsValue) -> Result<JsValue, JsValue> {
|
||||
let obj = unsafe { &*value.as_js_object_ptr() };
|
||||
let sym_iter_si = encode_symbol_key(vm.realm_id(), 0);
|
||||
let method = match vm.ordinary_get(obj, sym_iter_si, value) {
|
||||
Ok(m) => m,
|
||||
Err(err) => return Err(engine_error(vm, &err)),
|
||||
};
|
||||
if !is_callable(method) {
|
||||
return Err(crate::error::create_type_error(
|
||||
vm,
|
||||
"Iterable object does not have a callable @@iterator method",
|
||||
));
|
||||
}
|
||||
Ok(method)
|
||||
}
|
||||
|
||||
/// GetOptionsObject:undefined 建 null 原型空对象,对象原样,其余抛 TypeError。
|
||||
fn get_options_object<H: VmHost>(vm: &mut H, options: JsValue) -> Result<JsValue, JsValue> {
|
||||
if options.is_undefined() {
|
||||
@@ -535,11 +566,12 @@ fn make_iterator_helper<H: VmHost>(
|
||||
|
||||
/// 校验 wrapper 对象形态并读出 kind:`this` 非对象 → TypeError;其余 kind 以
|
||||
/// `__inner__` 槽为对象作判据,Zip 以 `__zips__` 槽为对象作判据(zip wrapper
|
||||
/// 无 `__inner__` 槽),Concat 以 `__items__` 槽为对象作判据(concat wrapper
|
||||
/// 无 `__inner__` 槽)。
|
||||
///
|
||||
/// # 返回
|
||||
/// - `Ok((kind, inner))`:kind 与底层值(其余 kind 为 `__inner__` 槽,Zip 为
|
||||
/// `__zips__` 槽);`Err` 为透传的异常值。
|
||||
/// `__zips__` 槽,Concat 为 `__items__` 槽);`Err` 为透传的异常值。
|
||||
fn validate_helper_this<H: VmHost>(
|
||||
vm: &mut H, this_val: JsValue, method: &str,
|
||||
) -> Result<(IteratorHelperKind, JsValue), JsValue> {
|
||||
@@ -557,6 +589,14 @@ fn validate_helper_this<H: VmHost>(
|
||||
}
|
||||
return Ok((kind, zips));
|
||||
}
|
||||
if kind == IteratorHelperKind::Concat {
|
||||
let items_si = vm.perm_intern(ITEMS_PROP);
|
||||
let items = vm.ordinary_get(obj, items_si, this_val).map_err(|e| engine_error(vm, &e))?;
|
||||
if !items.is_object() {
|
||||
return Err(crate::error::create_type_error(vm, &format!("{method} called on non-iterator-helper")));
|
||||
}
|
||||
return Ok((kind, items));
|
||||
}
|
||||
let inner_si = vm.perm_intern(INNER_PROP);
|
||||
let inner = vm.ordinary_get(obj, inner_si, this_val).map_err(|e| engine_error(vm, &e))?;
|
||||
if !inner.is_object() {
|
||||
@@ -967,12 +1007,17 @@ pub fn iterator_helper_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
IteratorHelperKind::Drop => helper_step_drop(vm, helper, inner, next),
|
||||
IteratorHelperKind::FlatMap => helper_step_flat_map(vm, this_val, helper, inner, next),
|
||||
IteratorHelperKind::Zip => helper_step_zip(vm, this_val, helper),
|
||||
IteratorHelperKind::Concat => helper_step_concat(vm, this_val, helper),
|
||||
};
|
||||
match step {
|
||||
Ok(Some(value)) => {
|
||||
// zip 产出后为 suspended-yield(state 3,return/throw 关闭期重入守卫
|
||||
// 需区分 suspended-start 的完成态短路);其余 kind 恒 0。
|
||||
let new_state = if kind == IteratorHelperKind::Zip { 3 } else { 0 };
|
||||
// zip 与 concat 产出后为 suspended-yield(state 3,return/throw 关闭期
|
||||
// 重入守卫需区分 suspended-start 的完成态短路);其余 kind 恒 0。
|
||||
let new_state = if matches!(kind, IteratorHelperKind::Zip | IteratorHelperKind::Concat) {
|
||||
3
|
||||
} else {
|
||||
0
|
||||
};
|
||||
vm.set_or_create_prop_value(helper, state_si, JsValue::int(new_state));
|
||||
NativeResult::Ok(make_iter_result(vm, value, false))
|
||||
}
|
||||
@@ -1032,6 +1077,24 @@ pub fn iterator_helper_return<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
||||
vm.set_or_create_prop_value(helper, state_si, JsValue::int(2));
|
||||
return result;
|
||||
}
|
||||
if kind == IteratorHelperKind::Concat {
|
||||
// 以 `__current__` 槽在场判定内层是否已开:已开则置执行中(重入守卫)
|
||||
// 后关当前内层(正常完成,关闭错误传播)返 done;未开段(首次 next
|
||||
// 前)与自然耗尽后均空操作,不转发 return。
|
||||
let current_si = vm.perm_intern(CURRENT_PROP);
|
||||
let current = vm.ordinary_get(helper, current_si, this_val).unwrap_or(JsValue::undefined());
|
||||
if current.is_object() {
|
||||
vm.set_or_create_prop_value(helper, state_si, JsValue::int(1));
|
||||
let result = match iterator_close_record(vm, current, None) {
|
||||
Ok(()) => NativeResult::Ok(make_iter_result(vm, JsValue::undefined(), true)),
|
||||
Err(v) => NativeResult::Err(v),
|
||||
};
|
||||
vm.set_or_create_prop_value(helper, state_si, JsValue::int(2));
|
||||
return result;
|
||||
}
|
||||
vm.set_or_create_prop_value(helper, state_si, JsValue::int(2));
|
||||
return NativeResult::Ok(make_iter_result(vm, JsValue::undefined(), true));
|
||||
}
|
||||
let kind_si = vm.perm_intern(KIND_PROP);
|
||||
if IteratorHelperKind::from_i32(read_slot_int(vm, helper, kind_si, 0)) == IteratorHelperKind::FlatMap {
|
||||
let inner_iter_si = vm.perm_intern(INNER_ITER_PROP);
|
||||
@@ -1092,6 +1155,21 @@ pub fn iterator_helper_throw<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
vm.set_or_create_prop_value(helper, state_si, JsValue::int(2));
|
||||
return NativeResult::Err(value);
|
||||
}
|
||||
if kind == IteratorHelperKind::Concat {
|
||||
// 与 return 臂同判据:`__current__` 在场则置执行中(重入守卫)后关当前
|
||||
// 内层(throw 完成,原值恒胜出,关闭错误被吞)再抛 value;未开段直接
|
||||
// 抛 value。
|
||||
let current_si = vm.perm_intern(CURRENT_PROP);
|
||||
let current = vm.ordinary_get(helper, current_si, this_val).unwrap_or(JsValue::undefined());
|
||||
if current.is_object() {
|
||||
vm.set_or_create_prop_value(helper, state_si, JsValue::int(1));
|
||||
let _ = iterator_close_record(vm, current, Some(value));
|
||||
vm.set_or_create_prop_value(helper, state_si, JsValue::int(2));
|
||||
return NativeResult::Err(value);
|
||||
}
|
||||
vm.set_or_create_prop_value(helper, state_si, JsValue::int(2));
|
||||
return NativeResult::Err(value);
|
||||
}
|
||||
let kind_si = vm.perm_intern(KIND_PROP);
|
||||
if IteratorHelperKind::from_i32(read_slot_int(vm, helper, kind_si, 0)) == IteratorHelperKind::FlatMap {
|
||||
let inner_iter_si = vm.perm_intern(INNER_ITER_PROP);
|
||||
@@ -1703,6 +1781,147 @@ fn helper_step_zip<H: VmHost>(
|
||||
Ok(Some(arr_val))
|
||||
}
|
||||
|
||||
/// `Iterator.concat(...items)`:返回按序依次产出各实参元素的迭代器。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 逐实参按序:对象检查(非对象 TypeError)加 GetMethod 只读缓存 `@@iterator`
|
||||
/// 方法(getter 构造期调用一次、方法不调用);任一实参抛错即终止,后续
|
||||
/// 实参不读。
|
||||
/// 2. 建 wrapper(内层迭代器懒开:各段首次 next 才调用方法)。
|
||||
///
|
||||
/// # 边界
|
||||
/// - 静态方法,无 this 检查、无 options 参数。
|
||||
/// - 方法为 undefined/null/其它不可调用值 → TypeError(构造期不触发方法调用)。
|
||||
pub fn iterator_concat<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let mut items: Vec<(JsValue, JsValue)> = Vec::new();
|
||||
for i in 1..args.len() {
|
||||
let item = vm.reg(args[i]);
|
||||
if !item.is_object() {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "Iterator.concat argument is not an object"));
|
||||
}
|
||||
let method = match get_iterator_method(vm, item) {
|
||||
Ok(m) => m,
|
||||
Err(v) => return NativeResult::Err(v),
|
||||
};
|
||||
items.push((method, item));
|
||||
}
|
||||
NativeResult::Ok(make_iterator_concat_helper(vm, &items))
|
||||
}
|
||||
|
||||
/// 建 concat wrapper:空对象挂 `%IteratorHelperPrototype%`,写 `__items__`
|
||||
/// ([method, iterable] 对数组)、`__seg__`(段下标,初始 0)、`__current__`
|
||||
/// (当前内层,初始 undefined)、`__current_next__`(缓存 next,初始
|
||||
/// undefined)、`__state__`(0)、`__kind__`(6)槽。
|
||||
///
|
||||
/// 独立构造器(不复用 [`make_iterator_helper`]):槽位布局不同,concat 无
|
||||
/// `__inner__`/`__next__` 槽。
|
||||
fn make_iterator_concat_helper<H: VmHost>(vm: &mut H, items: &[(JsValue, JsValue)]) -> JsValue {
|
||||
let helper_proto = vm.builtin_proto(ProtoKind::IteratorHelperProto);
|
||||
let wrapper = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(helper_proto)));
|
||||
let wrapper_obj = unsafe { &mut *wrapper };
|
||||
let array_proto = vm.builtin_proto(ProtoKind::ArrayProto);
|
||||
let items_arr =
|
||||
vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(array_proto), items.len()));
|
||||
for (i, (method, iterable)) in items.iter().enumerate() {
|
||||
let pair = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(array_proto), 2));
|
||||
// SAFETY: pair 是刚经统一入口分配的数组对象,元素区已就绪。
|
||||
unsafe {
|
||||
(*pair).set_prop_at(0, *method);
|
||||
(*pair).set_prop_at(1, *iterable);
|
||||
}
|
||||
// SAFETY: 同 epoch,写入后按实际元素数设 prop_count。
|
||||
unsafe {
|
||||
(*items_arr).set_prop_at(i, JsValue::from_js_object(pair));
|
||||
}
|
||||
}
|
||||
// SAFETY: 与 new_array 同 epoch,写入后按实际元素数设 prop_count。
|
||||
unsafe {
|
||||
(*items_arr).set_prop_count(items.len());
|
||||
}
|
||||
let items_si = vm.perm_intern(ITEMS_PROP);
|
||||
let seg_si = vm.perm_intern(SEG_PROP);
|
||||
let current_si = vm.perm_intern(CURRENT_PROP);
|
||||
let current_next_si = vm.perm_intern(CURRENT_NEXT_PROP);
|
||||
let state_si = vm.perm_intern(STATE_PROP);
|
||||
let kind_si = vm.perm_intern(KIND_PROP);
|
||||
vm.set_or_create_prop_value(wrapper_obj, items_si, JsValue::from_js_object(items_arr));
|
||||
vm.set_or_create_prop_value(wrapper_obj, seg_si, JsValue::int(0));
|
||||
vm.set_or_create_prop_value(wrapper_obj, current_si, JsValue::undefined());
|
||||
vm.set_or_create_prop_value(wrapper_obj, current_next_si, JsValue::undefined());
|
||||
vm.set_or_create_prop_value(wrapper_obj, state_si, JsValue::int(0));
|
||||
vm.set_or_create_prop_value(wrapper_obj, kind_si, JsValue::int(IteratorHelperKind::Concat as i32));
|
||||
JsValue::from_js_object(wrapper)
|
||||
}
|
||||
|
||||
/// concat 推进:自 `__seg__` 起逐段——未开段(`__current__` 非对象)先懒开
|
||||
/// (`Call(method, iterable)`,结果非对象抛 TypeError,加 GetIteratorDirect
|
||||
/// 写槽);内层步进:产出返回;done 清槽不关、切下一段;骤发只关当前内层
|
||||
/// (throw 完成,原值恒胜出)透传;全段耗尽收口。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// 每次用户调用窗口后重读 `__items__`/`__seg__`/`__current__` 槽(回调可改写
|
||||
/// wrapper 槽),不消费求值前快照。
|
||||
fn helper_step_concat<H: VmHost>(
|
||||
vm: &mut H, this_val: JsValue, helper: &mut JsObject,
|
||||
) -> Result<Option<JsValue>, JsValue> {
|
||||
let items_si = vm.perm_intern(ITEMS_PROP);
|
||||
let seg_si = vm.perm_intern(SEG_PROP);
|
||||
let current_si = vm.perm_intern(CURRENT_PROP);
|
||||
let current_next_si = vm.perm_intern(CURRENT_NEXT_PROP);
|
||||
loop {
|
||||
// 每次用户调用窗口后重读槽位(回调可改写 wrapper 槽)。
|
||||
let items_val = vm.ordinary_get(helper, items_si, this_val).unwrap_or(JsValue::undefined());
|
||||
if !items_val.is_object() {
|
||||
return Ok(None);
|
||||
}
|
||||
let items_obj = unsafe { &*items_val.as_js_object_ptr() };
|
||||
let item_count = items_obj.prop_count();
|
||||
let seg = read_slot_int(vm, helper, seg_si, 0) as u32;
|
||||
if seg >= item_count {
|
||||
return Ok(None);
|
||||
}
|
||||
let pair_val = items_obj.get_prop_at(seg);
|
||||
let (method, iterable) = if pair_val.is_object() {
|
||||
let pair_obj = unsafe { &*pair_val.as_js_object_ptr() };
|
||||
(pair_obj.get_prop_at(0), pair_obj.get_prop_at(1))
|
||||
} else {
|
||||
return Ok(None);
|
||||
};
|
||||
let current = vm.ordinary_get(helper, current_si, this_val).unwrap_or(JsValue::undefined());
|
||||
if !current.is_object() {
|
||||
// 懒开:本段首次 next 才调用方法(构造期只缓存不调用)。
|
||||
let inner = vm.call_function_sync(method, iterable, &[]).map_err(|e| engine_error(vm, &e))?;
|
||||
if !inner.is_object() {
|
||||
return Err(crate::error::create_type_error(
|
||||
vm,
|
||||
"Iterator.concat iterable @@iterator returned a non-object",
|
||||
));
|
||||
}
|
||||
let (inner, next) = get_iterator_direct(vm, inner)?;
|
||||
vm.set_or_create_prop_value(helper, current_si, inner);
|
||||
vm.set_or_create_prop_value(helper, current_next_si, next);
|
||||
continue;
|
||||
}
|
||||
let next = vm
|
||||
.ordinary_get(helper, current_next_si, this_val)
|
||||
.unwrap_or(JsValue::undefined());
|
||||
match iterator_record_step(vm, next, current) {
|
||||
Ok(Some(value)) => return Ok(Some(value)),
|
||||
Ok(None) => {
|
||||
// 自然耗尽:清槽不关内层,切下一段。
|
||||
vm.set_or_create_prop_value(helper, current_si, JsValue::undefined());
|
||||
vm.set_or_create_prop_value(helper, current_next_si, JsValue::undefined());
|
||||
vm.set_or_create_prop_value(helper, seg_si, JsValue::int((seg + 1) as i32));
|
||||
}
|
||||
Err(v) => {
|
||||
// 骤发:只关当前内层(throw 完成,原值恒胜出),透传原值。
|
||||
let _ = iterator_close_record(vm, current, Some(v));
|
||||
return Err(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `%IteratorPrototype%[@@iterator]`:返回 this(迭代器对象自迭代)。
|
||||
///
|
||||
/// 挂在 `%IteratorPrototype%` 上,所有集合迭代器经原型链继承,保证
|
||||
|
||||
@@ -7,7 +7,8 @@ use crate::bindings::{
|
||||
configure_native_constructor, BindEnv,
|
||||
};
|
||||
|
||||
/// 绑定 `Iterator` 全局构造器(`prototype` = %IteratorPrototype%、`from`/`zip` 方法)。
|
||||
/// 绑定 `Iterator` 全局构造器(`prototype` = %IteratorPrototype%、`from`/`zip`/
|
||||
/// `concat` 方法)。
|
||||
///
|
||||
/// 迭代器原型方法(%IteratorPrototype% 的 `@@iterator` 与各集合原型 `next`)由
|
||||
/// `rebind_dirty_builtins` 的 object 分支统一安装,本入口不重复绑定,避免
|
||||
@@ -30,6 +31,7 @@ pub fn bind_iterator(env: BindEnv<'_>, global: &mut JsObject) {
|
||||
&[
|
||||
("from", oxide_builtins::iterator::iterator_from::<crate::vm::Vm> as *const (), 1),
|
||||
("zip", oxide_builtins::iterator::iterator_zip::<crate::vm::Vm> as *const (), 1),
|
||||
("concat", oxide_builtins::iterator::iterator_concat::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -966,6 +966,7 @@ fn bind_iterator_global(env: BindEnv<'_>, global: &mut JsObject) {
|
||||
&[
|
||||
("from", oxide_builtins::iterator::iterator_from::<crate::vm::Vm> as *const (), 1),
|
||||
("zip", oxide_builtins::iterator::iterator_zip::<crate::vm::Vm> as *const (), 1),
|
||||
("concat", oxide_builtins::iterator::iterator_concat::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
let iterator_ptr = Box::into_raw(iterator);
|
||||
|
||||
@@ -3040,3 +3040,383 @@ fn iterator_zip_padding_not_exhausted_close() {
|
||||
.unwrap();
|
||||
assert!(r.as_bool(), "padding exhausted exactly at iterCount: no close, done landing undefined");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iterator_concat_binding_surface() {
|
||||
// 绑定面:name/length 0/原型/描述符/非构造/instanceof/结果原型。
|
||||
let mut vm = Vm::new();
|
||||
let bool_cases = [
|
||||
(
|
||||
"typeof Iterator.concat === 'function' && Iterator.concat.name === 'concat' \
|
||||
&& Iterator.concat.length === 0",
|
||||
true,
|
||||
),
|
||||
("Object.getPrototypeOf(Iterator.concat) === Function.prototype", true),
|
||||
(
|
||||
"var d = Object.getOwnPropertyDescriptor(Iterator, 'concat'); \
|
||||
d.writable === true && d.enumerable === false && d.configurable === true",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"var ok = false; try { new Iterator.concat(); } catch (e) { ok = e instanceof TypeError; } ok",
|
||||
true,
|
||||
),
|
||||
("var it = Iterator.concat(); it instanceof Iterator", true),
|
||||
(
|
||||
"var it = Iterator.concat(); var p = Object.getPrototypeOf(it); \
|
||||
p[Symbol.toStringTag] === 'Iterator Helper' \
|
||||
&& Object.getPrototypeOf(p) === Iterator.prototype",
|
||||
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_concat_zero_and_basic_sequences() {
|
||||
// 零实参首次 next 即 done;单/多段元素序列、空段穿插、段切换不关前段。
|
||||
let mut vm = Vm::new();
|
||||
let bool_cases = [
|
||||
(
|
||||
"var it = Iterator.concat(); var r = it.next(); \
|
||||
r.done === true && r.value === undefined && it.next().done === true",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"var r = Array.from(Iterator.concat([1, 2], [3], [4, 5])); \
|
||||
r.length === 5 && r[0] === 1 && r[4] === 5",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"var r = Array.from(Iterator.concat([], [1], [], [2], [])); \
|
||||
r.length === 2 && r[0] === 1 && r[1] === 2",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"var rets = 0; \
|
||||
var it = Iterator.concat(\
|
||||
{ [Symbol.iterator]() { var i = 0; \
|
||||
return { next() { if (i++ < 1) { return {done: false, value: 1}; } return {done: true}; }, \
|
||||
return() { rets++; return {}; } }; } }, \
|
||||
[2]); \
|
||||
var a = it.next(); var b = it.next(); var c = it.next(); \
|
||||
a.value === 1 && b.value === 2 && c.done === true && rets === 0",
|
||||
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_concat_lazy_open_timing() {
|
||||
// 构造期只读方法(getter 一次、方法不调用);方法每段首 next 恰好一次、
|
||||
// 段序即实参序;每次 next 新建结果对象。
|
||||
let mut vm = Vm::new();
|
||||
let bool_cases = [
|
||||
(
|
||||
"(() => { \
|
||||
var gets = 0, calls = 0; \
|
||||
var iterable = { get [Symbol.iterator]() { \
|
||||
++gets; return function () { ++calls; return [1, 2][Symbol.iterator](); }; } }; \
|
||||
var it = Iterator.concat(iterable); \
|
||||
var afterCtor = gets === 1 && calls === 0; \
|
||||
var r = Array.from(it); \
|
||||
return afterCtor && gets === 1 && calls === 1 && r.length === 2 && r[1] === 2; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"(() => { \
|
||||
var called = []; \
|
||||
var a = { [Symbol.iterator]() { called.push('a'); return [1][Symbol.iterator](); } }; \
|
||||
var b = { [Symbol.iterator]() { called.push('b'); return [2][Symbol.iterator](); } }; \
|
||||
var it = Iterator.concat(a, b); \
|
||||
var before = JSON.stringify(called) === '[]'; \
|
||||
var r1 = it.next(); \
|
||||
var afterFirst = r1.value === 1 && JSON.stringify(called) === '[\"a\"]'; \
|
||||
var r2 = it.next(); \
|
||||
var afterSecond = r2.value === 2 && JSON.stringify(called) === '[\"a\",\"b\"]'; \
|
||||
return before && afterFirst && afterSecond && it.next().done === true; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"var it = Iterator.concat([1]); \
|
||||
var r1 = it.next(); var r2 = it.next(); \
|
||||
r1 !== r2 && r1.value === 1 && r2.done === 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_concat_construction_validation() {
|
||||
// 构造期逐实参按序:非对象 TypeError(getter 不被调用);方法 undefined/null/
|
||||
// 非可调用 TypeError;getter 抛错透传原值且后续实参不读。
|
||||
let mut vm = Vm::new();
|
||||
let bool_cases = [
|
||||
(
|
||||
"var ok = false; try { Iterator.concat(1); } catch (e) { ok = e instanceof TypeError; } ok",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"var ok = false; try { Iterator.concat(null); } catch (e) { ok = e instanceof TypeError; } ok",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"var ok = false; try { Iterator.concat(Object.create(null)); } \
|
||||
catch (e) { ok = e instanceof TypeError; } ok",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"var ok = false; try { Iterator.concat({[Symbol.iterator]: null}); } \
|
||||
catch (e) { ok = e instanceof TypeError; } ok",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"var ok = false; try { Iterator.concat({[Symbol.iterator]: 42}); } \
|
||||
catch (e) { ok = e instanceof TypeError; } ok",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"(() => { \
|
||||
var secondRead = 0; \
|
||||
var first = { get [Symbol.iterator]() { throw new RangeError('boom'); } }; \
|
||||
var second = { get [Symbol.iterator]() { ++secondRead; return function () {}; } }; \
|
||||
var threw = false; \
|
||||
try { Iterator.concat(first, second); } \
|
||||
catch (e) { threw = e instanceof RangeError; } \
|
||||
return threw && secondRead === 0; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"var r = Array.from(Iterator.concat(Object('abc'), Object('123'))); \
|
||||
r.length === 6 && r[0] === 'a' && r[5] === '3'",
|
||||
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_concat_step_family() {
|
||||
// 内层 next 零参调用;结果非对象 TypeError;done 先读、done 为真不读 value
|
||||
// getter;value/done getter 抛错或 next 抛错均为骤发(只关当前内层、原值
|
||||
// 胜出、return 自身抛错被吞);方法结果非对象 TypeError。
|
||||
let mut vm = Vm::new();
|
||||
let bool_cases = [
|
||||
(
|
||||
"(() => { \
|
||||
var args = null; \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next: function (a) { args = a; return {done: false, value: 1}; } }; } }); \
|
||||
var r = it.next(); \
|
||||
return r.value === 1 && args === undefined; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"var ok = false; try { \
|
||||
Iterator.concat({ [Symbol.iterator]() { return 42; } }).next(); \
|
||||
} catch (e) { ok = e instanceof TypeError; } ok",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"(() => { \
|
||||
var valueGets = 0; \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next() { return { done: true, get value() { ++valueGets; return 1; } }; } }; } }); \
|
||||
var r = it.next(); \
|
||||
return r.done === true && valueGets === 0; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"(() => { \
|
||||
var threw = false; var returnCount = 0; \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next() { return { done: false, get value() { throw new RangeError('v'); } }; }, \
|
||||
return() { ++returnCount; throw new Error('swallowed'); } }; } }); \
|
||||
try { it.next(); } catch (e) { threw = e instanceof RangeError; } \
|
||||
return threw && returnCount === 1; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"(() => { \
|
||||
var threw = false; var returnCount = 0; \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next() { throw new RangeError('n'); }, \
|
||||
return() { ++returnCount; throw new Error('swallowed'); } }; } }); \
|
||||
try { it.next(); } catch (e) { threw = e instanceof RangeError; } \
|
||||
return threw && returnCount === 1; \
|
||||
})()",
|
||||
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_concat_return_forwarding() {
|
||||
// return 转发:完成态短路 done;未开段(首次 next 前)不转发;转发只一次;
|
||||
// 耗尽后不转发;内层 return 零参调用。
|
||||
let mut vm = Vm::new();
|
||||
let bool_cases = [
|
||||
(
|
||||
"(() => { \
|
||||
var rets = 0; \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next() { return {done: false, value: 1}; }, return() { ++rets; return {}; } }; } }); \
|
||||
it.return(); \
|
||||
return rets === 0; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"(() => { \
|
||||
var rets = 0; \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next() { return {done: false, value: 1}; }, return() { ++rets; return {}; } }; } }); \
|
||||
var r1 = it.next(); \
|
||||
it.return(); \
|
||||
var r2 = it.return(); \
|
||||
return r1.value === 1 && rets === 1 && r2.done === true && rets === 1; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"(() => { \
|
||||
var rets = 0; \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next() { return {done: true}; }, return() { ++rets; return {}; } }; } }); \
|
||||
var r = it.next(); \
|
||||
it.return(); \
|
||||
return r.done === true && rets === 0; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"(() => { \
|
||||
var args = null; \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next() { return {done: false, value: 1}; }, \
|
||||
return: function (a) { args = a; return {}; } }; } }); \
|
||||
it.next(); it.return(); \
|
||||
return args === undefined; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"(() => { \
|
||||
var rets = 0; \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next() { return {done: true}; }, return() { ++rets; return {}; } }; } }); \
|
||||
it.next(); \
|
||||
var r = it.return(); \
|
||||
return r.done === true && rets === 0; \
|
||||
})()",
|
||||
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_concat_throw_original_wins() {
|
||||
// throw:完成态重抛 value;未开段直接抛 value;已开段关当前内层(throw
|
||||
// 完成,原值恒胜出、return 自身抛错被吞)后抛 value。
|
||||
let mut vm = Vm::new();
|
||||
let bool_cases = [
|
||||
(
|
||||
"(() => { \
|
||||
var threw = false; \
|
||||
var it = Iterator.concat(); \
|
||||
try { it.throw('v'); } catch (e) { threw = e === 'v'; } \
|
||||
return threw; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"(() => { \
|
||||
var rets = 0; var threw = false; \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next() { return {done: false, value: 1}; }, \
|
||||
return() { ++rets; throw new Error('swallowed'); } }; } }); \
|
||||
try { it.throw('v'); } catch (e) { threw = e === 'v'; } \
|
||||
return threw && rets === 0; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"(() => { \
|
||||
var rets = 0; var threw = false; \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next() { return {done: false, value: 1}; }, \
|
||||
return() { ++rets; return {}; } }; } }); \
|
||||
it.next(); \
|
||||
try { it.throw('v'); } catch (e) { threw = e === 'v'; } \
|
||||
return threw && rets === 1; \
|
||||
})()",
|
||||
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_concat_reentrancy_guard() {
|
||||
// 步进期间 state=1:内层 next 回调内再调 next/return/throw 抛 TypeError。
|
||||
let mut vm = Vm::new();
|
||||
let bool_cases = [
|
||||
(
|
||||
"(() => { \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next() { it.next(); return {done: false}; } }; } }); \
|
||||
var ok = false; \
|
||||
try { it.next(); } catch (e) { ok = e instanceof TypeError; } \
|
||||
return ok; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"(() => { \
|
||||
var it = Iterator.concat({ [Symbol.iterator]() { \
|
||||
return { next() { return {done: false, value: 1}; }, \
|
||||
return() { it.return(); it.throw('x'); return {}; } }; } }); \
|
||||
it.next(); \
|
||||
var ok = false; \
|
||||
try { it.return(); } catch (e) { ok = e instanceof TypeError; } \
|
||||
return ok; \
|
||||
})()",
|
||||
true,
|
||||
),
|
||||
];
|
||||
for (src, expected) in bool_cases {
|
||||
let result = eval(&mut vm, src).unwrap();
|
||||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user