feat: using 声明登记释放栈指令与 GC 根

This commit is contained in:
2026-10-09 17:55:08 +00:00
parent 0df3f9d5fd
commit b5ff2c132a
10 changed files with 88 additions and 23 deletions
Generated
+19 -19
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]]
name = "oxide_builtins"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"chrono",
"fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1226,7 +1226,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1235,7 +1235,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1255,7 +1255,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1269,7 +1269,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1284,7 +1284,7 @@ dependencies = [
[[package]]
name = "oxide_engine"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1296,7 +1296,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1305,7 +1305,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"bumpalo",
"dashmap",
@@ -1319,7 +1319,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1331,7 +1331,7 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"tracing",
"tracing-appender",
@@ -1340,7 +1340,7 @@ dependencies = [
[[package]]
name = "oxide_parser"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1355,7 +1355,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1371,7 +1371,7 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"num-bigint",
"num-traits",
@@ -1384,7 +1384,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1402,7 +1402,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"bumpalo",
"dashmap",
@@ -1415,7 +1415,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.13.14"
version = "0.13.15"
dependencies = [
"bumpalo",
"chrono",
+1 -1
View File
@@ -23,7 +23,7 @@ members = [
]
[workspace.package]
version = "0.13.14"
version = "0.13.15"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+6
View File
@@ -297,6 +297,12 @@ define_opcodes! {
FOR_IN_DONE = 0x2B => "FOR_IN_DONE",
def = Some(SlotSpec::Slot(Slot::Rd)), uses = [],
pure = false, jump = false, term = false, ic = false,
// using/await using 声明登记:rd = 资源值寄存器,a = 释放提示(0 = 同步、1 = 异步)。
// 无寄存器 def(值压入 VM 释放栈,释放时机由作用域出口点决定);
// pure = false 防 DCE 删除压栈副作用。
DISPOSE_REGISTER = 0x2C => "DISPOSE_REGISTER",
def = None, uses = [SlotSpec::Slot(Slot::Rd)],
pure = false, jump = false, term = false, ic = false,
FOR_IN_CLEANUP = 0x2D => "FOR_IN_CLEANUP",
def = None, uses = [],
pure = false, jump = false, term = false, ic = false,
@@ -43,6 +43,17 @@ impl Emitter {
}
}
}
// using/await using 声明登记:把资源值压入 VM 释放栈(释放时机由
// 作用域出口点决定,本指令只登记)。a 槽为释放提示:0 = 同步、1 = 异步。
if matches!(decl.kind, VariableDeclarationKind::Using | VariableDeclarationKind::AwaitUsing) {
let hint = u16::from(matches!(decl.kind, VariableDeclarationKind::AwaitUsing));
ctx.inst(Inst::new(
OpCode::DISPOSE_REGISTER,
Operand::Reg(val_reg),
Operand::Imm(hint),
Operand::None,
));
}
} else {
let BindingPattern::BindingIdentifier(bi) = &d.id else {
return Err("destructuring declaration requires an initializer".into());
+3
View File
@@ -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 -3
View File
@@ -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() {
+4
View File
@@ -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` 读取。仅在一次
+12
View File
@@ -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];
+19
View File
@@ -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(), "执行状态清空应清空释放栈");
}
}
+5
View File
@@ -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 调用期间有效,