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:
@@ -1720,11 +1720,11 @@ pub fn array_sort<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
NativeResult::Ok(vm.reg(args[0]))
|
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 {
|
pub fn array_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
builtins_debug!("Array.prototype.values called with {} args", args.len());
|
builtins_debug!("Array.prototype.values called with {} args", args.len());
|
||||||
let this_val = vm.reg(args[0]);
|
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||||
match crate::iterator::make_iterator_for_value(vm, this_val) {
|
match make_array_iterator(vm, this_val, ARRAY_ITER_KIND_VALUES) {
|
||||||
Ok(iterator) => NativeResult::Ok(iterator),
|
Ok(iterator) => NativeResult::Ok(iterator),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
builtins_error!("Array.prototype.values: invalid receiver");
|
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_INDEX_PROP: &str = "__index__";
|
||||||
const ARRAY_ITER_KIND_PROP: &str = "__kind__";
|
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> {
|
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)),
|
||||||
};
|
};
|
||||||
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
|
let iter = vm
|
||||||
.epoch()
|
.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 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;
|
||||||
|
|||||||
@@ -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"))
|
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)`:为任意可迭代值包装一个迭代器对象。
|
/// `Iterator.from(iterable)`:为任意可迭代值包装一个迭代器对象。
|
||||||
/// 包装器带 `next` 与 `return`(用于 for-of 提前退出时的 IteratorClose 清理)。
|
/// 包装器带 `next` 与 `return`(用于 for-of 提前退出时的 IteratorClose 清理)。
|
||||||
pub fn iterator_from<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
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),
|
Ok(None) => return Ok(None),
|
||||||
Err(err) => return Err(err),
|
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
|
let wrapper = vm
|
||||||
.epoch()
|
.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 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;
|
||||||
@@ -679,13 +689,15 @@ pub(crate) fn typed_array_entries_iter_next<H: VmHost>(vm: &mut H, args: &[u8])
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 构造迭代器包装器,其 `next` 委托给调用方指定的按模式分发的 native 函数。
|
/// 构造迭代器包装器,其 `next` 委托给调用方指定的按模式分发的 native 函数。
|
||||||
/// 与 `make_iterator_for_value` 一致,但允许 Map/Set 原型方法选择
|
/// 与 `make_iterator_for_value` 一致,但允许 Map/Set/TA 原型方法选择
|
||||||
/// values/keys/entries 变体。
|
/// values/keys/entries 变体,并挂到各自的集合迭代器原型上。
|
||||||
pub(crate) fn make_mode_iterator<H: VmHost>(vm: &mut H, inner: JsValue, next_fn: *const ()) -> JsValue {
|
pub(crate) fn make_mode_iterator<H: VmHost>(
|
||||||
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
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
|
let wrapper = vm
|
||||||
.epoch()
|
.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 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 next_si = vm.kernel_core().perm_interner().intern("next").0;
|
||||||
|
|||||||
@@ -326,6 +326,7 @@ pub fn map_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||||
vm,
|
vm,
|
||||||
this_val,
|
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::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(
|
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||||
vm,
|
vm,
|
||||||
this_val,
|
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::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(
|
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||||
vm,
|
vm,
|
||||||
this_val,
|
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::map_keys_iter_next::<H> as *const (),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
JsValue::from_js_object(vm.alloc_object(stub))
|
||||||
};
|
};
|
||||||
|
|
||||||
// 复用 String.prototype.matchAll 的迭代器包装(next 经 string_match_all_next 推进)。
|
// 复用 String.prototype.matchAll 的迭代器包装(next 经 string_match_all_next 推进),
|
||||||
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
// 原型挂 %RegExpStringIteratorPrototype%(链到 %IteratorPrototype%)。
|
||||||
|
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()
|
||||||
.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 wrapper_obj = unsafe { &mut *wrapper };
|
||||||
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;
|
||||||
|
|||||||
@@ -340,6 +340,7 @@ pub fn set_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||||
vm,
|
vm,
|
||||||
this_val,
|
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::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(
|
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||||
vm,
|
vm,
|
||||||
this_val,
|
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::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(
|
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||||
vm,
|
vm,
|
||||||
this_val,
|
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::set_values_iter_next::<H> as *const (),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
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
|
let wrapper = vm
|
||||||
.epoch()
|
.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 wrapper_obj = unsafe { &mut *wrapper };
|
||||||
let input_si = vm.kernel_core().perm_interner().intern(MALL_INPUT).0;
|
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))
|
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 {
|
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 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));
|
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto));
|
||||||
|
|||||||
@@ -1174,6 +1174,7 @@ pub fn typed_array_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||||
vm,
|
vm,
|
||||||
this_val,
|
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 (),
|
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(
|
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||||
vm,
|
vm,
|
||||||
this_val,
|
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 (),
|
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(
|
NativeResult::Ok(crate::iterator::make_mode_iterator(
|
||||||
vm,
|
vm,
|
||||||
this_val,
|
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 (),
|
crate::iterator::typed_array_entries_iter_next::<H> as *const (),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,6 +270,18 @@ pub struct BuiltinWorld {
|
|||||||
pub plain_date_time_proto: P<JsObject>,
|
pub plain_date_time_proto: P<JsObject>,
|
||||||
pub bigint_constructor: P<JsObject>,
|
pub bigint_constructor: P<JsObject>,
|
||||||
pub bigint_proto: P<JsObject>,
|
pub bigint_proto: P<JsObject>,
|
||||||
|
/// `%IteratorPrototype%`:各集合迭代器原型的公共祖先,持有 `@@iterator`(返回自身)。
|
||||||
|
pub iterator_proto: P<JsObject>,
|
||||||
|
/// `%ArrayIteratorPrototype%`:Array 与 TypedArray 迭代器共享(`next` 挂其上)。
|
||||||
|
pub array_iterator_proto: P<JsObject>,
|
||||||
|
/// `%MapIteratorPrototype%`:Map 的 values/keys/entries 迭代器共享。
|
||||||
|
pub map_iterator_proto: P<JsObject>,
|
||||||
|
/// `%SetIteratorPrototype%`:Set 的 values/keys/entries 迭代器共享。
|
||||||
|
pub set_iterator_proto: P<JsObject>,
|
||||||
|
/// `%StringIteratorPrototype%`:String.prototype[@@iterator] 返回的迭代器。
|
||||||
|
pub string_iterator_proto: P<JsObject>,
|
||||||
|
/// `%RegExpStringIteratorPrototype%`:matchAll 返回的迭代器。
|
||||||
|
pub regexp_string_iterator_proto: P<JsObject>,
|
||||||
pub stub_objects: Vec<P<JsObject>>,
|
pub stub_objects: Vec<P<JsObject>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,6 +599,21 @@ fn wire_builtin_world_links(world: &BuiltinWorld) {
|
|||||||
set_proto_if_changed(&world.temporal_object, obj_proto_val);
|
set_proto_if_changed(&world.temporal_object, obj_proto_val);
|
||||||
set_proto_if_changed(&world.temporal_now_object, obj_proto_val);
|
set_proto_if_changed(&world.temporal_now_object, obj_proto_val);
|
||||||
|
|
||||||
|
// 迭代器原型链:%IteratorPrototype% → Object.prototype;各集合迭代器原型
|
||||||
|
// → %IteratorPrototype%(next/@@iterator 方法由绑定层安装到对应原型)。
|
||||||
|
set_proto_if_changed(&world.iterator_proto, obj_proto_val);
|
||||||
|
let iterator_proto_val = JsValue::from_js_object(world.iterator_proto.as_ptr() as *mut JsObject);
|
||||||
|
let iterator_protos: [&P<JsObject>; 5] = [
|
||||||
|
&world.array_iterator_proto,
|
||||||
|
&world.map_iterator_proto,
|
||||||
|
&world.set_iterator_proto,
|
||||||
|
&world.string_iterator_proto,
|
||||||
|
&world.regexp_string_iterator_proto,
|
||||||
|
];
|
||||||
|
for proto in &iterator_protos {
|
||||||
|
set_proto_if_changed(proto, iterator_proto_val);
|
||||||
|
}
|
||||||
|
|
||||||
let typed_array_proto_val = JsValue::from_js_object(world.typed_array_proto.as_ptr() as *mut JsObject);
|
let typed_array_proto_val = JsValue::from_js_object(world.typed_array_proto.as_ptr() as *mut JsObject);
|
||||||
let typed_array_protos: [&P<JsObject>; 11] = [
|
let typed_array_protos: [&P<JsObject>; 11] = [
|
||||||
&world.int8array_proto,
|
&world.int8array_proto,
|
||||||
@@ -780,6 +807,15 @@ impl BuiltinWorld {
|
|||||||
let (bigint_proto, bigint_constructor) = make_named_pair(string_forge, shape_forge, labels, "BigInt");
|
let (bigint_proto, bigint_constructor) = make_named_pair(string_forge, shape_forge, labels, "BigInt");
|
||||||
let stub_objects = Vec::new();
|
let stub_objects = Vec::new();
|
||||||
|
|
||||||
|
// 迭代器原型家族:链关系(→ %IteratorPrototype% → Object.prototype)在
|
||||||
|
// wire_builtin_world_links 中建立,next/@@iterator 方法由绑定层安装。
|
||||||
|
let iterator_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
|
let array_iterator_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
|
let map_iterator_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
|
let set_iterator_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
|
let string_iterator_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
|
let regexp_string_iterator_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
|
|
||||||
let world = Self {
|
let world = Self {
|
||||||
object_proto,
|
object_proto,
|
||||||
array_proto,
|
array_proto,
|
||||||
@@ -868,6 +904,12 @@ impl BuiltinWorld {
|
|||||||
plain_date_time_proto,
|
plain_date_time_proto,
|
||||||
bigint_constructor,
|
bigint_constructor,
|
||||||
bigint_proto,
|
bigint_proto,
|
||||||
|
iterator_proto,
|
||||||
|
array_iterator_proto,
|
||||||
|
map_iterator_proto,
|
||||||
|
set_iterator_proto,
|
||||||
|
string_iterator_proto,
|
||||||
|
regexp_string_iterator_proto,
|
||||||
stub_objects,
|
stub_objects,
|
||||||
};
|
};
|
||||||
wire_builtin_world_links(&world);
|
wire_builtin_world_links(&world);
|
||||||
@@ -1127,6 +1169,35 @@ impl BuiltinWorld {
|
|||||||
};
|
};
|
||||||
let stub_objects = if dirty.stubs { Vec::new() } else { current.stub_objects.clone() };
|
let stub_objects = if dirty.stubs { Vec::new() } else { current.stub_objects.clone() };
|
||||||
|
|
||||||
|
// 迭代器原型依赖 Object.prototype(链到其上):object 家族重建时一并重建,
|
||||||
|
// 否则旧原型链指向已释放的 object_proto。
|
||||||
|
let (
|
||||||
|
iterator_proto,
|
||||||
|
array_iterator_proto,
|
||||||
|
map_iterator_proto,
|
||||||
|
set_iterator_proto,
|
||||||
|
string_iterator_proto,
|
||||||
|
regexp_string_iterator_proto,
|
||||||
|
) = if dirty.object {
|
||||||
|
(
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
current.iterator_proto.clone(),
|
||||||
|
current.array_iterator_proto.clone(),
|
||||||
|
current.map_iterator_proto.clone(),
|
||||||
|
current.set_iterator_proto.clone(),
|
||||||
|
current.string_iterator_proto.clone(),
|
||||||
|
current.regexp_string_iterator_proto.clone(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let world = BuiltinWorld {
|
let world = BuiltinWorld {
|
||||||
object_proto,
|
object_proto,
|
||||||
array_proto,
|
array_proto,
|
||||||
@@ -1215,6 +1286,12 @@ impl BuiltinWorld {
|
|||||||
plain_date_time_proto,
|
plain_date_time_proto,
|
||||||
bigint_constructor,
|
bigint_constructor,
|
||||||
bigint_proto,
|
bigint_proto,
|
||||||
|
iterator_proto,
|
||||||
|
array_iterator_proto,
|
||||||
|
map_iterator_proto,
|
||||||
|
set_iterator_proto,
|
||||||
|
string_iterator_proto,
|
||||||
|
regexp_string_iterator_proto,
|
||||||
stub_objects,
|
stub_objects,
|
||||||
};
|
};
|
||||||
wire_builtin_world_links(&world);
|
wire_builtin_world_links(&world);
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ pub fn bind_array(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
|||||||
if let Some(pos) = core.shape_forge().lookup_position(array_proto.shape_id(), si) {
|
if let Some(pos) = core.shape_forge().lookup_position(array_proto.shape_id(), si) {
|
||||||
array_proto.set_data_meta(pos, PropAttributes::new(true, false, true));
|
array_proto.set_data_meta(pos, PropAttributes::new(true, false, true));
|
||||||
}
|
}
|
||||||
|
// Array.prototype[@@iterator] 与 values 共享同一函数对象。
|
||||||
|
super::bind_well_known_method_alias(core, array_proto, "values", 0);
|
||||||
|
|
||||||
let ctor_ptr = session.builtin_world().array_constructor.as_ptr() as *mut JsObject;
|
let ctor_ptr = session.builtin_world().array_constructor.as_ptr() as *mut JsObject;
|
||||||
bind_constructor!(core, global, "Array", ctor_ptr, oxide_builtins::array::array_constructor::<crate::vm::Vm>, 1, hash: true);
|
bind_constructor!(core, global, "Array", ctor_ptr, oxide_builtins::array::array_constructor::<crate::vm::Vm>, 1, hash: true);
|
||||||
|
|||||||
@@ -26,4 +26,18 @@ pub fn bind_iterator(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
|||||||
);
|
);
|
||||||
|
|
||||||
bind_global_value(core, global, "Iterator", JsValue::from_js_object(Box::into_raw(iterator)));
|
bind_global_value(core, global, "Iterator", JsValue::from_js_object(Box::into_raw(iterator)));
|
||||||
|
|
||||||
|
// %IteratorPrototype% 自身可迭代:@@iterator 返回 this,经原型链被所有
|
||||||
|
// 集合迭代器继承(it[Symbol.iterator]() === it 恒等)。
|
||||||
|
let iter_proto_ptr = session.builtin_world().iterator_proto.as_ptr() as *mut JsObject;
|
||||||
|
let iter_proto = unsafe { &mut *iter_proto_ptr };
|
||||||
|
crate::bindings::bind_well_known_method(
|
||||||
|
session.builtin_world(),
|
||||||
|
core,
|
||||||
|
iter_proto,
|
||||||
|
0,
|
||||||
|
"iterator",
|
||||||
|
oxide_builtins::iterator::iterator_symbol_iterator::<crate::vm::Vm> as *const (),
|
||||||
|
0,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,16 @@ pub fn bind_string(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
|||||||
core,
|
core,
|
||||||
&[("toString", oxide_builtins::string::string_to_string::<crate::vm::Vm> as *const (), 0)],
|
&[("toString", oxide_builtins::string::string_to_string::<crate::vm::Vm> as *const (), 0)],
|
||||||
);
|
);
|
||||||
|
// String.prototype[@@iterator]:逐 code point 迭代字符。
|
||||||
|
super::bind_well_known_method(
|
||||||
|
session.builtin_world(),
|
||||||
|
core,
|
||||||
|
proto,
|
||||||
|
0,
|
||||||
|
"iterator",
|
||||||
|
oxide_builtins::string::string_symbol_iterator::<crate::vm::Vm> as *const (),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
let si_str = core.perm_interner().intern("String").0;
|
let si_str = core.perm_interner().intern("String").0;
|
||||||
let str_shape = core.shape_forge().make_shape(global.shape_id(), si_str);
|
let str_shape = core.shape_forge().make_shape(global.shape_id(), si_str);
|
||||||
|
|||||||
@@ -380,6 +380,19 @@ fn bind_iterator_global(core: &Arc<KernelCore>, session: &KernelSession, global:
|
|||||||
&[("from", oxide_builtins::iterator::iterator_from::<crate::vm::Vm> as *const (), 1)],
|
&[("from", oxide_builtins::iterator::iterator_from::<crate::vm::Vm> as *const (), 1)],
|
||||||
);
|
);
|
||||||
bind_existing_global(core, global, "Iterator", JsValue::from_js_object(Box::into_raw(iterator)));
|
bind_existing_global(core, global, "Iterator", JsValue::from_js_object(Box::into_raw(iterator)));
|
||||||
|
|
||||||
|
// %IteratorPrototype% 自身可迭代(@@iterator 返回 this,经原型链被全部集合迭代器继承)。
|
||||||
|
let iter_proto_ptr = session.builtin_world().iterator_proto.as_ptr() as *mut JsObject;
|
||||||
|
let iter_proto = unsafe { &mut *iter_proto_ptr };
|
||||||
|
bind_well_known_method(
|
||||||
|
session.builtin_world(),
|
||||||
|
core,
|
||||||
|
iter_proto,
|
||||||
|
0,
|
||||||
|
"iterator",
|
||||||
|
oxide_builtins::iterator::iterator_symbol_iterator::<crate::vm::Vm> as *const (),
|
||||||
|
0,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||||
|
|||||||
@@ -95,3 +95,84 @@ fn for_of_string_chars() {
|
|||||||
let result = eval(&mut vm, "var out = []; for (const c of 'abc') out.push(c); out.join('')").unwrap();
|
let result = eval(&mut vm, "var out = []; for (const c of 'abc') out.push(c); out.join('')").unwrap();
|
||||||
assert_eq!(to_str(&vm, result), "abc");
|
assert_eq!(to_str(&vm, result), "abc");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iterator_proto_chain_and_self_iteration() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let cases = [
|
||||||
|
// 各家族迭代器自迭代恒等(%IteratorPrototype% 的 @@iterator 返回 this)。
|
||||||
|
("var it = [1,2].values(); it[Symbol.iterator]() === it", true),
|
||||||
|
("var it = new Map([['a',1]]).values(); it[Symbol.iterator]() === it", true),
|
||||||
|
("var it = new Set([1]).values(); it[Symbol.iterator]() === it", true),
|
||||||
|
("var it = new Uint8Array([1]).values(); it[Symbol.iterator]() === it", true),
|
||||||
|
("var it = 'abc'[Symbol.iterator](); it[Symbol.iterator]() === it", true),
|
||||||
|
("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),
|
||||||
|
// %IteratorPrototype% 自身可迭代:其 @@iterator 返回 this。
|
||||||
|
(
|
||||||
|
"var P = Object.getPrototypeOf(Object.getPrototypeOf([].values())); P[Symbol.iterator]() === P",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
// 原型链:%ArrayIteratorPrototype% → %IteratorPrototype% → Object.prototype。
|
||||||
|
(
|
||||||
|
"Object.getPrototypeOf(Object.getPrototypeOf([].values())) === \
|
||||||
|
Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf([].values())))",
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Object.getPrototypeOf(Object.getPrototypeOf([].values()))[Symbol.iterator] === \
|
||||||
|
Object.getPrototypeOf(new Map().values())[Symbol.iterator]",
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
for (src, expected) in cases {
|
||||||
|
let result = eval(&mut vm, src).unwrap();
|
||||||
|
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn array_string_symbol_iterator_bound() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
// G4 别名:Array/String 的 @@iterator 属性存在且可迭代。
|
||||||
|
let bool_cases = [
|
||||||
|
("[][Symbol.iterator] === [].values", true),
|
||||||
|
("var out = []; for (const c of 'a\\u{1F600}b') out.push(c); out.length === 3", true),
|
||||||
|
];
|
||||||
|
for (src, expected) in bool_cases {
|
||||||
|
let result = eval(&mut vm, src).unwrap();
|
||||||
|
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||||||
|
}
|
||||||
|
let str_cases = [
|
||||||
|
("typeof 'abc'[Symbol.iterator]", "function"),
|
||||||
|
("[...'ab'].join('')", "ab"),
|
||||||
|
("Array.from('ab').join('')", "ab"),
|
||||||
|
("[...[1,2,3]].join(',')", "1,2,3"),
|
||||||
|
("Array.from([1,2]).join(',')", "1,2"),
|
||||||
|
];
|
||||||
|
for (src, expected) in str_cases {
|
||||||
|
let result = eval(&mut vm, src).unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), expected, "for {}", src);
|
||||||
|
}
|
||||||
|
// String 迭代器语义:null/undefined 抛 TypeError,对象走 ToString。
|
||||||
|
let err = eval(&mut vm, "try { String.prototype[Symbol.iterator].call(null) } catch (e) { e.name }").unwrap();
|
||||||
|
assert_eq!(to_str(&vm, err), "TypeError");
|
||||||
|
let result = eval(&mut vm, "[...String.prototype[Symbol.iterator].call({toString: () => 'xy'})].join('')").unwrap();
|
||||||
|
assert_eq!(to_str(&vm, result), "xy");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iterator_proto_object_proto_parent() {
|
||||||
|
// 链:values() → %ArrayIteratorPrototype% → %IteratorPrototype% → Object.prototype。
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf([].values()))) === Object.prototype",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user