mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 18:51:13 +08:00
116 lines
6.1 KiB
Rust
116 lines
6.1 KiB
Rust
//! 子模块平表代际:`TableGen`(模块平表 + 每模块不可变常量缓存)及其表访问器,
|
|
//! 注册进 `Vm::tables` 按代际管理。
|
|
|
|
use std::sync::{Arc, OnceLock};
|
|
|
|
use oxide_bytecode::module::{CompiledModule, Constant};
|
|
use oxide_bytecode::opcode;
|
|
use oxide_types::object::JsObject;
|
|
use oxide_types::value::JsValue;
|
|
|
|
use super::Vm;
|
|
|
|
/// 单代子模块平表:模块平表 + 每模块不可变常量缓存,注册进 `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<Vec<Arc<CompiledModule>>>,
|
|
/// 每次 run 转换一次的不可变常量缓存:下标 0 = 顶层模块,sub_idx = modules[sub_idx]。
|
|
/// 每个 `OnceLock` 保存该模块常量本次运行中只转换一次的 `JsValue` 结果。
|
|
/// 不可变常量是标量 + perm 字符串,只读,GC 根收集按表代际遍历。
|
|
pub(crate) immutables: Vec<OnceLock<Vec<JsValue>>>,
|
|
}
|
|
|
|
impl Vm {
|
|
/// 活动模块已转换不可变常量的只读视图;任何 `run()` 之前为空。
|
|
#[inline(always)]
|
|
pub(crate) fn immutables(&self) -> &[JsValue] {
|
|
if self.active_immutables.is_null() {
|
|
&[]
|
|
} else {
|
|
// SAFETY: active_immutables 指向某表代际 immutables 内 OnceLock 的常量
|
|
// Vec,Vec 只填充一次且堆地址稳定;所指代际表在注册表内存活(当前
|
|
// 代际恒在,旧代际由存活函数对象引用保活)。
|
|
unsafe { &*self.active_immutables }
|
|
}
|
|
}
|
|
|
|
/// 当前代际的子模块平表(可变,动态扩表与测试注入的目标)。
|
|
pub(crate) fn current_table_mut(&mut self) -> &mut TableGen {
|
|
self.tables.get_mut(&self.current_gen).expect("当前代际表构造期已预登记")
|
|
}
|
|
|
|
/// 当前执行帧所属代际的子模块平表:按帧切换时记录的 `active_table_gen`
|
|
/// 解析(压帧 / inline 入口 / run 顶层 / 挂起恢复置位,弹帧还原),跨 run
|
|
/// 调用时可异于 `current_gen`——帧内指令操作数是帧字节码所属代际平表里的
|
|
/// flat_id,须按该代际解析。
|
|
///
|
|
/// # 边界与前提
|
|
/// - 执行帧代际表恒在注册表中:顶层即当前代际(恒保留),帧 / inline 的
|
|
/// 代际由存活 callee 函数对象保活,挂起恢复已按 callee 对象验证在位。
|
|
pub(crate) fn active_table(&self) -> &TableGen {
|
|
self.tables.get(&self.active_table_gen).expect("执行帧的代际表须在注册表中")
|
|
}
|
|
|
|
/// 函数对象 `sub_module_index` 指向的子模块条目:按对象自身记录的表代际
|
|
/// 解析平表后按下标定位。
|
|
///
|
|
/// # 边界与前提
|
|
/// - 原生函数哨兵 `sub_module_index() == 0` 不经本方法(调用方先行分派);
|
|
/// - 代际表已被回收或下标越界返回 None(调用方按各自越界口径报错)。
|
|
pub(crate) fn callee_module<'a>(&'a self, obj: &'a JsObject) -> Option<&'a CompiledModule> {
|
|
self.tables
|
|
.get(&obj.table_gen())
|
|
.and_then(|t| t.modules.get(obj.sub_module_index() as usize))
|
|
.map(|m| m.as_ref())
|
|
}
|
|
|
|
/// 活动帧所属代际平表里 `active_flat_id` 指向的模块条目(镜像槽同步 /
|
|
/// 重载的名集载体)。
|
|
///
|
|
/// # 边界与前提
|
|
/// - 活动代际表在执行期恒在注册表中(首 run 前为预登记的空表占位),
|
|
/// `active_flat_id` 越界返回 None(调用方按无模块处理)。
|
|
pub(crate) fn active_module(&self) -> Option<&CompiledModule> {
|
|
self.active_table()
|
|
.modules
|
|
.get(self.active_flat_id as usize)
|
|
.map(|m| m.as_ref())
|
|
}
|
|
|
|
/// 子模块表注册表的当前代际条目总数(run 边界回收行为的测试钉)。
|
|
pub fn table_gen_count(&self) -> usize {
|
|
self.tables.len()
|
|
}
|
|
|
|
/// 激活代际 `gen` 平表中模块 `cache_idx` 的不可变常量,只转换一次存入该代际
|
|
/// `immutables[cache_idx]`,并把 `active_immutables` 指向该 Vec。
|
|
/// `constants` 由调用方传入(它已持有 `&module.constants`)。
|
|
///
|
|
/// # 边界与前提
|
|
/// - `gen` 的表须已在注册表中(当前代际由 run() 登记,旧代际由存活函数
|
|
/// 对象引用保活);`cache_idx` 须小于该代际平表长度。
|
|
pub(crate) fn activate_immutables(&mut self, gen: u32, cache_idx: usize, constants: &[Constant]) {
|
|
// 用裸指针访问缓存槽,避免 get_or_init(借用表 immutables)与 &self 的
|
|
// convert_immutables 闭包和随后对 active_immutables 的写入发生借用冲突。
|
|
// 成立前提:代际表归注册表所有、只读、Box 承载地址稳定。
|
|
let table = self.tables.get_mut(&gen).expect("激活的代际表须在注册表中");
|
|
let slot: *const OnceLock<Vec<JsValue>> = &table.immutables[cache_idx];
|
|
let vec = unsafe { &*slot }.get_or_init(|| self.convert_immutables(constants));
|
|
self.active_immutables = vec.as_slice() as *const [JsValue];
|
|
}
|
|
|
|
/// 当前活动字节码的可变访问入口。bytecode 以 `Arc<[Instr]>` 与代际表 modules 源共享,
|
|
/// IC 写回经 `Arc::make_mut` 保证独占:独占时零拷贝原地写,共享时先深拷贝再写
|
|
/// (IC miss 才触发,频率低)。所有写操作必须经此方法,防止共享缓冲被多实例污染。
|
|
pub(crate) fn bytecode_mut(&mut self) -> &mut [opcode::Instr] {
|
|
Arc::make_mut(&mut self.bytecode)
|
|
}
|
|
}
|