mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 11:11:11 +08:00
refactor: builtin 注册表与结构体拆出独立 world 模块
This commit is contained in:
@@ -1,17 +1,13 @@
|
|||||||
//! 内置对象世界:持有并构造全部内置原型、构造器与全局单例(Math/JSON),
|
//! 内置对象世界:持有并构造全部内置原型、构造器与全局单例(Math/JSON),
|
||||||
//! 支持按脏标记重建,并提供绑定层的 native 方法指针表。
|
//! 支持按脏标记重建,并提供绑定层的 native 方法指针表。
|
||||||
|
|
||||||
use oxide_types::mem::P;
|
|
||||||
use oxide_types::object::{JsObject, NativeFnPtr};
|
|
||||||
use oxide_types::value::JsValue;
|
|
||||||
|
|
||||||
use crate::kernel::BuiltinId;
|
|
||||||
|
|
||||||
mod bind;
|
mod bind;
|
||||||
mod construct;
|
mod construct;
|
||||||
mod methods;
|
mod methods;
|
||||||
mod rebuild;
|
mod rebuild;
|
||||||
|
mod world;
|
||||||
pub use methods::{ArrayMethods, ErrorMethods, FunctionMethods, ObjectMethods, RegExpMethods, StringMethods};
|
pub use methods::{ArrayMethods, ErrorMethods, FunctionMethods, ObjectMethods, RegExpMethods, StringMethods};
|
||||||
|
pub use world::{BuiltinWorld, FnWrapperKey};
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! bind_method {
|
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user