fix: 迭代器原型形状补全(Iterator.prototype/string 中继层/next 原型化)

This commit is contained in:
2026-08-15 23:36:01 +08:00
parent 3358cf712e
commit e448c642a5
10 changed files with 350 additions and 167 deletions
+40 -9
View File
@@ -1734,17 +1734,19 @@ pub fn array_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
} }
/// Array Iterator 的内部 kind 编码:0=values,1=keys,2=entries。 /// Array Iterator 的内部 kind 编码:0=values,1=keys,2=entries。
const ARRAY_ITER_KIND_VALUES: i32 = 0; /// TypedArray 迭代器复用同一编码与 %ArrayIteratorPrototype%(规范同族)。
const ARRAY_ITER_KIND_KEYS: i32 = 1; pub(crate) const ARRAY_ITER_KIND_VALUES: i32 = 0;
const ARRAY_ITER_KIND_ENTRIES: i32 = 2; pub(crate) const ARRAY_ITER_KIND_KEYS: i32 = 1;
pub(crate) const ARRAY_ITER_KIND_ENTRIES: i32 = 2;
const ARRAY_ITER_TARGET_PROP: &str = "__target__"; const ARRAY_ITER_TARGET_PROP: &str = "__target__";
const ARRAY_ITER_INDEX_PROP: &str = "__index__"; const ARRAY_ITER_INDEX_PROP: &str = "__index__";
const ARRAY_ITER_KIND_PROP: &str = "__kind__"; const ARRAY_ITER_KIND_PROP: &str = "__kind__";
/// 创建 Array Iterator 对象:记录目标、当前下标与迭代种类,原型挂 /// 创建 Array/TA Iterator 对象:记录目标、当前下标与迭代种类,原型挂
/// `%ArrayIteratorPrototype%`(链到 `%IteratorPrototype%`,与 TA 迭代器共享)。 /// `%ArrayIteratorPrototype%`(链到 `%IteratorPrototype%`,Array 与 TA 共享)。
fn make_array_iterator<H: VmHost>(vm: &mut H, this_val: JsValue, kind: i32) -> Result<JsValue, JsValue> { /// `next` 不挂实例 own——由原型提供。
pub(crate) fn make_array_iterator<H: VmHost>(vm: &mut H, this_val: JsValue, kind: i32) -> Result<JsValue, JsValue> {
let target = match oxide_runtime_api::to_object(this_val, vm) { let target = match oxide_runtime_api::to_object(this_val, vm) {
Ok(v) => v, Ok(v) => v,
Err(msg) => return Err(array_type_error(vm, &msg)), Err(msg) => return Err(array_type_error(vm, &msg)),
@@ -1756,13 +1758,10 @@ fn make_array_iterator<H: VmHost>(vm: &mut H, this_val: JsValue, kind: i32) -> R
let target_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_TARGET_PROP).0; let target_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_TARGET_PROP).0;
let index_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_INDEX_PROP).0; let index_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_INDEX_PROP).0;
let kind_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_KIND_PROP).0; let kind_si = vm.kernel_core().perm_interner().intern(ARRAY_ITER_KIND_PROP).0;
let next_si = vm.kernel_core().perm_interner().intern("next").0;
let iter_ref = unsafe { &mut *iter }; let iter_ref = unsafe { &mut *iter };
vm.set_or_create_prop_value(iter_ref, target_si, target); vm.set_or_create_prop_value(iter_ref, target_si, target);
vm.set_or_create_prop_value(iter_ref, index_si, JsValue::int(0)); vm.set_or_create_prop_value(iter_ref, index_si, JsValue::int(0));
vm.set_or_create_prop_value(iter_ref, kind_si, JsValue::int(kind)); vm.set_or_create_prop_value(iter_ref, kind_si, JsValue::int(kind));
let next_fn = crate::iterator::make_native_function(vm, "next", array_iterator_next::<H> as *const (), 0);
vm.set_or_create_prop_value(iter_ref, next_si, next_fn);
Ok(JsValue::from_js_object(iter)) Ok(JsValue::from_js_object(iter))
} }
@@ -1815,6 +1814,38 @@ pub fn array_iterator_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
.unwrap_or(ARRAY_ITER_KIND_VALUES); .unwrap_or(ARRAY_ITER_KIND_VALUES);
let target_obj = unsafe { &*target.as_js_object_ptr() }; let target_obj = unsafe { &*target.as_js_object_ptr() };
// TypedArray 目标走底层 buffer 读取(元素非普通属性,length 语义不同):
// 与 Array 共享 %ArrayIteratorPrototype%,须在此分支区分两种取数路径。
if target_obj.is_typed_array_obj() {
let view = match crate::typed_array::get_typed_array_data(vm, target) {
Ok(view) => view,
Err(err) => return NativeResult::Err(err),
};
if (index as usize) >= view.length {
vm.set_or_create_prop_value(iter, target_si, JsValue::undefined());
return NativeResult::Ok(crate::iterator::make_iter_result(vm, JsValue::undefined(), true));
}
let element = match crate::typed_array::typed_array_element_get(vm, target_obj, index as u32) {
Ok(v) => v,
Err(e) => return NativeResult::Err(crate::error::create_type_error(vm, &e)),
};
let value = match kind {
ARRAY_ITER_KIND_KEYS => JsValue::int(index),
ARRAY_ITER_KIND_ENTRIES => {
let pair = create_new_array(vm, 2);
let pair_ref = unsafe { &mut *pair };
pair_ref.set_prop_at(0, JsValue::int(index));
pair_ref.set_prop_at(1, element);
JsValue::from_js_object(pair)
}
_ => element,
};
if let Err(msg) = vm.ordinary_set(iter, index_si, JsValue::int(index + 1), this_val) {
return NativeResult::Err(crate::error::create_error(vm, &msg));
}
return NativeResult::Ok(crate::iterator::make_iter_result(vm, value, false));
}
let length_key = vm.new_string("length"); let length_key = vm.new_string("length");
let length_si = vm.property_key_si(length_key); let length_si = vm.property_key_si(length_key);
let len_val = match vm.ordinary_get(target_obj, length_si, target) { let len_val = match vm.ordinary_get(target_obj, length_si, target) {
+74 -98
View File
@@ -7,6 +7,7 @@ use oxide_runtime_api::{to_object, NativeResult, VmHost};
const INNER_PROP: &str = "__inner__"; const INNER_PROP: &str = "__inner__";
const INDEX_PROP: &str = "__index__"; const INDEX_PROP: &str = "__index__";
const MODE_PROP: &str = "__mode__";
/// 占位构造函数:`Iterator` 不是构造函数,任何调用都抛 TypeError。 /// 占位构造函数:`Iterator` 不是构造函数,任何调用都抛 TypeError。
pub fn iterator_constructor<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult { pub fn iterator_constructor<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
@@ -52,6 +53,18 @@ pub fn make_iterator_for_value_without_return<H: VmHost>(vm: &mut H, value: JsVa
} }
} }
/// 同 [`make_iterator_for_value`],但包装器原型指向调用方指定的原型
/// (`String.prototype[@@iterator]` 用 %StringIteratorPrototype%)。
pub(crate) fn make_iterator_for_value_with_proto<H: VmHost>(
vm: &mut H, value: JsValue, wrapper_proto: *mut JsObject,
) -> Result<JsValue, JsValue> {
match try_make_iterator_inner_proto(vm, value, true, Some(wrapper_proto)) {
Ok(Some(iterator)) => Ok(iterator),
Ok(None) => Err(crate::error::create_type_error(vm, "value is not iterable")),
Err(err) => Err(err),
}
}
/// 尝试创建迭代器包装对象,把"不可迭代"与"真异常"区分返回。 /// 尝试创建迭代器包装对象,把"不可迭代"与"真异常"区分返回。
/// ///
/// # 步骤 /// # 步骤
@@ -67,14 +80,26 @@ pub fn make_iterator_for_value_without_return<H: VmHost>(vm: &mut H, value: JsVa
/// `yield*` 委托不需要且须避免创建时访问内层 return getter)。 /// `yield*` 委托不需要且须避免创建时访问内层 return getter)。
pub(crate) fn try_make_iterator_inner<H: VmHost>( pub(crate) fn try_make_iterator_inner<H: VmHost>(
vm: &mut H, value: JsValue, bind_return: bool, vm: &mut H, value: JsValue, bind_return: bool,
) -> Result<Option<JsValue>, JsValue> {
try_make_iterator_inner_proto(vm, value, bind_return, None)
}
/// 同 [`try_make_iterator_inner`],但允许调用方指定包装器原型
/// (String 迭代器挂 %StringIteratorPrototype%,其余默认 %IteratorPrototype%)。
pub(crate) fn try_make_iterator_inner_proto<H: VmHost>(
vm: &mut H, value: JsValue, bind_return: bool, wrapper_proto: Option<*mut JsObject>,
) -> Result<Option<JsValue>, JsValue> { ) -> Result<Option<JsValue>, JsValue> {
let inner = match get_iterator(vm, value) { let inner = match get_iterator(vm, value) {
Ok(Some(inner)) => inner, Ok(Some(inner)) => inner,
Ok(None) => return Ok(None), Ok(None) => return Ok(None),
Err(err) => return Err(err), Err(err) => return Err(err),
}; };
// 通用包装器挂 %IteratorPrototype%:经原型链获得 @@iterator(返回自身)。 // 通用包装器默认挂 %IteratorPrototype%(经原型链获得 @@iterator 返回自身);
let iterator_proto = vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject; // 调用方指定原型时优先(如 String 迭代器的 %StringIteratorPrototype%)。
let iterator_proto = match wrapper_proto {
Some(proto) => proto,
None => vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject,
};
let wrapper = vm let wrapper = vm
.epoch() .epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(iterator_proto))); .alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(iterator_proto)));
@@ -536,13 +561,30 @@ fn make_map_set_pair<H: VmHost>(vm: &mut H, a: JsValue, b: JsValue) -> JsValue {
JsValue::from_js_object(pair) JsValue::from_js_object(pair)
} }
/// Map/Set 迭代器模式编码:创建时存 wrapper `__mode__` 槽,%MapIteratorPrototype%/
/// %SetIteratorPrototype% 的单一 next 读槽后按模式推进(值含家族区分,
/// 分发不依赖具体原型)。判别值稳定,改动须同步 `from_i32`。
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
enum MapSetMode { #[repr(i32)]
MapEntries, pub(crate) enum MapSetMode {
MapValues, MapEntries = 0,
MapKeys, MapValues = 1,
SetEntries, MapKeys = 2,
SetValues, SetValues = 3,
SetEntries = 4,
}
impl MapSetMode {
fn from_i32(value: i32) -> Self {
match value {
0 => Self::MapEntries,
1 => Self::MapValues,
2 => Self::MapKeys,
3 => Self::SetValues,
4 => Self::SetEntries,
_ => Self::MapEntries,
}
}
} }
/// 把 Map/Set 迭代器包装器推进一步,按指定模式产出 `{value, done}` 结果。 /// 把 Map/Set 迭代器包装器推进一步,按指定模式产出 `{value, done}` 结果。
@@ -593,7 +635,11 @@ fn map_set_step<H: VmHost>(
} }
} }
fn map_set_next_dispatch<H: VmHost>(vm: &mut H, args: &[u8], mode: MapSetMode) -> NativeResult { /// Map/Set 迭代器 `next`:模式从 wrapper `__mode__` 槽读取后按模式推进。
///
/// %MapIteratorPrototype%/%SetIteratorPrototype% 绑定同一实现——家族与模式
/// 在创建时编码进 `__mode__` 槽,`next` 挂在原型上(wrapper 不设 own next)。
pub fn map_set_iterator_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !this_val.is_object() { if !this_val.is_object() {
return NativeResult::Err(crate::error::create_type_error(vm, "iterator next called on non-object")); return NativeResult::Err(crate::error::create_type_error(vm, "iterator next called on non-object"));
@@ -601,110 +647,40 @@ fn map_set_next_dispatch<H: VmHost>(vm: &mut H, args: &[u8], mode: MapSetMode) -
let wrapper = unsafe { &mut *this_val.as_js_object_ptr() }; let wrapper = unsafe { &mut *this_val.as_js_object_ptr() };
let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0; let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0;
let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0; let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0;
let mode_si = vm.kernel_core().perm_interner().intern(MODE_PROP).0;
let inner = match vm.ordinary_get(wrapper, inner_si, this_val) { let inner = match vm.ordinary_get(wrapper, inner_si, this_val) {
Ok(inner) if !inner.is_undefined() => inner, Ok(inner) if !inner.is_undefined() => inner,
_ => return NativeResult::Err(crate::error::create_type_error(vm, "iterator has no inner collection")), _ => return NativeResult::Err(crate::error::create_type_error(vm, "iterator has no inner collection")),
}; };
let mode = vm
.ordinary_get(wrapper, mode_si, this_val)
.ok()
.and_then(|v| if v.is_int() { Some(MapSetMode::from_i32(v.as_int())) } else { None })
.unwrap_or(MapSetMode::MapEntries);
NativeResult::Ok(map_set_step(vm, wrapper, inner, index_si, mode)) NativeResult::Ok(map_set_step(vm, wrapper, inner, index_si, mode))
} }
pub(crate) fn map_entries_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { /// 构造 Map/Set 迭代器包装器:`__inner__`/`__index__`/`__mode__` 三槽记录状态。
map_set_next_dispatch::<H>(vm, args, MapSetMode::MapEntries) ///
} /// `next` 不挂实例 own——由 %MapIteratorPrototype%/%SetIteratorPrototype% 上的
/// 单一 next(读 `__mode__` 分发)经原型链提供,符合规范原型形状。
pub(crate) fn map_values_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub(crate) fn make_collection_iterator<H: VmHost>(
map_set_next_dispatch::<H>(vm, args, MapSetMode::MapValues) vm: &mut H, inner: JsValue, proto_val: JsValue, mode: MapSetMode,
}
pub(crate) fn map_keys_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
map_set_next_dispatch::<H>(vm, args, MapSetMode::MapKeys)
}
pub(crate) fn set_values_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
map_set_next_dispatch::<H>(vm, args, MapSetMode::SetValues)
}
pub(crate) fn set_entries_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
map_set_next_dispatch::<H>(vm, args, MapSetMode::SetEntries)
}
#[derive(Clone, Copy)]
enum TypedArrayMode {
Values,
Keys,
Entries,
}
/// 按模式让 TypedArray 迭代器包装器推进一步:values 产出元素,keys 产出索引,
/// entries 产出 `[index, element]` 对;耗尽后把下标推进到哨兵值防止"复活"。
fn typed_array_step<H: VmHost>(
vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32, mode: TypedArrayMode,
) -> Result<JsValue, JsValue> {
let index = current_index(vm, wrapper, index_si);
let view = crate::typed_array::get_typed_array_data(vm, inner)?;
if index >= view.length {
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int(i32::MAX));
return Ok(make_iter_result(vm, JsValue::undefined(), true));
}
let elem = crate::typed_array::typed_array_element_get(vm, unsafe { &*inner.as_js_object_ptr() }, index as u32)
.map_err(|e| crate::error::create_type_error(vm, &e))?;
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32));
let value = match mode {
TypedArrayMode::Values => elem,
TypedArrayMode::Keys => JsValue::int(index as i32),
TypedArrayMode::Entries => make_map_set_pair(vm, JsValue::int(index as i32), elem),
};
Ok(make_iter_result(vm, value, false))
}
/// TypedArray 模式迭代器 `next` 的分发:校验包装器后按模式推进。
fn typed_array_next_dispatch<H: VmHost>(vm: &mut H, args: &[u8], mode: TypedArrayMode) -> 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 next called on non-object"));
}
let wrapper = unsafe { &mut *this_val.as_js_object_ptr() };
let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0;
let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0;
let inner = match vm.ordinary_get(wrapper, inner_si, this_val) {
Ok(inner) if !inner.is_undefined() => inner,
_ => return NativeResult::Err(crate::error::create_type_error(vm, "iterator has no inner typed array")),
};
match typed_array_step(vm, wrapper, inner, index_si, mode) {
Ok(result) => NativeResult::Ok(result),
Err(err) => NativeResult::Err(err),
}
}
pub(crate) fn typed_array_values_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
typed_array_next_dispatch::<H>(vm, args, TypedArrayMode::Values)
}
pub(crate) fn typed_array_keys_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
typed_array_next_dispatch::<H>(vm, args, TypedArrayMode::Keys)
}
pub(crate) fn typed_array_entries_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
typed_array_next_dispatch::<H>(vm, args, TypedArrayMode::Entries)
}
/// 构造迭代器包装器,其 `next` 委托给调用方指定的按模式分发的 native 函数。
/// 与 `make_iterator_for_value` 一致,但允许 Map/Set/TA 原型方法选择
/// values/keys/entries 变体,并挂到各自的集合迭代器原型上。
pub(crate) fn make_mode_iterator<H: VmHost>(
vm: &mut H, inner: JsValue, proto_val: JsValue, next_fn: *const (),
) -> JsValue { ) -> JsValue {
let proto_ptr = if proto_val.is_object() { proto_val.as_js_object_ptr() } else { std::ptr::null_mut() }; let proto_ptr = if proto_val.is_object() {
proto_val.as_js_object_ptr()
} else {
std::ptr::null_mut()
};
let wrapper = vm let wrapper = vm
.epoch() .epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr))); .alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr)));
let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0; let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0;
let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0; let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0;
let next_si = vm.kernel_core().perm_interner().intern("next").0; let mode_si = vm.kernel_core().perm_interner().intern(MODE_PROP).0;
let wrapper_obj = unsafe { &mut *wrapper }; let wrapper_obj = unsafe { &mut *wrapper };
vm.set_or_create_prop_value(wrapper_obj, inner_si, inner); vm.set_or_create_prop_value(wrapper_obj, inner_si, inner);
vm.set_or_create_prop_value(wrapper_obj, index_si, JsValue::int(0)); vm.set_or_create_prop_value(wrapper_obj, index_si, JsValue::int(0));
let next = make_native_function(vm, "next", next_fn, 0); vm.set_or_create_prop_value(wrapper_obj, mode_si, JsValue::int(mode as i32));
vm.set_or_create_prop_value(wrapper_obj, next_si, next);
JsValue::from_js_object(wrapper) JsValue::from_js_object(wrapper)
} }
+6 -6
View File
@@ -323,11 +323,11 @@ pub fn map_size<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
pub fn map_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn map_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let _inner = native_try!(get_map_inner(vm, this_val)); let _inner = native_try!(get_map_inner(vm, this_val));
NativeResult::Ok(crate::iterator::make_mode_iterator( NativeResult::Ok(crate::iterator::make_collection_iterator(
vm, vm,
this_val, this_val,
JsValue::from_js_object(vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject), JsValue::from_js_object(vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject),
crate::iterator::map_entries_iter_next::<H> as *const (), crate::iterator::MapSetMode::MapEntries,
)) ))
} }
@@ -335,11 +335,11 @@ pub fn map_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
pub fn map_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn map_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let _inner = native_try!(get_map_inner(vm, this_val)); let _inner = native_try!(get_map_inner(vm, this_val));
NativeResult::Ok(crate::iterator::make_mode_iterator( NativeResult::Ok(crate::iterator::make_collection_iterator(
vm, vm,
this_val, this_val,
JsValue::from_js_object(vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject), JsValue::from_js_object(vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject),
crate::iterator::map_values_iter_next::<H> as *const (), crate::iterator::MapSetMode::MapValues,
)) ))
} }
@@ -347,10 +347,10 @@ pub fn map_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
pub fn map_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn map_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let _inner = native_try!(get_map_inner(vm, this_val)); let _inner = native_try!(get_map_inner(vm, this_val));
NativeResult::Ok(crate::iterator::make_mode_iterator( NativeResult::Ok(crate::iterator::make_collection_iterator(
vm, vm,
this_val, this_val,
JsValue::from_js_object(vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject), JsValue::from_js_object(vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject),
crate::iterator::map_keys_iter_next::<H> as *const (), crate::iterator::MapSetMode::MapKeys,
)) ))
} }
+2 -6
View File
@@ -547,8 +547,8 @@ pub fn regexp_symbol_match_all<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
JsValue::from_js_object(vm.alloc_object(stub)) JsValue::from_js_object(vm.alloc_object(stub))
}; };
// 复用 String.prototype.matchAll 的迭代器包装(next 经 string_match_all_next 推进), // 复用 String.prototype.matchAll 的迭代器包装(next 经 string_match_all_next 推进,
// 原型挂 %RegExpStringIteratorPrototype%(链到 %IteratorPrototype%)。 // 挂 %RegExpStringIteratorPrototype% 原型,不设实例 own next)。
let regexp_iter_proto = vm.session().builtin_world().regexp_string_iterator_proto.as_ptr() as *mut JsObject; let regexp_iter_proto = vm.session().builtin_world().regexp_string_iterator_proto.as_ptr() as *mut JsObject;
let wrapper = vm let wrapper = vm
.epoch() .epoch()
@@ -557,13 +557,9 @@ pub fn regexp_symbol_match_all<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
let input_si = vm.kernel_core().perm_interner().intern(crate::string::MALL_INPUT).0; let input_si = vm.kernel_core().perm_interner().intern(crate::string::MALL_INPUT).0;
let index_si = vm.kernel_core().perm_interner().intern(crate::string::MALL_INDEX).0; let index_si = vm.kernel_core().perm_interner().intern(crate::string::MALL_INDEX).0;
let re_si = vm.kernel_core().perm_interner().intern(crate::string::MALL_RE).0; let re_si = vm.kernel_core().perm_interner().intern(crate::string::MALL_RE).0;
let next_si = vm.kernel_core().perm_interner().intern("next").0;
let input_val = vm.new_string(&haystack); let input_val = vm.new_string(&haystack);
vm.set_or_create_prop_value(wrapper_obj, input_si, input_val); vm.set_or_create_prop_value(wrapper_obj, input_si, input_val);
vm.set_or_create_prop_value(wrapper_obj, index_si, JsValue::int(last_index as i32)); vm.set_or_create_prop_value(wrapper_obj, index_si, JsValue::int(last_index as i32));
vm.set_or_create_prop_value(wrapper_obj, re_si, iter_re); vm.set_or_create_prop_value(wrapper_obj, re_si, iter_re);
let next_fn =
crate::string::make_public_native_fn(vm, "next", crate::string::string_match_all_next::<H> as *const (), 0);
vm.set_or_create_prop_value(wrapper_obj, next_si, next_fn);
NativeResult::Ok(JsValue::from_js_object(wrapper)) NativeResult::Ok(JsValue::from_js_object(wrapper))
} }
+6 -6
View File
@@ -337,11 +337,11 @@ pub fn set_size<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
pub fn set_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn set_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let _inner = native_try!(get_set_inner(vm, this_val)); let _inner = native_try!(get_set_inner(vm, this_val));
NativeResult::Ok(crate::iterator::make_mode_iterator( NativeResult::Ok(crate::iterator::make_collection_iterator(
vm, vm,
this_val, this_val,
JsValue::from_js_object(vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject), JsValue::from_js_object(vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject),
crate::iterator::set_entries_iter_next::<H> as *const (), crate::iterator::MapSetMode::SetEntries,
)) ))
} }
@@ -349,11 +349,11 @@ pub fn set_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
pub fn set_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn set_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let _inner = native_try!(get_set_inner(vm, this_val)); let _inner = native_try!(get_set_inner(vm, this_val));
NativeResult::Ok(crate::iterator::make_mode_iterator( NativeResult::Ok(crate::iterator::make_collection_iterator(
vm, vm,
this_val, this_val,
JsValue::from_js_object(vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject), JsValue::from_js_object(vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject),
crate::iterator::set_values_iter_next::<H> as *const (), crate::iterator::MapSetMode::SetValues,
)) ))
} }
@@ -362,11 +362,11 @@ pub fn set_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let _inner = native_try!(get_set_inner(vm, this_val)); let _inner = native_try!(get_set_inner(vm, this_val));
// Set 的 keys() 是 values() 的别名——返回同样的逐元素迭代器。 // Set 的 keys() 是 values() 的别名——返回同样的逐元素迭代器。
NativeResult::Ok(crate::iterator::make_mode_iterator( NativeResult::Ok(crate::iterator::make_collection_iterator(
vm, vm,
this_val, this_val,
JsValue::from_js_object(vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject), JsValue::from_js_object(vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject),
crate::iterator::set_values_iter_next::<H> as *const (), crate::iterator::MapSetMode::SetValues,
)) ))
} }
+6 -20
View File
@@ -1535,7 +1535,8 @@ pub fn string_match_all<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
} }
fn builder_wrapper<H: VmHost>(vm: &mut H, input: &str, re_obj: JsValue) -> NativeResult { fn builder_wrapper<H: VmHost>(vm: &mut H, input: &str, re_obj: JsValue) -> NativeResult {
// matchAll 迭代器挂 %RegExpStringIteratorPrototype%(链到 %IteratorPrototype%)。 // matchAll 迭代器挂 %RegExpStringIteratorPrototype%(链到 %IteratorPrototype%),
// next 由原型提供(不挂实例 own)。
let regexp_iter_proto = vm.session().builtin_world().regexp_string_iterator_proto.as_ptr() as *mut JsObject; let regexp_iter_proto = vm.session().builtin_world().regexp_string_iterator_proto.as_ptr() as *mut JsObject;
let wrapper = vm let wrapper = vm
.epoch() .epoch()
@@ -1545,16 +1546,12 @@ fn builder_wrapper<H: VmHost>(vm: &mut H, input: &str, re_obj: JsValue) -> Nativ
let input_si = vm.kernel_core().perm_interner().intern(MALL_INPUT).0; let input_si = vm.kernel_core().perm_interner().intern(MALL_INPUT).0;
let index_si = vm.kernel_core().perm_interner().intern(MALL_INDEX).0; let index_si = vm.kernel_core().perm_interner().intern(MALL_INDEX).0;
let re_si = vm.kernel_core().perm_interner().intern(MALL_RE).0; let re_si = vm.kernel_core().perm_interner().intern(MALL_RE).0;
let next_si = vm.kernel_core().perm_interner().intern("next").0;
let input_val = vm.new_string(input); let input_val = vm.new_string(input);
vm.set_or_create_prop_value(wrapper_obj, input_si, input_val); vm.set_or_create_prop_value(wrapper_obj, input_si, input_val);
vm.set_or_create_prop_value(wrapper_obj, index_si, JsValue::int(0)); vm.set_or_create_prop_value(wrapper_obj, index_si, JsValue::int(0));
vm.set_or_create_prop_value(wrapper_obj, re_si, re_obj); vm.set_or_create_prop_value(wrapper_obj, re_si, re_obj);
let next_fn = make_public_native_fn(vm, "next", string_match_all_next::<H> as *const (), 0);
vm.set_or_create_prop_value(wrapper_obj, next_si, next_fn);
NativeResult::Ok(JsValue::from_js_object(wrapper)) NativeResult::Ok(JsValue::from_js_object(wrapper))
} }
@@ -1587,26 +1584,15 @@ pub fn string_symbol_iterator<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
} }
}; };
let s_val = vm.new_string_owned(s); let s_val = vm.new_string_owned(s);
match crate::iterator::make_iterator_for_value(vm, s_val) { // String 迭代器挂 %StringIteratorPrototype%(链到 %IteratorPrototype%):
// 包装机制复用通用路径,仅替换原型为中继层。
let string_iter_proto = vm.session().builtin_world().string_iterator_proto.as_ptr() as *mut JsObject;
match crate::iterator::make_iterator_for_value_with_proto(vm, s_val, string_iter_proto) {
Ok(iterator) => NativeResult::Ok(iterator), Ok(iterator) => NativeResult::Ok(iterator),
Err(err) => NativeResult::Err(err), Err(err) => NativeResult::Err(err),
} }
} }
pub(crate) fn make_public_native_fn<H: VmHost>(vm: &mut H, name: &str, native_fn: *const (), arg_count: u8) -> JsValue {
let function_proto = vm.session().builtin_world().function_proto.as_ptr() as *mut JsObject;
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto));
func.set_function(true);
func.set_native_fn(Some(unsafe { oxide_types::object::NativeFnPtr::from_raw(native_fn) }));
func.set_native_arg_count(arg_count);
let func = vm.alloc_object(func);
let name_si = vm.kernel_core().perm_interner().intern("name").0;
let value = vm.new_string(name);
let func_ref = unsafe { &mut *func };
vm.set_or_create_prop_value(func_ref, name_si, value);
JsValue::from_js_object(func)
}
/// `matchAll` 迭代器的 `next`:返回 `{value: 匹配数组, done}`,耗尽后 done 为 true。 /// `matchAll` 迭代器的 `next`:返回 `{value: 匹配数组, done}`,耗尽后 done 为 true。
pub fn string_match_all_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn string_match_all_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
+14 -18
View File
@@ -1171,36 +1171,32 @@ pub fn typed_array_join<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
pub fn typed_array_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn typed_array_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
native_try!(get_typed_array_data(vm, this_val)); native_try!(get_typed_array_data(vm, this_val));
NativeResult::Ok(crate::iterator::make_mode_iterator( // TA 迭代器与 Array 共享 %ArrayIteratorPrototype%(规范 CreateArrayIterator
vm, // 同族),next 由原型提供;接收者合法性已在上方校验。
this_val, match crate::array::make_array_iterator(vm, this_val, crate::array::ARRAY_ITER_KIND_VALUES) {
JsValue::from_js_object(vm.session().builtin_world().array_iterator_proto.as_ptr() as *mut JsObject), Ok(iter) => NativeResult::Ok(iter),
crate::iterator::typed_array_values_iter_next::<H> as *const (), Err(err) => NativeResult::Err(err),
)) }
} }
/// `%TypedArray%.prototype.keys()`:返回迭代元素索引的迭代器。 /// `%TypedArray%.prototype.keys()`:返回迭代元素索引的迭代器。
pub fn typed_array_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn typed_array_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
native_try!(get_typed_array_data(vm, this_val)); native_try!(get_typed_array_data(vm, this_val));
NativeResult::Ok(crate::iterator::make_mode_iterator( match crate::array::make_array_iterator(vm, this_val, crate::array::ARRAY_ITER_KIND_KEYS) {
vm, Ok(iter) => NativeResult::Ok(iter),
this_val, Err(err) => NativeResult::Err(err),
JsValue::from_js_object(vm.session().builtin_world().array_iterator_proto.as_ptr() as *mut JsObject), }
crate::iterator::typed_array_keys_iter_next::<H> as *const (),
))
} }
/// `%TypedArray%.prototype.entries()`:返回迭代 `[index, element]` 对的迭代器。 /// `%TypedArray%.prototype.entries()`:返回迭代 `[index, element]` 对的迭代器。
pub fn typed_array_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn typed_array_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
native_try!(get_typed_array_data(vm, this_val)); native_try!(get_typed_array_data(vm, this_val));
NativeResult::Ok(crate::iterator::make_mode_iterator( match crate::array::make_array_iterator(vm, this_val, crate::array::ARRAY_ITER_KIND_ENTRIES) {
vm, Ok(iter) => NativeResult::Ok(iter),
this_val, Err(err) => NativeResult::Err(err),
JsValue::from_js_object(vm.session().builtin_world().array_iterator_proto.as_ptr() as *mut JsObject), }
crate::iterator::typed_array_entries_iter_next::<H> as *const (),
))
} }
/// 默认数值排序比较:NaN 视为最大排到末尾,其余按数值升序。 /// 默认数值排序比较:NaN 视为最大排到末尾,其余按数值升序。
+33 -1
View File
@@ -5,7 +5,10 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject; use oxide_types::object::JsObject;
use oxide_types::value::JsValue; use oxide_types::value::JsValue;
use crate::bindings::{apply_binding_table, bind_global_value, configure_native_constructor}; use crate::bindings::{
apply_binding_table, bind_global_value, bind_iterator_function_prototype, bind_iterator_proto_next,
configure_native_constructor,
};
/// 绑定迭代器基础设施:`%IteratorPrototype%` 与 `%ArrayIteratorPrototype%` 等。 /// 绑定迭代器基础设施:`%IteratorPrototype%` 与 `%ArrayIteratorPrototype%` 等。
pub fn bind_iterator(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) { pub fn bind_iterator(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
@@ -17,6 +20,7 @@ pub fn bind_iterator(core: &Arc<KernelCore>, session: &KernelSession, global: &m
oxide_builtins::iterator::iterator_constructor::<crate::vm::Vm> as *const (), oxide_builtins::iterator::iterator_constructor::<crate::vm::Vm> as *const (),
0, 0,
); );
bind_iterator_function_prototype(core, session, &mut iterator);
apply_binding_table( apply_binding_table(
session.builtin_world(), session.builtin_world(),
@@ -40,4 +44,32 @@ pub fn bind_iterator(core: &Arc<KernelCore>, session: &KernelSession, global: &m
oxide_builtins::iterator::iterator_symbol_iterator::<crate::vm::Vm> as *const (), oxide_builtins::iterator::iterator_symbol_iterator::<crate::vm::Vm> as *const (),
0, 0,
); );
// 集合迭代器原型各自安装 next(%ArrayIteratorPrototype% 服务 Array/TA 两族;
// Map/Set 共用按 `__mode__` 分发的实现;%RegExpStringIteratorPrototype% 供 matchAll)。
let world = session.builtin_world();
bind_iterator_proto_next(
core,
session,
world.array_iterator_proto.as_ptr() as *mut JsObject,
oxide_builtins::array::array_iterator_next::<crate::vm::Vm> as *const (),
);
bind_iterator_proto_next(
core,
session,
world.map_iterator_proto.as_ptr() as *mut JsObject,
oxide_builtins::iterator::map_set_iterator_next::<crate::vm::Vm> as *const (),
);
bind_iterator_proto_next(
core,
session,
world.set_iterator_proto.as_ptr() as *mut JsObject,
oxide_builtins::iterator::map_set_iterator_next::<crate::vm::Vm> as *const (),
);
bind_iterator_proto_next(
core,
session,
world.regexp_string_iterator_proto.as_ptr() as *mut JsObject,
oxide_builtins::string::string_match_all_next::<crate::vm::Vm> as *const (),
);
} }
+53
View File
@@ -234,6 +234,30 @@ pub(crate) fn bind_well_known_data_property(
target.bump_generation(); target.bump_generation();
} }
/// 在 Iterator 函数对象上绑定 `prototype` 属性 = %IteratorPrototype%(规范形状:
/// 构造器函数带 prototype 属性;描述符不可写/不可枚举/不可配置)。
pub(crate) fn bind_iterator_function_prototype(
core: &Arc<KernelCore>, session: &KernelSession, iterator: &mut JsObject,
) {
let iter_proto_ptr = session.builtin_world().iterator_proto.as_ptr() as *mut JsObject;
let sf = core.perm_interner().as_ref();
let sh = core.shape_forge().as_ref();
let si_prototype = sf.intern("prototype").0;
let iterator_shape = sh.make_shape(iterator.shape_id(), si_prototype);
iterator.set_shape_id(iterator_shape);
let pos = iterator.ensure_hash_props().len() as u32;
iterator.ensure_hash_props().push(JsValue::from_js_object(iter_proto_ptr));
iterator.set_data_meta(pos, PropAttributes::new(false, false, false));
}
/// 在集合迭代器原型上绑定 `next` 方法(规范形状:next 挂原型,wrapper 不设 own)。
pub(crate) fn bind_iterator_proto_next(
core: &Arc<KernelCore>, session: &KernelSession, proto: *mut JsObject, func: *const (),
) {
let proto = unsafe { &mut *proto };
apply_binding_table(session.builtin_world(), proto, core, &[("next", func, 0)]);
}
pub(crate) fn bind_global_value(core: &Arc<KernelCore>, global: &mut JsObject, name: &str, value: JsValue) { pub(crate) fn bind_global_value(core: &Arc<KernelCore>, global: &mut JsObject, name: &str, value: JsValue) {
let si = core.perm_interner().intern(name).0; let si = core.perm_interner().intern(name).0;
let shape = core.shape_forge().make_shape(global.shape_id(), si); let shape = core.shape_forge().make_shape(global.shape_id(), si);
@@ -373,6 +397,7 @@ fn bind_iterator_global(core: &Arc<KernelCore>, session: &KernelSession, global:
oxide_builtins::iterator::iterator_constructor::<crate::vm::Vm> as *const (), oxide_builtins::iterator::iterator_constructor::<crate::vm::Vm> as *const (),
0, 0,
); );
bind_iterator_function_prototype(core, session, &mut iterator);
apply_binding_table( apply_binding_table(
session.builtin_world(), session.builtin_world(),
&mut iterator, &mut iterator,
@@ -393,6 +418,34 @@ fn bind_iterator_global(core: &Arc<KernelCore>, session: &KernelSession, global:
oxide_builtins::iterator::iterator_symbol_iterator::<crate::vm::Vm> as *const (), oxide_builtins::iterator::iterator_symbol_iterator::<crate::vm::Vm> as *const (),
0, 0,
); );
// 集合迭代器原型各自安装 next(%ArrayIteratorPrototype% 服务 Array/TA 两族;
// Map/Set 共用按 `__mode__` 分发的实现;%RegExpStringIteratorPrototype% 供 matchAll)。
let world = session.builtin_world();
bind_iterator_proto_next(
core,
session,
world.array_iterator_proto.as_ptr() as *mut JsObject,
oxide_builtins::array::array_iterator_next::<crate::vm::Vm> as *const (),
);
bind_iterator_proto_next(
core,
session,
world.map_iterator_proto.as_ptr() as *mut JsObject,
oxide_builtins::iterator::map_set_iterator_next::<crate::vm::Vm> as *const (),
);
bind_iterator_proto_next(
core,
session,
world.set_iterator_proto.as_ptr() as *mut JsObject,
oxide_builtins::iterator::map_set_iterator_next::<crate::vm::Vm> as *const (),
);
bind_iterator_proto_next(
core,
session,
world.regexp_string_iterator_proto.as_ptr() as *mut JsObject,
oxide_builtins::string::string_match_all_next::<crate::vm::Vm> as *const (),
);
} }
fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) { fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
@@ -109,9 +109,18 @@ fn iterator_proto_chain_and_self_iteration() {
("var it = 'ab'.matchAll(/a/g); it[Symbol.iterator]() === it", true), ("var it = 'ab'.matchAll(/a/g); it[Symbol.iterator]() === it", true),
// Array/TA 共享 %ArrayIteratorPrototype%;Map/Set 各自原型内部共享。 // Array/TA 共享 %ArrayIteratorPrototype%;Map/Set 各自原型内部共享。
("Object.getPrototypeOf([].values()) === Object.getPrototypeOf([].entries())", true), ("Object.getPrototypeOf([].values()) === Object.getPrototypeOf([].entries())", true),
("Object.getPrototypeOf(new Uint8Array(0).values()) === Object.getPrototypeOf([].values())", true), (
("Object.getPrototypeOf(new Map().values()) === Object.getPrototypeOf(new Map().keys())", true), "Object.getPrototypeOf(new Uint8Array(0).values()) === Object.getPrototypeOf([].values())",
("Object.getPrototypeOf(new Set().values()) === Object.getPrototypeOf(new Set().entries())", true), true,
),
(
"Object.getPrototypeOf(new Map().values()) === Object.getPrototypeOf(new Map().keys())",
true,
),
(
"Object.getPrototypeOf(new Set().values()) === Object.getPrototypeOf(new Set().entries())",
true,
),
// %IteratorPrototype% 自身可迭代:其 @@iterator 返回 this。 // %IteratorPrototype% 自身可迭代:其 @@iterator 返回 this。
( (
"var P = Object.getPrototypeOf(Object.getPrototypeOf([].values())); P[Symbol.iterator]() === P", "var P = Object.getPrototypeOf(Object.getPrototypeOf([].values())); P[Symbol.iterator]() === P",
@@ -176,3 +185,107 @@ fn iterator_proto_object_proto_parent() {
.unwrap(); .unwrap();
assert!(result.as_bool()); assert!(result.as_bool());
} }
#[test]
fn iterator_function_prototype_property() {
// Iterator.prototype 属性绑定:=== %IteratorPrototype%([].values() 原型链中继),
// 且 %IteratorPrototype% 自身可迭代。
let mut vm = Vm::new();
let cases = [
("Iterator.prototype !== undefined", true),
("Object.getPrototypeOf(Object.getPrototypeOf([].values())) === Iterator.prototype", true),
(
"Object.getPrototypeOf(Object.getPrototypeOf(new Map().values())) === Iterator.prototype",
true,
),
("Iterator.prototype[Symbol.iterator]() === Iterator.prototype", true),
];
for (src, expected) in cases {
let result = eval(&mut vm, src).unwrap();
assert_eq!(result.as_bool(), expected, "for {}", src);
}
}
#[test]
fn string_iterator_proto_layer() {
// String 迭代器挂 %StringIteratorPrototype% 中继层:与 Array 迭代器原型不同,
// 且经链到 %IteratorPrototype%(Iterator.prototype)。
let mut vm = Vm::new();
let cases = [
(
"Object.getPrototypeOf(Object.getPrototypeOf('a'[Symbol.iterator]())) === Iterator.prototype",
true,
),
(
"Object.getPrototypeOf('a'[Symbol.iterator]()) !== Object.getPrototypeOf([].values())",
true,
),
];
for (src, expected) in cases {
let result = eval(&mut vm, src).unwrap();
assert_eq!(result.as_bool(), expected, "for {}", src);
}
let out = eval(&mut vm, "[...'abc'].join('')").unwrap();
assert_eq!(to_str(&vm, out), "abc");
}
#[test]
fn iterator_next_on_prototypes() {
// next 挂集合迭代器原型(%ArrayIteratorPrototype%.next 等),wrapper 无 own next。
let mut vm = Vm::new();
let cases = [
("typeof Object.getPrototypeOf([].values()).next === 'function'", true),
("Object.getPrototypeOf([].values()).hasOwnProperty('next')", true),
("[].values().hasOwnProperty('next') === false", true),
("typeof Object.getPrototypeOf(new Map().values()).next === 'function'", true),
("new Map().values().hasOwnProperty('next') === false", true),
("typeof Object.getPrototypeOf(new Set().values()).next === 'function'", true),
("new Set().values().hasOwnProperty('next') === false", true),
("typeof Object.getPrototypeOf(new Uint8Array([1]).values()).next === 'function'", true),
("new Uint8Array([1]).values().hasOwnProperty('next') === false", true),
("typeof Object.getPrototypeOf('ab'.matchAll(/a/g)).next === 'function'", true),
("'ab'.matchAll(/a/g).hasOwnProperty('next') === false", true),
];
for (src, expected) in cases {
let result = eval(&mut vm, src).unwrap();
assert_eq!(result.as_bool(), expected, "for {}", src);
}
}
#[test]
fn iterator_proto_next_consumption_unchanged() {
// 行为回归:next 移上原型后各家族消费路径不变(迭代协议经原型链解析)。
let mut vm = Vm::new();
let str_cases = [
("[...[1,2,3]].join(',')", "1,2,3"),
("var it = [1,2].values(); it.next().value + ',' + it.next().value", "1,2"),
("var it = ['x','y'].entries(); it.next().value.join(':')", "0:x"),
("[...new Map([['a',1]])][0].join(':')", "a:1"),
("var mk = new Map([['k',9]]); mk.keys().next().value", "k"),
("var se = new Set([7]); se.entries().next().value.join(':')", "7:7"),
("new Uint8Array([9]).entries().next().value.join(',')", "0,9"),
("[...'ab'].join('')", "ab"),
("'a\\u{1F600}b'[Symbol.iterator]().next().value", "a"),
("var mm = 'ab'.matchAll(/b/g); mm.next().value[0]", "b"),
];
for (src, expected) in str_cases {
let result = eval(&mut vm, src).unwrap();
assert_eq!(to_str(&vm, result), expected, "for {}", src);
}
// 数值结果(含 int 0)用恒等比较断言,避免格式化路径差异。
let num_cases = [
("[10,20].keys().next().value === 0", true),
("[1,2].values().next().value === 1", true),
("new Map([['a',1]]).values().next().value === 1", true),
("new Set([5,6]).keys().next().value === 5", true),
("new Uint8Array([5]).values().next().value === 5", true),
("new Uint8Array([7]).entries().next().value[1] === 7", true),
];
for (src, expected) in num_cases {
let result = eval(&mut vm, src).unwrap();
assert_eq!(result.as_bool(), expected, "for {}", src);
}
// TA 迭代耗尽后恒 done(数组路径 target 置 undefined 防复活)。
let done = eval(&mut vm, "var it = new Uint8Array([1]).values(); it.next(); it.next().done").unwrap();
assert!(done.as_bool());
}