Files
OxideJS-New/crates/oxide_kernel/src/kernel.rs
T

1068 lines
45 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#![allow(clippy::arc_with_non_send_sync)]
use std::num::NonZeroUsize;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use oxide_types::mem::P;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::builtin::BuiltinWorld;
use crate::code_forge::CodeForge;
use crate::kernel_info;
use crate::prop_forge::PropForge;
use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
use crate::string_forge::PermInterner;
use oxide_log;
use oxide_log::{Level, SUBSYSTEM_COUNT};
/// kernel 运行配置:VM 池规模、步数/调用深度/单 run 分配上限、session GC 阈值、
/// perm interner 重建阈值、日志级别与内置对象预热开关。由三个预设构造器
/// ([`KernelConfig::minimal`] / [`KernelConfig::standard`] / [`KernelConfig::full`])
/// 或默认值创建。
#[derive(Clone)]
pub struct KernelConfig {
pub min_pool_size: usize,
pub max_pool_size: Option<usize>,
/// perm interner advisory 重建阈值:唯一键数 `entry_count` **超过**该值时,
/// 宿主应在安全边界(无存活 VM)整体重建 kernel,并把
/// [`KernelCore::should_rebuild_perm`] 返回的建议上限写入新 kernel 配置;
/// None = 无阈值(三个预设默认值,行为与无旋钮一致)。
/// 见 [`KernelCore::should_rebuild_perm`]。
pub perm_interner_max_entries: Option<u32>,
pub max_steps: Option<u64>,
/// 单次 run 的分配上限(epoch + session arena + session 堆账目字节):
/// 超限的 run 以 `VM memory limit exceeded` 失败,防单测试 arena 高水位
/// 拖垮宿主内存。None 为无上限(CLI/嵌入默认;runner 按场景设置)。
pub max_alloc_bytes: Option<usize>,
pub max_call_depth: usize,
pub session_gc_threshold: usize,
pub max_cached_modules: usize,
pub log_levels: [Level; SUBSYSTEM_COUNT],
pub warmup_builtin_shapes: bool,
pub warmup_builtin_code: bool,
pub warmup_builtin_ic: bool,
}
impl KernelConfig {
/// 最小配置:小 VM 池、关闭 code/IC 预热,适合嵌入式或单次执行场景。
pub fn minimal() -> Self {
Self {
min_pool_size: 4,
max_pool_size: Some(8),
perm_interner_max_entries: None,
max_steps: None,
max_alloc_bytes: None,
max_call_depth: 1024,
session_gc_threshold: 33_554_432,
max_cached_modules: 512,
log_levels: [Level::Off; SUBSYSTEM_COUNT],
warmup_builtin_shapes: true,
warmup_builtin_code: false,
warmup_builtin_ic: false,
}
}
/// 标准配置:默认 VM 池大小,开启内置对象 code 预热。
pub fn standard() -> Self {
Self {
min_pool_size: 8,
max_pool_size: Some(32),
perm_interner_max_entries: None,
max_steps: None,
max_alloc_bytes: None,
max_call_depth: 1024,
session_gc_threshold: 33_554_432,
max_cached_modules: 512,
log_levels: [Level::Off; SUBSYSTEM_COUNT],
warmup_builtin_shapes: true,
warmup_builtin_code: true,
warmup_builtin_ic: false,
}
}
/// 全量配置:无上限 VM 池,开启 shapes/code/IC 全量预热,为性能场景服务。
pub fn full() -> Self {
Self {
min_pool_size: 16,
max_pool_size: None,
perm_interner_max_entries: None,
max_steps: None,
max_alloc_bytes: None,
max_call_depth: 1024,
session_gc_threshold: 33_554_432,
max_cached_modules: 512,
log_levels: [Level::Off; SUBSYSTEM_COUNT],
warmup_builtin_shapes: true,
warmup_builtin_code: true,
warmup_builtin_ic: true,
}
}
/// 读取 session GC 阈值(字节数)。
pub fn session_gc_threshold(&self) -> usize {
self.session_gc_threshold
}
/// 设置 session GC 阈值(字节数),超过后触发一次 session 级 GC。
pub fn set_session_gc_threshold(&mut self, bytes: usize) {
self.session_gc_threshold = bytes;
}
/// 读取 code cache 的 module 数量上限。
pub fn max_cached_modules(&self) -> usize {
self.max_cached_modules
}
/// 设置 code cache 的 module 数量上限。
pub fn set_max_cached_modules(&mut self, cap: usize) {
self.max_cached_modules = cap;
}
}
impl Default for KernelConfig {
fn default() -> Self {
Self::minimal()
}
}
/// 不可变、跨所有 VM 实例永久共享的状态。
/// 构造后从不原地重建——forge 表为 append-only;宿主可经
/// [`KernelCore::should_rebuild_perm`] 阈值在安全边界(无存活 VM)整体
/// 重建(换 `Arc`,非原地清表)。
pub struct KernelCore {
pub config: KernelConfig,
pub perm_interner: Arc<PermInterner>,
pub shape_forge: Arc<ShapeForge>,
pub code_forge: Arc<CodeForge>,
pub prop_forge: Arc<PropForge>,
/// 边界不变量守卫计数:当前持有本 kernel `Arc` 的 Vm 数。
/// `Vm` 构造器增、`Drop for Vm` 减(恰好一次),供 sweep / kernel 重建 /
/// kernel drop 处断言"无存活 VM"边界。
active_vms: AtomicUsize,
}
impl KernelCore {
/// 按配置创建共享核心:初始化日志系统与四个共享 forge(interner/shape/code/prop)。
pub fn new(config: KernelConfig) -> Arc<Self> {
oxide_log::init(&oxide_log::LogConfig {
output: oxide_log::Output::Stderr,
levels: config.log_levels,
});
let perm_interner = Arc::new(PermInterner::new());
let shape_forge = Arc::new(ShapeForge::new());
let code_forge = Arc::new(CodeForge::new(
NonZeroUsize::new(config.max_cached_modules).expect("max_cached_modules must be greater than zero"),
));
let prop_forge = Arc::new(PropForge::new());
let max_cached = config.max_cached_modules;
let min_pool = config.min_pool_size;
let core = Arc::new(Self {
config,
perm_interner,
shape_forge,
code_forge,
prop_forge,
active_vms: AtomicUsize::new(0),
});
kernel_info!("KernelCore initialized: max_cached_modules={}, min_pool={}", max_cached, min_pool);
core
}
/// 只读访问永久字符串 intern 表。
pub fn perm_interner(&self) -> &Arc<PermInterner> {
&self.perm_interner
}
/// 只读访问共享 hidden class(shape)存储。
pub fn shape_forge(&self) -> &Arc<ShapeForge> {
&self.shape_forge
}
/// 只读访问共享 bytecode cache。
pub fn code_forge(&self) -> &Arc<CodeForge> {
&self.code_forge
}
/// 只读访问共享属性模板缓存。
pub fn prop_forge(&self) -> &Arc<PropForge> {
&self.prop_forge
}
/// 只读访问构建时固定的配置。
pub fn config(&self) -> &KernelConfig {
&self.config
}
/// 读取 session GC 阈值(字节数)。
pub fn session_gc_threshold(&self) -> usize {
self.config.session_gc_threshold
}
/// 设置 session GC 阈值(字节数)。
pub fn set_session_gc_threshold(&mut self, bytes: usize) {
self.config.session_gc_threshold = bytes;
}
/// 读取 code cache 的 module 数量上限。
pub fn max_cached_modules(&self) -> usize {
self.config.max_cached_modules
}
/// 设置 code cache 的 module 数量上限。
pub fn set_max_cached_modules(&mut self, cap: usize) {
self.config.max_cached_modules = cap;
}
/// 批边界清理瞬时 shape/prop 缓存,防止跨测试累积膨胀。
///
/// 瞬时 forge(非根 shape/transition/position + prop 模板)的 id 空间只在两类
/// 边界复位:(1)kernel 整体重建——新核构造即空(结构性必清;宿主义务 =
/// 重建前旧核无存活 VM,否则旧核连同其 forge 永久驻留);(2)批内兜底 sweep
/// ——本函数,数据依赖:仅当 shape 或 prop 表超过 50k 阈值时才执行
/// [`ShapeForge::clear_transient`] 与 [`PropForge::clear`](阈值是增长闸门,
/// 宿主的检查节奏只是采样点)。字符串 intern 表 append-only、CodeForge LRU
/// 自管理,均不碰。
///
/// # 边界与前提
/// - 调用点须无存活 VM:对象头与 IC 词持 shape id,清空使 id 空间复位,跨复位
/// 存活的 VM 会因 id 复用碰撞静默错槽;该前提由 debug_assert 守卫(开发期
/// fail-fast),release 构建无断言,宿主可经 [`Self::active_vms`] 自检;
/// - 引擎不自动重建:id 空间的另一类复位(kernel 整体重建)由宿主在
/// "无存活 VM" 边界驱动(runner 由循环结构满足:VM 每测试新建即弃,
/// 重建/sweep 点都在 VM 作用域外)。
pub fn sweep_runner_forges(&self) {
// id 空间复位在有存活 VM 时是静默错槽隐患,守卫先于阈值判断。
debug_assert!(
self.active_vms.load(Ordering::Relaxed) == 0,
"sweep_runner_forges requires no live VMs (shape id space reset)"
);
// 键 interner 是 append-only(无逐次清理);仅当瞬时 shape/prop 表超阈值
// 时清表(批内兜底)。
if self.shape_forge.len() > 50_000 {
self.shape_forge.clear_transient();
self.prop_forge.clear();
} else if self.prop_forge.len() > 50_000 {
self.prop_forge.clear();
}
}
/// 宿主是否应整体重建本 kernel,并返回重建后的建议上限(advisory 信号)。
///
/// perm interner 唯一键数 `entry_count` **超过**配置的
/// [`KernelConfig::perm_interner_max_entries`] 阈值时返回
/// `Some(建议上限)`——建议上限 = 阈值取 2 的幂后加倍,宿主应在整体重建
/// 后把它写入新 kernel 的 `perm_interner_max_entries`(增长不立即再次
/// 触顶);阈值未设或键数未超阈值时返回 `None`。纯 advisory 契约:引擎
/// 不自动重建——重建须由宿主在"无存活 VM"边界驱动:归还全部 `VmGuard`
/// (池排空)→ 旧 `Arc<KernelCore>` 归零整体释放(PermInterner/ShapeForge/
/// CodeForge/PropForge,含全部泄漏键文本与物化串)→ 新建 kernel + 新池/
/// VM。存活 VM 的代际表注册表(`immutables`)与 P 对象持有旧 kernel 的物化串裸
/// 指针与 shape id,VM 存活期间重建会使其悬垂。三个预设默认 None = 永不
/// 触发(有意裁定:CLI eval/run/REPL/bench/test262 宿主均未接重建边界,
/// None 保证零行为漂移)。
///
/// # 边界与前提
/// - 仅在宿主边界(run 间 / 测试间 / 迭代间)调用,勿入 dispatch 热路径:
/// `entry_count` 为 O(1) 读锁,intern 路径零加码;
/// - 仅具备安全重建边界的宿主(test262 runner 批边界、嵌入宿主迭代
/// 之间)应启用旋钮;REPL 类宿主(单持久 VM,重建 = 丢失顶层状态)
/// 不应启用。
///
/// # 副作用
/// 无:只读查询。
pub fn should_rebuild_perm(&self) -> Option<u32> {
let cap = self.config.perm_interner_max_entries?;
(self.perm_interner.entry_count() > cap).then_some(cap.next_power_of_two().saturating_mul(2))
}
/// 边界不变量守卫计数:登记一个 Vm 出生(`Vm` 两条构造路径完整构造后调用)。
pub fn note_vm_started(&self) {
self.active_vms.fetch_add(1, Ordering::Relaxed);
}
/// 边界不变量守卫计数:注销一个 Vm 死亡(`Drop for Vm` 恰好调用一次)。
pub fn note_vm_ended(&self) {
self.active_vms.fetch_sub(1, Ordering::Relaxed);
}
/// 读取当前持有本 kernel 的存活 Vm 数;release 宿主可在 id 空间复位边界自检。
pub fn active_vms(&self) -> usize {
self.active_vms.load(Ordering::Relaxed)
}
}
impl Drop for KernelCore {
fn drop(&mut self) {
// kernel 能 drop 说明持 Arc 的 Vm 已全部归零;计数非零 = 纯计数漂移
// (note_vm_started/note_vm_ended 配对挂接回归探测)。
debug_assert_eq!(
self.active_vms.load(Ordering::Relaxed),
0,
"KernelCore dropped with live VMs (note_vm_ended counter drift)"
);
}
}
/// 每会话可变状态:内置原型对象与全局对象。
/// 在 full_reset() 时重建,实现 JS 执行之间的完全隔离。
pub struct KernelSession {
pub builtin_world: Arc<BuiltinWorld>,
pub global_object: P<JsObject>,
pub builtin_snapshot: BuiltinSnapshot,
}
/// 供 reset 脏检查用的世代快照。
///
/// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及
/// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的
/// builtin 家族。
pub const NUM_BUILTINS: usize = 94;
/// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。
///
/// 覆盖各构造器/原型、Error 家族(含 SuppressedError 原型)、集合类型、
/// TypedArray 家族与 well-known symbols;`repr(u8)` 使其可直接作为数组下标
/// (`u8` 值即下标)。
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum BuiltinId {
ObjectProto = 0,
ArrayProto = 1,
FunctionProto = 2,
StringProto = 3,
NumberProto = 4,
BooleanProto = 5,
ErrorProto = 6,
SymbolProto = 7,
ObjectConstructor = 8,
ArrayConstructor = 9,
FunctionConstructor = 10,
StringConstructor = 11,
NumberConstructor = 12,
BooleanConstructor = 13,
ErrorConstructor = 14,
SymbolConstructor = 15,
TypeErrorProto = 16,
ReferenceErrorProto = 17,
RangeErrorProto = 18,
SyntaxErrorProto = 19,
UriErrorProto = 20,
EvalErrorProto = 21,
MathObject = 22,
JsonObject = 23,
DateConstructor = 24,
DateProto = 25,
SetConstructor = 26,
SetProto = 27,
MapConstructor = 28,
MapProto = 29,
RegExpConstructor = 30,
RegExpProto = 31,
ArrayBufferConstructor = 32,
ArrayBufferProto = 33,
DataViewConstructor = 34,
DataViewProto = 35,
TypedArrayProto = 36,
Int8ArrayConstructor = 37,
Int8ArrayProto = 38,
Uint8ArrayConstructor = 39,
Uint8ArrayProto = 40,
Uint8ClampedArrayConstructor = 41,
Uint8ClampedArrayProto = 42,
Int16ArrayConstructor = 43,
Int16ArrayProto = 44,
Uint16ArrayConstructor = 45,
Uint16ArrayProto = 46,
Int32ArrayConstructor = 47,
Int32ArrayProto = 48,
Uint32ArrayConstructor = 49,
Uint32ArrayProto = 50,
Float32ArrayConstructor = 51,
Float32ArrayProto = 52,
Float64ArrayConstructor = 53,
Float64ArrayProto = 54,
BigInt64ArrayConstructor = 55,
BigInt64ArrayProto = 56,
BigUint64ArrayConstructor = 57,
BigUint64ArrayProto = 58,
SymMatch = 59,
SymReplace = 60,
SymSearch = 61,
SymSplit = 62,
SymIterator = 63,
SymToPrimitive = 64,
SymHasInstance = 65,
SymMatchAll = 66,
SymAsyncIterator = 67,
TemporalObject = 68,
TemporalNowObject = 69,
InstantConstructor = 70,
InstantProto = 71,
PlainDateConstructor = 72,
PlainDateProto = 73,
PlainTimeConstructor = 74,
PlainTimeProto = 75,
BigIntConstructor = 76,
BigIntProto = 77,
DurationConstructor = 78,
DurationProto = 79,
SymToStringTag = 80,
SymSpecies = 85,
ZonedDateTimeConstructor = 81,
ZonedDateTimeProto = 82,
PlainDateTimeConstructor = 83,
PlainDateTimeProto = 84,
SymAsyncDispose = 86,
SymDispose = 87,
SuppressedErrorProto = 88,
Console = 89,
PlainMonthDayConstructor = 90,
PlainMonthDayProto = 91,
PlainYearMonthConstructor = 92,
PlainYearMonthProto = 93,
}
impl BuiltinId {
/// 全部内置对象的 id 常量表,供快照/脏检查按序遍历。
/// 顺序必须与枚举判别值一致(`ALL[i]` 的 `u8` 值 == `i`):
/// 快照数组按下标填充,脏检查按下标回读,错位会误判 builtin 家族永久脏。
pub const ALL: [BuiltinId; NUM_BUILTINS] = [
BuiltinId::ObjectProto,
BuiltinId::ArrayProto,
BuiltinId::FunctionProto,
BuiltinId::StringProto,
BuiltinId::NumberProto,
BuiltinId::BooleanProto,
BuiltinId::ErrorProto,
BuiltinId::SymbolProto,
BuiltinId::ObjectConstructor,
BuiltinId::ArrayConstructor,
BuiltinId::FunctionConstructor,
BuiltinId::StringConstructor,
BuiltinId::NumberConstructor,
BuiltinId::BooleanConstructor,
BuiltinId::ErrorConstructor,
BuiltinId::SymbolConstructor,
BuiltinId::TypeErrorProto,
BuiltinId::ReferenceErrorProto,
BuiltinId::RangeErrorProto,
BuiltinId::SyntaxErrorProto,
BuiltinId::UriErrorProto,
BuiltinId::EvalErrorProto,
BuiltinId::MathObject,
BuiltinId::JsonObject,
BuiltinId::DateConstructor,
BuiltinId::DateProto,
BuiltinId::SetConstructor,
BuiltinId::SetProto,
BuiltinId::MapConstructor,
BuiltinId::MapProto,
BuiltinId::RegExpConstructor,
BuiltinId::RegExpProto,
BuiltinId::ArrayBufferConstructor,
BuiltinId::ArrayBufferProto,
BuiltinId::DataViewConstructor,
BuiltinId::DataViewProto,
BuiltinId::TypedArrayProto,
BuiltinId::Int8ArrayConstructor,
BuiltinId::Int8ArrayProto,
BuiltinId::Uint8ArrayConstructor,
BuiltinId::Uint8ArrayProto,
BuiltinId::Uint8ClampedArrayConstructor,
BuiltinId::Uint8ClampedArrayProto,
BuiltinId::Int16ArrayConstructor,
BuiltinId::Int16ArrayProto,
BuiltinId::Uint16ArrayConstructor,
BuiltinId::Uint16ArrayProto,
BuiltinId::Int32ArrayConstructor,
BuiltinId::Int32ArrayProto,
BuiltinId::Uint32ArrayConstructor,
BuiltinId::Uint32ArrayProto,
BuiltinId::Float32ArrayConstructor,
BuiltinId::Float32ArrayProto,
BuiltinId::Float64ArrayConstructor,
BuiltinId::Float64ArrayProto,
BuiltinId::BigInt64ArrayConstructor,
BuiltinId::BigInt64ArrayProto,
BuiltinId::BigUint64ArrayConstructor,
BuiltinId::BigUint64ArrayProto,
BuiltinId::SymMatch,
BuiltinId::SymReplace,
BuiltinId::SymSearch,
BuiltinId::SymSplit,
BuiltinId::SymIterator,
BuiltinId::SymToPrimitive,
BuiltinId::SymHasInstance,
BuiltinId::SymMatchAll,
BuiltinId::SymAsyncIterator,
BuiltinId::TemporalObject,
BuiltinId::TemporalNowObject,
BuiltinId::InstantConstructor,
BuiltinId::InstantProto,
BuiltinId::PlainDateConstructor,
BuiltinId::PlainDateProto,
BuiltinId::PlainTimeConstructor,
BuiltinId::PlainTimeProto,
BuiltinId::BigIntConstructor,
BuiltinId::BigIntProto,
BuiltinId::DurationConstructor,
BuiltinId::DurationProto,
BuiltinId::SymToStringTag,
BuiltinId::ZonedDateTimeConstructor,
BuiltinId::ZonedDateTimeProto,
BuiltinId::PlainDateTimeConstructor,
BuiltinId::PlainDateTimeProto,
BuiltinId::SymSpecies,
BuiltinId::SymAsyncDispose,
BuiltinId::SymDispose,
BuiltinId::SuppressedErrorProto,
BuiltinId::Console,
BuiltinId::PlainMonthDayConstructor,
BuiltinId::PlainMonthDayProto,
BuiltinId::PlainYearMonthConstructor,
BuiltinId::PlainYearMonthProto,
];
}
// 顺序钉:ALL 与判别值 0..NUM_BUILTINS 严格同序(无换序、无重复、无遗漏);
// 错位即快照/脏检查按下标误读,钉成编译失败。
const _: () = {
let mut i = 0;
while i < BuiltinId::ALL.len() {
if BuiltinId::ALL[i] as usize != i {
panic!("BuiltinId::ALL 顺序与判别值不一致");
}
i += 1;
}
};
/// 内置对象世代(generation)快照:记录构造时各对象及其 stub 的世代号与数量。
///
/// 供 [`KernelSession::dirty_since_snapshot`] 对比,判断哪些 builtin 家族在运行期被污染。
#[derive(Clone, Debug)]
pub struct BuiltinSnapshot {
pub generations: [u32; NUM_BUILTINS],
pub global_object_generation: u32,
pub stub_objects_len: usize,
pub stub_object_generations: Vec<u32>,
}
impl BuiltinSnapshot {
fn gen(obj: &P<JsObject>) -> u32 {
obj.generation()
}
/// 对给定 builtin world 与 global object 采集一份世代快照。
pub fn new(world: &BuiltinWorld, global_object: &P<JsObject>) -> Self {
let mut generations = [0u32; NUM_BUILTINS];
for (i, id) in BuiltinId::ALL.iter().enumerate() {
generations[i] = Self::gen(world.get_by_id(*id));
}
debug_assert_eq!(generations.len(), NUM_BUILTINS);
Self {
generations,
global_object_generation: Self::gen(global_object),
stub_objects_len: world.stub_objects.len(),
stub_object_generations: world.stub_objects.iter().map(Self::gen).collect(),
}
}
}
/// 按 builtin 家族划分的脏标记位集合:运行期哪些内置对象被用户代码修改过。
///
/// `any_builtin_dirty` 只关注 builtin world 内部对象,`any` 额外包含 global object。
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct BuiltinDirtySet {
pub object: bool,
pub array: bool,
pub function: bool,
pub string: bool,
pub number: bool,
pub boolean: bool,
pub error_family: bool,
pub symbol_family: bool,
pub math: bool,
pub json: bool,
pub date: bool,
pub set: bool,
pub map: bool,
pub regexp: bool,
pub array_buffer: bool,
pub data_view: bool,
pub typed_array_family: bool,
pub temporal: bool,
pub stubs: bool,
pub global: bool,
pub console: bool,
}
impl BuiltinDirtySet {
/// 是否存在任何 builtin world 内部对象被污染(不含 global object)。
pub fn any_builtin_dirty(&self) -> bool {
self.object
|| self.array
|| self.function
|| self.string
|| self.number
|| self.boolean
|| self.error_family
|| self.symbol_family
|| self.math
|| self.json
|| self.date
|| self.set
|| self.map
|| self.regexp
|| self.array_buffer
|| self.data_view
|| self.typed_array_family
|| self.temporal
|| self.stubs
|| self.console
}
/// 是否存在任何污染(builtin world 或 global object)。
pub fn any(&self) -> bool {
self.any_builtin_dirty() || self.global
}
}
impl KernelSession {
fn new_global_object(core: &KernelCore) -> P<JsObject> {
let mut global_obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null());
let si_nan = core.perm_interner.intern("NaN").0;
let si_undef = core.perm_interner.intern("undefined").0;
let si_infinity = core.perm_interner.intern("Infinity").0;
// 全局三常量描述符:{ writable:false, enumerable:false, configurable:false }。
// e:false 是枚举面守卫(Object.keys / for-in 由 enumerable 决定);
// c:false 是 delete / defineProperty 可观察的规范值(delete globalThis.undefined
// 返回 false、属性不可删,后续声明/定义均不得 reconfigure)。
let nan_shape = core.shape_forge.make_shape(EMPTY_SHAPE_ID, si_nan);
global_obj.set_shape_id(nan_shape);
global_obj.ensure_hash_props().push(JsValue::float(f64::NAN));
global_obj.set_data_meta(
global_obj.prop_vec_len().saturating_sub(1) as u32,
oxide_types::object::PropAttributes::new(false, false, false),
);
let undef_shape = core.shape_forge.make_shape(nan_shape, si_undef);
global_obj.set_shape_id(undef_shape);
global_obj.ensure_hash_props().push(JsValue::undefined());
global_obj.set_data_meta(
global_obj.prop_vec_len().saturating_sub(1) as u32,
oxide_types::object::PropAttributes::new(false, false, false),
);
let inf_shape = core.shape_forge.make_shape(undef_shape, si_infinity);
global_obj.set_shape_id(inf_shape);
global_obj.ensure_hash_props().push(JsValue::float(f64::INFINITY));
global_obj.set_data_meta(
global_obj.prop_vec_len().saturating_sub(1) as u32,
oxide_types::object::PropAttributes::new(false, false, false),
);
P::new(global_obj)
}
/// 从 KernelCore 构建一个全新 session。所有 string/shape intern 调用在
/// 第二次及后续调用时命中缓存——净开销 < 0.5 ms。
pub fn new(core: &KernelCore) -> Self {
let builtin_world = Arc::new(BuiltinWorld::new(&core.perm_interner, &core.shape_forge));
let global_object = Self::new_global_object(core);
let builtin_snapshot = BuiltinSnapshot::new(&builtin_world, &global_object);
Self {
builtin_world,
global_object,
builtin_snapshot,
}
}
/// 只读访问当前 session 的 builtin world。
pub fn builtin_world(&self) -> &Arc<BuiltinWorld> {
&self.builtin_world
}
/// 只读访问当前 session 的 global object。
pub fn global_object(&self) -> &P<JsObject> {
&self.global_object
}
/// 重新采集内置对象世代快照,作为下一次脏检查的基准。
pub fn record_snapshot(&mut self) {
self.builtin_snapshot = BuiltinSnapshot::new(&self.builtin_world, &self.global_object);
}
/// 对比当前世代与最近快照,返回各 builtin 家族是否被污染。
pub fn dirty_since_snapshot(&self) -> BuiltinDirtySet {
let world = self.builtin_world.as_ref();
let snapshot = &self.builtin_snapshot;
let stub_generations_dirty = world
.stub_objects
.iter()
.zip(snapshot.stub_object_generations.iter())
.any(|(obj, generation)| BuiltinSnapshot::gen(obj) != *generation);
let gen = |id: BuiltinId| BuiltinSnapshot::gen(world.get_by_id(id));
let snap = |id: BuiltinId| snapshot.generations[id as usize];
BuiltinDirtySet {
object: gen(BuiltinId::ObjectProto) != snap(BuiltinId::ObjectProto)
|| gen(BuiltinId::ObjectConstructor) != snap(BuiltinId::ObjectConstructor),
array: gen(BuiltinId::ArrayProto) != snap(BuiltinId::ArrayProto)
|| gen(BuiltinId::ArrayConstructor) != snap(BuiltinId::ArrayConstructor),
function: gen(BuiltinId::FunctionProto) != snap(BuiltinId::FunctionProto)
|| gen(BuiltinId::FunctionConstructor) != snap(BuiltinId::FunctionConstructor),
string: gen(BuiltinId::StringProto) != snap(BuiltinId::StringProto)
|| gen(BuiltinId::StringConstructor) != snap(BuiltinId::StringConstructor),
number: gen(BuiltinId::NumberProto) != snap(BuiltinId::NumberProto)
|| gen(BuiltinId::NumberConstructor) != snap(BuiltinId::NumberConstructor),
boolean: gen(BuiltinId::BooleanProto) != snap(BuiltinId::BooleanProto)
|| gen(BuiltinId::BooleanConstructor) != snap(BuiltinId::BooleanConstructor),
error_family: gen(BuiltinId::ErrorProto) != snap(BuiltinId::ErrorProto)
|| gen(BuiltinId::ErrorConstructor) != snap(BuiltinId::ErrorConstructor)
|| gen(BuiltinId::TypeErrorProto) != snap(BuiltinId::TypeErrorProto)
|| gen(BuiltinId::ReferenceErrorProto) != snap(BuiltinId::ReferenceErrorProto)
|| gen(BuiltinId::RangeErrorProto) != snap(BuiltinId::RangeErrorProto)
|| gen(BuiltinId::SyntaxErrorProto) != snap(BuiltinId::SyntaxErrorProto)
|| gen(BuiltinId::UriErrorProto) != snap(BuiltinId::UriErrorProto)
|| gen(BuiltinId::EvalErrorProto) != snap(BuiltinId::EvalErrorProto)
|| gen(BuiltinId::SuppressedErrorProto) != snap(BuiltinId::SuppressedErrorProto),
symbol_family: gen(BuiltinId::SymbolProto) != snap(BuiltinId::SymbolProto)
|| gen(BuiltinId::SymbolConstructor) != snap(BuiltinId::SymbolConstructor)
|| gen(BuiltinId::SymMatch) != snap(BuiltinId::SymMatch)
|| gen(BuiltinId::SymReplace) != snap(BuiltinId::SymReplace)
|| gen(BuiltinId::SymSearch) != snap(BuiltinId::SymSearch)
|| gen(BuiltinId::SymSplit) != snap(BuiltinId::SymSplit)
|| gen(BuiltinId::SymIterator) != snap(BuiltinId::SymIterator)
|| gen(BuiltinId::SymToPrimitive) != snap(BuiltinId::SymToPrimitive)
|| gen(BuiltinId::SymHasInstance) != snap(BuiltinId::SymHasInstance)
|| gen(BuiltinId::SymMatchAll) != snap(BuiltinId::SymMatchAll)
|| gen(BuiltinId::SymAsyncIterator) != snap(BuiltinId::SymAsyncIterator)
|| gen(BuiltinId::SymToStringTag) != snap(BuiltinId::SymToStringTag)
|| gen(BuiltinId::SymSpecies) != snap(BuiltinId::SymSpecies)
|| gen(BuiltinId::SymAsyncDispose) != snap(BuiltinId::SymAsyncDispose)
|| gen(BuiltinId::SymDispose) != snap(BuiltinId::SymDispose),
math: gen(BuiltinId::MathObject) != snap(BuiltinId::MathObject),
json: gen(BuiltinId::JsonObject) != snap(BuiltinId::JsonObject),
date: gen(BuiltinId::DateConstructor) != snap(BuiltinId::DateConstructor)
|| gen(BuiltinId::DateProto) != snap(BuiltinId::DateProto),
set: gen(BuiltinId::SetConstructor) != snap(BuiltinId::SetConstructor)
|| gen(BuiltinId::SetProto) != snap(BuiltinId::SetProto),
map: gen(BuiltinId::MapConstructor) != snap(BuiltinId::MapConstructor)
|| gen(BuiltinId::MapProto) != snap(BuiltinId::MapProto),
regexp: gen(BuiltinId::RegExpConstructor) != snap(BuiltinId::RegExpConstructor)
|| gen(BuiltinId::RegExpProto) != snap(BuiltinId::RegExpProto),
array_buffer: gen(BuiltinId::ArrayBufferConstructor) != snap(BuiltinId::ArrayBufferConstructor)
|| gen(BuiltinId::ArrayBufferProto) != snap(BuiltinId::ArrayBufferProto),
data_view: gen(BuiltinId::DataViewConstructor) != snap(BuiltinId::DataViewConstructor)
|| gen(BuiltinId::DataViewProto) != snap(BuiltinId::DataViewProto),
typed_array_family: gen(BuiltinId::TypedArrayProto) != snap(BuiltinId::TypedArrayProto)
|| gen(BuiltinId::Int8ArrayConstructor) != snap(BuiltinId::Int8ArrayConstructor)
|| gen(BuiltinId::Int8ArrayProto) != snap(BuiltinId::Int8ArrayProto)
|| gen(BuiltinId::Uint8ArrayConstructor) != snap(BuiltinId::Uint8ArrayConstructor)
|| gen(BuiltinId::Uint8ArrayProto) != snap(BuiltinId::Uint8ArrayProto)
|| gen(BuiltinId::Uint8ClampedArrayConstructor) != snap(BuiltinId::Uint8ClampedArrayConstructor)
|| gen(BuiltinId::Uint8ClampedArrayProto) != snap(BuiltinId::Uint8ClampedArrayProto)
|| gen(BuiltinId::Int16ArrayConstructor) != snap(BuiltinId::Int16ArrayConstructor)
|| gen(BuiltinId::Int16ArrayProto) != snap(BuiltinId::Int16ArrayProto)
|| gen(BuiltinId::Uint16ArrayConstructor) != snap(BuiltinId::Uint16ArrayConstructor)
|| gen(BuiltinId::Uint16ArrayProto) != snap(BuiltinId::Uint16ArrayProto)
|| gen(BuiltinId::Int32ArrayConstructor) != snap(BuiltinId::Int32ArrayConstructor)
|| gen(BuiltinId::Int32ArrayProto) != snap(BuiltinId::Int32ArrayProto)
|| gen(BuiltinId::Uint32ArrayConstructor) != snap(BuiltinId::Uint32ArrayConstructor)
|| gen(BuiltinId::Uint32ArrayProto) != snap(BuiltinId::Uint32ArrayProto)
|| gen(BuiltinId::Float32ArrayConstructor) != snap(BuiltinId::Float32ArrayConstructor)
|| gen(BuiltinId::Float32ArrayProto) != snap(BuiltinId::Float32ArrayProto)
|| gen(BuiltinId::Float64ArrayConstructor) != snap(BuiltinId::Float64ArrayConstructor)
|| gen(BuiltinId::Float64ArrayProto) != snap(BuiltinId::Float64ArrayProto)
|| gen(BuiltinId::BigInt64ArrayConstructor) != snap(BuiltinId::BigInt64ArrayConstructor)
|| gen(BuiltinId::BigInt64ArrayProto) != snap(BuiltinId::BigInt64ArrayProto)
|| gen(BuiltinId::BigUint64ArrayConstructor) != snap(BuiltinId::BigUint64ArrayConstructor)
|| gen(BuiltinId::BigUint64ArrayProto) != snap(BuiltinId::BigUint64ArrayProto),
temporal: gen(BuiltinId::TemporalObject) != snap(BuiltinId::TemporalObject)
|| gen(BuiltinId::TemporalNowObject) != snap(BuiltinId::TemporalNowObject)
|| gen(BuiltinId::InstantConstructor) != snap(BuiltinId::InstantConstructor)
|| gen(BuiltinId::InstantProto) != snap(BuiltinId::InstantProto)
|| gen(BuiltinId::PlainDateConstructor) != snap(BuiltinId::PlainDateConstructor)
|| gen(BuiltinId::PlainDateProto) != snap(BuiltinId::PlainDateProto)
|| gen(BuiltinId::PlainTimeConstructor) != snap(BuiltinId::PlainTimeConstructor)
|| gen(BuiltinId::PlainTimeProto) != snap(BuiltinId::PlainTimeProto)
|| gen(BuiltinId::DurationConstructor) != snap(BuiltinId::DurationConstructor)
|| gen(BuiltinId::DurationProto) != snap(BuiltinId::DurationProto)
|| gen(BuiltinId::ZonedDateTimeConstructor) != snap(BuiltinId::ZonedDateTimeConstructor)
|| gen(BuiltinId::ZonedDateTimeProto) != snap(BuiltinId::ZonedDateTimeProto)
|| gen(BuiltinId::PlainDateTimeConstructor) != snap(BuiltinId::PlainDateTimeConstructor)
|| gen(BuiltinId::PlainDateTimeProto) != snap(BuiltinId::PlainDateTimeProto)
|| gen(BuiltinId::PlainMonthDayConstructor) != snap(BuiltinId::PlainMonthDayConstructor)
|| gen(BuiltinId::PlainMonthDayProto) != snap(BuiltinId::PlainMonthDayProto)
|| gen(BuiltinId::PlainYearMonthConstructor) != snap(BuiltinId::PlainYearMonthConstructor)
|| gen(BuiltinId::PlainYearMonthProto) != snap(BuiltinId::PlainYearMonthProto),
stubs: world.stub_objects.len() != snapshot.stub_objects_len
|| stub_generations_dirty
|| gen(BuiltinId::BigIntConstructor) != snap(BuiltinId::BigIntConstructor)
|| gen(BuiltinId::BigIntProto) != snap(BuiltinId::BigIntProto),
global: BuiltinSnapshot::gen(&self.global_object) != snapshot.global_object_generation,
console: gen(BuiltinId::Console) != snap(BuiltinId::Console),
}
}
/// 是否自上次快照以来存在任何污染。
pub fn is_dirty_since_snapshot(&self) -> bool {
self.dirty_since_snapshot().any()
}
/// 收尾释放 session 拥有的手工堆数据:builtin world(方法 wrapper 登记表 +
/// 全部 P 对象属性区)与 global 对象属性区。对象本体随 Arc 引用归零释放。
///
/// # 注意事项
/// 幂等(登记表按值取走、属性区释放后置空),session 生命周期内可安全重入;
/// 仅应在 session 真正终止时调用——选择性重置换出的旧 world/global 不走本路径。
pub fn teardown_builtins(&mut self) {
self.builtin_world.teardown_heap_data();
// SAFETY: global 归本 session 所有,收尾时恰好释放其属性区一次。
let global = unsafe { &mut *(self.global_object.as_ptr() as *mut JsObject) };
global.release_raw_heap();
}
/// 选择性重置:只重建被污染的对象(global 或相应 builtin 家族),并返回脏集合。
///
/// 相比全量重建,可保留未污染的内置对象指针与世代,减少隔离成本。
pub fn selective_reset(&mut self, core: &Arc<KernelCore>) -> BuiltinDirtySet {
let dirty = self.dirty_since_snapshot();
if dirty.global {
// 旧 global 的属性区在替换前释放,避免随旧引用永久泄漏。
let old_global = unsafe { &mut *(self.global_object.as_ptr() as *mut JsObject) };
old_global.release_raw_heap();
self.global_object = Self::new_global_object(core);
}
if dirty.any_builtin_dirty() {
let old_world = self.builtin_world.clone();
let new_world = BuiltinWorld::rebuild_with_dirty(
&old_world,
core.perm_interner.as_ref(),
core.shape_forge.as_ref(),
&dirty,
);
// 旧 world 的登记表并入新 world:存活 wrapper(未污染家族别名)仍须在
// session 收尾统一释放;已弃 wrapper 随之恰好释放一次,不二次持有。
new_world.inherit_leaked_objects(&old_world);
// 重建收尾:保留对象(登记表 wrapper + 共用保留 P 字段)proto 槽
// 重指新指针,随后被替换旧对象属性区恰好释放一次(含保活钉住
// 对的属性区,本体钉保留);重指须先于释放、先于换出完成。
new_world.retire_replaced(&old_world);
self.builtin_world = Arc::new(new_world);
}
dirty
}
}
impl Drop for KernelSession {
fn drop(&mut self) {
self.teardown_builtins();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shape_forge::EMPTY_SHAPE_ID;
#[test]
fn test_kernel_new() {
let core = KernelCore::new(KernelConfig::minimal());
let (i1, _) = core.perm_interner().intern("test");
let (i2, _) = core.perm_interner().intern("test");
assert_eq!(i1, i2);
}
#[test]
fn test_kernel_builtins_accessible() {
let core = KernelCore::new(KernelConfig::minimal());
let session = KernelSession::new(&core);
assert!(!session.builtin_world().object_proto.is_function());
assert!(session.builtin_world().object_constructor.is_function());
}
#[test]
fn test_kernel_shape_forge() {
let core = KernelCore::new(KernelConfig::minimal());
assert!(core.shape_forge().get_shape(EMPTY_SHAPE_ID).is_some());
}
#[test]
fn test_kernel_string_forge() {
let core = KernelCore::new(KernelConfig::minimal());
let (i1, _) = core.perm_interner().intern("hello");
let (i2, _) = core.perm_interner().intern("hello");
assert_eq!(i1, i2);
}
#[test]
fn test_kernel_config_presets() {
assert_eq!(KernelConfig::minimal().max_pool_size, Some(8));
assert_eq!(KernelConfig::standard().max_pool_size, Some(32));
assert_eq!(KernelConfig::minimal().max_steps, None);
assert_eq!(KernelConfig::standard().max_steps, None);
assert_eq!(KernelConfig::full().max_steps, None);
assert_eq!(KernelConfig::minimal().log_levels, [Level::Off; SUBSYSTEM_COUNT]);
assert!(!KernelConfig::minimal().warmup_builtin_ic);
assert!(KernelConfig::full().warmup_builtin_ic);
assert_eq!(KernelConfig::full().max_pool_size, None);
}
#[test]
fn should_rebuild_perm_default_off() {
// 预设默认 None = 无阈值:任意键数下永不触发,钉死零漂移默认面。
let core = KernelCore::new(KernelConfig::minimal());
for i in 0..10 {
core.perm_interner().intern(&format!("key{i}"));
}
assert_eq!(core.should_rebuild_perm(), None);
}
#[test]
fn should_rebuild_perm_exceeds_threshold() {
let mut config = KernelConfig::minimal();
config.perm_interner_max_entries = Some(2);
let core = KernelCore::new(config);
core.perm_interner().intern("a");
core.perm_interner().intern("b");
// 恰在阈值不触发(超阈值,非达到)。
assert_eq!(core.should_rebuild_perm(), None);
core.perm_interner().intern("c");
// 建议上限 = 阈值取 2 的幂后加倍:2 -> 4。
assert_eq!(core.should_rebuild_perm(), Some(4));
}
#[test]
fn should_rebuild_perm_below_threshold() {
let mut config = KernelConfig::minimal();
config.perm_interner_max_entries = Some(100);
let core = KernelCore::new(config);
core.perm_interner().intern("a");
core.perm_interner().intern("b");
assert_eq!(core.should_rebuild_perm(), None);
}
#[test]
fn test_session_rebuild_shares_forges() {
let core = KernelCore::new(KernelConfig::minimal());
let (i1, _) = core.perm_interner().intern("hello");
let _s2 = KernelSession::new(&core);
let (i2, _) = core.perm_interner().intern("hello");
assert_eq!(i1, i2);
}
#[test]
fn snapshot_fresh_session_is_clean() {
let core = KernelCore::new(KernelConfig::minimal());
let session = KernelSession::new(&core);
let dirty = session.dirty_since_snapshot();
assert!(!dirty.any());
assert!(!dirty.any_builtin_dirty());
assert!(!session.is_dirty_since_snapshot());
}
#[test]
fn snapshot_detects_array_dirty() {
let core = KernelCore::new(KernelConfig::minimal());
let session = KernelSession::new(&core);
unsafe { &mut *(session.builtin_world.array_proto.as_ptr() as *mut JsObject) }.bump_generation();
let dirty = session.dirty_since_snapshot();
assert!(dirty.array);
assert!(dirty.any_builtin_dirty());
assert!(session.is_dirty_since_snapshot());
assert!(!dirty.global);
assert!(!dirty.object);
}
#[test]
fn snapshot_detects_global_dirty() {
let core = KernelCore::new(KernelConfig::minimal());
let session = KernelSession::new(&core);
unsafe { &mut *(session.global_object.as_ptr() as *mut JsObject) }.bump_generation();
let dirty = session.dirty_since_snapshot();
assert!(dirty.global);
assert!(dirty.any());
assert!(!dirty.any_builtin_dirty());
}
#[test]
fn snapshot_detects_stub_dirty() {
let core = KernelCore::new(KernelConfig::minimal());
let mut session = KernelSession::new(&core);
Arc::get_mut(&mut session.builtin_world)
.expect("fresh session owns its builtin world")
.stub_objects
.push(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())));
session.record_snapshot();
Arc::get_mut(&mut session.builtin_world)
.expect("fresh session owns its builtin world")
.stub_objects
.push(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())));
let dirty = session.dirty_since_snapshot();
assert!(dirty.stubs);
assert!(dirty.any_builtin_dirty());
assert!(!dirty.global);
}
#[test]
fn selective_reset_clean_keeps_builtin_world() {
let core = KernelCore::new(KernelConfig::minimal());
let mut session = KernelSession::new(&core);
let world_ptr = Arc::as_ptr(&session.builtin_world);
let dirty = session.selective_reset(&core);
assert!(!dirty.any());
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&session.builtin_world)));
}
#[test]
fn selective_reset_rebuilds_global_only_when_global_dirty() {
let core = KernelCore::new(KernelConfig::minimal());
let mut session = KernelSession::new(&core);
let world_ptr = Arc::as_ptr(&session.builtin_world);
let global_ptr = session.global_object.as_ptr();
unsafe { &mut *(session.global_object.as_ptr() as *mut JsObject) }.bump_generation();
let dirty = session.selective_reset(&core);
assert!(dirty.global);
assert!(!dirty.any_builtin_dirty());
assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&session.builtin_world)));
assert!(!std::ptr::eq(global_ptr, session.global_object.as_ptr()));
}
#[test]
fn selective_reset_rebuilds_dirty_builtin_group() {
let core = KernelCore::new(KernelConfig::minimal());
let mut session = KernelSession::new(&core);
let object_proto = session.builtin_world.object_proto.as_ptr();
let array_proto = session.builtin_world.array_proto.as_ptr();
let world_ptr = Arc::as_ptr(&session.builtin_world);
unsafe { &mut *(session.builtin_world.array_proto.as_ptr() as *mut JsObject) }.bump_generation();
let dirty = session.selective_reset(&core);
assert!(dirty.array);
assert!(!std::ptr::eq(world_ptr, Arc::as_ptr(&session.builtin_world)));
assert!(std::ptr::eq(object_proto, session.builtin_world.object_proto.as_ptr()));
assert!(!std::ptr::eq(array_proto, session.builtin_world.array_proto.as_ptr()));
let ctor_proto = session.builtin_world.array_constructor.get_prop_at(0).as_js_object_ptr();
assert!(std::ptr::eq(ctor_proto, session.builtin_world.array_proto.as_ptr() as *mut JsObject));
}
}