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