//! 注册表职责:BuiltinWorld 结构(108 固定 P 字段 + stub 族 + Box::into_raw //! 登记表)、get_by_id 派发、all_p_fields 108 元组枚举(新增 P 字段四处同步 //! 约束载体)与登记表 track/find/inherit/teardown。 use oxide_types::mem::P; use oxide_types::object::{JsObject, NativeFnPtr}; use oxide_types::value::JsValue; use crate::kernel::BuiltinId; /// 全部内置对象(原型、构造器、全局单例 Math/JSON 与 stub 对象)的持有者。 /// /// 每个 session 独立持有自己的 `BuiltinWorld`,保证 session 间内置对象隔离; /// 由 [`BuiltinWorld::new`] 全量构造,或 [`BuiltinWorld::rebuild_with_dirty`] 按脏标记部分重建。 pub struct BuiltinWorld { pub object_proto: P, pub array_proto: P, pub function_proto: P, pub string_proto: P, pub number_proto: P, pub boolean_proto: P, pub error_proto: P, pub symbol_proto: P, pub object_constructor: P, pub array_constructor: P, pub function_constructor: P, pub string_constructor: P, pub number_constructor: P, pub boolean_constructor: P, pub error_constructor: P, pub symbol_constructor: P, pub type_error_proto: P, pub reference_error_proto: P, pub range_error_proto: P, pub syntax_error_proto: P, pub uri_error_proto: P, pub eval_error_proto: P, pub suppressed_error_proto: P, pub math_object: P, pub json_object: P, pub date_constructor: P, pub date_proto: P, pub set_constructor: P, pub set_proto: P, pub map_constructor: P, pub map_proto: P, pub regexp_constructor: P, pub regexp_proto: P, pub array_buffer_constructor: P, pub array_buffer_proto: P, /// `SharedArrayBuffer.prototype` 与 `SharedArrayBuffer` 构造器:经 /// `make_named_pair` 成对构造,方法/访问器由绑定层安装。 pub shared_array_buffer_proto: P, pub shared_array_buffer_constructor: P, /// `Atomics` 全局纯对象(非函数):10 个原子方法由绑定层安装, /// `@@toStringTag` 与原型链在 `wire_builtin_world_links` 落到 Object.prototype。 pub atomics_object: P, pub data_view_constructor: P, pub data_view_proto: P, pub typed_array_proto: P, pub typed_array_constructor: P, pub int8array_constructor: P, pub int8array_proto: P, pub uint8array_constructor: P, pub uint8array_proto: P, pub uint8clampedarray_constructor: P, pub uint8clampedarray_proto: P, pub int16array_constructor: P, pub int16array_proto: P, pub uint16array_constructor: P, pub uint16array_proto: P, pub int32array_constructor: P, pub int32array_proto: P, pub uint32array_constructor: P, pub uint32array_proto: P, pub float32array_constructor: P, pub float32array_proto: P, pub float64array_constructor: P, pub float64array_proto: P, pub bigint64array_constructor: P, pub bigint64array_proto: P, pub biguint64array_constructor: P, pub biguint64array_proto: P, pub temporal_object: P, pub temporal_now_object: P, pub instant_constructor: P, pub instant_proto: P, pub plain_date_constructor: P, pub plain_date_proto: P, pub plain_time_constructor: P, pub plain_time_proto: P, pub duration_constructor: P, pub duration_proto: P, pub zoned_date_time_constructor: P, pub zoned_date_time_proto: P, pub plain_date_time_constructor: P, pub plain_date_time_proto: P, pub plain_month_day_constructor: P, pub plain_month_day_proto: P, pub plain_year_month_constructor: P, pub plain_year_month_proto: P, pub bigint_constructor: P, pub bigint_proto: P, /// `%IteratorPrototype%`:各集合迭代器原型的公共祖先,持有 `@@iterator`(返回自身)。 pub iterator_proto: P, /// `%ArrayIteratorPrototype%`:Array 与 TypedArray 迭代器共享(`next` 挂其上)。 pub array_iterator_proto: P, /// `%MapIteratorPrototype%`:Map 的 values/keys/entries 迭代器共享。 pub map_iterator_proto: P, /// `%SetIteratorPrototype%`:Set 的 values/keys/entries 迭代器共享。 pub set_iterator_proto: P, /// `%StringIteratorPrototype%`:`String.prototype[@@iterator]` 返回的迭代器。 pub string_iterator_proto: P, /// `String.prototype[@@iterator]` 默认迭代器函数对象的裸指针,绑定层安装方法时 /// 捕获写入。wrapper 本体归 `leaked_objects` 释放登记表所有,session 收尾统一释放。 /// /// 字符串迭代器协议判定(判断某函数是否就是该默认迭代器)按指针与 /// 本值比较:默认迭代器正是 @@iterator 槽存储的值本身(槽值与迭代器函数 /// 同一对象),无法像集合迭代器那样复用"与迭代器原型槽比较"的判法, /// 故独立存一份指针。`Cell` 供绑定层经 `&Arc` 共享引用写入。 pub string_default_iterator: std::cell::Cell<*const JsObject>, /// `%ThrowTypeError%` 函数对象裸指针:Function.prototype 的 caller/arguments /// 受限访问器与 unmapped arguments.callee 访问器共享同一对象(跨形态恒等)。 /// 绑定层安装访问器时写入,运行期创建 arguments 对象时读取;wrapper 本体归 /// `leaked_objects` 释放登记表所有,session 收尾统一释放。 pub throw_type_error: std::cell::Cell<*const JsObject>, /// `%RegExpStringIteratorPrototype%`:matchAll 返回的迭代器。 pub regexp_string_iterator_proto: P, /// `%AsyncIteratorPrototype%`:异步迭代器原型(链到 Object.prototype), /// 持有 `@@asyncIterator`/`@@asyncDispose`;`%AsyncGeneratorPrototype%` 链到它。 pub async_iterator_proto: P, /// `%IteratorHelperPrototype%`:Iterator helpers 结果对象的共享原型,链到 %IteratorPrototype%。 pub iterator_helper_proto: P, /// `DisposableStack.prototype`:同步资源栈原型(链到 Object.prototype), /// 方法/别名/@@toStringTag 由绑定层安装。 pub disposable_stack_proto: P, /// `AsyncDisposableStack.prototype`:异步资源栈原型(形状与同步栈一致)。 pub async_disposable_stack_proto: P, pub stub_objects: Vec>, pub console_object: P, /// `MessagePort.prototype` 槽位:纯内核接线占位(空对象),真实原型本体 /// 与构造器对由绑定层后续任务填充;脏家族位为 `message_channel`。 pub message_port_proto: P, /// `MessageChannel` 构造器槽位:纯内核接线占位(空对象),真实构造器 /// 本体由绑定层后续任务填充。 pub message_channel_constructor: P, /// `BroadcastChannel.prototype` 槽位:纯内核接线占位(空对象),真实 /// 原型本体与构造器对由绑定层后续任务填充;脏家族位为 `broadcast_channel`。 pub broadcast_channel_proto: P, /// `BroadcastChannel` 构造器槽位:纯内核接线占位(空对象),真实构造器 /// 本体由绑定层后续任务填充。 pub broadcast_channel_constructor: P, /// `Event.prototype` 槽位:纯内核接线占位(空对象),真实原型本体 /// 与构造器对由绑定层后续任务填充;脏家族位为 `event`。 pub event_proto: P, /// `Event` 构造器槽位:纯内核接线占位(空对象),真实构造器本体由 /// 绑定层后续任务填充。 pub event_constructor: P, /// `MessageEvent.prototype` 槽位:纯内核接线占位(空对象),真实原型 /// 本体与构造器对由绑定层后续任务填充。 pub message_event_proto: P, /// `MessageEvent` 构造器槽位:纯内核接线占位(空对象),真实构造器 /// 本体由绑定层后续任务填充。 pub message_event_constructor: P, /// `ErrorEvent.prototype` 槽位:纯内核接线占位(空对象),真实原型 /// 本体与构造器对由绑定层后续任务填充。 pub error_event_proto: P, /// `ErrorEvent` 构造器槽位:纯内核接线占位(空对象),真实构造器 /// 本体由绑定层后续任务填充。 pub error_event_constructor: P, /// `CustomEvent.prototype` 槽位:纯内核接线占位(空对象),真实原型 /// 本体与构造器对由绑定层后续任务填充。 pub custom_event_proto: P, /// `CustomEvent` 构造器槽位:纯内核接线占位(空对象),真实构造器 /// 本体由绑定层后续任务填充。 pub custom_event_constructor: P, /// `EventTarget.prototype` 槽位:纯内核接线占位(空对象),真实原型 /// 本体由绑定层后续任务填充(EventTarget 无构造器,仅暴露原型)。 pub event_target_proto: P, /// 释放登记表(`Box::into_raw` 对象的清单,session 收尾统一释放,非内存泄漏): /// 绑定层经 `Box::into_raw` 持有的函数/宿主对象(方法 wrapper、访问器、 /// 错误构造器、Reflect/Iterator、内建原型构造器、`$262` 宿主等)。 /// 这些对象本体在堆上、不属任何 arena,session 收尾时按表统一释放 /// (属性区 + 本体);选择性重建替换 world 时本表整体并入新 world /// (`inherit_leaked_objects`),仍由 session 收尾统一释放,不悬垂、不双放。 /// /// 可复用 native 函数 wrapper 带复用键([`FnWrapperKey`]):选择性重建 /// 重绑按键命中旧 wrapper,迁移到重建 P 对象槽位,登记表跨重建不增长。 /// /// 每条目另记洁净世代基线:wrapper 本体被用户写会递增其世代(写的是 /// wrapper 自身而非所属 P 对象),基线偏离即视为污染,选择性重建据此 /// 失效复用键、强制新建 wrapper。 pub(crate) leaked_objects: std::cell::RefCell>, } /// native 函数 wrapper 的复用键:(目标家族,目标站点标签,属性槽位键,wrapper 名)。 /// /// `family` 字段:目标对象属于本 world 固定 P 字段时取该字段在 `all_p_fields` /// 枚举中的下标加 1(字段序跨重建不变),否则为 0;`label` 字段:非 P 目标 /// 的绑定站点标识(站点名的 intern 键),用于区分 Generator/AsyncGenerator /// 原型上同名的 next/return/throw 槽。选择性重建重绑按键命中旧 wrapper 并 /// 迁移,避免跨重建无界累积登记表;同键重复登记意味着复用键设计缺陷(debug 断言)。 #[derive(Clone, Copy, PartialEq, Eq)] pub struct FnWrapperKey { family: u16, label: u32, slot: u32, name: u32, } impl FnWrapperKey { /// 按(family, label, slot, name)四元组构造复用键。 pub const fn new(family: u16, label: u32, slot: u32, name: u32) -> Self { Self { family, label, slot, name } } /// 是否属于 VM 内建原型(generator/async)的站点标签键。 /// /// 这些站点绑定在 VM 自有的原型 Box 对象上(非本 world 固定 P 字段), /// 故家族为 0 而站点标签非零。其 wrapper 由 VM 内建原型持有,选择性重建 /// 不可清键失效:清键会让原型仍引用旧 wrapper 而另建新 wrapper,留下 /// 悬垂引用。普通方法 wrapper(P 目标家族非零,或 global/构造器等无标签 /// 站点)不在此列,正常参与失效。 const fn is_vm_intrinsic_site(&self) -> bool { self.family == 0 && self.label != 0 } } /// 登记表条目:对象指针 + 可选复用键(`None` = 不可复用对象)。 /// /// `clean_generation` 是登记(或重建收尾经 /// [`BuiltinWorld::refresh_leaked_object_baselines`] 刷新)时的对象世代; /// 属性写使其偏离即视为 wrapper 本体污染。`key = None` 除不可复用对象外, /// 还可能是被失效的旧可复用 wrapper——复用键已清空、不再参与重建复用,但 /// 本体仍可能被旧引用持有,须滞留至 session 收尾(`teardown_heap_data`) /// 统一释放,不得提前释放。 pub(crate) struct LeakedSlot { pub(crate) ptr: *mut JsObject, key: Option, clean_generation: u32, } impl BuiltinWorld { /// 把 Function.prototype 包装为 `JsValue` 返回,供方法 wrapper 取原型。 pub fn fn_proto_val(&self) -> JsValue { JsValue::from_js_object(self.function_proto.as_ptr() as *mut JsObject) } /// 登记一个绑定层经 `Box::into_raw` 持有的函数/宿主对象(不可复用对象), /// 供 [`Self::teardown_heap_data`] 在 session 收尾时统一释放;可复用 /// native 函数 wrapper 走 [`Self::track_fn_wrapper`]。 /// /// 登记点同时读取对象世代作洁净基线(属性写使其偏离即视为本体污染)。 #[expect(clippy::not_unsafe_ptr_arg_deref)] // 指针由绑定层保证存活,此函数仅读世代、不转移所有权 pub fn track_leaked_object(&self, obj_ptr: *mut JsObject) { // SAFETY: obj_ptr 是刚 Box::into_raw 的存活对象,登记点读取其世代作洁净基线。 let clean_generation = unsafe { (*obj_ptr).generation() }; self.leaked_objects.borrow_mut().push(LeakedSlot { ptr: obj_ptr, key: None, clean_generation, }); } /// 登记一个可复用 native 函数 wrapper(带复用键),随登记表在 session /// 收尾统一释放。 /// /// # 边界与前提 /// 登记表已有同键旧槽且其 native 函数指针与新 wrapper 不同时,视为 /// wrapper 函数指针变更(如构造器由占位实现换为真身):旧 wrapper /// 不可再复用,清其复用键。 /// /// # 副作用 /// - 登记表追加新条目。 /// - 同键旧槽函数指针不同时,清空其复用键。 /// /// # 注意事项 /// 同键且同函数指针的重复登记意味着复用键设计缺陷(同家族槽位对应 /// 两个不同 wrapper 对象)——debug 断言立即失败。 #[expect(clippy::not_unsafe_ptr_arg_deref)] // 指针由绑定层保证存活,此函数仅读世代、不转移所有权 pub fn track_fn_wrapper(&self, obj_ptr: *mut JsObject, key: FnWrapperKey) { // SAFETY: obj_ptr 是刚装好 length/name 的存活 wrapper,读取其 native 函数指针。 let new_fn_ptr = unsafe { (*obj_ptr).native_fn().map(|p| p.0) }; // 同键旧槽函数指针不同:wrapper 函数指针已变更,旧 wrapper 不再 // 参与复用,清键后本体滞留至 session 收尾统一释放(与失效语义一致)。 for slot in self.leaked_objects.borrow_mut().iter_mut() { if slot.key != Some(key) { continue; } // SAFETY: 登记表指针 session 存活期内有效。 let old_fn_ptr = unsafe { (*slot.ptr).native_fn().map(|p| p.0) }; if old_fn_ptr != new_fn_ptr { slot.key = None; } } debug_assert!( !self.leaked_objects.borrow().iter().any(|s| s.key == Some(key)), "同键 native 函数 wrapper 重复登记" ); // SAFETY: obj_ptr 是刚装好 length/name 的存活 wrapper,此时世代即洁净基线。 let clean_generation = unsafe { (*obj_ptr).generation() }; self.leaked_objects.borrow_mut().push(LeakedSlot { ptr: obj_ptr, key: Some(key), clean_generation, }); } /// 查找复用键相同且 native 函数/参数个数匹配的既有 wrapper(选择性重建 /// 重绑的复用入口)。 /// /// # 边界与前提 /// 登记表指针 session 存活期内有效(full_reset 是无存活 VM 的时刻, /// 无并发读者);native 函数与参数个数一并校验,防绑定表漂移时误换旧实现。 pub fn find_fn_wrapper( &self, key: FnWrapperKey, native_fn_ptr: NativeFnPtr, arg_count: u8, ) -> Option<*mut JsObject> { self.leaked_objects .borrow() .iter() .find(|s| { s.key == Some(key) && { // SAFETY: 登记表指针 session 存活期内有效。 let obj = unsafe { &*s.ptr }; obj.native_fn().map(|p| p.0) == Some(native_fn_ptr.0) && obj.native_arg_count() == arg_count } }) .map(|s| s.ptr) } /// 释放登记表中是否存在世代偏离洁净基线的条目(wrapper 本体被写)。 /// /// # 边界与前提 /// 登记表指针 session 存活期内有效。不可复用宿主对象(`Reflect`/`Iterator` /// /`$262`)与其上方法 wrapper 一并纳入检测——它们同样可被用户写,且其 /// 所属 global 需要随重建整批换新。 pub fn has_dirty_leaked_objects(&self) -> bool { self.leaked_objects.borrow().iter().any(|slot| { // SAFETY: 登记表指针 session 存活期内有效。 unsafe { (*slot.ptr).generation() != slot.clean_generation } }) } /// 使偏离洁净基线的可复用 wrapper 复用键失效(清为 `None`):重建时 /// [`Self::find_fn_wrapper`] 落空、改走新建分支,旧 wrapper 不再被复用回 /// 新原型槽。 /// /// # 注意事项 /// - 只清键不释放本体:旧 P 对象在重建收尾前仍可能引用它,本体随 session /// 收尾统一释放;提前释放会双放。 /// - keyless 单例本就不可复用,无需清键;VM 内建 generator/async 原型 /// wrapper([`FnWrapperKey::is_vm_intrinsic_site`])由 VM 内建原型持有, /// 清键会遗留悬垂引用,同样跳过。 pub fn invalidate_dirty_leaked_objects(&self) { for slot in self.leaked_objects.borrow_mut().iter_mut() { let Some(key) = slot.key else { continue }; if key.is_vm_intrinsic_site() { continue; } // SAFETY: 登记表指针 session 存活期内有效。 if unsafe { (*slot.ptr).generation() != slot.clean_generation } { slot.key = None; } } } /// 以各条目当前世代重刷洁净基线。 /// /// # 副作用 /// 覆盖全部条目的 `clean_generation`;由 [`crate::kernel::KernelSession::record_snapshot`] /// 在重建收尾(原型槽重指与重绑内部写完成)后调用,避免这些内部写在下一次 /// 判脏时被误报为 wrapper 本体污染。 pub fn refresh_leaked_object_baselines(&self) { for slot in self.leaked_objects.borrow_mut().iter_mut() { // SAFETY: 登记表指针 session 存活期内有效。 slot.clean_generation = unsafe { (*slot.ptr).generation() }; } } /// wrapper 复用键的目标家族标签:目标对象是本 world 固定 P 字段时返回 /// 其枚举下标 + 1(`all_p_fields` 顺序跨重建不变),非 P 目标返回 0。 pub fn wrapper_family_of(&self, obj: *const JsObject) -> u16 { for (i, p) in self.all_p_fields().iter().enumerate() { if std::ptr::eq(p.as_ptr(), obj) { return (i + 1) as u16; } } 0 } /// 返回释放登记表的对象数;选择性重建继承登记表时,宿主基准用例以该数 /// 为采样点验证登记表无界增长。 pub fn leaked_object_count(&self) -> usize { self.leaked_objects.borrow().len() } /// 选择性重建时把旧 world 的登记表整体并入新 world(见 /// [`crate::kernel::KernelSession::selective_reset`])。 pub fn inherit_leaked_objects(&self, from: &BuiltinWorld) { self.leaked_objects.borrow_mut().append(&mut from.leaked_objects.borrow_mut()); } /// 枚举本 world 全部固定 P 对象字段(按结构体字段序,含迭代器原型族、 /// stub 之外的全部命名空间对象与 console)。 /// /// # 注意事项 /// session 收尾(`teardown_heap_data`)与选择性重建收尾(`retire_replaced`) /// 的 P 字段枚举唯一入口:`BuiltinWorld` 新增 P 字段须在此同步补一行,否则 /// 收尾时该字段属性区无法释放、重建原型槽改写/释放漏掉该字段。 pub(crate) fn all_p_fields(&self) -> [&P; 108] { [ &self.object_proto, &self.array_proto, &self.function_proto, &self.string_proto, &self.number_proto, &self.boolean_proto, &self.error_proto, &self.symbol_proto, &self.object_constructor, &self.array_constructor, &self.function_constructor, &self.string_constructor, &self.number_constructor, &self.boolean_constructor, &self.error_constructor, &self.symbol_constructor, &self.type_error_proto, &self.reference_error_proto, &self.range_error_proto, &self.syntax_error_proto, &self.uri_error_proto, &self.eval_error_proto, &self.suppressed_error_proto, &self.math_object, &self.json_object, &self.date_constructor, &self.date_proto, &self.set_constructor, &self.set_proto, &self.map_constructor, &self.map_proto, &self.regexp_constructor, &self.regexp_proto, &self.array_buffer_constructor, &self.array_buffer_proto, &self.shared_array_buffer_proto, &self.shared_array_buffer_constructor, &self.atomics_object, &self.data_view_constructor, &self.data_view_proto, &self.typed_array_proto, &self.typed_array_constructor, &self.int8array_constructor, &self.int8array_proto, &self.uint8array_constructor, &self.uint8array_proto, &self.uint8clampedarray_constructor, &self.uint8clampedarray_proto, &self.int16array_constructor, &self.int16array_proto, &self.uint16array_constructor, &self.uint16array_proto, &self.int32array_constructor, &self.int32array_proto, &self.uint32array_constructor, &self.uint32array_proto, &self.float32array_constructor, &self.float32array_proto, &self.float64array_constructor, &self.float64array_proto, &self.bigint64array_constructor, &self.bigint64array_proto, &self.biguint64array_constructor, &self.biguint64array_proto, &self.temporal_object, &self.temporal_now_object, &self.instant_constructor, &self.instant_proto, &self.plain_date_constructor, &self.plain_date_proto, &self.plain_time_constructor, &self.plain_time_proto, &self.duration_constructor, &self.duration_proto, &self.zoned_date_time_constructor, &self.zoned_date_time_proto, &self.plain_date_time_constructor, &self.plain_date_time_proto, &self.plain_month_day_constructor, &self.plain_month_day_proto, &self.plain_year_month_constructor, &self.plain_year_month_proto, &self.bigint_constructor, &self.bigint_proto, &self.iterator_proto, &self.array_iterator_proto, &self.map_iterator_proto, &self.set_iterator_proto, &self.string_iterator_proto, &self.regexp_string_iterator_proto, &self.async_iterator_proto, &self.iterator_helper_proto, &self.disposable_stack_proto, &self.async_disposable_stack_proto, &self.console_object, &self.message_port_proto, &self.message_channel_constructor, &self.broadcast_channel_proto, &self.broadcast_channel_constructor, &self.event_proto, &self.event_constructor, &self.message_event_proto, &self.message_event_constructor, &self.error_event_proto, &self.error_event_constructor, &self.custom_event_proto, &self.custom_event_constructor, &self.event_target_proto, ] } /// 释放本 world 拥有的全部手工堆数据。 /// /// # 释放范围 /// 1. `Box::into_raw` 持有的函数/宿主对象(登记表):先释放其堆外属性区, /// 再释放对象本体; /// 2. 全部 P 对象字段(`all_p_fields` 枚举 + stub 族)的堆外属性区—— /// 对象本体随 Arc 引用归零释放。 /// /// # 注意事项 /// 仅由 session 收尾调用(`KernelSession` 的 `Drop` 与 session 替换前), /// 幂等:登记表按值取走,属性区释放后置空。选择性重建不走本路径: /// 登记表整体并入新 world(`inherit_leaked_objects`),仍由 /// session 收尾统一释放;被替换家族的旧 P 字段属性区在重建收尾 /// (`retire_replaced`)恰好释放一次,与本路径对象集不相交,不双放。 pub fn teardown_heap_data(&self) { for slot in self.leaked_objects.borrow_mut().drain(..) { let ptr = slot.ptr; if ptr.is_null() { continue; } // SAFETY: ptr 是绑定层 Box::into_raw 产物,session 存活期内有效, // 此处恰好释放一次(登记表按值取走,重入时表已空)。 unsafe { let obj = &mut *ptr; obj.release_raw_heap(); drop(Box::from_raw(ptr)); } } for p in self.all_p_fields() { // SAFETY: p 是本 world 的 P 对象,属性区仅在此释放并置空(幂等)。 unsafe { (&mut *p.as_mut_ptr()).release_raw_heap(); } } for p in &self.stub_objects { // SAFETY: 同上,stub 对象归本 world 所有。 unsafe { (&mut *p.as_mut_ptr()).release_raw_heap(); } } } /// 按 [`BuiltinId`] 取对应内置对象的指针引用。 pub fn get_by_id(&self, id: BuiltinId) -> &P { match id { BuiltinId::ObjectProto => &self.object_proto, BuiltinId::ArrayProto => &self.array_proto, BuiltinId::FunctionProto => &self.function_proto, BuiltinId::StringProto => &self.string_proto, BuiltinId::NumberProto => &self.number_proto, BuiltinId::BooleanProto => &self.boolean_proto, BuiltinId::ErrorProto => &self.error_proto, BuiltinId::SymbolProto => &self.symbol_proto, BuiltinId::ObjectConstructor => &self.object_constructor, BuiltinId::ArrayConstructor => &self.array_constructor, BuiltinId::FunctionConstructor => &self.function_constructor, BuiltinId::StringConstructor => &self.string_constructor, BuiltinId::NumberConstructor => &self.number_constructor, BuiltinId::BooleanConstructor => &self.boolean_constructor, BuiltinId::ErrorConstructor => &self.error_constructor, BuiltinId::SymbolConstructor => &self.symbol_constructor, BuiltinId::TypeErrorProto => &self.type_error_proto, BuiltinId::ReferenceErrorProto => &self.reference_error_proto, BuiltinId::RangeErrorProto => &self.range_error_proto, BuiltinId::SyntaxErrorProto => &self.syntax_error_proto, BuiltinId::UriErrorProto => &self.uri_error_proto, BuiltinId::EvalErrorProto => &self.eval_error_proto, BuiltinId::SuppressedErrorProto => &self.suppressed_error_proto, BuiltinId::MathObject => &self.math_object, BuiltinId::JsonObject => &self.json_object, BuiltinId::DateConstructor => &self.date_constructor, BuiltinId::DateProto => &self.date_proto, BuiltinId::SetConstructor => &self.set_constructor, BuiltinId::SetProto => &self.set_proto, BuiltinId::MapConstructor => &self.map_constructor, BuiltinId::MapProto => &self.map_proto, BuiltinId::RegExpConstructor => &self.regexp_constructor, BuiltinId::RegExpProto => &self.regexp_proto, BuiltinId::ArrayBufferConstructor => &self.array_buffer_constructor, BuiltinId::ArrayBufferProto => &self.array_buffer_proto, BuiltinId::SharedArrayBufferProto => &self.shared_array_buffer_proto, BuiltinId::SharedArrayBufferConstructor => &self.shared_array_buffer_constructor, BuiltinId::AtomicsObject => &self.atomics_object, BuiltinId::DataViewConstructor => &self.data_view_constructor, BuiltinId::DataViewProto => &self.data_view_proto, BuiltinId::TypedArrayProto => &self.typed_array_proto, BuiltinId::Int8ArrayConstructor => &self.int8array_constructor, BuiltinId::Int8ArrayProto => &self.int8array_proto, BuiltinId::Uint8ArrayConstructor => &self.uint8array_constructor, BuiltinId::Uint8ArrayProto => &self.uint8array_proto, BuiltinId::Uint8ClampedArrayConstructor => &self.uint8clampedarray_constructor, BuiltinId::Uint8ClampedArrayProto => &self.uint8clampedarray_proto, BuiltinId::Int16ArrayConstructor => &self.int16array_constructor, BuiltinId::Int16ArrayProto => &self.int16array_proto, BuiltinId::Uint16ArrayConstructor => &self.uint16array_constructor, BuiltinId::Uint16ArrayProto => &self.uint16array_proto, BuiltinId::Int32ArrayConstructor => &self.int32array_constructor, BuiltinId::Int32ArrayProto => &self.int32array_proto, BuiltinId::Uint32ArrayConstructor => &self.uint32array_constructor, BuiltinId::Uint32ArrayProto => &self.uint32array_proto, BuiltinId::Float32ArrayConstructor => &self.float32array_constructor, BuiltinId::Float32ArrayProto => &self.float32array_proto, BuiltinId::Float64ArrayConstructor => &self.float64array_constructor, BuiltinId::Float64ArrayProto => &self.float64array_proto, BuiltinId::BigInt64ArrayConstructor => &self.bigint64array_constructor, BuiltinId::BigInt64ArrayProto => &self.bigint64array_proto, BuiltinId::BigUint64ArrayConstructor => &self.biguint64array_constructor, BuiltinId::BigUint64ArrayProto => &self.biguint64array_proto, BuiltinId::TemporalObject => &self.temporal_object, BuiltinId::TemporalNowObject => &self.temporal_now_object, BuiltinId::InstantConstructor => &self.instant_constructor, BuiltinId::InstantProto => &self.instant_proto, BuiltinId::PlainDateConstructor => &self.plain_date_constructor, BuiltinId::PlainDateProto => &self.plain_date_proto, BuiltinId::PlainTimeConstructor => &self.plain_time_constructor, BuiltinId::PlainTimeProto => &self.plain_time_proto, BuiltinId::DurationConstructor => &self.duration_constructor, BuiltinId::DurationProto => &self.duration_proto, BuiltinId::ZonedDateTimeConstructor => &self.zoned_date_time_constructor, BuiltinId::ZonedDateTimeProto => &self.zoned_date_time_proto, BuiltinId::PlainDateTimeConstructor => &self.plain_date_time_constructor, BuiltinId::PlainDateTimeProto => &self.plain_date_time_proto, BuiltinId::PlainMonthDayConstructor => &self.plain_month_day_constructor, BuiltinId::PlainMonthDayProto => &self.plain_month_day_proto, BuiltinId::PlainYearMonthConstructor => &self.plain_year_month_constructor, BuiltinId::PlainYearMonthProto => &self.plain_year_month_proto, BuiltinId::BigIntConstructor => &self.bigint_constructor, BuiltinId::BigIntProto => &self.bigint_proto, BuiltinId::Console => &self.console_object, BuiltinId::AsyncIteratorProto => &self.async_iterator_proto, BuiltinId::MessagePortProto => &self.message_port_proto, BuiltinId::MessageChannelCtor => &self.message_channel_constructor, BuiltinId::BroadcastChannelProto => &self.broadcast_channel_proto, BuiltinId::BroadcastChannelCtor => &self.broadcast_channel_constructor, BuiltinId::EventProto => &self.event_proto, BuiltinId::EventCtor => &self.event_constructor, BuiltinId::MessageEventProto => &self.message_event_proto, BuiltinId::MessageEventCtor => &self.message_event_constructor, BuiltinId::ErrorEventProto => &self.error_event_proto, BuiltinId::ErrorEventCtor => &self.error_event_constructor, BuiltinId::CustomEventProto => &self.custom_event_proto, BuiltinId::CustomEventCtor => &self.custom_event_constructor, BuiltinId::EventTargetProto => &self.event_target_proto, } } } #[cfg(test)] mod tests { use super::*; use crate::shape_forge::EMPTY_SHAPE_ID; /// 选择性重建的释放 + 原型槽改写面动态自测:被替换旧 P 对象属性区恰好释放 /// 一次(置空可断言,含经 `mem::forget` 抬升 Arc 计数的 Function 家族属性区、 /// 本体永久保留),保留字段指针不变且 proto 槽改写到新指针,登记表并入新 world。 #[test] fn selective_reset_releases_replaced_family_heap() { use crate::kernel::{KernelConfig, KernelCore, KernelSession}; let core = KernelCore::new(KernelConfig::minimal()); let mut session = KernelSession::new(&core); // 持有旧 world Arc:被替换对象本体在断言期仍可读(属性区指针可检查)。 let old_world = std::sync::Arc::clone(&session.builtin_world); let array_proto = old_world.array_proto.as_ptr() as *mut JsObject; let object_proto = old_world.object_proto.as_ptr() as *mut JsObject; let fn_proto = old_world.function_proto.as_ptr() as *mut JsObject; // 旧原型各造一个命名属性区(绑定安装属性区后的状态);登记表放一个 // proto 槽指向旧 fn_proto 的 wrapper(槽值绑定时写入)。 let wrapper = Box::into_raw(Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))); unsafe { (*wrapper).set_proto(JsValue::from_js_object(fn_proto)).ok(); (&mut *array_proto).ensure_hash_props().push(JsValue::int(1)); (&mut *object_proto).ensure_hash_props().push(JsValue::int(2)); (&mut *fn_proto).ensure_hash_props().push(JsValue::int(3)); } old_world.track_leaked_object(wrapper); unsafe { (&mut *array_proto).bump_generation(); (&mut *fn_proto).bump_generation(); } let dirty = session.selective_reset(&core); assert!(dirty.array); assert!(dirty.function); // 被替换家族(array):四处堆外属性区已释放置空。 let old_array = unsafe { &*array_proto }; assert!(old_array.hash_props_raw().is_null()); assert!(old_array.array_elements_raw().is_null()); assert!(old_array.array_elements_meta_raw().is_null()); assert!(old_array.prop_meta_raw().is_null()); // 经 `mem::forget` 抬升 Arc 计数的 Function 家族对象对:本体永久保留(仍可读), // 属性区于原型槽改写完成后同样释放——改写后旧对象无读者。 assert!(unsafe { &*fn_proto }.hash_props_raw().is_null()); // 未脏家族(object):沿用同一对象,字段指针与属性区均不受影响。 assert!(!unsafe { &*object_proto }.hash_props_raw().is_null()); assert!(std::ptr::eq(object_proto, session.builtin_world.object_proto.as_ptr() as *mut JsObject)); // 原型槽改写:保留字段(object_constructor)与保留 wrapper 的 proto 槽 // 均改写到新 fn_proto,无残留旧指针。 let new_fn_proto = session.builtin_world.function_proto.as_ptr() as *mut JsObject; let object_ctor = old_world.object_constructor.as_ptr() as *mut JsObject; assert!(std::ptr::eq( object_ctor, session.builtin_world.object_constructor.as_ptr() as *mut JsObject )); assert!(std::ptr::eq(unsafe { (*object_ctor).proto().as_js_object_ptr() }, new_fn_proto)); assert!(std::ptr::eq(unsafe { (*wrapper).proto().as_js_object_ptr() }, new_fn_proto)); // 登记表并入新 world:保留 wrapper 仍须由 session 收尾统一释放。 assert!(session.builtin_world.leaked_objects.borrow().iter().any(|s| s.ptr == wrapper)); } /// SAB 家族脏线冒烟:仅 SAB 对世代漂移 → 选择性重置只判 SAB 家族脏, /// ArrayBuffer 家族指针原样保留(钉 dirty_since_snapshot 家族线接线)。 #[test] fn sab_family_selective_rebuild() { use crate::kernel::{KernelConfig, KernelCore, KernelSession}; let core = KernelCore::new(KernelConfig::minimal()); let mut session = KernelSession::new(&core); let old_world = std::sync::Arc::clone(&session.builtin_world); let sab_proto = old_world.shared_array_buffer_proto.as_ptr() as *mut JsObject; let sab_ctor = old_world.shared_array_buffer_constructor.as_ptr() as *mut JsObject; let ab_proto = old_world.array_buffer_proto.as_ptr() as *mut JsObject; unsafe { (&mut *sab_proto).bump_generation(); (&mut *sab_ctor).bump_generation(); } let dirty = session.selective_reset(&core); assert!(dirty.shared_array_buffer); assert!(!dirty.array_buffer); assert!(!dirty.data_view); // 脏家族换新对象对,未脏家族指针原样保留。 assert!(!std::ptr::eq( sab_proto, session.builtin_world.shared_array_buffer_proto.as_ptr() as *mut JsObject )); assert!(!std::ptr::eq( sab_ctor, session.builtin_world.shared_array_buffer_constructor.as_ptr() as *mut JsObject )); assert!(std::ptr::eq(ab_proto, session.builtin_world.array_buffer_proto.as_ptr() as *mut JsObject)); } /// Atomics 家族脏线冒烟:仅 Atomics 单例世代漂移 → 选择性重置只判 Atomics /// 家族脏,ArrayBuffer 家族指针原样保留(钉 dirty_since_snapshot 家族线接线)。 #[test] fn atomics_family_selective_rebuild() { use crate::kernel::{KernelConfig, KernelCore, KernelSession}; let core = KernelCore::new(KernelConfig::minimal()); let mut session = KernelSession::new(&core); let old_world = std::sync::Arc::clone(&session.builtin_world); let atomics = old_world.atomics_object.as_ptr() as *mut JsObject; let ab_proto = old_world.array_buffer_proto.as_ptr() as *mut JsObject; unsafe { (&mut *atomics).bump_generation(); } let dirty = session.selective_reset(&core); assert!(dirty.atomics); assert!(!dirty.array_buffer); assert!(!dirty.shared_array_buffer); // 脏家族换新对象,未脏家族指针原样保留。 assert!(!std::ptr::eq(atomics, session.builtin_world.atomics_object.as_ptr() as *mut JsObject)); assert!(std::ptr::eq(ab_proto, session.builtin_world.array_buffer_proto.as_ptr() as *mut JsObject)); } }