diff --git a/crates/oxide_vm/src/dispatch/misc.rs b/crates/oxide_vm/src/dispatch/misc.rs index 90cf36c7..3a0faa2d 100644 --- a/crates/oxide_vm/src/dispatch/misc.rs +++ b/crates/oxide_vm/src/dispatch/misc.rs @@ -204,13 +204,13 @@ impl Vm { /// 后续属性值经 SET_PROP_BATCH 纯槽写。 /// /// # 步骤 - /// 1. 逐个键常量取 perm string → `property_key_si`(与逐属性 SET_PROP 路径 - /// 逐字节同键),链式 `make_shape` 累加 shape。 + /// 1. 逐键直读模块 si 侧表(模块装载期 `activate_immutables` 已预 intern, + /// 与逐属性 SET_PROP 路径逐字节同键),链式 `make_shape` 累加 shape。 /// 2. 分配对象、预填充 `nprops` 个数据槽、`generation += nprops`(与逐属性路径终值一致)。 /// /// # 边界与前提 /// - `nprops = 0` 时退化为普通空对象(无 ext 键表)。 - /// - 键常量越界取 undefined 防御(emit 保证合法下标)。 + /// - 侧表条目缺失(防御面,正常路径不出现)回退旧路径按键值重推 si。 #[inline(always)] pub(crate) fn dispatch_new_object(&mut self, rd: usize, instr: u32) -> Result<(), String> { let nprops = opcode::a(instr) as usize; @@ -218,17 +218,23 @@ impl Vm { let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject; let mut shape_id = EMPTY_SHAPE_ID; if nprops > 0 { - // 先把键常量值拷出(immutables 借 self,property_key_si 需 &mut self)。 - let key_vals: Vec = { - let imm = self.immutables(); - self.bytecode[self.pc..self.pc + nprops] - .iter() - .map(|w| imm.get(*w as usize).copied().unwrap_or(JsValue::undefined())) - .collect() - }; + let key_idxs: Vec = self.bytecode[self.pc..self.pc + nprops].to_vec(); self.pc += nprops; - for key_val in key_vals { - let si = self.property_key_si(key_val)?; + // si 侧表与 immutables 同点激活:活动模块的侧表此刻必已填充。 + let si_table = self.active_table().si_tables[self.active_flat_id as usize] + .get() + .expect("活动模块的 si 侧表已随不可变常量激活同步填充"); + let key_si: Vec> = + key_idxs.iter().map(|k| si_table.get(*k as usize).copied().flatten()).collect(); + for (key_idx, si_opt) in key_idxs.iter().zip(key_si.iter()) { + let si = match si_opt { + Some(si) => *si, + None => { + let imm = self.immutables(); + let key_val = imm.get(*key_idx as usize).copied().unwrap_or(JsValue::undefined()); + self.property_key_si(key_val)? + } + }; shape_id = self.kernel_core.shape_forge().make_shape(shape_id, si); } } diff --git a/crates/oxide_vm/src/vm/dynamic.rs b/crates/oxide_vm/src/vm/dynamic.rs index 2e355d1e..a86c615a 100644 --- a/crates/oxide_vm/src/vm/dynamic.rs +++ b/crates/oxide_vm/src/vm/dynamic.rs @@ -78,8 +78,9 @@ impl Vm { let mut added = Vec::new(); rehome_subtree(&anonymous, base, &mut added); Arc::make_mut(&mut table.modules).extend(added); - // 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。 + // 平表变长后同步扩容常量缓存与 si 侧表,否则激活新模块常量时越界 panic。 table.immutables.resize(table.modules.len(), OnceLock::new()); + table.si_tables.resize(table.modules.len(), OnceLock::new()); let func_val = self.create_function_object(base, self.current_gen, false, false, false, false); let func_obj = unsafe { &mut *func_val.as_js_object_ptr() }; @@ -128,8 +129,9 @@ impl Vm { let mut added = Vec::new(); rehome_subtree(&module, base + 1, &mut added); Arc::make_mut(&mut table.modules).extend(added); - // 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。 + // 平表变长后同步扩容常量缓存与 si 侧表,否则激活新模块常量时越界 panic。 table.immutables.resize(table.modules.len(), OnceLock::new()); + table.si_tables.resize(table.modules.len(), OnceLock::new()); Ok(self.create_function_object(base, self.current_gen, false, false, false, false)) } @@ -171,8 +173,9 @@ impl Vm { let mut added = Vec::new(); rehome_subtree(&module, base + 1, &mut added); Arc::make_mut(&mut table.modules).extend(added); - // 平表变长后同步扩容常量缓存,否则激活新模块常量时越界 panic。 + // 平表变长后同步扩容常量缓存与 si 侧表,否则激活新模块常量时越界 panic。 table.immutables.resize(table.modules.len(), OnceLock::new()); + table.si_tables.resize(table.modules.len(), OnceLock::new()); Ok(self.create_function_object(base, self.current_gen, false, false, false, false)) } } diff --git a/crates/oxide_vm/src/vm/mod.rs b/crates/oxide_vm/src/vm/mod.rs index b1f35af7..ab585670 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -484,6 +484,7 @@ impl Vm { let table = self.current_table_mut(); table.modules = modules; table.immutables.resize(table.modules.len(), OnceLock::new()); + table.si_tables.resize(table.modules.len(), OnceLock::new()); } } diff --git a/crates/oxide_vm/src/vm/tables.rs b/crates/oxide_vm/src/vm/tables.rs index 7e6cd02c..53e99a26 100644 --- a/crates/oxide_vm/src/vm/tables.rs +++ b/crates/oxide_vm/src/vm/tables.rs @@ -25,6 +25,11 @@ pub(crate) struct TableGen { /// 每个 `OnceLock` 保存该模块常量本次运行中只转换一次的 `JsValue` 结果。 /// 不可变常量是标量 + perm 字符串,只读,GC 根收集按表代际遍历。 pub(crate) immutables: Vec>>, + /// NEW_OBJECT 静态键 si 侧表,与 `immutables` 平行(下标 = flat_id,内部下标 = + /// 常量池下标):`Some(si)` 是模块装载期预 intern 的键 si,`None` 表示该常量 + /// 不是静态键。填充与 `immutables` 激活同点(`activate_immutables`),构造期 + /// 直读免逐构造键推导。 + pub(crate) si_tables: Vec>>>, } impl Vm { @@ -102,10 +107,44 @@ impl Vm { // 成立前提:代际表归注册表所有、只读、Box 承载地址稳定。 let table = self.tables.get_mut(&gen).expect("激活的代际表须在注册表中"); let slot: *const OnceLock> = &table.immutables[cache_idx]; + // si 侧表与 immutables 同点填充:模块字节码从平表取(同裸指针手法, + // 平表 Arc 归注册表所有、地址稳定),预 intern 静态键 si。 + let si_slot: *const OnceLock>> = &table.si_tables[cache_idx]; + let bc: *const [opcode::Instr] = &table.modules[cache_idx].bytecode[..]; let vec = unsafe { &*slot }.get_or_init(|| self.convert_immutables(constants)); + unsafe { &*si_slot }.get_or_init(|| self.fill_si_table(vec, unsafe { &*bc })); self.active_immutables = vec.as_slice() as *const [JsValue]; } + /// 填充模块的 NEW_OBJECT 静态键 si 侧表:按 dispatch 主循环同口径 + /// (`ext_word_count` 逐指令推进)扫字节码,收集每条带键表 NEW_OBJECT 的 + /// 键常量池下标,按键值经 `property_key_si` 预推导 si 存入侧表 + /// (下标 = 常量池下标),其余常量保持 None。 + /// + /// # 边界与前提 + /// - 键值取自已转换的不可变常量(`values`),与构造期读同一池。 + /// - 键推导失败(非字符串键的防御面)保持 None,构造期回退旧路径同口径重推。 + fn fill_si_table(&mut self, values: &[JsValue], bytecode: &[opcode::Instr]) -> Vec> { + let mut si = vec![None; values.len()]; + let mut i = 0; + while i < bytecode.len() { + let instr = bytecode[i]; + if opcode::opcode(instr) == opcode::OpCode::NEW_OBJECT && opcode::a(instr) > 0 { + let nprops = opcode::a(instr) as usize; + for &w in &bytecode[i + 1..i + 1 + nprops] { + let idx = w as usize; + if idx < si.len() && si[idx].is_none() { + if let Ok(key_si) = self.property_key_si(values[idx]) { + si[idx] = Some(key_si); + } + } + } + } + i += 1 + opcode::ext_word_count(bytecode, i); + } + si + } + /// 当前活动字节码的可变访问入口。bytecode 以 `Arc<[Instr]>` 与代际表 modules 源共享, /// IC 写回经 `Arc::make_mut` 保证独占:独占时零拷贝原地写,共享时先深拷贝再写 /// (IC miss 才触发,频率低)。所有写操作必须经此方法,防止共享缓冲被多实例污染。 diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index 7c0c20d3..e27647c0 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -673,6 +673,7 @@ impl Vm { self.current_gen, Box::new(TableGen { immutables: (0..modules.len()).map(|_| OnceLock::new()).collect(), + si_tables: (0..modules.len()).map(|_| OnceLock::new()).collect(), modules, }), ); diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index c36fb526..5d4a8e53 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -75,6 +75,7 @@ impl Vm { Box::new(TableGen { modules: Arc::new(Vec::new()), immutables: Vec::new(), + si_tables: Vec::new(), }), )]), current_gen: 0, @@ -208,6 +209,7 @@ impl Vm { Box::new(TableGen { modules: Arc::new(Vec::new()), immutables: Vec::new(), + si_tables: Vec::new(), }), )]), current_gen: 0, @@ -389,6 +391,7 @@ impl Vm { Box::new(TableGen { modules: Arc::new(Vec::new()), immutables: Vec::new(), + si_tables: Vec::new(), }), ); self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);