refactor: builtin 注册表与结构体拆出独立 world 模块

This commit is contained in:
2026-09-16 04:18:56 +00:00
parent f08e35c5f2
commit 63a1e84956
2 changed files with 571 additions and 566 deletions
+2 -566
View File
@@ -1,17 +1,13 @@
//! 内置对象世界:持有并构造全部内置原型、构造器与全局单例(Math/JSON),
//! 支持按脏标记重建,并提供绑定层的 native 方法指针表。
use oxide_types::mem::P;
use oxide_types::object::{JsObject, NativeFnPtr};
use oxide_types::value::JsValue;
use crate::kernel::BuiltinId;
mod bind;
mod construct;
mod methods;
mod rebuild;
mod world;
pub use methods::{ArrayMethods, ErrorMethods, FunctionMethods, ObjectMethods, RegExpMethods, StringMethods};
pub use world::{BuiltinWorld, FnWrapperKey};
#[macro_export]
macro_rules! bind_method {
@@ -56,563 +52,3 @@ macro_rules! bind_methods_static {
})*
};
}
/// 全部内置对象(原型、构造器、全局单例 Math/JSON、well-known symbol 与 stub 对象)的持有者。
///
/// 每个 session 独立持有自己的 `BuiltinWorld`,保证 session 间内置对象隔离;
/// 由 [`BuiltinWorld::new`] 全量构造,或 [`BuiltinWorld::rebuild_with_dirty`] 按脏标记部分重建。
pub struct BuiltinWorld {
pub object_proto: P<JsObject>,
pub array_proto: P<JsObject>,
pub function_proto: P<JsObject>,
pub string_proto: P<JsObject>,
pub number_proto: P<JsObject>,
pub boolean_proto: P<JsObject>,
pub error_proto: P<JsObject>,
pub symbol_proto: P<JsObject>,
pub object_constructor: P<JsObject>,
pub array_constructor: P<JsObject>,
pub function_constructor: P<JsObject>,
pub string_constructor: P<JsObject>,
pub number_constructor: P<JsObject>,
pub boolean_constructor: P<JsObject>,
pub error_constructor: P<JsObject>,
pub symbol_constructor: P<JsObject>,
pub type_error_proto: P<JsObject>,
pub reference_error_proto: P<JsObject>,
pub range_error_proto: P<JsObject>,
pub syntax_error_proto: P<JsObject>,
pub uri_error_proto: P<JsObject>,
pub eval_error_proto: P<JsObject>,
pub suppressed_error_proto: P<JsObject>,
pub math_object: P<JsObject>,
pub json_object: P<JsObject>,
pub date_constructor: P<JsObject>,
pub date_proto: P<JsObject>,
pub set_constructor: P<JsObject>,
pub set_proto: P<JsObject>,
pub map_constructor: P<JsObject>,
pub map_proto: P<JsObject>,
pub regexp_constructor: P<JsObject>,
pub regexp_proto: P<JsObject>,
pub array_buffer_constructor: P<JsObject>,
pub array_buffer_proto: P<JsObject>,
pub data_view_constructor: P<JsObject>,
pub data_view_proto: P<JsObject>,
pub typed_array_proto: P<JsObject>,
pub typed_array_constructor: P<JsObject>,
pub int8array_constructor: P<JsObject>,
pub int8array_proto: P<JsObject>,
pub uint8array_constructor: P<JsObject>,
pub uint8array_proto: P<JsObject>,
pub uint8clampedarray_constructor: P<JsObject>,
pub uint8clampedarray_proto: P<JsObject>,
pub int16array_constructor: P<JsObject>,
pub int16array_proto: P<JsObject>,
pub uint16array_constructor: P<JsObject>,
pub uint16array_proto: P<JsObject>,
pub int32array_constructor: P<JsObject>,
pub int32array_proto: P<JsObject>,
pub uint32array_constructor: P<JsObject>,
pub uint32array_proto: P<JsObject>,
pub float32array_constructor: P<JsObject>,
pub float32array_proto: P<JsObject>,
pub float64array_constructor: P<JsObject>,
pub float64array_proto: P<JsObject>,
pub bigint64array_constructor: P<JsObject>,
pub bigint64array_proto: P<JsObject>,
pub biguint64array_constructor: P<JsObject>,
pub biguint64array_proto: P<JsObject>,
pub sym_match: P<JsObject>,
pub sym_replace: P<JsObject>,
pub sym_search: P<JsObject>,
pub sym_split: P<JsObject>,
pub sym_iterator: P<JsObject>,
pub sym_to_primitive: P<JsObject>,
pub sym_has_instance: P<JsObject>,
pub sym_match_all: P<JsObject>,
pub sym_async_iterator: P<JsObject>,
pub sym_to_string_tag: P<JsObject>,
pub sym_species: P<JsObject>,
pub sym_async_dispose: P<JsObject>,
pub sym_dispose: P<JsObject>,
pub temporal_object: P<JsObject>,
pub temporal_now_object: P<JsObject>,
pub instant_constructor: P<JsObject>,
pub instant_proto: P<JsObject>,
pub plain_date_constructor: P<JsObject>,
pub plain_date_proto: P<JsObject>,
pub plain_time_constructor: P<JsObject>,
pub plain_time_proto: P<JsObject>,
pub duration_constructor: P<JsObject>,
pub duration_proto: P<JsObject>,
pub zoned_date_time_constructor: P<JsObject>,
pub zoned_date_time_proto: P<JsObject>,
pub plain_date_time_constructor: P<JsObject>,
pub plain_date_time_proto: P<JsObject>,
pub plain_month_day_constructor: P<JsObject>,
pub plain_month_day_proto: P<JsObject>,
pub plain_year_month_constructor: P<JsObject>,
pub plain_year_month_proto: P<JsObject>,
pub bigint_constructor: P<JsObject>,
pub bigint_proto: P<JsObject>,
/// `%IteratorPrototype%`:各集合迭代器原型的公共祖先,持有 `@@iterator`(返回自身)。
pub iterator_proto: P<JsObject>,
/// `%ArrayIteratorPrototype%`:Array 与 TypedArray 迭代器共享(`next` 挂其上)。
pub array_iterator_proto: P<JsObject>,
/// `%MapIteratorPrototype%`:Map 的 values/keys/entries 迭代器共享。
pub map_iterator_proto: P<JsObject>,
/// `%SetIteratorPrototype%`:Set 的 values/keys/entries 迭代器共享。
pub set_iterator_proto: P<JsObject>,
/// `%StringIteratorPrototype%`:String.prototype[@@iterator] 返回的迭代器。
pub string_iterator_proto: P<JsObject>,
/// String.prototype[@@iterator] 默认迭代器函数对象指针(绑定层捕获,原始值指针、
/// 所有权归 wrapper 释放表)。String 臂覆盖判定以此做指针比较:默认迭代器即
/// @@iterator 槽本身(自别名),集合式锚点槽比较不可复用,须独立存指针。
/// `Cell` 供绑定层经 `&Arc<BuiltinWorld>` 共享引用写入。
pub string_default_iterator: std::cell::Cell<*const JsObject>,
/// `%RegExpStringIteratorPrototype%`:matchAll 返回的迭代器。
pub regexp_string_iterator_proto: P<JsObject>,
/// `%IteratorHelperPrototype%`:Iterator helpers 结果对象的共享原型,链到 %IteratorPrototype%。
pub iterator_helper_proto: P<JsObject>,
/// `DisposableStack.prototype`:同步资源栈原型(链到 Object.prototype),
/// 方法/别名/@@toStringTag 由绑定层安装。
pub disposable_stack_proto: P<JsObject>,
/// `AsyncDisposableStack.prototype`:异步资源栈原型(形状与同步栈一致)。
pub async_disposable_stack_proto: P<JsObject>,
pub stub_objects: Vec<P<JsObject>>,
pub console_object: P<JsObject>,
/// 绑定层经 `Box::into_raw` 持有的函数/宿主对象登记表(方法 wrapper、
/// 访问器、错误构造器、Reflect/Iterator、内建原型构造器、`$262` 宿主等)。
/// 这些对象本体在堆上、不属任何 arena,`session` 收尾时按表统一释放
/// (属性区 + 本体);选择性重建换 world 时本表整体并入新 world
/// (`inherit_leaked_objects`),仍由 session 收尾统一释放,不悬垂、不双放。
///
/// 可复用 native 函数 wrapper 带复用键([`FnWrapperKey`]):选择性重建
/// 重绑按键命中前轮旧 wrapper,迁移到重建 P 对象槽位,登记表跨轮不增长。
leaked_objects: std::cell::RefCell<Vec<LeakedSlot>>,
}
/// native 函数 wrapper 的复用键:(目标家族,目标站点标签,属性槽位键,wrapper 名)。
///
/// 家族目标是 [`BuiltinWorld::all_p_fields`] 枚举的稳定下标(重建跨轮不变,
/// 1..=N),此时标签恒 0;非 P 目标(global 对象、Box 自建构造器、宿主对象、
/// VM 内建原型)家族为 0,以绑定站点标签(站点名的 perm 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 {
pub const fn new(family: u16, label: u32, slot: u32, name: u32) -> Self {
Self { family, label, slot, name }
}
}
/// 登记表条目:对象指针 + 可选复用键(`None` = 不可复用对象)。
struct LeakedSlot {
ptr: *mut JsObject,
key: Option<FnWrapperKey>,
}
impl BuiltinWorld {
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`]。
pub fn track_leaked_object(&self, obj_ptr: *mut JsObject) {
self.leaked_objects.borrow_mut().push(LeakedSlot { ptr: obj_ptr, key: None });
}
/// 登记一个可复用 native 函数 wrapper(带复用键),随登记表在 session
/// 收尾统一释放。
///
/// # 注意事项
/// 同键重复登记意味着复用键设计缺陷(同家族槽位对应两个不同 wrapper
/// 对象)——debug 断言立即失败。
pub fn track_fn_wrapper(&self, obj_ptr: *mut JsObject, key: FnWrapperKey) {
debug_assert!(
!self.leaked_objects.borrow().iter().any(|s| s.key == Some(key)),
"同键 native 函数 wrapper 重复登记"
);
self.leaked_objects
.borrow_mut()
.push(LeakedSlot { ptr: obj_ptr, key: Some(key) });
}
/// 查找复用键相同且 native 函数/参数个数匹配的既有 wrapper(选择性重建
/// 重绑的复用入口)。
///
/// # 边界与前提
/// 登记表指针 session 存活期内有效(full_reset 安全点无并发读者);
/// 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 复用键的目标家族标签:目标对象是本 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<JsObject>; 104] {
[
&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.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.sym_match,
&self.sym_replace,
&self.sym_search,
&self.sym_split,
&self.sym_iterator,
&self.sym_to_primitive,
&self.sym_has_instance,
&self.sym_match_all,
&self.sym_async_iterator,
&self.sym_to_string_tag,
&self.sym_species,
&self.sym_async_dispose,
&self.sym_dispose,
&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.iterator_helper_proto,
&self.disposable_stack_proto,
&self.async_disposable_stack_proto,
&self.console_object,
]
}
/// 释放本 world 拥有的全部手工堆数据。
///
/// # 口径
/// 1. `Box::into_raw` 持有的函数/宿主对象(登记表):先释放其堆外属性区,
/// 再释放对象本体;
/// 2. 全部 P 对象字段(`all_p_fields` 枚举 + stub 族)的堆外属性区——
/// 对象本体随 Arc 引用归零释放。
///
/// # 注意事项
/// 仅由 session 收尾调用(`KernelSession` 的 `Drop` 与 session 替换前),
/// 幂等:登记表按值取走,属性区释放后置空。选择性重建(dirty rebuild)
/// 不走本路径:登记表整体并入新 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<JsObject> {
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::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::SymMatch => &self.sym_match,
BuiltinId::SymReplace => &self.sym_replace,
BuiltinId::SymSearch => &self.sym_search,
BuiltinId::SymSplit => &self.sym_split,
BuiltinId::SymIterator => &self.sym_iterator,
BuiltinId::SymToPrimitive => &self.sym_to_primitive,
BuiltinId::SymHasInstance => &self.sym_has_instance,
BuiltinId::SymMatchAll => &self.sym_match_all,
BuiltinId::SymAsyncIterator => &self.sym_async_iterator,
BuiltinId::SymToStringTag => &self.sym_to_string_tag,
BuiltinId::SymSpecies => &self.sym_species,
BuiltinId::SymAsyncDispose => &self.sym_async_dispose,
BuiltinId::SymDispose => &self.sym_dispose,
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,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shape_forge::EMPTY_SHAPE_ID;
/// 选择性重建的释放 + 重指面动态自测:被替换旧 P 对象属性区恰好释放一次
/// (置空可断言,含保活钉住对的属性区、本体钉保留),保留字段指针不变且
/// 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());
// 保活钉住对(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));
}
}
+569
View File
@@ -0,0 +1,569 @@
//! 注册表职责:BuiltinWorld 结构(104 固定 P 字段 + stub 族 + Box::into_raw
//! 登记表)、get_by_id 派发、all_p_fields 104 元组枚举(新增 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、well-known symbol 与 stub 对象)的持有者。
///
/// 每个 session 独立持有自己的 `BuiltinWorld`,保证 session 间内置对象隔离;
/// 由 [`BuiltinWorld::new`] 全量构造,或 [`BuiltinWorld::rebuild_with_dirty`] 按脏标记部分重建。
pub struct BuiltinWorld {
pub object_proto: P<JsObject>,
pub array_proto: P<JsObject>,
pub function_proto: P<JsObject>,
pub string_proto: P<JsObject>,
pub number_proto: P<JsObject>,
pub boolean_proto: P<JsObject>,
pub error_proto: P<JsObject>,
pub symbol_proto: P<JsObject>,
pub object_constructor: P<JsObject>,
pub array_constructor: P<JsObject>,
pub function_constructor: P<JsObject>,
pub string_constructor: P<JsObject>,
pub number_constructor: P<JsObject>,
pub boolean_constructor: P<JsObject>,
pub error_constructor: P<JsObject>,
pub symbol_constructor: P<JsObject>,
pub type_error_proto: P<JsObject>,
pub reference_error_proto: P<JsObject>,
pub range_error_proto: P<JsObject>,
pub syntax_error_proto: P<JsObject>,
pub uri_error_proto: P<JsObject>,
pub eval_error_proto: P<JsObject>,
pub suppressed_error_proto: P<JsObject>,
pub math_object: P<JsObject>,
pub json_object: P<JsObject>,
pub date_constructor: P<JsObject>,
pub date_proto: P<JsObject>,
pub set_constructor: P<JsObject>,
pub set_proto: P<JsObject>,
pub map_constructor: P<JsObject>,
pub map_proto: P<JsObject>,
pub regexp_constructor: P<JsObject>,
pub regexp_proto: P<JsObject>,
pub array_buffer_constructor: P<JsObject>,
pub array_buffer_proto: P<JsObject>,
pub data_view_constructor: P<JsObject>,
pub data_view_proto: P<JsObject>,
pub typed_array_proto: P<JsObject>,
pub typed_array_constructor: P<JsObject>,
pub int8array_constructor: P<JsObject>,
pub int8array_proto: P<JsObject>,
pub uint8array_constructor: P<JsObject>,
pub uint8array_proto: P<JsObject>,
pub uint8clampedarray_constructor: P<JsObject>,
pub uint8clampedarray_proto: P<JsObject>,
pub int16array_constructor: P<JsObject>,
pub int16array_proto: P<JsObject>,
pub uint16array_constructor: P<JsObject>,
pub uint16array_proto: P<JsObject>,
pub int32array_constructor: P<JsObject>,
pub int32array_proto: P<JsObject>,
pub uint32array_constructor: P<JsObject>,
pub uint32array_proto: P<JsObject>,
pub float32array_constructor: P<JsObject>,
pub float32array_proto: P<JsObject>,
pub float64array_constructor: P<JsObject>,
pub float64array_proto: P<JsObject>,
pub bigint64array_constructor: P<JsObject>,
pub bigint64array_proto: P<JsObject>,
pub biguint64array_constructor: P<JsObject>,
pub biguint64array_proto: P<JsObject>,
pub sym_match: P<JsObject>,
pub sym_replace: P<JsObject>,
pub sym_search: P<JsObject>,
pub sym_split: P<JsObject>,
pub sym_iterator: P<JsObject>,
pub sym_to_primitive: P<JsObject>,
pub sym_has_instance: P<JsObject>,
pub sym_match_all: P<JsObject>,
pub sym_async_iterator: P<JsObject>,
pub sym_to_string_tag: P<JsObject>,
pub sym_species: P<JsObject>,
pub sym_async_dispose: P<JsObject>,
pub sym_dispose: P<JsObject>,
pub temporal_object: P<JsObject>,
pub temporal_now_object: P<JsObject>,
pub instant_constructor: P<JsObject>,
pub instant_proto: P<JsObject>,
pub plain_date_constructor: P<JsObject>,
pub plain_date_proto: P<JsObject>,
pub plain_time_constructor: P<JsObject>,
pub plain_time_proto: P<JsObject>,
pub duration_constructor: P<JsObject>,
pub duration_proto: P<JsObject>,
pub zoned_date_time_constructor: P<JsObject>,
pub zoned_date_time_proto: P<JsObject>,
pub plain_date_time_constructor: P<JsObject>,
pub plain_date_time_proto: P<JsObject>,
pub plain_month_day_constructor: P<JsObject>,
pub plain_month_day_proto: P<JsObject>,
pub plain_year_month_constructor: P<JsObject>,
pub plain_year_month_proto: P<JsObject>,
pub bigint_constructor: P<JsObject>,
pub bigint_proto: P<JsObject>,
/// `%IteratorPrototype%`:各集合迭代器原型的公共祖先,持有 `@@iterator`(返回自身)。
pub iterator_proto: P<JsObject>,
/// `%ArrayIteratorPrototype%`:Array 与 TypedArray 迭代器共享(`next` 挂其上)。
pub array_iterator_proto: P<JsObject>,
/// `%MapIteratorPrototype%`:Map 的 values/keys/entries 迭代器共享。
pub map_iterator_proto: P<JsObject>,
/// `%SetIteratorPrototype%`:Set 的 values/keys/entries 迭代器共享。
pub set_iterator_proto: P<JsObject>,
/// `%StringIteratorPrototype%`:String.prototype[@@iterator] 返回的迭代器。
pub string_iterator_proto: P<JsObject>,
/// String.prototype[@@iterator] 默认迭代器函数对象指针(绑定层捕获,原始值指针、
/// 所有权归 wrapper 释放表)。String 臂覆盖判定以此做指针比较:默认迭代器即
/// @@iterator 槽本身(自别名),集合式锚点槽比较不可复用,须独立存指针。
/// `Cell` 供绑定层经 `&Arc<BuiltinWorld>` 共享引用写入。
pub string_default_iterator: std::cell::Cell<*const JsObject>,
/// `%RegExpStringIteratorPrototype%`:matchAll 返回的迭代器。
pub regexp_string_iterator_proto: P<JsObject>,
/// `%IteratorHelperPrototype%`:Iterator helpers 结果对象的共享原型,链到 %IteratorPrototype%。
pub iterator_helper_proto: P<JsObject>,
/// `DisposableStack.prototype`:同步资源栈原型(链到 Object.prototype),
/// 方法/别名/@@toStringTag 由绑定层安装。
pub disposable_stack_proto: P<JsObject>,
/// `AsyncDisposableStack.prototype`:异步资源栈原型(形状与同步栈一致)。
pub async_disposable_stack_proto: P<JsObject>,
pub stub_objects: Vec<P<JsObject>>,
pub console_object: P<JsObject>,
/// 绑定层经 `Box::into_raw` 持有的函数/宿主对象登记表(方法 wrapper、
/// 访问器、错误构造器、Reflect/Iterator、内建原型构造器、`$262` 宿主等)。
/// 这些对象本体在堆上、不属任何 arena,`session` 收尾时按表统一释放
/// (属性区 + 本体);选择性重建换 world 时本表整体并入新 world
/// (`inherit_leaked_objects`),仍由 session 收尾统一释放,不悬垂、不双放。
///
/// 可复用 native 函数 wrapper 带复用键([`FnWrapperKey`]):选择性重建
/// 重绑按键命中前轮旧 wrapper,迁移到重建 P 对象槽位,登记表跨轮不增长。
pub(crate) leaked_objects: std::cell::RefCell<Vec<LeakedSlot>>,
}
/// native 函数 wrapper 的复用键:(目标家族,目标站点标签,属性槽位键,wrapper 名)。
///
/// 家族目标是 [`BuiltinWorld::all_p_fields`] 枚举的稳定下标(重建跨轮不变,
/// 1..=N),此时标签恒 0;非 P 目标(global 对象、Box 自建构造器、宿主对象、
/// VM 内建原型)家族为 0,以绑定站点标签(站点名的 perm 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 {
pub const fn new(family: u16, label: u32, slot: u32, name: u32) -> Self {
Self { family, label, slot, name }
}
}
/// 登记表条目:对象指针 + 可选复用键(`None` = 不可复用对象)。
pub(crate) struct LeakedSlot {
pub(crate) ptr: *mut JsObject,
key: Option<FnWrapperKey>,
}
impl BuiltinWorld {
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`]。
pub fn track_leaked_object(&self, obj_ptr: *mut JsObject) {
self.leaked_objects.borrow_mut().push(LeakedSlot { ptr: obj_ptr, key: None });
}
/// 登记一个可复用 native 函数 wrapper(带复用键),随登记表在 session
/// 收尾统一释放。
///
/// # 注意事项
/// 同键重复登记意味着复用键设计缺陷(同家族槽位对应两个不同 wrapper
/// 对象)——debug 断言立即失败。
pub fn track_fn_wrapper(&self, obj_ptr: *mut JsObject, key: FnWrapperKey) {
debug_assert!(
!self.leaked_objects.borrow().iter().any(|s| s.key == Some(key)),
"同键 native 函数 wrapper 重复登记"
);
self.leaked_objects
.borrow_mut()
.push(LeakedSlot { ptr: obj_ptr, key: Some(key) });
}
/// 查找复用键相同且 native 函数/参数个数匹配的既有 wrapper(选择性重建
/// 重绑的复用入口)。
///
/// # 边界与前提
/// 登记表指针 session 存活期内有效(full_reset 安全点无并发读者);
/// 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 复用键的目标家族标签:目标对象是本 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<JsObject>; 104] {
[
&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.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.sym_match,
&self.sym_replace,
&self.sym_search,
&self.sym_split,
&self.sym_iterator,
&self.sym_to_primitive,
&self.sym_has_instance,
&self.sym_match_all,
&self.sym_async_iterator,
&self.sym_to_string_tag,
&self.sym_species,
&self.sym_async_dispose,
&self.sym_dispose,
&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.iterator_helper_proto,
&self.disposable_stack_proto,
&self.async_disposable_stack_proto,
&self.console_object,
]
}
/// 释放本 world 拥有的全部手工堆数据。
///
/// # 口径
/// 1. `Box::into_raw` 持有的函数/宿主对象(登记表):先释放其堆外属性区,
/// 再释放对象本体;
/// 2. 全部 P 对象字段(`all_p_fields` 枚举 + stub 族)的堆外属性区——
/// 对象本体随 Arc 引用归零释放。
///
/// # 注意事项
/// 仅由 session 收尾调用(`KernelSession` 的 `Drop` 与 session 替换前),
/// 幂等:登记表按值取走,属性区释放后置空。选择性重建(dirty rebuild)
/// 不走本路径:登记表整体并入新 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<JsObject> {
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::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::SymMatch => &self.sym_match,
BuiltinId::SymReplace => &self.sym_replace,
BuiltinId::SymSearch => &self.sym_search,
BuiltinId::SymSplit => &self.sym_split,
BuiltinId::SymIterator => &self.sym_iterator,
BuiltinId::SymToPrimitive => &self.sym_to_primitive,
BuiltinId::SymHasInstance => &self.sym_has_instance,
BuiltinId::SymMatchAll => &self.sym_match_all,
BuiltinId::SymAsyncIterator => &self.sym_async_iterator,
BuiltinId::SymToStringTag => &self.sym_to_string_tag,
BuiltinId::SymSpecies => &self.sym_species,
BuiltinId::SymAsyncDispose => &self.sym_async_dispose,
BuiltinId::SymDispose => &self.sym_dispose,
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,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shape_forge::EMPTY_SHAPE_ID;
/// 选择性重建的释放 + 重指面动态自测:被替换旧 P 对象属性区恰好释放一次
/// (置空可断言,含保活钉住对的属性区、本体钉保留),保留字段指针不变且
/// 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());
// 保活钉住对(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));
}
}