From b5ff2c132aaac40e98e140f0533dd687282123b9 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Fri, 9 Oct 2026 17:55:08 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20using=20=E5=A3=B0=E6=98=8E=E7=99=BB?= =?UTF-8?q?=E8=AE=B0=E9=87=8A=E6=94=BE=E6=A0=88=E6=8C=87=E4=BB=A4=E4=B8=8E?= =?UTF-8?q?=20GC=20=E6=A0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 38 +++++++++---------- Cargo.toml | 2 +- crates/oxide_bytecode/src/opcode.rs | 6 +++ crates/oxide_emit/src/stmt/declaration/var.rs | 11 ++++++ crates/oxide_vm/src/vm/dispatch_loop.rs | 3 ++ crates/oxide_vm/src/vm/gc_hooks.rs | 11 ++++-- crates/oxide_vm/src/vm/mod.rs | 4 ++ crates/oxide_vm/src/vm_dispatch_misc.rs | 12 ++++++ crates/oxide_vm/src/vm_runtime.rs | 19 ++++++++++ crates/oxide_vm/src/vm_support.rs | 5 +++ 10 files changed, 88 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e0d6242..ed612dd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index 3df1d5af..aae9ea52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.13.14" +version = "0.13.15" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_bytecode/src/opcode.rs b/crates/oxide_bytecode/src/opcode.rs index aae6429c..5567a5b4 100644 --- a/crates/oxide_bytecode/src/opcode.rs +++ b/crates/oxide_bytecode/src/opcode.rs @@ -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, diff --git a/crates/oxide_emit/src/stmt/declaration/var.rs b/crates/oxide_emit/src/stmt/declaration/var.rs index 974287d5..56a4b84d 100644 --- a/crates/oxide_emit/src/stmt/declaration/var.rs +++ b/crates/oxide_emit/src/stmt/declaration/var.rs @@ -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()); diff --git a/crates/oxide_vm/src/vm/dispatch_loop.rs b/crates/oxide_vm/src/vm/dispatch_loop.rs index f6a513c8..dce630a1 100644 --- a/crates/oxide_vm/src/vm/dispatch_loop.rs +++ b/crates/oxide_vm/src/vm/dispatch_loop.rs @@ -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)?; } diff --git a/crates/oxide_vm/src/vm/gc_hooks.rs b/crates/oxide_vm/src/vm/gc_hooks.rs index 7978ca2b..0683dbe8 100644 --- a/crates/oxide_vm/src/vm/gc_hooks.rs +++ b/crates/oxide_vm/src/vm/gc_hooks.rs @@ -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() { diff --git a/crates/oxide_vm/src/vm/mod.rs b/crates/oxide_vm/src/vm/mod.rs index 2f0e203f..0c56b1fa 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -230,6 +230,10 @@ pub struct Vm { /// VM 级 spill 栈。`CallFrame.spill_offset` 定位本帧区:调用子函数时从边界后分配, /// 帧恢复时截断到边界,子函数 spill 数据随帧丢弃。 pub(crate) spill_stack: Vec, + /// using/await using 声明的资源释放栈:DISPOSE_REGISTER 把资源值压入, + /// 作用域出口点逆序释放(释放点由后续指令族承担)。栈内 JsValue 是 GC 根 + /// (`for_each_value` 遍历);run 边界清空,防跨 run 残留条目在下一 run 被误释放。 + pub(crate) dispose_stack: Vec, /// 本次 native 调用的 spill 溢出实参区:`spill_stack[base..base+count)`。 /// 实参数超过寄存器窗口(253)时,窗口外的实参转存 spill 栈(GC 根), /// native 侧经 `VmHost::native_arg_count`/`native_arg_at` 读取。仅在一次 diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index 80f4e3ca..fa9feb42 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -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]; diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index a744a49a..aa4871d1 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -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(), "执行状态清空应清空释放栈"); + } } diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index 49391226..be613210 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -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 调用期间有效,