From de770c4f59963511277661b477749afd6a59ffdf Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 16 Sep 2026 16:37:05 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20VM=20=E6=89=A7=E8=A1=8C=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E7=B1=BB=E5=9E=8B=E4=BB=8E=20vm.rs=20=E7=A7=BB?= =?UTF-8?q?=E5=85=A5=20vm=20=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/vm/frames.rs | 198 ++++++++++++++++ crates/oxide_vm/src/vm/inline.rs | 61 +++++ crates/oxide_vm/src/{vm.rs => vm/mod.rs} | 273 +---------------------- crates/oxide_vm/src/vm/tables.rs | 24 ++ 4 files changed, 295 insertions(+), 261 deletions(-) create mode 100644 crates/oxide_vm/src/vm/frames.rs create mode 100644 crates/oxide_vm/src/vm/inline.rs rename crates/oxide_vm/src/{vm.rs => vm/mod.rs} (92%) create mode 100644 crates/oxide_vm/src/vm/tables.rs diff --git a/crates/oxide_vm/src/vm/frames.rs b/crates/oxide_vm/src/vm/frames.rs new file mode 100644 index 00000000..c2d33f4f --- /dev/null +++ b/crates/oxide_vm/src/vm/frames.rs @@ -0,0 +1,198 @@ +//! 调用帧与完成值类型:帧续行方式、压帧实参来源、调用帧、for-in 迭代游标、 +//! try/catch/finally 处理记录,以及 break/continue/return 完成值(含访问器)。 + +use oxide_types::value::JsValue; + +/// 调用帧被挂起后,恢复时需要继续的执行方式。 +/// +/// `AccessorGet` 表示 accessor getter 返回后需把结果写回 `target_reg`; +/// `AccessorSet` 表示 setter 调用完成;`None` 为普通调用返回。 +#[derive(Debug, Clone, Copy)] +pub enum FrameContinuation { + None, + AccessorGet { target_reg: u8 }, + AccessorSet, +} + +/// 压帧实参来源:已物化切片或寄存器连续区间。 +/// +/// `RegRange` 直接引用调用方寄存器文件(CALL/NEW/SUPER_CALL 的实参在寄存器中 +/// 天然连续排列),免去临时堆 `Vec` 物化,每次调用省 1 次分配/释放。 +#[derive(Clone, Copy)] +pub(crate) enum FrameArgs<'a> { + /// 已物化实参:spread 展开、TailCall、accessor 与初始执行路径。 + Slice(&'a [JsValue]), + /// 寄存器连续区间 `regs[first .. first+count)`。 + RegRange { first: u8, count: usize }, +} + +impl FrameArgs<'_> { + /// 实参总数。 + pub(crate) fn len(&self) -> usize { + match self { + FrameArgs::Slice(s) => s.len(), + FrameArgs::RegRange { count, .. } => *count, + } + } +} + +/// 一次函数调用的调用帧:记录返回地址、调用方寄存器窗口与 `this`/`new.target`。 +/// +/// 调用方寄存器窗口在 `save_stack` 中按 `saved_reg_offset` 保存,返回时由 +/// `restore_frame` 恢复;`continuation` 描述 getter/setter 场景的恢复方式。 +pub struct CallFrame { + pub return_addr: usize, + pub function_name: u32, + /// 压帧保存的调用方寄存器窗口长度(= save_stack 中本帧段大小)。 + /// 普通字节码调用按调用点存活上界截断;运行时发起(accessor/内联)为 + /// 调用方 `active_reg_limit` 全量。 + pub caller_reg_limit: u8, + /// 压帧时刻调用方 `active_reg_limit`(真实值):帧恢复时据此还原,与 + /// `caller_reg_limit`(可能被存活上界截断的窗口)解耦。 + pub caller_active_reg_limit: u8, + pub saved_reg_offset: u32, + /// 记录本帧 spill 栈起始长度,作为 SPILL/UNSPILL 的帧边界基址(push 时快照)。 + pub spill_offset: u32, + /// 本帧完整实参在 spill 栈的起始下标(帧恢复时随 spill 区一起截断丢弃)。 + pub arguments_base: u32, + /// 实际传入的实参个数(可大于形参个数)。 + pub arguments_count: u16, + pub saved_this: JsValue, + pub saved_new_target: JsValue, + pub callee: JsValue, + pub construct_result_reg: Option, + pub constructed_this: Option, + pub is_derived_constructor: bool, + /// derived 构造帧是否已成功调用 super()(父构造器正常返回后置位)。 + /// + /// 置位识别依赖 ABI 不变量:`construct_result_reg == Some(254)` 仅 SUPER_CALL + /// 压帧产生(emit 从寄存器 ≥1 分配,254 保留给 this);父构造器抛错走 unwind + /// 弹帧不进 do_return,不置位。此标志取代依赖 regs[254] 值判定 super 状态的 + /// 旧方案——多层继承时中间 derived 帧由 SUPER_CALL 以构造 this 压入, + /// regs[254] 恒非 undefined,值判定三重失效。 + pub super_called: bool, + /// 本帧函数的严格模式标志(来源:模块编译产物 `is_strict`)。 + /// 属性写失败时据此分派:严格模式抛 TypeError,sloppy 静默 no-op。 + pub strict: bool, + pub continuation: FrameContinuation, +} + +/// 一次 `for-in` 迭代的游标:已收集的 key 列表与当前下标。 +/// +/// 每个 key 与它的 intern id 配对保存,使整型下标 key 无需重新 intern 即可排到字符串 key 之前。 +pub struct ForInIter<'bump> { + /// 每个 key 与其字符串 intern id 配对,使 for-in 排序时整型下标 key 无需 + /// 重新 intern 即可排到字符串 key 之前。 + pub keys: bumpalo::collections::Vec<'bump, (JsValue, u32)>, + pub index: usize, +} + +/// try/catch/finally 处理记录,异常展开时用于定位跳转目标与清理范围。 +#[derive(Debug, Clone, Copy)] +pub struct TryHandler { + pub catch_pc: Option, + pub finally_pc: Option, + /// finally 体是否已进入(normal JMP / 异常展开 / 完成穿越都会置位)。 + /// 用标志而非 pc 范围判定"当前是否执行 finally 体":try 体末指令可能因 DCE + /// 紧贴 finally 入口,pc 边界会误判。 + pub finally_active: bool, + pub frame_depth: usize, + /// try 入口时 for_of_iters 的长度,界定异常展开时哪些迭代器需要 IteratorClose。 + pub for_of_depth: usize, +} + +/// 控制流完成:break/continue/return 逃出 finally 域时暂存的完成目标。 +/// +/// 仿 `pending_exception` 的侧通道:finally 执行期间悬挂在此,由 TRY_FINALLY_END +/// 逐个恢复(`remaining_finally` 为仍需穿越的 finally 体数,进入一个递减一个)。 +/// `for_of_count`/`for_in_count` 为逃出时需关闭的迭代器层数:在全部 finally 穿越 +/// 之后、跳转/返回之前执行(规范 §13.7.5.4 的 IteratorClose 在完成值之后)。 +#[derive(Debug, Clone, Copy)] +pub enum Completion { + Break { + target_pc: usize, + remaining_finally: usize, + for_of_count: usize, + for_in_count: usize, + }, + Continue { + target_pc: usize, + remaining_finally: usize, + for_of_count: usize, + for_in_count: usize, + }, + Return { + value: JsValue, + remaining_finally: usize, + for_of_count: usize, + for_in_count: usize, + }, +} + +impl Completion { + /// 仍需穿越的 finally 体数。 + pub fn remaining_finally(&self) -> usize { + match *self { + Completion::Break { remaining_finally, .. } + | Completion::Continue { remaining_finally, .. } + | Completion::Return { remaining_finally, .. } => remaining_finally, + } + } + + /// 逃出时需关闭的 for-of 迭代器层数。 + pub fn for_of_count(&self) -> usize { + match *self { + Completion::Break { for_of_count, .. } + | Completion::Continue { for_of_count, .. } + | Completion::Return { for_of_count, .. } => for_of_count, + } + } + + /// 逃出时需弹出的 for-in 迭代器层数。 + pub fn for_in_count(&self) -> usize { + match *self { + Completion::Break { for_in_count, .. } + | Completion::Continue { for_in_count, .. } + | Completion::Return { for_in_count, .. } => for_in_count, + } + } + + /// 复制并改写剩余 finally 计数(进入一个 finally 后递减)。 + pub fn with_remaining(&self, remaining: usize) -> Completion { + match *self { + Completion::Break { + target_pc, + for_of_count, + for_in_count, + .. + } => Completion::Break { + target_pc, + remaining_finally: remaining, + for_of_count, + for_in_count, + }, + Completion::Continue { + target_pc, + for_of_count, + for_in_count, + .. + } => Completion::Continue { + target_pc, + remaining_finally: remaining, + for_of_count, + for_in_count, + }, + Completion::Return { + value, + for_of_count, + for_in_count, + .. + } => Completion::Return { + value, + remaining_finally: remaining, + for_of_count, + for_in_count, + }, + } + } +} diff --git a/crates/oxide_vm/src/vm/inline.rs b/crates/oxide_vm/src/vm/inline.rs new file mode 100644 index 00000000..68de74bd --- /dev/null +++ b/crates/oxide_vm/src/vm/inline.rs @@ -0,0 +1,61 @@ +//! 内联同步执行核心的堆上快照 `InlineSyncState`;字段登记表宏 +//! `inline_core_fields!` 在 `vm_runtime` 模块。 + +use std::sync::Arc; + +use oxide_bytecode::opcode; +use oxide_types::object::Cell; +use oxide_types::value::JsValue; +use smallvec::SmallVec; + +use super::frames::{CallFrame, Completion, ForInIter, TryHandler}; +use crate::vm_state::ForOfEntry; + +/// `call_bytecode_function_inline` 使用的堆分配快照。 +/// 放在堆上避免 JS 代码链式同步字节码调用(如 sort 比较器、accessor)时 +/// 耗尽 Rust 栈。 +pub(crate) struct InlineSyncState { + /// 寄存器窗口副本:`regs[0..len]`,len ≤ 254(含 RegAlloc 最高合法物理槽 + /// 253)。`regs[254]/[255]` 不在此列,由 `saved_this`/`saved_new_target` + /// 单独保存(callee 也会重写这两个槽)。 + pub(crate) regs: Box<[JsValue]>, + pub(crate) saved_this: JsValue, + pub(crate) saved_new_target: JsValue, + pub(crate) pc: usize, + pub(crate) bytecode: Arc<[opcode::Instr]>, + pub(crate) active_immutables: *const [JsValue], + pub(crate) active_reg_limit: u8, + pub(crate) root_reg_limit: u8, + pub(crate) try_stack: Vec, + pub(crate) frames: SmallVec<[CallFrame; 16]>, + pub(crate) exception_value: Option, + pub(crate) pending_exception: Option, + pub(crate) pending_error_kind: Option<&'static str>, + pub(crate) pending_completion: Option, + pub(crate) for_in_iters: Vec<*mut ForInIter<'static>>, + pub(crate) for_of_iters: Vec, + pub(crate) saved_bytecode_stack: Vec>, + pub(crate) saved_immutables_stack: Vec<*const [JsValue]>, + pub(crate) save_stack: Vec, + pub(crate) spill_stack: Vec, + pub(crate) cell_stack: Vec>, + pub(crate) inline_callee: Option, + /// 三个内嵌 dispatch 调度标志(`Vm::generator_dispatch` / `async_dispatch` / + /// `construct_dispatch`)的属主快照。save 时记录外层值并清零 VM 侧、restore + /// 时写回:嵌套 state-swap 调用不得继承外层调度上下文,否则其内部构造帧 + /// 弹出后 frames 清空,`do_return` 会把嵌套调用误判为属主内嵌 dispatch 提前 + /// 交付,跳过被调函数剩余字节码(详见 `do_return` 交付条件注释)。 + pub(crate) generator_dispatch: bool, + pub(crate) async_dispatch: bool, + pub(crate) construct_dispatch: bool, + /// inline 目标函数的严格模式标志(内联执行期间写路径的 strict/sloppy 判定 + /// 来源;嵌套内联时随本快照保存/恢复)。 + pub(crate) inline_strict: bool, + /// inline 起始时 `frames.len()` 基线(嵌套内联时随本快照保存/恢复)。 + pub(crate) inline_frames_base: usize, + pub(crate) inline_args_base: u32, + pub(crate) inline_args_count: u16, + pub(crate) accessor_frame_target_reg: Option, + pub(crate) active_flat_id: u32, + pub(crate) active_table_gen: u32, +} diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm/mod.rs similarity index 92% rename from crates/oxide_vm/src/vm.rs rename to crates/oxide_vm/src/vm/mod.rs index c0f72f2d..dbf3b425 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -12,7 +12,9 @@ use smallvec::SmallVec; pub use crate::bindings::init_kernel_builtins; use crate::native::NativeFn; use crate::session_gc::SessionGc; -use crate::vm_state::{ForOfEntry, GcState, IterState, ProfilingState, SymbolState}; +#[cfg(test)] +use crate::vm_state::ForOfEntry; +use crate::vm_state::{GcState, IterState, ProfilingState, SymbolState}; use crate::{vm_debug, vm_error, vm_trace, vm_warn}; use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_runtime_api as coercion; @@ -25,6 +27,15 @@ use oxide_types::private_key::{ }; use oxide_types::value::{JsValue, PTR_MASK}; +mod frames; +mod inline; +mod tables; + +pub(crate) use frames::FrameArgs; +pub use frames::{CallFrame, Completion, ForInIter, FrameContinuation, TryHandler}; +pub(crate) use inline::InlineSyncState; +pub(crate) use tables::TableGen; + pub(crate) const MAX_PROTO_CHAIN_DEPTH: usize = 1024; /// 判定字符串是否为规范数组下标(无前导零的纯数字串),并反解其值。 @@ -181,266 +192,6 @@ macro_rules! binary_arith { }} } -/// 调用帧被挂起后,恢复时需要继续的执行方式。 -/// -/// `AccessorGet` 表示 accessor getter 返回后需把结果写回 `target_reg`; -/// `AccessorSet` 表示 setter 调用完成;`None` 为普通调用返回。 -#[derive(Debug, Clone, Copy)] -pub enum FrameContinuation { - None, - AccessorGet { target_reg: u8 }, - AccessorSet, -} - -/// 压帧实参来源:已物化切片或寄存器连续区间。 -/// -/// `RegRange` 直接引用调用方寄存器文件(CALL/NEW/SUPER_CALL 的实参在寄存器中 -/// 天然连续排列),免去临时堆 `Vec` 物化,每次调用省 1 次分配/释放。 -#[derive(Clone, Copy)] -pub(crate) enum FrameArgs<'a> { - /// 已物化实参:spread 展开、TailCall、accessor 与初始执行路径。 - Slice(&'a [JsValue]), - /// 寄存器连续区间 `regs[first .. first+count)`。 - RegRange { first: u8, count: usize }, -} - -impl FrameArgs<'_> { - /// 实参总数。 - pub(crate) fn len(&self) -> usize { - match self { - FrameArgs::Slice(s) => s.len(), - FrameArgs::RegRange { count, .. } => *count, - } - } -} - -/// 一次函数调用的调用帧:记录返回地址、调用方寄存器窗口与 `this`/`new.target`。 -/// -/// 调用方寄存器窗口在 `save_stack` 中按 `saved_reg_offset` 保存,返回时由 -/// `restore_frame` 恢复;`continuation` 描述 getter/setter 场景的恢复方式。 -pub struct CallFrame { - pub return_addr: usize, - pub function_name: u32, - /// 压帧保存的调用方寄存器窗口长度(= save_stack 中本帧段大小)。 - /// 普通字节码调用按调用点存活上界截断;运行时发起(accessor/内联)为 - /// 调用方 `active_reg_limit` 全量。 - pub caller_reg_limit: u8, - /// 压帧时刻调用方 `active_reg_limit`(真实值):帧恢复时据此还原,与 - /// `caller_reg_limit`(可能被存活上界截断的窗口)解耦。 - pub caller_active_reg_limit: u8, - pub saved_reg_offset: u32, - /// 记录本帧 spill 栈起始长度,作为 SPILL/UNSPILL 的帧边界基址(push 时快照)。 - pub spill_offset: u32, - /// 本帧完整实参在 spill 栈的起始下标(帧恢复时随 spill 区一起截断丢弃)。 - pub arguments_base: u32, - /// 实际传入的实参个数(可大于形参个数)。 - pub arguments_count: u16, - pub saved_this: JsValue, - pub saved_new_target: JsValue, - pub callee: JsValue, - pub construct_result_reg: Option, - pub constructed_this: Option, - pub is_derived_constructor: bool, - /// derived 构造帧是否已成功调用 super()(父构造器正常返回后置位)。 - /// - /// 置位识别依赖 ABI 不变量:`construct_result_reg == Some(254)` 仅 SUPER_CALL - /// 压帧产生(emit 从寄存器 ≥1 分配,254 保留给 this);父构造器抛错走 unwind - /// 弹帧不进 do_return,不置位。此标志取代依赖 regs[254] 值判定 super 状态的 - /// 旧方案——多层继承时中间 derived 帧由 SUPER_CALL 以构造 this 压入, - /// regs[254] 恒非 undefined,值判定三重失效。 - pub super_called: bool, - /// 本帧函数的严格模式标志(来源:模块编译产物 `is_strict`)。 - /// 属性写失败时据此分派:严格模式抛 TypeError,sloppy 静默 no-op。 - pub strict: bool, - pub continuation: FrameContinuation, -} - -/// 一次 `for-in` 迭代的游标:已收集的 key 列表与当前下标。 -/// -/// 每个 key 与它的 intern id 配对保存,使整型下标 key 无需重新 intern 即可排到字符串 key 之前。 -pub struct ForInIter<'bump> { - /// 每个 key 与其字符串 intern id 配对,使 for-in 排序时整型下标 key 无需 - /// 重新 intern 即可排到字符串 key 之前。 - pub keys: bumpalo::collections::Vec<'bump, (JsValue, u32)>, - pub index: usize, -} - -/// try/catch/finally 处理记录,异常展开时用于定位跳转目标与清理范围。 -#[derive(Debug, Clone, Copy)] -pub struct TryHandler { - pub catch_pc: Option, - pub finally_pc: Option, - /// finally 体是否已进入(normal JMP / 异常展开 / 完成穿越都会置位)。 - /// 用标志而非 pc 范围判定"当前是否执行 finally 体":try 体末指令可能因 DCE - /// 紧贴 finally 入口,pc 边界会误判。 - pub finally_active: bool, - pub frame_depth: usize, - /// try 入口时 for_of_iters 的长度,界定异常展开时哪些迭代器需要 IteratorClose。 - pub for_of_depth: usize, -} - -/// 控制流完成:break/continue/return 逃出 finally 域时暂存的完成目标。 -/// -/// 仿 `pending_exception` 的侧通道:finally 执行期间悬挂在此,由 TRY_FINALLY_END -/// 逐个恢复(`remaining_finally` 为仍需穿越的 finally 体数,进入一个递减一个)。 -/// `for_of_count`/`for_in_count` 为逃出时需关闭的迭代器层数:在全部 finally 穿越 -/// 之后、跳转/返回之前执行(规范 §13.7.5.4 的 IteratorClose 在完成值之后)。 -#[derive(Debug, Clone, Copy)] -pub enum Completion { - Break { - target_pc: usize, - remaining_finally: usize, - for_of_count: usize, - for_in_count: usize, - }, - Continue { - target_pc: usize, - remaining_finally: usize, - for_of_count: usize, - for_in_count: usize, - }, - Return { - value: JsValue, - remaining_finally: usize, - for_of_count: usize, - for_in_count: usize, - }, -} - -impl Completion { - /// 仍需穿越的 finally 体数。 - pub fn remaining_finally(&self) -> usize { - match *self { - Completion::Break { remaining_finally, .. } - | Completion::Continue { remaining_finally, .. } - | Completion::Return { remaining_finally, .. } => remaining_finally, - } - } - - /// 逃出时需关闭的 for-of 迭代器层数。 - pub fn for_of_count(&self) -> usize { - match *self { - Completion::Break { for_of_count, .. } - | Completion::Continue { for_of_count, .. } - | Completion::Return { for_of_count, .. } => for_of_count, - } - } - - /// 逃出时需弹出的 for-in 迭代器层数。 - pub fn for_in_count(&self) -> usize { - match *self { - Completion::Break { for_in_count, .. } - | Completion::Continue { for_in_count, .. } - | Completion::Return { for_in_count, .. } => for_in_count, - } - } - - /// 复制并改写剩余 finally 计数(进入一个 finally 后递减)。 - pub fn with_remaining(&self, remaining: usize) -> Completion { - match *self { - Completion::Break { - target_pc, - for_of_count, - for_in_count, - .. - } => Completion::Break { - target_pc, - remaining_finally: remaining, - for_of_count, - for_in_count, - }, - Completion::Continue { - target_pc, - for_of_count, - for_in_count, - .. - } => Completion::Continue { - target_pc, - remaining_finally: remaining, - for_of_count, - for_in_count, - }, - Completion::Return { - value, - for_of_count, - for_in_count, - .. - } => Completion::Return { - value, - remaining_finally: remaining, - for_of_count, - for_in_count, - }, - } - } -} - -/// `call_bytecode_function_inline` 使用的堆分配快照。 -/// 放在堆上避免 JS 代码链式同步字节码调用(如 sort 比较器、accessor)时 -/// 耗尽 Rust 栈。 -pub(crate) struct InlineSyncState { - /// 寄存器窗口副本:`regs[0..len]`,len ≤ 254(含 RegAlloc 最高合法物理槽 - /// 253)。`regs[254]/[255]` 不在此列,由 `saved_this`/`saved_new_target` - /// 单独保存(callee 也会重写这两个槽)。 - pub(crate) regs: Box<[JsValue]>, - pub(crate) saved_this: JsValue, - pub(crate) saved_new_target: JsValue, - pub(crate) pc: usize, - pub(crate) bytecode: Arc<[opcode::Instr]>, - pub(crate) active_immutables: *const [JsValue], - pub(crate) active_reg_limit: u8, - pub(crate) root_reg_limit: u8, - pub(crate) try_stack: Vec, - pub(crate) frames: SmallVec<[CallFrame; 16]>, - pub(crate) exception_value: Option, - pub(crate) pending_exception: Option, - pub(crate) pending_error_kind: Option<&'static str>, - pub(crate) pending_completion: Option, - pub(crate) for_in_iters: Vec<*mut ForInIter<'static>>, - pub(crate) for_of_iters: Vec, - pub(crate) saved_bytecode_stack: Vec>, - pub(crate) saved_immutables_stack: Vec<*const [JsValue]>, - pub(crate) save_stack: Vec, - pub(crate) spill_stack: Vec, - pub(crate) cell_stack: Vec>, - pub(crate) inline_callee: Option, - /// 三个内嵌 dispatch 调度标志(`Vm::generator_dispatch` / `async_dispatch` / - /// `construct_dispatch`)的属主快照。save 时记录外层值并清零 VM 侧、restore - /// 时写回:嵌套 state-swap 调用不得继承外层调度上下文,否则其内部构造帧 - /// 弹出后 frames 清空,`do_return` 会把嵌套调用误判为属主内嵌 dispatch 提前 - /// 交付,跳过被调函数剩余字节码(详见 `do_return` 交付条件注释)。 - pub(crate) generator_dispatch: bool, - pub(crate) async_dispatch: bool, - pub(crate) construct_dispatch: bool, - /// inline 目标函数的严格模式标志(内联执行期间写路径的 strict/sloppy 判定 - /// 来源;嵌套内联时随本快照保存/恢复)。 - pub(crate) inline_strict: bool, - /// inline 起始时 `frames.len()` 基线(嵌套内联时随本快照保存/恢复)。 - pub(crate) inline_frames_base: usize, - pub(crate) inline_args_base: u32, - pub(crate) inline_args_count: u16, - pub(crate) accessor_frame_target_reg: Option, - pub(crate) active_flat_id: u32, - pub(crate) active_table_gen: u32, -} - -/// 单代子模块平表:模块平表 + 每模块不可变常量缓存,注册进 `Vm::tables` 按代际管理。 -/// -/// 以 `Box` 承载(HashMap 扩容搬 Box 指针不搬被指物):`active_immutables` 与 -/// `saved_immutables_stack` 是伸入 immutables 内部常量 Vec 的胖指针,表地址须 -/// 在其整个生命周期内稳定。动态扩表(`create_dynamic_function` / -/// `create_dynamic_script`)对 modules 外层 Arc 做 `make_mut`:彼时该 Arc 的 -/// 强引用唯一持有者是本表(函数对象只持 u32 下标不持 Arc),原地扩展不分叉。 -pub(crate) struct TableGen { - /// 全局扁平模块表:下标 = 模块 `flat_id`(顶层 0,子模块 flatten 后全局唯一)。 - /// 函数对象 `sub_module_index` 即该平表下标,逃逸函数也能自足解析。 - pub(crate) modules: Arc>>, - /// 每次 run 转换一次的不可变常量缓存:下标 0 = 顶层模块,sub_idx = modules[sub_idx]。 - /// 每个 `OnceLock` 保存该模块常量本次运行中只转换一次的 `JsValue` 结果。 - /// 不可变常量是标量 + perm 字符串,只读,GC 根收集按表代际遍历。 - pub(crate) immutables: Vec>>, -} - /// 基于寄存器的 JS 虚拟机:持有执行状态、寄存器文件、调用栈与 session 内存。 /// /// 执行入口为 [`Vm::run`](见 `vm_runtime` 模块);内存模型为 epoch arena + diff --git a/crates/oxide_vm/src/vm/tables.rs b/crates/oxide_vm/src/vm/tables.rs new file mode 100644 index 00000000..837f8284 --- /dev/null +++ b/crates/oxide_vm/src/vm/tables.rs @@ -0,0 +1,24 @@ +//! 子模块平表代际:`TableGen`(模块平表 + 每模块不可变常量缓存),注册进 +//! `Vm::tables` 按代际管理。 + +use std::sync::{Arc, OnceLock}; + +use oxide_bytecode::module::CompiledModule; +use oxide_types::value::JsValue; + +/// 单代子模块平表:模块平表 + 每模块不可变常量缓存,注册进 `Vm::tables` 按代际管理。 +/// +/// 以 `Box` 承载(HashMap 扩容搬 Box 指针不搬被指物):`active_immutables` 与 +/// `saved_immutables_stack` 是伸入 immutables 内部常量 Vec 的胖指针,表地址须 +/// 在其整个生命周期内稳定。动态扩表(`create_dynamic_function` / +/// `create_dynamic_script`)对 modules 外层 Arc 做 `make_mut`:彼时该 Arc 的 +/// 强引用唯一持有者是本表(函数对象只持 u32 下标不持 Arc),原地扩展不分叉。 +pub(crate) struct TableGen { + /// 全局扁平模块表:下标 = 模块 `flat_id`(顶层 0,子模块 flatten 后全局唯一)。 + /// 函数对象 `sub_module_index` 即该平表下标,逃逸函数也能自足解析。 + pub(crate) modules: Arc>>, + /// 每次 run 转换一次的不可变常量缓存:下标 0 = 顶层模块,sub_idx = modules[sub_idx]。 + /// 每个 `OnceLock` 保存该模块常量本次运行中只转换一次的 `JsValue` 结果。 + /// 不可变常量是标量 + perm 字符串,只读,GC 根收集按表代际遍历。 + pub(crate) immutables: Vec>>, +}