mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: using 声明登记释放栈指令与 GC 根
This commit is contained in:
@@ -286,6 +286,9 @@ impl Vm {
|
||||
OpCode::MAKE_CELL_FRESH => {
|
||||
self.dispatch_make_cell_fresh(rd, instr)?;
|
||||
}
|
||||
OpCode::DISPOSE_REGISTER => {
|
||||
self.dispatch_dispose_register(rd, a);
|
||||
}
|
||||
OpCode::CELL_GET => {
|
||||
self.dispatch_cell_get(rd, a, b)?;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use super::frames::Completion;
|
||||
use super::Vm;
|
||||
use crate::session_gc::SessionGc;
|
||||
|
||||
/// GC 根组来源:`for_each_value` 枚举的 25 个根组,变体顺序与根清单
|
||||
/// GC 根组来源:`for_each_value` 枚举的 26 个根组,变体顺序与根清单
|
||||
/// 的枚举顺序一致。
|
||||
///
|
||||
/// 变体下标是逐组计数数组(`SessionGc::root_counts`)的下标;`COUNT` 与
|
||||
@@ -40,11 +40,12 @@ pub(crate) enum RootGroup {
|
||||
ForInIters,
|
||||
Global,
|
||||
WorkerObjects,
|
||||
DisposeStack,
|
||||
}
|
||||
|
||||
impl RootGroup {
|
||||
/// 根组总数(与变体数同源,绑定逐组计数数组的长度)。
|
||||
pub const COUNT: usize = 25;
|
||||
pub const COUNT: usize = 26;
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
@@ -52,7 +53,7 @@ impl Vm {
|
||||
/// 覆盖执行核心的全部 JsValue 持有点:regs/帧/各栈段/cell/在途异常与完成/
|
||||
/// 挂起信号/迭代器/微任务/global/Worker 对象注册表。
|
||||
///
|
||||
/// 闭包接收根组来源(`RootGroup`)与根值:组是枚举顺序中的 25 根组之一,
|
||||
/// 闭包接收根组来源(`RootGroup`)与根值:组是枚举顺序中的 26 根组之一,
|
||||
/// 调用方按组计数(如 `SessionGc` 的逐组计数)。
|
||||
pub(crate) fn for_each_value(&self, mut f: impl FnMut(RootGroup, JsValue)) {
|
||||
for value in &self.regs {
|
||||
@@ -82,6 +83,10 @@ impl Vm {
|
||||
for &v in &self.spill_stack {
|
||||
f(RootGroup::SpillStack, v);
|
||||
}
|
||||
// 释放栈持 using 声明的资源值:漏根 → sweep 释放 → 释放点解引用悬垂。
|
||||
for &v in &self.dispose_stack {
|
||||
f(RootGroup::DisposeStack, v);
|
||||
}
|
||||
for cell_vec in &self.cell_stack {
|
||||
for &cell_ptr in cell_vec {
|
||||
if cell_ptr.is_null() {
|
||||
|
||||
@@ -230,6 +230,10 @@ pub struct Vm {
|
||||
/// VM 级 spill 栈。`CallFrame.spill_offset` 定位本帧区:调用子函数时从边界后分配,
|
||||
/// 帧恢复时截断到边界,子函数 spill 数据随帧丢弃。
|
||||
pub(crate) spill_stack: Vec<JsValue>,
|
||||
/// using/await using 声明的资源释放栈:DISPOSE_REGISTER 把资源值压入,
|
||||
/// 作用域出口点逆序释放(释放点由后续指令族承担)。栈内 JsValue 是 GC 根
|
||||
/// (`for_each_value` 遍历);run 边界清空,防跨 run 残留条目在下一 run 被误释放。
|
||||
pub(crate) dispose_stack: Vec<JsValue>,
|
||||
/// 本次 native 调用的 spill 溢出实参区:`spill_stack[base..base+count)`。
|
||||
/// 实参数超过寄存器窗口(253)时,窗口外的实参转存 spill 栈(GC 根),
|
||||
/// native 侧经 `VmHost::native_arg_count`/`native_arg_at` 读取。仅在一次
|
||||
|
||||
@@ -649,6 +649,18 @@ impl Vm {
|
||||
self.iters.pop_for_in();
|
||||
}
|
||||
|
||||
/// using/await using 声明登记:把资源值(rd 槽)压入释放栈。
|
||||
///
|
||||
/// a 槽是释放提示(0 = 同步、1 = 异步),登记期原样保留供释放点分派;
|
||||
/// 本指令只登记,释放时机由作用域出口点决定。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 释放栈增长(栈内 JsValue 是 GC 根,run 边界清空)。
|
||||
pub(crate) fn dispatch_dispose_register(&mut self, rd: usize, a: usize) {
|
||||
vm_trace!("DISPOSE_REGISTER rd={} hint={}", rd, a);
|
||||
self.dispose_stack.push(self.regs[rd]);
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_for_of_init(&mut self, a: usize) -> Result<(), String> {
|
||||
vm_trace!("FOR_OF_INIT r{}={:?}", a, self.regs[a]);
|
||||
let iterable = self.regs[a];
|
||||
|
||||
@@ -1143,4 +1143,23 @@ mod tests {
|
||||
vm.drain_microtasks();
|
||||
assert!(vm.job_queue.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn using_declaration_registers_dispose_stack() {
|
||||
// using 声明经 DISPOSE_REGISTER 把资源值压入释放栈;run 边界(下次 run
|
||||
// 入口)清栈,上一 run 的残留条目不得跨 run 可见。
|
||||
let mut vm = Vm::new();
|
||||
let module = Arc::new(compile("function f() { using x = { a: 1 }; } f();"));
|
||||
vm.run(&module).expect("run");
|
||||
assert_eq!(vm.dispose_stack.len(), 1, "using 声明应登记一个资源值");
|
||||
assert!(vm.dispose_stack[0].is_object(), "登记值应为资源对象");
|
||||
|
||||
// 二次 run 入口清栈后重新登记:残留条目不跨 run 存活。
|
||||
vm.run(&module).expect("run2");
|
||||
assert_eq!(vm.dispose_stack.len(), 1, "二次 run 应恰好登记一条新条目");
|
||||
|
||||
// 执行状态清空同样清释放栈。
|
||||
vm.clear_execution_state();
|
||||
assert!(vm.dispose_stack.is_empty(), "执行状态清空应清空释放栈");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,7 @@ impl Vm {
|
||||
saved_immutables_stack: Vec::new(),
|
||||
save_stack: Vec::new(),
|
||||
spill_stack: Vec::new(),
|
||||
dispose_stack: Vec::new(),
|
||||
native_overflow_base: 0,
|
||||
native_overflow_count: 0,
|
||||
try_stack: Vec::new(),
|
||||
@@ -291,6 +292,7 @@ impl Vm {
|
||||
saved_immutables_stack: Vec::new(),
|
||||
save_stack: Vec::new(),
|
||||
spill_stack: Vec::new(),
|
||||
dispose_stack: Vec::new(),
|
||||
native_overflow_base: 0,
|
||||
native_overflow_count: 0,
|
||||
try_stack: Vec::new(),
|
||||
@@ -515,6 +517,9 @@ impl Vm {
|
||||
self.saved_immutables_stack.clear();
|
||||
self.save_stack.clear();
|
||||
self.spill_stack.clear();
|
||||
// 释放栈条目持资源值(session 对象指针):跨 run/reset 残留会在下一 run
|
||||
// 被误释放或悬垂,随执行状态一并清空。
|
||||
self.dispose_stack.clear();
|
||||
self.native_overflow_base = 0;
|
||||
self.native_overflow_count = 0;
|
||||
// native pack 实参区上界与溢出描述符同构:仅一次 native 调用期间有效,
|
||||
|
||||
Reference in New Issue
Block a user