mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
fix: 迭代器原型形状补全(Iterator.prototype/string 中继层/next 原型化)
This commit is contained in:
@@ -5,7 +5,10 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::bindings::{apply_binding_table, bind_global_value, configure_native_constructor};
|
||||
use crate::bindings::{
|
||||
apply_binding_table, bind_global_value, bind_iterator_function_prototype, bind_iterator_proto_next,
|
||||
configure_native_constructor,
|
||||
};
|
||||
|
||||
/// 绑定迭代器基础设施:`%IteratorPrototype%` 与 `%ArrayIteratorPrototype%` 等。
|
||||
pub fn bind_iterator(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
@@ -17,6 +20,7 @@ pub fn bind_iterator(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
||||
oxide_builtins::iterator::iterator_constructor::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
);
|
||||
bind_iterator_function_prototype(core, session, &mut iterator);
|
||||
|
||||
apply_binding_table(
|
||||
session.builtin_world(),
|
||||
@@ -40,4 +44,32 @@ pub fn bind_iterator(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
||||
oxide_builtins::iterator::iterator_symbol_iterator::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
);
|
||||
|
||||
// 集合迭代器原型各自安装 next(%ArrayIteratorPrototype% 服务 Array/TA 两族;
|
||||
// Map/Set 共用按 `__mode__` 分发的实现;%RegExpStringIteratorPrototype% 供 matchAll)。
|
||||
let world = session.builtin_world();
|
||||
bind_iterator_proto_next(
|
||||
core,
|
||||
session,
|
||||
world.array_iterator_proto.as_ptr() as *mut JsObject,
|
||||
oxide_builtins::array::array_iterator_next::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
bind_iterator_proto_next(
|
||||
core,
|
||||
session,
|
||||
world.map_iterator_proto.as_ptr() as *mut JsObject,
|
||||
oxide_builtins::iterator::map_set_iterator_next::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
bind_iterator_proto_next(
|
||||
core,
|
||||
session,
|
||||
world.set_iterator_proto.as_ptr() as *mut JsObject,
|
||||
oxide_builtins::iterator::map_set_iterator_next::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
bind_iterator_proto_next(
|
||||
core,
|
||||
session,
|
||||
world.regexp_string_iterator_proto.as_ptr() as *mut JsObject,
|
||||
oxide_builtins::string::string_match_all_next::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -234,6 +234,30 @@ pub(crate) fn bind_well_known_data_property(
|
||||
target.bump_generation();
|
||||
}
|
||||
|
||||
/// 在 Iterator 函数对象上绑定 `prototype` 属性 = %IteratorPrototype%(规范形状:
|
||||
/// 构造器函数带 prototype 属性;描述符不可写/不可枚举/不可配置)。
|
||||
pub(crate) fn bind_iterator_function_prototype(
|
||||
core: &Arc<KernelCore>, session: &KernelSession, iterator: &mut JsObject,
|
||||
) {
|
||||
let iter_proto_ptr = session.builtin_world().iterator_proto.as_ptr() as *mut JsObject;
|
||||
let sf = core.perm_interner().as_ref();
|
||||
let sh = core.shape_forge().as_ref();
|
||||
let si_prototype = sf.intern("prototype").0;
|
||||
let iterator_shape = sh.make_shape(iterator.shape_id(), si_prototype);
|
||||
iterator.set_shape_id(iterator_shape);
|
||||
let pos = iterator.ensure_hash_props().len() as u32;
|
||||
iterator.ensure_hash_props().push(JsValue::from_js_object(iter_proto_ptr));
|
||||
iterator.set_data_meta(pos, PropAttributes::new(false, false, false));
|
||||
}
|
||||
|
||||
/// 在集合迭代器原型上绑定 `next` 方法(规范形状:next 挂原型,wrapper 不设 own)。
|
||||
pub(crate) fn bind_iterator_proto_next(
|
||||
core: &Arc<KernelCore>, session: &KernelSession, proto: *mut JsObject, func: *const (),
|
||||
) {
|
||||
let proto = unsafe { &mut *proto };
|
||||
apply_binding_table(session.builtin_world(), proto, core, &[("next", func, 0)]);
|
||||
}
|
||||
|
||||
pub(crate) fn bind_global_value(core: &Arc<KernelCore>, global: &mut JsObject, name: &str, value: JsValue) {
|
||||
let si = core.perm_interner().intern(name).0;
|
||||
let shape = core.shape_forge().make_shape(global.shape_id(), si);
|
||||
@@ -373,6 +397,7 @@ fn bind_iterator_global(core: &Arc<KernelCore>, session: &KernelSession, global:
|
||||
oxide_builtins::iterator::iterator_constructor::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
);
|
||||
bind_iterator_function_prototype(core, session, &mut iterator);
|
||||
apply_binding_table(
|
||||
session.builtin_world(),
|
||||
&mut iterator,
|
||||
@@ -393,6 +418,34 @@ fn bind_iterator_global(core: &Arc<KernelCore>, session: &KernelSession, global:
|
||||
oxide_builtins::iterator::iterator_symbol_iterator::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
);
|
||||
|
||||
// 集合迭代器原型各自安装 next(%ArrayIteratorPrototype% 服务 Array/TA 两族;
|
||||
// Map/Set 共用按 `__mode__` 分发的实现;%RegExpStringIteratorPrototype% 供 matchAll)。
|
||||
let world = session.builtin_world();
|
||||
bind_iterator_proto_next(
|
||||
core,
|
||||
session,
|
||||
world.array_iterator_proto.as_ptr() as *mut JsObject,
|
||||
oxide_builtins::array::array_iterator_next::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
bind_iterator_proto_next(
|
||||
core,
|
||||
session,
|
||||
world.map_iterator_proto.as_ptr() as *mut JsObject,
|
||||
oxide_builtins::iterator::map_set_iterator_next::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
bind_iterator_proto_next(
|
||||
core,
|
||||
session,
|
||||
world.set_iterator_proto.as_ptr() as *mut JsObject,
|
||||
oxide_builtins::iterator::map_set_iterator_next::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
bind_iterator_proto_next(
|
||||
core,
|
||||
session,
|
||||
world.regexp_string_iterator_proto.as_ptr() as *mut JsObject,
|
||||
oxide_builtins::string::string_match_all_next::<crate::vm::Vm> as *const (),
|
||||
);
|
||||
}
|
||||
|
||||
fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
|
||||
@@ -109,9 +109,18 @@ fn iterator_proto_chain_and_self_iteration() {
|
||||
("var it = 'ab'.matchAll(/a/g); it[Symbol.iterator]() === it", true),
|
||||
// Array/TA 共享 %ArrayIteratorPrototype%;Map/Set 各自原型内部共享。
|
||||
("Object.getPrototypeOf([].values()) === Object.getPrototypeOf([].entries())", true),
|
||||
("Object.getPrototypeOf(new Uint8Array(0).values()) === Object.getPrototypeOf([].values())", true),
|
||||
("Object.getPrototypeOf(new Map().values()) === Object.getPrototypeOf(new Map().keys())", true),
|
||||
("Object.getPrototypeOf(new Set().values()) === Object.getPrototypeOf(new Set().entries())", true),
|
||||
(
|
||||
"Object.getPrototypeOf(new Uint8Array(0).values()) === Object.getPrototypeOf([].values())",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"Object.getPrototypeOf(new Map().values()) === Object.getPrototypeOf(new Map().keys())",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"Object.getPrototypeOf(new Set().values()) === Object.getPrototypeOf(new Set().entries())",
|
||||
true,
|
||||
),
|
||||
// %IteratorPrototype% 自身可迭代:其 @@iterator 返回 this。
|
||||
(
|
||||
"var P = Object.getPrototypeOf(Object.getPrototypeOf([].values())); P[Symbol.iterator]() === P",
|
||||
@@ -176,3 +185,107 @@ fn iterator_proto_object_proto_parent() {
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iterator_function_prototype_property() {
|
||||
// Iterator.prototype 属性绑定:=== %IteratorPrototype%([].values() 原型链中继),
|
||||
// 且 %IteratorPrototype% 自身可迭代。
|
||||
let mut vm = Vm::new();
|
||||
let cases = [
|
||||
("Iterator.prototype !== undefined", true),
|
||||
("Object.getPrototypeOf(Object.getPrototypeOf([].values())) === Iterator.prototype", true),
|
||||
(
|
||||
"Object.getPrototypeOf(Object.getPrototypeOf(new Map().values())) === Iterator.prototype",
|
||||
true,
|
||||
),
|
||||
("Iterator.prototype[Symbol.iterator]() === Iterator.prototype", true),
|
||||
];
|
||||
for (src, expected) in cases {
|
||||
let result = eval(&mut vm, src).unwrap();
|
||||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_iterator_proto_layer() {
|
||||
// String 迭代器挂 %StringIteratorPrototype% 中继层:与 Array 迭代器原型不同,
|
||||
// 且经链到 %IteratorPrototype%(Iterator.prototype)。
|
||||
let mut vm = Vm::new();
|
||||
let cases = [
|
||||
(
|
||||
"Object.getPrototypeOf(Object.getPrototypeOf('a'[Symbol.iterator]())) === Iterator.prototype",
|
||||
true,
|
||||
),
|
||||
(
|
||||
"Object.getPrototypeOf('a'[Symbol.iterator]()) !== Object.getPrototypeOf([].values())",
|
||||
true,
|
||||
),
|
||||
];
|
||||
for (src, expected) in cases {
|
||||
let result = eval(&mut vm, src).unwrap();
|
||||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||||
}
|
||||
let out = eval(&mut vm, "[...'abc'].join('')").unwrap();
|
||||
assert_eq!(to_str(&vm, out), "abc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iterator_next_on_prototypes() {
|
||||
// next 挂集合迭代器原型(%ArrayIteratorPrototype%.next 等),wrapper 无 own next。
|
||||
let mut vm = Vm::new();
|
||||
let cases = [
|
||||
("typeof Object.getPrototypeOf([].values()).next === 'function'", true),
|
||||
("Object.getPrototypeOf([].values()).hasOwnProperty('next')", true),
|
||||
("[].values().hasOwnProperty('next') === false", true),
|
||||
("typeof Object.getPrototypeOf(new Map().values()).next === 'function'", true),
|
||||
("new Map().values().hasOwnProperty('next') === false", true),
|
||||
("typeof Object.getPrototypeOf(new Set().values()).next === 'function'", true),
|
||||
("new Set().values().hasOwnProperty('next') === false", true),
|
||||
("typeof Object.getPrototypeOf(new Uint8Array([1]).values()).next === 'function'", true),
|
||||
("new Uint8Array([1]).values().hasOwnProperty('next') === false", true),
|
||||
("typeof Object.getPrototypeOf('ab'.matchAll(/a/g)).next === 'function'", true),
|
||||
("'ab'.matchAll(/a/g).hasOwnProperty('next') === false", true),
|
||||
];
|
||||
for (src, expected) in cases {
|
||||
let result = eval(&mut vm, src).unwrap();
|
||||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iterator_proto_next_consumption_unchanged() {
|
||||
// 行为回归:next 移上原型后各家族消费路径不变(迭代协议经原型链解析)。
|
||||
let mut vm = Vm::new();
|
||||
let str_cases = [
|
||||
("[...[1,2,3]].join(',')", "1,2,3"),
|
||||
("var it = [1,2].values(); it.next().value + ',' + it.next().value", "1,2"),
|
||||
("var it = ['x','y'].entries(); it.next().value.join(':')", "0:x"),
|
||||
("[...new Map([['a',1]])][0].join(':')", "a:1"),
|
||||
("var mk = new Map([['k',9]]); mk.keys().next().value", "k"),
|
||||
("var se = new Set([7]); se.entries().next().value.join(':')", "7:7"),
|
||||
("new Uint8Array([9]).entries().next().value.join(',')", "0,9"),
|
||||
("[...'ab'].join('')", "ab"),
|
||||
("'a\\u{1F600}b'[Symbol.iterator]().next().value", "a"),
|
||||
("var mm = 'ab'.matchAll(/b/g); mm.next().value[0]", "b"),
|
||||
];
|
||||
for (src, expected) in str_cases {
|
||||
let result = eval(&mut vm, src).unwrap();
|
||||
assert_eq!(to_str(&vm, result), expected, "for {}", src);
|
||||
}
|
||||
// 数值结果(含 int 0)用恒等比较断言,避免格式化路径差异。
|
||||
let num_cases = [
|
||||
("[10,20].keys().next().value === 0", true),
|
||||
("[1,2].values().next().value === 1", true),
|
||||
("new Map([['a',1]]).values().next().value === 1", true),
|
||||
("new Set([5,6]).keys().next().value === 5", true),
|
||||
("new Uint8Array([5]).values().next().value === 5", true),
|
||||
("new Uint8Array([7]).entries().next().value[1] === 7", true),
|
||||
];
|
||||
for (src, expected) in num_cases {
|
||||
let result = eval(&mut vm, src).unwrap();
|
||||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||||
}
|
||||
// TA 迭代耗尽后恒 done(数组路径 target 置 undefined 防复活)。
|
||||
let done = eval(&mut vm, "var it = new Uint8Array([1]).values(); it.next(); it.next().done").unwrap();
|
||||
assert!(done.as_bool());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user