mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
fix: 迭代器原型形状补全(Iterator.prototype/string 中继层/next 原型化)
This commit is contained in:
@@ -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。
|
||||
const ARRAY_ITER_KIND_VALUES: i32 = 0;
|
||||
const ARRAY_ITER_KIND_KEYS: i32 = 1;
|
||||
const ARRAY_ITER_KIND_ENTRIES: i32 = 2;
|
||||
/// TypedArray 迭代器复用同一编码与 %ArrayIteratorPrototype%(规范同族)。
|
||||
pub(crate) const ARRAY_ITER_KIND_VALUES: i32 = 0;
|
||||
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_INDEX_PROP: &str = "__index__";
|
||||
const ARRAY_ITER_KIND_PROP: &str = "__kind__";
|
||||
|
||||
/// 创建 Array Iterator 对象:记录目标、当前下标与迭代种类,原型挂
|
||||
/// `%ArrayIteratorPrototype%`(链到 `%IteratorPrototype%`,与 TA 迭代器共享)。
|
||||
fn make_array_iterator<H: VmHost>(vm: &mut H, this_val: JsValue, kind: i32) -> Result<JsValue, JsValue> {
|
||||
/// 创建 Array/TA Iterator 对象:记录目标、当前下标与迭代种类,原型挂
|
||||
/// `%ArrayIteratorPrototype%`(链到 `%IteratorPrototype%`,Array 与 TA 共享)。
|
||||
/// `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) {
|
||||
Ok(v) => v,
|
||||
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 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 next_si = vm.kernel_core().perm_interner().intern("next").0;
|
||||
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, index_si, JsValue::int(0));
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -1815,6 +1814,38 @@ pub fn array_iterator_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
.unwrap_or(ARRAY_ITER_KIND_VALUES);
|
||||
|
||||
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_si = vm.property_key_si(length_key);
|
||||
let len_val = match vm.ordinary_get(target_obj, length_si, target) {
|
||||
|
||||
@@ -7,6 +7,7 @@ use oxide_runtime_api::{to_object, NativeResult, VmHost};
|
||||
|
||||
const INNER_PROP: &str = "__inner__";
|
||||
const INDEX_PROP: &str = "__index__";
|
||||
const MODE_PROP: &str = "__mode__";
|
||||
|
||||
/// 占位构造函数:`Iterator` 不是构造函数,任何调用都抛 TypeError。
|
||||
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)。
|
||||
pub(crate) fn try_make_iterator_inner<H: VmHost>(
|
||||
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> {
|
||||
let inner = match get_iterator(vm, value) {
|
||||
Ok(Some(inner)) => inner,
|
||||
Ok(None) => return Ok(None),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
// 通用包装器挂 %IteratorPrototype%:经原型链获得 @@iterator(返回自身)。
|
||||
let iterator_proto = vm.session().builtin_world().iterator_proto.as_ptr() as *mut JsObject;
|
||||
// 通用包装器默认挂 %IteratorPrototype%(经原型链获得 @@iterator 返回自身);
|
||||
// 调用方指定原型时优先(如 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
|
||||
.epoch()
|
||||
.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)
|
||||
}
|
||||
|
||||
/// Map/Set 迭代器模式编码:创建时存 wrapper `__mode__` 槽,%MapIteratorPrototype%/
|
||||
/// %SetIteratorPrototype% 的单一 next 读槽后按模式推进(值含家族区分,
|
||||
/// 分发不依赖具体原型)。判别值稳定,改动须同步 `from_i32`。
|
||||
#[derive(Clone, Copy)]
|
||||
enum MapSetMode {
|
||||
MapEntries,
|
||||
MapValues,
|
||||
MapKeys,
|
||||
SetEntries,
|
||||
SetValues,
|
||||
#[repr(i32)]
|
||||
pub(crate) enum MapSetMode {
|
||||
MapEntries = 0,
|
||||
MapValues = 1,
|
||||
MapKeys = 2,
|
||||
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}` 结果。
|
||||
@@ -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] });
|
||||
if !this_val.is_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 inner_si = vm.kernel_core().perm_interner().intern(INNER_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) {
|
||||
Ok(inner) if !inner.is_undefined() => inner,
|
||||
_ => 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))
|
||||
}
|
||||
|
||||
pub(crate) fn map_entries_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
map_set_next_dispatch::<H>(vm, args, MapSetMode::MapEntries)
|
||||
}
|
||||
|
||||
pub(crate) fn map_values_iter_next<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
map_set_next_dispatch::<H>(vm, args, MapSetMode::MapValues)
|
||||
}
|
||||
|
||||
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 (),
|
||||
/// 构造 Map/Set 迭代器包装器:`__inner__`/`__index__`/`__mode__` 三槽记录状态。
|
||||
///
|
||||
/// `next` 不挂实例 own——由 %MapIteratorPrototype%/%SetIteratorPrototype% 上的
|
||||
/// 单一 next(读 `__mode__` 分发)经原型链提供,符合规范原型形状。
|
||||
pub(crate) fn make_collection_iterator<H: VmHost>(
|
||||
vm: &mut H, inner: JsValue, proto_val: JsValue, mode: MapSetMode,
|
||||
) -> 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
|
||||
.epoch()
|
||||
.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 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 };
|
||||
vm.set_or_create_prop_value(wrapper_obj, inner_si, inner);
|
||||
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, next_si, next);
|
||||
vm.set_or_create_prop_value(wrapper_obj, mode_si, JsValue::int(mode as i32));
|
||||
JsValue::from_js_object(wrapper)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
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,
|
||||
this_val,
|
||||
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 {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
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,
|
||||
this_val,
|
||||
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 {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
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,
|
||||
this_val,
|
||||
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,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
};
|
||||
|
||||
// 复用 String.prototype.matchAll 的迭代器包装(next 经 string_match_all_next 推进),
|
||||
// 原型挂 %RegExpStringIteratorPrototype%(链到 %IteratorPrototype%)。
|
||||
// 复用 String.prototype.matchAll 的迭代器包装(next 经 string_match_all_next 推进,
|
||||
// 挂 %RegExpStringIteratorPrototype% 原型,不设实例 own next)。
|
||||
let regexp_iter_proto = vm.session().builtin_world().regexp_string_iterator_proto.as_ptr() as *mut JsObject;
|
||||
let wrapper = vm
|
||||
.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 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 next_si = vm.kernel_core().perm_interner().intern("next").0;
|
||||
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, index_si, JsValue::int(last_index as i32));
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
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,
|
||||
this_val,
|
||||
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 {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
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,
|
||||
this_val,
|
||||
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 _inner = native_try!(get_set_inner(vm, this_val));
|
||||
// Set 的 keys() 是 values() 的别名——返回同样的逐元素迭代器。
|
||||
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||
NativeResult::Ok(crate::iterator::make_collection_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
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,
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
// 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 wrapper = vm
|
||||
.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 index_si = vm.kernel_core().perm_interner().intern(MALL_INDEX).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);
|
||||
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, 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))
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
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),
|
||||
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。
|
||||
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] });
|
||||
|
||||
@@ -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 {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
native_try!(get_typed_array_data(vm, this_val));
|
||||
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
JsValue::from_js_object(vm.session().builtin_world().array_iterator_proto.as_ptr() as *mut JsObject),
|
||||
crate::iterator::typed_array_values_iter_next::<H> as *const (),
|
||||
))
|
||||
// TA 迭代器与 Array 共享 %ArrayIteratorPrototype%(规范 CreateArrayIterator
|
||||
// 同族),next 由原型提供;接收者合法性已在上方校验。
|
||||
match crate::array::make_array_iterator(vm, this_val, crate::array::ARRAY_ITER_KIND_VALUES) {
|
||||
Ok(iter) => NativeResult::Ok(iter),
|
||||
Err(err) => NativeResult::Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.keys()`:返回迭代元素索引的迭代器。
|
||||
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] });
|
||||
native_try!(get_typed_array_data(vm, this_val));
|
||||
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
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 (),
|
||||
))
|
||||
match crate::array::make_array_iterator(vm, this_val, crate::array::ARRAY_ITER_KIND_KEYS) {
|
||||
Ok(iter) => NativeResult::Ok(iter),
|
||||
Err(err) => NativeResult::Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// `%TypedArray%.prototype.entries()`:返回迭代 `[index, element]` 对的迭代器。
|
||||
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] });
|
||||
native_try!(get_typed_array_data(vm, this_val));
|
||||
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||
vm,
|
||||
this_val,
|
||||
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 (),
|
||||
))
|
||||
match crate::array::make_array_iterator(vm, this_val, crate::array::ARRAY_ITER_KIND_ENTRIES) {
|
||||
Ok(iter) => NativeResult::Ok(iter),
|
||||
Err(err) => NativeResult::Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// 默认数值排序比较:NaN 视为最大排到末尾,其余按数值升序。
|
||||
|
||||
@@ -5,7 +5,10 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_types::object::JsObject;
|
||||
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%` 等。
|
||||
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 (),
|
||||
0,
|
||||
);
|
||||
bind_iterator_function_prototype(core, session, &mut iterator);
|
||||
|
||||
apply_binding_table(
|
||||
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 (),
|
||||
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 (),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -234,6 +234,30 @@ pub(crate) fn bind_well_known_data_property(
|
||||
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) {
|
||||
let si = core.perm_interner().intern(name).0;
|
||||
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 (),
|
||||
0,
|
||||
);
|
||||
bind_iterator_function_prototype(core, session, &mut iterator);
|
||||
apply_binding_table(
|
||||
session.builtin_world(),
|
||||
&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 (),
|
||||
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) {
|
||||
|
||||
@@ -109,9 +109,18 @@ fn iterator_proto_chain_and_self_iteration() {
|
||||
("var it = 'ab'.matchAll(/a/g); it[Symbol.iterator]() === it", true),
|
||||
// Array/TA 共享 %ArrayIteratorPrototype%;Map/Set 各自原型内部共享。
|
||||
("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 Set().values()) === Object.getPrototypeOf(new Set().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 Set().values()) === Object.getPrototypeOf(new Set().entries())",
|
||||
true,
|
||||
),
|
||||
// %IteratorPrototype% 自身可迭代:其 @@iterator 返回 this。
|
||||
(
|
||||
"var P = Object.getPrototypeOf(Object.getPrototypeOf([].values())); P[Symbol.iterator]() === P",
|
||||
@@ -176,3 +185,107 @@ fn iterator_proto_object_proto_parent() {
|
||||
.unwrap();
|
||||
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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user