diff --git a/Cargo.lock b/Cargo.lock index ebff378f..8bfb1e8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.8.49" +version = "0.8.50" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.8.49" +version = "0.8.50" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.8.49" +version = "0.8.50" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.8.49" +version = "0.8.50" dependencies = [ "ansi_term", "bumpalo", @@ -1225,7 +1225,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.8.49" +version = "0.8.50" dependencies = [ "lru", "oxide_bytecode", @@ -1234,7 +1234,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.8.49" +version = "0.8.50" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1253,7 +1253,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.8.49" +version = "0.8.50" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1267,7 +1267,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.8.49" +version = "0.8.50" dependencies = [ "num-bigint", "oxc_allocator", @@ -1282,7 +1282,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.8.49" +version = "0.8.50" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1291,7 +1291,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.8.49" +version = "0.8.50" dependencies = [ "bumpalo", "dashmap", @@ -1306,7 +1306,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.8.49" +version = "0.8.50" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1318,7 +1318,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.8.49" +version = "0.8.50" dependencies = [ "tracing", "tracing-appender", @@ -1327,7 +1327,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.8.49" +version = "0.8.50" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1342,7 +1342,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.8.49" +version = "0.8.50" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1358,7 +1358,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.8.49" +version = "0.8.50" dependencies = [ "num-bigint", "num-traits", @@ -1370,7 +1370,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.8.49" +version = "0.8.50" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1387,7 +1387,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.8.49" +version = "0.8.50" dependencies = [ "bumpalo", "hashbrown 0.14.5", @@ -1399,7 +1399,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.8.49" +version = "0.8.50" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 35f354d6..239521ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ members = [ ] [workspace.package] -version = "0.8.49" +version = "0.8.50" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_builtins/src/symbol.rs b/crates/oxide_builtins/src/symbol.rs index 626ae43f..7033cea5 100644 --- a/crates/oxide_builtins/src/symbol.rs +++ b/crates/oxide_builtins/src/symbol.rs @@ -90,7 +90,7 @@ pub fn symbol_to_string(vm: &mut H, args: &[u8]) -> NativeResult { Err(e) => return NativeResult::Err(e), }; let idx = sym.as_symbol_index(); - let desc = vm.symbol_description(idx).unwrap_or("").to_string(); + let desc = vm.symbol_description(idx).unwrap_or_default(); let result = format!("Symbol({})", desc); NativeResult::Ok(vm.new_string_owned(result)) } @@ -129,7 +129,7 @@ pub fn symbol_description_getter(vm: &mut H, args: &[u8]) -> NativeRe }; let idx = sym.as_symbol_index(); match vm.symbol_description(idx) { - Some(desc) => NativeResult::Ok(vm.new_string_owned(desc.to_string())), + Some(desc) => NativeResult::Ok(vm.new_string_owned(desc)), None => NativeResult::Ok(JsValue::undefined()), } } diff --git a/crates/oxide_kernel/src/kernel/core.rs b/crates/oxide_kernel/src/kernel/core.rs index 9ee05d02..0bf2ccf1 100644 --- a/crates/oxide_kernel/src/kernel/core.rs +++ b/crates/oxide_kernel/src/kernel/core.rs @@ -4,7 +4,7 @@ use std::num::NonZeroUsize; use std::path::PathBuf; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering}; use std::sync::Arc; use crate::code_forge::CodeForge; @@ -30,6 +30,10 @@ pub struct KernelCore { /// `Vm` 构造器增、`Drop for Vm` 减(恰好一次),供 sweep / kernel 重建 / /// kernel drop 处断言"无存活 VM"边界。 active_vms: AtomicUsize, + /// realm 编号计数器:进程内单调递增,`alloc_realm_id` 取当前值后自增。 + /// 首个 realm 编号为 0(与旧符号编码一致),至多 2^9 = 512 个 realm + /// (属性键符号空间 29 位减局部下标 20 位)。 + realm_counter: AtomicU32, } impl KernelCore { @@ -59,6 +63,7 @@ impl KernelCore { code_forge, prop_forge, active_vms: AtomicUsize::new(0), + realm_counter: AtomicU32::new(0), }); kernel_info!("KernelCore initialized: max_cached_modules={}, min_pool={}", max_cached, min_pool); core @@ -188,6 +193,15 @@ impl KernelCore { pub fn active_vms(&self) -> usize { self.active_vms.load(Ordering::Relaxed) } + + /// 分配一个 realm 编号:取计数器当前值后自增,进程内单调递增。 + /// + /// 首个 realm 编号为 0(与旧符号编码一致),供 `Realm` 构造时固化。 + /// 编号空间至多 2^9 = 512(属性键符号空间约束),超出后回绕(debug 断言不触发, + /// 实际单进程 realm 数远小于该上界)。 + pub fn alloc_realm_id(&self) -> u32 { + self.realm_counter.fetch_add(1, Ordering::Relaxed) + } } impl Drop for KernelCore { diff --git a/crates/oxide_runtime_api/src/coercion.rs b/crates/oxide_runtime_api/src/coercion.rs index d33d47c5..ab8223c8 100644 --- a/crates/oxide_runtime_api/src/coercion.rs +++ b/crates/oxide_runtime_api/src/coercion.rs @@ -986,7 +986,7 @@ fn string_to_bigint_full(host: &mut H, s: &str) -> Result(val: JsValue, host: &mut H) -> Result { // 原始 Symbol:步骤 2a,SymbolDescriptiveString(不经 ToString)。 if val.is_symbol() { - let desc = host.symbol_description(val.as_symbol_index()).unwrap_or(""); + let desc = host.symbol_description(val.as_symbol_index()).unwrap_or_default(); return Ok(format!("Symbol({desc})")); } // 步骤 3:ToString。装箱 Symbol 经 ToPrimitive 得 Symbol 本体,ToString 抛 TypeError。 diff --git a/crates/oxide_runtime_api/src/host.rs b/crates/oxide_runtime_api/src/host.rs index 4fa8b473..f53ec614 100644 --- a/crates/oxide_runtime_api/src/host.rs +++ b/crates/oxide_runtime_api/src/host.rs @@ -258,9 +258,14 @@ pub trait VmHost { /// 动态编译脚本(`eval` 字符串模式):按脚本模式编译,var/函数声明落全局对象。 /// 编译或解析失败返回 `Err`,由调用方转为 `SyntaxError`。 fn create_dynamic_script(&mut self, code: &str) -> Result; + /// 读取本 VM 所属 realm 的编号(符号身份 = (realm 编号, 局部下标) 的 realm 维度)。 + /// + /// 供 `oxide_builtins` 侧构造 realm 感知符号键 / 符号值时取 realm 编号。 + fn realm_id(&self) -> u32; /// `None` 表示无描述(`Symbol()`/`Symbol(undefined)`),`Some(desc)` 为字符串描述。 fn symbol_intern(&mut self, desc: Option) -> u32; - fn symbol_description(&self, idx: u32) -> Option<&str>; + /// 符号描述字符串(符号表入 realm 后数据在 `RefCell` 内,返回克隆串免临时 `Ref` 悬垂)。 + fn symbol_description(&self, idx: u32) -> Option; fn symbol_lookup_global(&self, key: &str) -> Option; fn symbol_register_global(&mut self, key: String, idx: u32); fn symbol_key_for_id(&self, idx: u32) -> Option; diff --git a/crates/oxide_types/src/private_key.rs b/crates/oxide_types/src/private_key.rs index 93a1e1bc..dba16c35 100644 --- a/crates/oxide_types/src/private_key.rs +++ b/crates/oxide_types/src/private_key.rs @@ -173,5 +173,59 @@ pub const fn well_known_symbol_id_from_key(key: u32) -> Option { } } +/// 符号键局部下标占用的位数(低 20 位)。 +/// +/// 每 realm 至多 2^20 个符号,充足;realm 编号占高位(属性键符号空间 29 位 +/// 减 20 位,至多 2^9 = 512 个 realm)。 +pub const SYMBOL_LOCAL_INDEX_BITS: u32 = 20; + +/// 符号键局部下标的位掩码(低 20 位)。 +pub const SYMBOL_LOCAL_INDEX_MASK: u32 = (1 << SYMBOL_LOCAL_INDEX_BITS) - 1; + +/// 把 (realm 编号, 全局下标) 编码为属性键。 +/// +/// 全局下标沿用现有口径(0..14 well-known、15 起用户符号);编码时按现有口径 +/// 叠加 well-known 预留槽偏移(用户符号下标再加一次偏移),realm 编号前置到 +/// 高位。realm 编号为 0 时与旧编码([`make_well_known_symbol_key`] / +/// [`make_symbol_key`])逐字节一致。 +/// +/// # 边界与前提 +/// - `realm_id < 2^9`(属性键符号空间 29 位减局部下标 20 位),`global_idx` +/// 的键偏移须落在 20 位内(每 realm 至多 2^20 个符号)。 +#[inline] +pub const fn encode_symbol_key(realm_id: u32, global_idx: u32) -> u32 { + let offset = if global_idx < WELL_KNOWN_SYMBOL_COUNT { + global_idx + } else { + WELL_KNOWN_SYMBOL_COUNT + global_idx + }; + SYMBOL_KEY_BASE | ((realm_id << SYMBOL_LOCAL_INDEX_BITS) | (offset & SYMBOL_LOCAL_INDEX_MASK)) +} + +/// 从属性键反解 realm 编号(与 [`encode_symbol_key`] 的 realm 维度互逆)。 +#[inline] +pub const fn symbol_realm_id_from_key(key: u32) -> u32 { + (key - SYMBOL_KEY_BASE) >> SYMBOL_LOCAL_INDEX_BITS +} + +/// 从属性键反解全局下标(与 [`encode_symbol_key`] 的下标维度互逆)。 +/// +/// 按现有口径反解:well-known 区间直取,用户区间减一次 well-known 预留槽偏移。 +#[inline] +pub const fn symbol_local_index_from_key(key: u32) -> u32 { + let local = (key - SYMBOL_KEY_BASE) & SYMBOL_LOCAL_INDEX_MASK; + if local < WELL_KNOWN_SYMBOL_COUNT { + local + } else { + local - WELL_KNOWN_SYMBOL_COUNT + } +} + +/// 把属性键反解为 (realm 编号, 全局下标)(与 [`encode_symbol_key`] 互逆)。 +#[inline] +pub const fn decode_symbol_key(key: u32) -> (u32, u32) { + (symbol_realm_id_from_key(key), symbol_local_index_from_key(key)) +} + #[cfg(test)] mod tests; diff --git a/crates/oxide_types/src/private_key/tests.rs b/crates/oxide_types/src/private_key/tests.rs index 5b04a384..305b79bd 100644 --- a/crates/oxide_types/src/private_key/tests.rs +++ b/crates/oxide_types/src/private_key/tests.rs @@ -74,3 +74,36 @@ fn int_key_roundtrip_max() { assert!(key < PRIVATE_NAME_BASE); assert_eq!(int_key_value(key), INT_KEY_COUNT - 1); } + +#[test] +fn realm_zero_symbol_keys_match_legacy() { + // realm 编号为 0 时新编码与旧编码逐字节一致:well-known 与用户符号各取若干下标比对。 + for id in 0..WELL_KNOWN_SYMBOL_COUNT { + assert_eq!(encode_symbol_key(0, id), make_well_known_symbol_key(id)); + } + for idx in WELL_KNOWN_SYMBOL_COUNT..(WELL_KNOWN_SYMBOL_COUNT + 8) { + assert_eq!(encode_symbol_key(0, idx), make_symbol_key(idx)); + } +} + +#[test] +fn symbol_key_realm_roundtrip() { + for realm in [0u32, 1, 7, 511] { + for idx in [0u32, 14, 15, 16, 1000] { + let key = encode_symbol_key(realm, idx); + assert!(is_symbol_key(key)); + assert_eq!(decode_symbol_key(key), (realm, idx)); + assert_eq!(symbol_realm_id_from_key(key), realm); + assert_eq!(symbol_local_index_from_key(key), idx); + } + } +} + +#[test] +fn symbol_key_realm_dimensions_distinct() { + // 同下标不同 realm 产生不同键;同 realm 不同下标产生不同键。 + assert_ne!(encode_symbol_key(0, 15), encode_symbol_key(1, 15)); + assert_ne!(encode_symbol_key(1, 15), encode_symbol_key(1, 16)); + // 跨 realm 同名 well-known 符号也产生不同键。 + assert_ne!(encode_symbol_key(0, WELL_KNOWN_SYMBOL_ITERATOR), encode_symbol_key(2, WELL_KNOWN_SYMBOL_ITERATOR)); +} diff --git a/crates/oxide_types/src/value.rs b/crates/oxide_types/src/value.rs index 8f1847d5..ed80e14a 100644 --- a/crates/oxide_types/src/value.rs +++ b/crates/oxide_types/src/value.rs @@ -364,6 +364,18 @@ impl JsValue { Self(make_tag(TAG_SYMBOL) | (index as u64)) } + /// 构造 realm 感知 symbol 值(payload = (realm 编号 << 20) | 全局下标)。 + /// + /// 全局下标沿用现有口径(0..14 well-known、15 起用户符号)。realm 编号为 + /// 0 时与 [`symbol`](Self::symbol) 逐字节一致。 + /// + /// # 边界与前提 + /// - `realm_id < 2^12`(payload 32 位减局部下标 20 位),`local_index` + /// 须落在 20 位内(每 realm 至多 2^20 个符号)。 + pub fn symbol_realm(realm_id: u32, local_index: u32) -> Self { + Self(make_tag(TAG_SYMBOL) | (((realm_id << 20) | (local_index & 0x000F_FFFF)) as u64)) + } + /// 是否为 symbol。 pub fn is_symbol(&self) -> bool { is_nan_boxed(self.0) && get_tag(self.0) == TAG_SYMBOL @@ -375,6 +387,19 @@ impl JsValue { (self.0 & INT_MASK) as u32 } + /// 解出 symbol 的 realm 编号;调用方须先保证 [`is_symbol`](JsValue::is_symbol)。 + pub fn as_symbol_realm_id(&self) -> u32 { + debug_assert!(self.is_symbol()); + ((self.0 & INT_MASK) >> 20) as u32 + } + + /// 解出 symbol 的全局下标(0..14 well-known、15 起用户符号); + /// 调用方须先保证 [`is_symbol`](JsValue::is_symbol)。 + pub fn as_symbol_local_index(&self) -> u32 { + debug_assert!(self.is_symbol()); + ((self.0 & INT_MASK) & 0x000F_FFFF) as u32 + } + /// 构造 BigInt 值(payload 为堆分配 `i128` 的 48 位指针)。 /// /// 与 `JsString` 同机制:`i128` 本体存于 VM 管理的堆 box,这里携带指针。 diff --git a/crates/oxide_vm/src/realm.rs b/crates/oxide_vm/src/realm.rs index 3bba844d..c7dbb604 100644 --- a/crates/oxide_vm/src/realm.rs +++ b/crates/oxide_vm/src/realm.rs @@ -19,7 +19,7 @@ use oxide_types::mem::P; use oxide_types::object::JsObject; use crate::session_gc::SessionGc; -use crate::vm_state::GcState; +use crate::vm_state::{GcState, SymbolState}; /// 每 VM 的 realm 组合:内核会话(builtin world 与 global 对象)、 /// session GC 簿记与 10 个内建原型槽。 @@ -30,8 +30,12 @@ use crate::vm_state::GcState; /// 透传。 /// /// 字段顺序按原 `Vm` 声明顺序(session 先、10 个 P 字段次之、gc 最后), -/// 保 Drop 相对顺序。 +/// 保 Drop 相对顺序。`realm_id` 是分配时固化的编号(无 Drop 关切); +/// `symbols` 是 per-realm 符号表(经 `RefCell` 内部可变性,与 session/gc 同型)。 pub(crate) struct Realm { + /// realm 编号:构造时经 `KernelCore::alloc_realm_id` 分配,进程内单调递增, + /// 首个 realm 编号为 0(与旧符号编码一致)。符号身份 = (realm 编号, 局部下标)。 + pub(crate) realm_id: u32, pub(crate) session: RefCell, /// `%Object.prototype%`:session 的 Object 原型(global 的 `[[Prototype]]` 挂它)。 pub(crate) object_prototype: RefCell>, @@ -56,6 +60,9 @@ pub(crate) struct Realm { pub(crate) async_generator_function_proto: RefCell>, /// session 堆与 GC 簿记(对象/字符串/BigInt/cell 四表 + 水位与账目)。 pub(crate) gc: RefCell, + /// per-realm 符号表:well-known 名表 + 用户符号描述 + `Symbol.for` 全局注册表。 + /// 只持 Rust `String` 与 `u32` 下标,无 GC 根,移入 realm 后 `Send` 性不变。 + pub(crate) symbols: RefCell, } impl Drop for Realm { diff --git a/crates/oxide_vm/src/vm/gc_hooks.rs b/crates/oxide_vm/src/vm/gc_hooks.rs index 31501cd7..3ae9956f 100644 --- a/crates/oxide_vm/src/vm/gc_hooks.rs +++ b/crates/oxide_vm/src/vm/gc_hooks.rs @@ -304,7 +304,7 @@ impl Vm { /// 全局 symbol 注册表中已注册的 key 数量。 pub fn symbol_registry_len(&self) -> usize { - self.symbols.registry_len() + self.realm.symbols.borrow().registry_len() } /// inline cache 命中次数。 diff --git a/crates/oxide_vm/src/vm/mod.rs b/crates/oxide_vm/src/vm/mod.rs index 885ee0d2..eac8279e 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -19,7 +19,7 @@ pub use crate::bindings::init_kernel_builtins; use crate::native::NativeFn; use crate::realm::Realm; use crate::vm_debug; -use crate::vm_state::{IterState, ProfilingState, SampleState, SymbolState}; +use crate::vm_state::{IterState, ProfilingState, SampleState}; use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::error::JsErrorKind; use oxide_types::object::{Cell, JsObject, JsString, NativeFnPtr}; @@ -341,8 +341,6 @@ pub struct Vm { /// 异步生成器 body `dispatch()` 的 AWAIT 让出信号:置 true 表示挂起在 await, /// 恢复方(异步生成器内嵌 dispatch 循环)据此快照挂起状态。 pub(crate) async_gen_suspended: bool, - /// 分组保存 `Symbol` intern 状态。 - pub(crate) symbols: SymbolState, /// 分组保存活跃的 for-in / for-of 迭代器状态。 pub(crate) iters: IterState, /// 分组保存 inline cache 与指令计数器。 @@ -455,6 +453,11 @@ impl Vm { self.realm.session.borrow() } + /// 读取本 VM 所属 realm 的编号(符号身份 = (realm 编号, 局部下标) 的 realm 维度)。 + pub fn realm_id(&self) -> u32 { + self.realm.realm_id + } + /// 判定裸指针是否指向当前 session 的 `%Object.prototype%`。 pub(crate) fn is_object_prototype(&self, ptr: *const JsObject) -> bool { let proto_ptr = self.realm.session.borrow().builtin_world().object_proto.as_ptr(); diff --git a/crates/oxide_vm/src/vm/vmhost.rs b/crates/oxide_vm/src/vm/vmhost.rs index 9a06b6fb..bbd73f20 100644 --- a/crates/oxide_vm/src/vm/vmhost.rs +++ b/crates/oxide_vm/src/vm/vmhost.rs @@ -220,20 +220,23 @@ impl oxide_runtime_api::VmHost for Vm { fn create_dynamic_script(&mut self, code: &str) -> Result { self.create_dynamic_script(code) } - fn symbol_intern(&mut self, desc: Option) -> u32 { - self.symbols.intern(desc) + fn realm_id(&self) -> u32 { + self.realm_id() } - fn symbol_description(&self, idx: u32) -> Option<&str> { - self.symbols.description(idx) + fn symbol_intern(&mut self, desc: Option) -> u32 { + self.realm.symbols.borrow_mut().intern(desc) + } + fn symbol_description(&self, idx: u32) -> Option { + self.realm.symbols.borrow().description(idx).map(|s| s.to_string()) } fn symbol_lookup_global(&self, key: &str) -> Option { - self.symbols.lookup_global(key) + self.realm.symbols.borrow().lookup_global(key) } fn symbol_register_global(&mut self, key: String, idx: u32) { - self.symbols.register_global(key, idx) + self.realm.symbols.borrow_mut().register_global(key, idx) } fn symbol_key_for_id(&self, idx: u32) -> Option { - self.symbols.key_for_id(idx) + self.realm.symbols.borrow().key_for_id(idx) } fn atomics_new_waiter_promise(&mut self) -> JsValue { self.atomics_new_waiter_promise() diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index 4b45a613..e4287a5c 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -58,6 +58,8 @@ impl Vm { let obj_proto = P::clone(&session.builtin_world().object_proto); // 提前缓存执行期字符串 GC 初始水位(构造后 config 不再变化)。 let gc_threshold = core.config().session_gc_threshold; + // realm 编号在构造时分配并固化:首个 realm 编号为 0(与旧符号编码一致)。 + let realm_id = core.alloc_realm_id(); let mut vm = Self { regs: [JsValue::undefined(); 256], pc: 0, @@ -66,6 +68,7 @@ impl Vm { frames: smallvec::SmallVec::new(), kernel_core: core, realm: Arc::new(Realm { + realm_id, session: RefCell::new(session), object_prototype: RefCell::new(obj_proto), generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), @@ -92,6 +95,11 @@ impl Vm { pending_forced_collect: false, gc_pressure_mode: gc_pressure_mode_from_env(), }), + symbols: RefCell::new(SymbolState { + symbol_counter: 0, + symbol_descriptions: Vec::new(), + symbol_registry: std::collections::HashMap::new(), + }), }), length_si, length_perm_ptr, @@ -153,11 +161,6 @@ impl Vm { async_gen_context: None, async_gen_dispatch: false, async_gen_suspended: false, - symbols: SymbolState { - symbol_counter: 0, - symbol_descriptions: Vec::new(), - symbol_registry: std::collections::HashMap::new(), - }, iters: IterState { for_in_iters: Vec::new(), for_of_iters: Vec::new(), @@ -205,6 +208,8 @@ impl Vm { let obj_proto = P::clone(&session.builtin_world().object_proto); // 提前缓存执行期字符串 GC 初始水位(构造后 config 不再变化)。 let gc_threshold = core.config().session_gc_threshold; + // realm 编号在构造时分配并固化:首个 realm 编号为 0(与旧符号编码一致)。 + let realm_id = core.alloc_realm_id(); let mut vm = Self { regs: [JsValue::undefined(); 256], pc: 0, @@ -213,6 +218,7 @@ impl Vm { frames: smallvec::SmallVec::new(), kernel_core: core, realm: Arc::new(Realm { + realm_id, session: RefCell::new(session), object_prototype: RefCell::new(obj_proto), generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), @@ -239,6 +245,11 @@ impl Vm { pending_forced_collect: false, gc_pressure_mode: gc_pressure_mode_from_env(), }), + symbols: RefCell::new(SymbolState { + symbol_counter: 0, + symbol_descriptions: Vec::new(), + symbol_registry: std::collections::HashMap::new(), + }), }), length_si, length_perm_ptr, @@ -300,11 +311,6 @@ impl Vm { async_gen_context: None, async_gen_dispatch: false, async_gen_suspended: false, - symbols: SymbolState { - symbol_counter: 0, - symbol_descriptions: Vec::new(), - symbol_registry: std::collections::HashMap::new(), - }, iters: IterState { for_in_iters: Vec::new(), for_of_iters: Vec::new(), @@ -450,7 +456,7 @@ impl Vm { let threshold = self.realm.gc.borrow().gc_threshold_cached; self.realm.gc.borrow_mut().gc_watermark = threshold; self.realm.gc.borrow_mut().session_gc = crate::session_gc::SessionGc::new(); - self.symbols.reset(); + self.realm.symbols.borrow_mut().reset(); self.root_reg_limit = 0; self.active_reg_limit = 0; // 步数上限覆盖随池回收清位:执行路径按请求必设覆盖(含 None), diff --git a/crates/oxide_vm/src/vm_support/tests.rs b/crates/oxide_vm/src/vm_support/tests.rs index 3710d009..3bc0c8ae 100644 --- a/crates/oxide_vm/src/vm_support/tests.rs +++ b/crates/oxide_vm/src/vm_support/tests.rs @@ -1108,13 +1108,13 @@ fn benign_run_does_not_falsely_dirty_builtins() { #[test] fn full_reset_clears_symbol_state() { let mut vm = Vm::new(); - vm.symbols.intern(Some("shared".to_string())); + vm.realm.symbols.borrow_mut().intern(Some("shared".to_string())); vm.full_reset(); - assert_eq!(vm.symbols.symbol_counter, 0); - assert!(vm.symbols.symbol_descriptions.is_empty()); - assert!(vm.symbols.symbol_registry.is_empty()); + assert_eq!(vm.realm.symbols.borrow().symbol_counter, 0); + assert!(vm.realm.symbols.borrow().symbol_descriptions.is_empty()); + assert!(vm.realm.symbols.borrow().symbol_registry.is_empty()); } /// 编译源文本为模块(供步数上限测试直接 `run` 并检查 `Result`)。