From e448c642a53f079df8309ce905b00721d75427f8 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sat, 15 Aug 2026 23:36:01 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=BF=AD=E4=BB=A3=E5=99=A8=E5=8E=9F?= =?UTF-8?q?=E5=9E=8B=E5=BD=A2=E7=8A=B6=E8=A1=A5=E5=85=A8=EF=BC=88Iterator.?= =?UTF-8?q?prototype/string=20=E4=B8=AD=E7=BB=A7=E5=B1=82/next=20=E5=8E=9F?= =?UTF-8?q?=E5=9E=8B=E5=8C=96=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/array.rs | 49 ++++- crates/oxide_builtins/src/iterator.rs | 172 ++++++++---------- crates/oxide_builtins/src/map.rs | 12 +- crates/oxide_builtins/src/regexp.rs | 8 +- crates/oxide_builtins/src/set.rs | 12 +- crates/oxide_builtins/src/string.rs | 26 +-- crates/oxide_builtins/src/typed_array.rs | 32 ++-- crates/oxide_vm/src/bindings/bind_iterator.rs | 34 +++- crates/oxide_vm/src/bindings/mod.rs | 53 ++++++ .../oxide_vm/tests/builtins_iterator_tests.rs | 119 +++++++++++- 10 files changed, 350 insertions(+), 167 deletions(-) diff --git a/crates/oxide_builtins/src/array.rs b/crates/oxide_builtins/src/array.rs index d49bf44..944db7a 100644 --- a/crates/oxide_builtins/src/array.rs +++ b/crates/oxide_builtins/src/array.rs @@ -1734,17 +1734,19 @@ pub fn array_values(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(vm: &mut H, this_val: JsValue, kind: i32) -> Result { +/// 创建 Array/TA Iterator 对象:记录目标、当前下标与迭代种类,原型挂 +/// `%ArrayIteratorPrototype%`(链到 `%IteratorPrototype%`,Array 与 TA 共享)。 +/// `next` 不挂实例 own——由原型提供。 +pub(crate) fn make_array_iterator(vm: &mut H, this_val: JsValue, kind: i32) -> Result { 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(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:: 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(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) { diff --git a/crates/oxide_builtins/src/iterator.rs b/crates/oxide_builtins/src/iterator.rs index d0218e6..6f45d42 100644 --- a/crates/oxide_builtins/src/iterator.rs +++ b/crates/oxide_builtins/src/iterator.rs @@ -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(vm: &mut H, _args: &[u8]) -> NativeResult { @@ -52,6 +53,18 @@ pub fn make_iterator_for_value_without_return(vm: &mut H, value: JsVa } } +/// 同 [`make_iterator_for_value`],但包装器原型指向调用方指定的原型 +/// (`String.prototype[@@iterator]` 用 %StringIteratorPrototype%)。 +pub(crate) fn make_iterator_for_value_with_proto( + vm: &mut H, value: JsValue, wrapper_proto: *mut JsObject, +) -> Result { + 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(vm: &mut H, value: JsVa /// `yield*` 委托不需要且须避免创建时访问内层 return getter)。 pub(crate) fn try_make_iterator_inner( vm: &mut H, value: JsValue, bind_return: bool, +) -> Result, 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( + vm: &mut H, value: JsValue, bind_return: bool, wrapper_proto: Option<*mut JsObject>, ) -> Result, 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(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( } } -fn map_set_next_dispatch(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(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(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(vm: &mut H, args: &[u8]) -> NativeResult { - map_set_next_dispatch::(vm, args, MapSetMode::MapEntries) -} - -pub(crate) fn map_values_iter_next(vm: &mut H, args: &[u8]) -> NativeResult { - map_set_next_dispatch::(vm, args, MapSetMode::MapValues) -} - -pub(crate) fn map_keys_iter_next(vm: &mut H, args: &[u8]) -> NativeResult { - map_set_next_dispatch::(vm, args, MapSetMode::MapKeys) -} - -pub(crate) fn set_values_iter_next(vm: &mut H, args: &[u8]) -> NativeResult { - map_set_next_dispatch::(vm, args, MapSetMode::SetValues) -} - -pub(crate) fn set_entries_iter_next(vm: &mut H, args: &[u8]) -> NativeResult { - map_set_next_dispatch::(vm, args, MapSetMode::SetEntries) -} - -#[derive(Clone, Copy)] -enum TypedArrayMode { - Values, - Keys, - Entries, -} - -/// 按模式让 TypedArray 迭代器包装器推进一步:values 产出元素,keys 产出索引, -/// entries 产出 `[index, element]` 对;耗尽后把下标推进到哨兵值防止"复活"。 -fn typed_array_step( - vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32, mode: TypedArrayMode, -) -> Result { - 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(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(vm: &mut H, args: &[u8]) -> NativeResult { - typed_array_next_dispatch::(vm, args, TypedArrayMode::Values) -} - -pub(crate) fn typed_array_keys_iter_next(vm: &mut H, args: &[u8]) -> NativeResult { - typed_array_next_dispatch::(vm, args, TypedArrayMode::Keys) -} - -pub(crate) fn typed_array_entries_iter_next(vm: &mut H, args: &[u8]) -> NativeResult { - typed_array_next_dispatch::(vm, args, TypedArrayMode::Entries) -} - -/// 构造迭代器包装器,其 `next` 委托给调用方指定的按模式分发的 native 函数。 -/// 与 `make_iterator_for_value` 一致,但允许 Map/Set/TA 原型方法选择 -/// values/keys/entries 变体,并挂到各自的集合迭代器原型上。 -pub(crate) fn make_mode_iterator( - 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( + 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) } diff --git a/crates/oxide_builtins/src/map.rs b/crates/oxide_builtins/src/map.rs index b4fbaf1..3850422 100644 --- a/crates/oxide_builtins/src/map.rs +++ b/crates/oxide_builtins/src/map.rs @@ -323,11 +323,11 @@ pub fn map_size(vm: &mut H, args: &[u8]) -> NativeResult { pub fn map_entries(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:: as *const (), + crate::iterator::MapSetMode::MapEntries, )) } @@ -335,11 +335,11 @@ pub fn map_entries(vm: &mut H, args: &[u8]) -> NativeResult { pub fn map_values(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:: as *const (), + crate::iterator::MapSetMode::MapValues, )) } @@ -347,10 +347,10 @@ pub fn map_values(vm: &mut H, args: &[u8]) -> NativeResult { pub fn map_keys(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:: as *const (), + crate::iterator::MapSetMode::MapKeys, )) } diff --git a/crates/oxide_builtins/src/regexp.rs b/crates/oxide_builtins/src/regexp.rs index beac827..7fc5398 100644 --- a/crates/oxide_builtins/src/regexp.rs +++ b/crates/oxide_builtins/src/regexp.rs @@ -547,8 +547,8 @@ pub fn regexp_symbol_match_all(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(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:: as *const (), 0); - vm.set_or_create_prop_value(wrapper_obj, next_si, next_fn); NativeResult::Ok(JsValue::from_js_object(wrapper)) } diff --git a/crates/oxide_builtins/src/set.rs b/crates/oxide_builtins/src/set.rs index 0b5ff06..00ee3c8 100644 --- a/crates/oxide_builtins/src/set.rs +++ b/crates/oxide_builtins/src/set.rs @@ -337,11 +337,11 @@ pub fn set_size(vm: &mut H, args: &[u8]) -> NativeResult { pub fn set_entries(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:: as *const (), + crate::iterator::MapSetMode::SetEntries, )) } @@ -349,11 +349,11 @@ pub fn set_entries(vm: &mut H, args: &[u8]) -> NativeResult { pub fn set_values(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:: as *const (), + crate::iterator::MapSetMode::SetValues, )) } @@ -362,11 +362,11 @@ pub fn set_keys(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:: as *const (), + crate::iterator::MapSetMode::SetValues, )) } diff --git a/crates/oxide_builtins/src/string.rs b/crates/oxide_builtins/src/string.rs index 3b6c12c..8d104de 100644 --- a/crates/oxide_builtins/src/string.rs +++ b/crates/oxide_builtins/src/string.rs @@ -1535,7 +1535,8 @@ pub fn string_match_all(vm: &mut H, args: &[u8]) -> NativeResult { } fn builder_wrapper(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(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:: 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(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(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(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); diff --git a/crates/oxide_builtins/src/typed_array.rs b/crates/oxide_builtins/src/typed_array.rs index 2a5092a..cb92a09 100644 --- a/crates/oxide_builtins/src/typed_array.rs +++ b/crates/oxide_builtins/src/typed_array.rs @@ -1171,36 +1171,32 @@ pub fn typed_array_join(vm: &mut H, args: &[u8]) -> NativeResult { pub fn typed_array_values(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:: 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(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:: 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(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:: 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 视为最大排到末尾,其余按数值升序。 diff --git a/crates/oxide_vm/src/bindings/bind_iterator.rs b/crates/oxide_vm/src/bindings/bind_iterator.rs index 8609425..bcf65b8 100644 --- a/crates/oxide_vm/src/bindings/bind_iterator.rs +++ b/crates/oxide_vm/src/bindings/bind_iterator.rs @@ -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, session: &KernelSession, global: &mut JsObject) { @@ -17,6 +20,7 @@ pub fn bind_iterator(core: &Arc, session: &KernelSession, global: &m oxide_builtins::iterator::iterator_constructor:: 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, session: &KernelSession, global: &m oxide_builtins::iterator::iterator_symbol_iterator:: 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:: as *const (), + ); + bind_iterator_proto_next( + core, + session, + world.map_iterator_proto.as_ptr() as *mut JsObject, + oxide_builtins::iterator::map_set_iterator_next:: as *const (), + ); + bind_iterator_proto_next( + core, + session, + world.set_iterator_proto.as_ptr() as *mut JsObject, + oxide_builtins::iterator::map_set_iterator_next:: 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:: as *const (), + ); } diff --git a/crates/oxide_vm/src/bindings/mod.rs b/crates/oxide_vm/src/bindings/mod.rs index 7452f50..6efbdef 100644 --- a/crates/oxide_vm/src/bindings/mod.rs +++ b/crates/oxide_vm/src/bindings/mod.rs @@ -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, 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, 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, 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, session: &KernelSession, global: oxide_builtins::iterator::iterator_constructor:: 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, session: &KernelSession, global: oxide_builtins::iterator::iterator_symbol_iterator:: 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:: as *const (), + ); + bind_iterator_proto_next( + core, + session, + world.map_iterator_proto.as_ptr() as *mut JsObject, + oxide_builtins::iterator::map_set_iterator_next:: as *const (), + ); + bind_iterator_proto_next( + core, + session, + world.set_iterator_proto.as_ptr() as *mut JsObject, + oxide_builtins::iterator::map_set_iterator_next:: 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:: as *const (), + ); } fn bind_stub_globals(core: &Arc, session: &KernelSession, global: &mut JsObject) { diff --git a/crates/oxide_vm/tests/builtins_iterator_tests.rs b/crates/oxide_vm/tests/builtins_iterator_tests.rs index b9f273a..4d17a4a 100644 --- a/crates/oxide_vm/tests/builtins_iterator_tests.rs +++ b/crates/oxide_vm/tests/builtins_iterator_tests.rs @@ -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()); +}