feat: 迭代器原型基建:%IteratorPrototype% 链 + 各集合迭代器原型

新增 %IteratorPrototype%(含 @@iterator 返回自身,经原型链被全部迭代器
继承,it[Symbol.iterator]() === it 恒等)与 %ArrayIteratorPrototype%/
%MapIteratorPrototype%/%SetIteratorPrototype%/%StringIteratorPrototype%/
%RegExpStringIteratorPrototype%(链到 %IteratorPrototype%);Array/Map/Set/
TA/String/matchAll 迭代器包装统一挂对应原型;补 Array/String 的 @@iterator
别名绑定。集合迭代器原型不含 return 方法(规范集合迭代器无 return)。
This commit is contained in:
2026-08-15 22:57:03 +08:00
parent c052f5a619
commit 2359c4e0a4
13 changed files with 274 additions and 18 deletions
+7 -6
View File
@@ -1720,11 +1720,11 @@ pub fn array_sort<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
NativeResult::Ok(vm.reg(args[0]))
}
/// `Array.prototype.values()`:返回迭代数组元素的迭代器。
/// `Array.prototype.values()`:返回迭代数组元素的迭代器(%ArrayIteratorPrototype%)。
pub fn array_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
builtins_debug!("Array.prototype.values called with {} args", args.len());
let this_val = vm.reg(args[0]);
match crate::iterator::make_iterator_for_value(vm, this_val) {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
match make_array_iterator(vm, this_val, ARRAY_ITER_KIND_VALUES) {
Ok(iterator) => NativeResult::Ok(iterator),
Err(err) => {
builtins_error!("Array.prototype.values: invalid receiver");
@@ -1742,16 +1742,17 @@ const ARRAY_ITER_TARGET_PROP: &str = "__target__";
const ARRAY_ITER_INDEX_PROP: &str = "__index__";
const ARRAY_ITER_KIND_PROP: &str = "__kind__";
/// 创建 Array Iterator 对象:记录目标、当前下标与迭代种类。
/// 创建 Array Iterator 对象:记录目标、当前下标与迭代种类,原型挂
/// `%ArrayIteratorPrototype%`(链到 `%IteratorPrototype%`,与 TA 迭代器共享)。
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)),
};
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
let array_iter_proto = vm.session().builtin_world().array_iterator_proto.as_ptr() as *mut JsObject;
let iter = vm
.epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(array_iter_proto)));
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;
+19 -7
View File
@@ -13,6 +13,15 @@ pub fn iterator_constructor<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult
NativeResult::Err(crate::error::create_type_error(vm, "Iterator is not a constructor"))
}
/// `%IteratorPrototype%[@@iterator]`:返回 this(迭代器对象自迭代)。
///
/// 挂在 `%IteratorPrototype%` 上,所有集合迭代器经原型链继承,保证
/// `it[Symbol.iterator]() === it` 恒等成立;this 为任意值(含原始值)时原样返回。
pub fn iterator_symbol_iterator<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = if args.is_empty() { JsValue::undefined() } else { vm.reg(args[0]) };
NativeResult::Ok(this_val)
}
/// `Iterator.from(iterable)`:为任意可迭代值包装一个迭代器对象。
/// 包装器带 `next` 与 `return`(用于 for-of 提前退出时的 IteratorClose 清理)。
pub fn iterator_from<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
@@ -64,10 +73,11 @@ pub(crate) fn try_make_iterator_inner<H: VmHost>(
Ok(None) => return Ok(None),
Err(err) => return Err(err),
};
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
// 通用包装器挂 %IteratorPrototype%:经原型链获得 @@iterator(返回自身)。
let iterator_proto = 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(object_proto)));
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(iterator_proto)));
let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0;
let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0;
@@ -679,13 +689,15 @@ pub(crate) fn typed_array_entries_iter_next<H: VmHost>(vm: &mut H, args: &[u8])
}
/// 构造迭代器包装器,其 `next` 委托给调用方指定的按模式分发的 native 函数。
/// 与 `make_iterator_for_value` 一致,但允许 Map/Set 原型方法选择
/// values/keys/entries 变体。
pub(crate) fn make_mode_iterator<H: VmHost>(vm: &mut H, inner: JsValue, next_fn: *const ()) -> JsValue {
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
/// 与 `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 {
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(object_proto)));
.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;
+3
View File
@@ -326,6 +326,7 @@ pub fn map_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
NativeResult::Ok(crate::iterator::make_mode_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 (),
))
}
@@ -337,6 +338,7 @@ pub fn map_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
NativeResult::Ok(crate::iterator::make_mode_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 (),
))
}
@@ -348,6 +350,7 @@ pub fn map_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
NativeResult::Ok(crate::iterator::make_mode_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 (),
))
}
+4 -3
View File
@@ -547,11 +547,12 @@ 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 推进)。
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
// 复用 String.prototype.matchAll 的迭代器包装(next 经 string_match_all_next 推进),
// 原型挂 %RegExpStringIteratorPrototype%(链到 %IteratorPrototype%)。
let regexp_iter_proto = vm.session().builtin_world().regexp_string_iterator_proto.as_ptr() as *mut JsObject;
let wrapper = vm
.epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(regexp_iter_proto)));
let wrapper_obj = unsafe { &mut *wrapper };
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;
+3
View File
@@ -340,6 +340,7 @@ pub fn set_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
NativeResult::Ok(crate::iterator::make_mode_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 (),
))
}
@@ -351,6 +352,7 @@ pub fn set_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
NativeResult::Ok(crate::iterator::make_mode_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 (),
))
}
@@ -363,6 +365,7 @@ pub fn set_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
NativeResult::Ok(crate::iterator::make_mode_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 (),
))
}
+38 -2
View File
@@ -1535,10 +1535,11 @@ 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 {
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
// matchAll 迭代器挂 %RegExpStringIteratorPrototype%(链到 %IteratorPrototype%)。
let regexp_iter_proto = vm.session().builtin_world().regexp_string_iterator_proto.as_ptr() as *mut JsObject;
let wrapper = vm
.epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(regexp_iter_proto)));
let wrapper_obj = unsafe { &mut *wrapper };
let input_si = vm.kernel_core().perm_interner().intern(MALL_INPUT).0;
@@ -1557,6 +1558,41 @@ fn builder_wrapper<H: VmHost>(vm: &mut H, input: &str, re_obj: JsValue) -> Nativ
NativeResult::Ok(JsValue::from_js_object(wrapper))
}
/// `String.prototype[Symbol.iterator]()`:返回按 Unicode code point 迭代字符的迭代器。
///
/// # 步骤
/// 1. null/undefined 抛 TypeError(RequireObjectCoercible)
/// 2. this 经 ToString 完整转换(对象取 toString 结果,Symbol 抛 TypeError)
/// 3. 包成统一迭代器包装(next 逐 code point 产出,耗尽后 done)
///
/// # 边界与前提
/// - 对象 toString 抛出的原始异常原样传播
/// - 包装器挂 `%StringIteratorPrototype%` → `%IteratorPrototype%` 链
pub fn string_symbol_iterator<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_null() || this_val.is_undefined() {
return NativeResult::Err(crate::error::create_type_error(
vm,
"String.prototype[Symbol.iterator] called on null or undefined",
));
}
let s = match oxide_runtime_api::to_string_full(this_val, vm) {
Ok(s) => s,
Err(_) => {
// ToString 触发对象 toString/valueOf 抛出的原生异常须原样传播。
if let Some(exc) = vm.take_uncaught_value() {
return NativeResult::Err(exc);
}
return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a string"));
}
};
let s_val = vm.new_string_owned(s);
match crate::iterator::make_iterator_for_value(vm, s_val) {
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));
+3
View File
@@ -1174,6 +1174,7 @@ pub fn typed_array_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
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 (),
))
}
@@ -1185,6 +1186,7 @@ pub fn typed_array_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
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 (),
))
}
@@ -1196,6 +1198,7 @@ pub fn typed_array_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
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 (),
))
}