fix: object 家族 dirty 时重建迭代器绑定并对齐 Iterator.prototype,绑定幂等化避免重复槽
This commit is contained in:
@@ -6,11 +6,14 @@ use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::bindings::{
|
||||
apply_binding_table, bind_global_value, bind_iterator_function_prototype, bind_iterator_proto_next,
|
||||
configure_native_constructor,
|
||||
apply_binding_table, bind_global_value, bind_iterator_function_prototype, configure_native_constructor,
|
||||
};
|
||||
|
||||
/// 绑定迭代器基础设施:`%IteratorPrototype%` 与 `%ArrayIteratorPrototype%` 等。
|
||||
/// 绑定 `Iterator` 全局构造器(`prototype` = %IteratorPrototype%、`from` 方法)。
|
||||
///
|
||||
/// 迭代器原型方法(%IteratorPrototype% 的 `@@iterator` 与各集合原型 `next`)由
|
||||
/// `rebind_dirty_builtins` 的 object 分支统一安装,本入口不重复绑定,避免
|
||||
/// 保留原型经 dirty reset 时属性槽膨胀。
|
||||
pub fn bind_iterator(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
let function_proto = session.builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
let mut iterator = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto)));
|
||||
@@ -30,46 +33,4 @@ 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)));
|
||||
|
||||
// %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,
|
||||
);
|
||||
|
||||
// 集合迭代器原型各自安装 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 (),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -251,13 +251,96 @@ pub(crate) fn bind_iterator_function_prototype(
|
||||
}
|
||||
|
||||
/// 在集合迭代器原型上绑定 `next` 方法(规范形状:next 挂原型,wrapper 不设 own)。
|
||||
///
|
||||
/// `next` 键已存在时跳过:保留原型在 dirty reset 重复经过时不再追加重复属性槽。
|
||||
pub(crate) fn bind_iterator_proto_next(
|
||||
core: &Arc<KernelCore>, session: &KernelSession, proto: *mut JsObject, func: *const (),
|
||||
) {
|
||||
let proto = unsafe { &mut *proto };
|
||||
let si_next = core.perm_interner().intern("next").0;
|
||||
if core.shape_forge().lookup_position(proto.shape_id(), si_next).is_some() {
|
||||
return;
|
||||
}
|
||||
apply_binding_table(session.builtin_world(), proto, core, &[("next", func, 0)]);
|
||||
}
|
||||
|
||||
/// 把迭代器原型方法安装到当前 builtin world 的迭代器原型上:
|
||||
/// `%IteratorPrototype%` 的 `@@iterator`(返回 this)与 Array/Map/Set/
|
||||
/// RegExpString 四个集合原型的 `next`。
|
||||
///
|
||||
/// 在迭代器原型全新重建后调用(object 家族 dirty 或全量初始化);各绑定点
|
||||
/// 自带幂等检查,保留原型重复经过时安全跳过。
|
||||
fn bind_iterator_protos(core: &Arc<KernelCore>, session: &KernelSession) {
|
||||
let world = session.builtin_world();
|
||||
|
||||
// %IteratorPrototype% 自身可迭代:@@iterator 返回 this,经原型链被全部
|
||||
// 集合迭代器继承(it[Symbol.iterator]() === it 恒等)。
|
||||
let iter_proto_ptr = world.iterator_proto.as_ptr() as *mut JsObject;
|
||||
let iter_proto = unsafe { &mut *iter_proto_ptr };
|
||||
let sym_iter = oxide_types::private_key::make_well_known_symbol_key(0);
|
||||
if core.shape_forge().lookup_position(iter_proto.shape_id(), sym_iter).is_none() {
|
||||
bind_well_known_method(
|
||||
world,
|
||||
core,
|
||||
iter_proto,
|
||||
0,
|
||||
"iterator",
|
||||
oxide_builtins::iterator::iterator_symbol_iterator::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
// %ArrayIteratorPrototype% 服务 Array/TA 两族;Map/Set 共用按 `__mode__`
|
||||
// 分发的实现;%RegExpStringIteratorPrototype% 供 matchAll。
|
||||
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 (),
|
||||
);
|
||||
}
|
||||
|
||||
/// 对齐保留 global 上 `Iterator` 函数对象的 `prototype` 属性:object 家族重建会
|
||||
/// 产生新的 `%IteratorPrototype%`,保留的 Iterator 函数对象须指向新原型。
|
||||
///
|
||||
/// global 同时重建(global dirty)或初始化时无 `Iterator` 属性,直接跳过——
|
||||
/// 彼时由 `bind_iterator_global` 以新原型创建函数对象。
|
||||
fn sync_iterator_function_prototype(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
let si_iterator = core.perm_interner().intern("Iterator").0;
|
||||
let Some(pos) = core.shape_forge().lookup_position(global.shape_id(), si_iterator) else {
|
||||
return;
|
||||
};
|
||||
let iterator_val = global.get_prop_at(pos);
|
||||
if !iterator_val.is_object() {
|
||||
return;
|
||||
}
|
||||
let iterator = unsafe { &mut *iterator_val.as_js_object_ptr() };
|
||||
let si_prototype = core.perm_interner().intern("prototype").0;
|
||||
let Some(proto_pos) = core.shape_forge().lookup_position(iterator.shape_id(), si_prototype) else {
|
||||
return;
|
||||
};
|
||||
let new_proto = JsValue::from_js_object(session.builtin_world().iterator_proto.as_ptr() as *mut JsObject);
|
||||
iterator.set_prop_at(proto_pos, new_proto);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -405,47 +488,9 @@ fn bind_iterator_global(core: &Arc<KernelCore>, session: &KernelSession, global:
|
||||
&[("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)));
|
||||
|
||||
// %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,
|
||||
);
|
||||
|
||||
// 集合迭代器原型各自安装 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 (),
|
||||
);
|
||||
// 迭代器原型方法(%IteratorPrototype% 的 @@iterator 与各集合原型 next)由
|
||||
// `bind_iterator_protos` 在 object 家族重建时统一安装,这里不重复绑定,
|
||||
// 避免保留原型经 dirty reset 时属性槽膨胀。
|
||||
}
|
||||
|
||||
fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
@@ -699,6 +744,10 @@ pub fn rebind_dirty_builtins(core: &Arc<KernelCore>, session: &mut KernelSession
|
||||
|
||||
if dirty.map_or(true, |d| d.object) {
|
||||
bind_object::bind_object(core, session, global);
|
||||
// object 家族重建连带重建 6 个迭代器原型(其链到新 Object.prototype),
|
||||
// 须同步安装原型方法并让保留 global 上的 Iterator 指向新 %IteratorPrototype%。
|
||||
bind_iterator_protos(core, session);
|
||||
sync_iterator_function_prototype(core, session, global);
|
||||
}
|
||||
if dirty.map_or(true, |d| d.array) {
|
||||
bind_array::bind_array(core, session, global);
|
||||
|
||||
@@ -1004,6 +1004,83 @@ mod tests {
|
||||
assert!(!vm.session.is_dirty_since_snapshot());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_reset_object_dirty_rebinds_iterator_family() {
|
||||
let mut vm = Vm::new();
|
||||
let old_object_proto = vm.session.builtin_world().object_proto.as_ptr();
|
||||
let old_iterator_proto = vm.session.builtin_world().iterator_proto.as_ptr();
|
||||
// 用户修改 Object.prototype:object 家族世代递增,global 未动。
|
||||
unsafe { &mut *(old_object_proto as *mut JsObject) }.bump_generation();
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
// object 家族与迭代器原型全部重建(新原型链到新 Object.prototype)。
|
||||
assert!(!std::ptr::eq(old_object_proto, vm.session.builtin_world().object_proto.as_ptr()));
|
||||
assert!(!std::ptr::eq(old_iterator_proto, vm.session.builtin_world().iterator_proto.as_ptr()));
|
||||
// global 保留(dirty.global=false):其 Iterator 函数对象的 prototype
|
||||
// 属性须对齐到重建后的 %IteratorPrototype%。
|
||||
let iter_val = global_prop(&vm, "Iterator");
|
||||
let si_prototype = vm.kernel_core.perm_interner().intern("prototype").0;
|
||||
let iter_obj = unsafe { &*iter_val.as_js_object_ptr() };
|
||||
let proto_pos = vm
|
||||
.kernel_core
|
||||
.shape_forge()
|
||||
.lookup_position(iter_obj.shape_id(), si_prototype)
|
||||
.expect("Iterator should have prototype slot");
|
||||
assert!(std::ptr::eq(
|
||||
iter_obj.get_prop_at(proto_pos).as_js_object_ptr(),
|
||||
vm.session.builtin_world().iterator_proto.as_ptr() as *mut JsObject
|
||||
));
|
||||
// full_reset 后 session 干净;此后 run_source 执行才重新累积世代变化。
|
||||
assert!(!vm.session.is_dirty_since_snapshot());
|
||||
// 迭代器家族功能完整:原型 next 就位,for-of/spread/Array.from/Iterator.from/
|
||||
// Map/Set/String/yield* 全部可用,原型链与 Iterator.prototype 一致。
|
||||
let r = run_source(&mut vm, "[...[1,2,3]].join(',')");
|
||||
assert_eq!(vm.lookup_str(r).as_deref(), Some("1,2,3"));
|
||||
let r = run_source(&mut vm, "Array.from([1,2]).join(',')");
|
||||
assert_eq!(vm.lookup_str(r).as_deref(), Some("1,2"));
|
||||
let r = run_source(&mut vm, "Iterator.from({next:function(){return {value:42,done:false}}}).next().value");
|
||||
assert_eq!(r, JsValue::int(42));
|
||||
let r = run_source(&mut vm, "[...new Map([[1,2],[3,4]])].map(function(x){return x.join(':')}).join(';')");
|
||||
assert_eq!(vm.lookup_str(r).as_deref(), Some("1:2;3:4"));
|
||||
let r = run_source(&mut vm, "[...new Set([1,2,3])].join(',')");
|
||||
assert_eq!(vm.lookup_str(r).as_deref(), Some("1,2,3"));
|
||||
let r = run_source(&mut vm, "[...'ab'].join(',')");
|
||||
assert_eq!(vm.lookup_str(r).as_deref(), Some("a,b"));
|
||||
let r = run_source(&mut vm, "function* g(){yield* [1,2]} [...g()].join(',')");
|
||||
assert_eq!(vm.lookup_str(r).as_deref(), Some("1,2"));
|
||||
let r = run_source(
|
||||
&mut vm,
|
||||
"Object.getPrototypeOf(Object.getPrototypeOf([].values())) === Iterator.prototype",
|
||||
);
|
||||
assert_eq!(r, JsValue::bool(true));
|
||||
let r = run_source(&mut vm, "var it=[].values(); it[Symbol.iterator]()===it");
|
||||
assert_eq!(r, JsValue::bool(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_reset_global_dirty_keeps_iterator_proto_slots_stable() {
|
||||
let mut vm = Vm::new();
|
||||
let arr_iter_proto = vm.session.builtin_world().array_iterator_proto.as_ptr() as *mut JsObject;
|
||||
let slots_before = unsafe { &*arr_iter_proto }.hash_props_vec().map_or(0, |v| v.len());
|
||||
// global 世代递增(用户写 global),builtin 家族未动。
|
||||
unsafe { &mut *(vm.session.global_object.as_ptr() as *mut JsObject) }.bump_generation();
|
||||
|
||||
vm.full_reset();
|
||||
|
||||
// builtin 未脏 → 迭代器原型保留原对象且属性槽不膨胀(重复 full_reset 不再追加)。
|
||||
let arr_iter_proto_after = vm.session.builtin_world().array_iterator_proto.as_ptr() as *mut JsObject;
|
||||
assert!(std::ptr::eq(arr_iter_proto, arr_iter_proto_after));
|
||||
let slots_after = unsafe { &*arr_iter_proto_after }.hash_props_vec().map_or(0, |v| v.len());
|
||||
assert_eq!(slots_before, slots_after);
|
||||
assert!(!vm.session.is_dirty_since_snapshot());
|
||||
// 迭代器功能经保留原型仍完整(run_source 起再次累积世代变化)。
|
||||
let r = run_source(&mut vm, "[...[1,2,3]].join(',')");
|
||||
assert_eq!(vm.lookup_str(r).as_deref(), Some("1,2,3"));
|
||||
let r = run_source(&mut vm, "var it=new Set([1]).values(); it[Symbol.iterator]()===it");
|
||||
assert_eq!(r, JsValue::bool(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bigint_literal_arithmetic_and_comparison() {
|
||||
let mut vm = Vm::new();
|
||||
|
||||
Reference in New Issue
Block a user