mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 七类作用域出口正常释放 using 资源,for-of 每迭代
This commit is contained in:
Generated
+19
-19
@@ -1160,7 +1160,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_builtins"
|
name = "oxide_builtins"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"fast-float",
|
"fast-float",
|
||||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_bytecode"
|
name = "oxide_bytecode"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cfg"
|
name = "oxide_cfg"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_emit",
|
"oxide_emit",
|
||||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cli"
|
name = "oxide_cli"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
@@ -1226,7 +1226,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_code_cache"
|
name = "oxide_code_cache"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lru",
|
"lru",
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
@@ -1235,7 +1235,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_compiler"
|
name = "oxide_compiler"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1255,7 +1255,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_dce"
|
name = "oxide_dce"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1269,7 +1269,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_emit"
|
name = "oxide_emit"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
@@ -1284,7 +1284,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_engine"
|
name = "oxide_engine"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_compiler",
|
"oxide_compiler",
|
||||||
@@ -1296,7 +1296,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_ir"
|
name = "oxide_ir"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1305,7 +1305,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_kernel"
|
name = "oxide_kernel"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -1319,7 +1319,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_liveness"
|
name = "oxide_liveness"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1331,7 +1331,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_log"
|
name = "oxide_log"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
@@ -1340,7 +1340,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_parser"
|
name = "oxide_parser"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
"oxc_ast",
|
"oxc_ast",
|
||||||
@@ -1355,7 +1355,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_regalloc"
|
name = "oxide_regalloc"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1371,7 +1371,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_runtime_api"
|
name = "oxide_runtime_api"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -1384,7 +1384,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_test262"
|
name = "oxide_test262"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_compiler",
|
"oxide_compiler",
|
||||||
@@ -1402,7 +1402,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_types"
|
name = "oxide_types"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -1415,7 +1415,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_vm"
|
name = "oxide_vm"
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.13.15"
|
version = "0.13.16"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
|
|||||||
@@ -525,6 +525,18 @@ define_opcodes! {
|
|||||||
def = Some(SlotSpec::Slot(Slot::Rd)), uses = [],
|
def = Some(SlotSpec::Slot(Slot::Rd)), uses = [],
|
||||||
pure = false, jump = false, term = false, ic = false,
|
pure = false, jump = false, term = false, ic = false,
|
||||||
|
|
||||||
|
// ── using/await using 释放 (0x6A, 0x6B) ──
|
||||||
|
// 作用域入口水位:把当前释放栈深度压入水位栈(DISPOSE_REGISTER 的配对出口),
|
||||||
|
// 无寄存器 def/use;pure = false 防 DCE 删除水位副作用。
|
||||||
|
DISPOSE_MARK = 0x6A => "DISPOSE_MARK",
|
||||||
|
def = None, uses = [],
|
||||||
|
pure = false, jump = false, term = false, ic = false,
|
||||||
|
// 作用域出口:弹出水位,逆序释放水位以上资源并截断释放栈(规范
|
||||||
|
// DisposeResources),无寄存器 def/use。
|
||||||
|
DISPOSE_POP = 0x6B => "DISPOSE_POP",
|
||||||
|
def = None, uses = [],
|
||||||
|
pure = false, jump = false, term = false, ic = false,
|
||||||
|
|
||||||
// ── define 语义属性写入 (0x6D, 0x6F) ──
|
// ── define 语义属性写入 (0x6D, 0x6F) ──
|
||||||
DEFINE_PROP = 0x6D => "DEFINE_PROP",
|
DEFINE_PROP = 0x6D => "DEFINE_PROP",
|
||||||
def = None, uses = [SlotSpec::Slot(Slot::Rd), SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)],
|
def = None, uses = [SlotSpec::Slot(Slot::Rd), SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)],
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
|
|
||||||
use oxide_bytecode::module::{Constant, UpvalueCapture};
|
use oxide_bytecode::module::{Constant, UpvalueCapture};
|
||||||
|
use oxide_bytecode::opcode::OpCode;
|
||||||
use oxide_ir::inst::Inst;
|
use oxide_ir::inst::Inst;
|
||||||
use oxide_ir::operand::{LabelId, Operand};
|
use oxide_ir::operand::{LabelId, Operand};
|
||||||
use oxide_ir::IRFunction;
|
use oxide_ir::IRFunction;
|
||||||
@@ -238,6 +239,7 @@ impl CompileCtx {
|
|||||||
finally_depth: 0,
|
finally_depth: 0,
|
||||||
for_of_depth: 0,
|
for_of_depth: 0,
|
||||||
for_in_depth: 0,
|
for_in_depth: 0,
|
||||||
|
dispose_depth: 0,
|
||||||
label_counter: 0,
|
label_counter: 0,
|
||||||
},
|
},
|
||||||
scopes: ScopeCtx {
|
scopes: ScopeCtx {
|
||||||
@@ -588,6 +590,7 @@ impl CompileCtx {
|
|||||||
}
|
}
|
||||||
let fod = self.labels.for_of_depth;
|
let fod = self.labels.for_of_depth;
|
||||||
let fid = self.labels.for_in_depth;
|
let fid = self.labels.for_in_depth;
|
||||||
|
let dd = self.labels.dispose_depth;
|
||||||
let v_reg = self.alloc_reg();
|
let v_reg = self.alloc_reg();
|
||||||
let undef_idx = self.add_constant(Constant::Undefined);
|
let undef_idx = self.add_constant(Constant::Undefined);
|
||||||
self.inst(Inst::load_const(Operand::Reg(v_reg), undef_idx));
|
self.inst(Inst::load_const(Operand::Reg(v_reg), undef_idx));
|
||||||
@@ -597,6 +600,7 @@ impl CompileCtx {
|
|||||||
finally_depth_at_open: fd,
|
finally_depth_at_open: fd,
|
||||||
for_of_depth_at_open: fod,
|
for_of_depth_at_open: fod,
|
||||||
for_in_depth_at_open: fid,
|
for_in_depth_at_open: fid,
|
||||||
|
dispose_depth_at_open: dd,
|
||||||
kind,
|
kind,
|
||||||
v_reg,
|
v_reg,
|
||||||
});
|
});
|
||||||
@@ -622,11 +626,13 @@ impl CompileCtx {
|
|||||||
let fd = self.labels.finally_depth;
|
let fd = self.labels.finally_depth;
|
||||||
let fod = self.labels.for_of_depth;
|
let fod = self.labels.for_of_depth;
|
||||||
let fid = self.labels.for_in_depth;
|
let fid = self.labels.for_in_depth;
|
||||||
|
let dd = self.labels.dispose_depth;
|
||||||
self.labels.switch_stack.push(SwitchEntry {
|
self.labels.switch_stack.push(SwitchEntry {
|
||||||
break_label,
|
break_label,
|
||||||
finally_depth_at_open: fd,
|
finally_depth_at_open: fd,
|
||||||
for_of_depth_at_open: fod,
|
for_of_depth_at_open: fod,
|
||||||
for_in_depth_at_open: fid,
|
for_in_depth_at_open: fid,
|
||||||
|
dispose_depth_at_open: dd,
|
||||||
result_reg,
|
result_reg,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -711,6 +717,22 @@ impl CompileCtx {
|
|||||||
self.labels.finally_depth -= 1;
|
self.labels.finally_depth -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 发 DISPOSE_MARK:作用域入口登记释放栈水位,dispose_depth 递增。
|
||||||
|
///
|
||||||
|
/// # 边界与前提
|
||||||
|
/// - 与 `emit_dispose_pop` 成对;编译期不变式是函数体收尾 dispose_depth 归零
|
||||||
|
/// (`assemble_ir` 前 debug_assert 校验)。
|
||||||
|
pub(crate) fn emit_dispose_mark(&mut self) {
|
||||||
|
self.inst(Inst::new(OpCode::DISPOSE_MARK, Operand::None, Operand::None, Operand::None));
|
||||||
|
self.labels.dispose_depth += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发 DISPOSE_POP:作用域出口逆序释放水位以上资源,dispose_depth 递减。
|
||||||
|
pub(crate) fn emit_dispose_pop(&mut self) {
|
||||||
|
self.inst(Inst::new(OpCode::DISPOSE_POP, Operand::None, Operand::None, Operand::None));
|
||||||
|
self.labels.dispose_depth -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
/// 记录一个纯 catch handler 打开(对应 emit try_begin 后的运行时 TRY_BEGIN)。
|
/// 记录一个纯 catch handler 打开(对应 emit try_begin 后的运行时 TRY_BEGIN)。
|
||||||
pub(crate) fn push_open_catch_handler(&mut self) {
|
pub(crate) fn push_open_catch_handler(&mut self) {
|
||||||
self.open_try_handlers.push(true);
|
self.open_try_handlers.push(true);
|
||||||
@@ -743,6 +765,7 @@ impl CompileCtx {
|
|||||||
let fd = self.labels.finally_depth;
|
let fd = self.labels.finally_depth;
|
||||||
let fod = self.labels.for_of_depth;
|
let fod = self.labels.for_of_depth;
|
||||||
let fid = self.labels.for_in_depth;
|
let fid = self.labels.for_in_depth;
|
||||||
|
let dd = self.labels.dispose_depth;
|
||||||
self.labels.label_scopes.push(LabelScope {
|
self.labels.label_scopes.push(LabelScope {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
break_label,
|
break_label,
|
||||||
@@ -750,6 +773,7 @@ impl CompileCtx {
|
|||||||
finally_depth_at_open: fd,
|
finally_depth_at_open: fd,
|
||||||
for_of_depth_at_open: fod,
|
for_of_depth_at_open: fod,
|
||||||
for_in_depth_at_open: fid,
|
for_in_depth_at_open: fid,
|
||||||
|
dispose_depth_at_open: dd,
|
||||||
completion_reg,
|
completion_reg,
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -785,6 +809,7 @@ impl CompileCtx {
|
|||||||
let fd = self.labels.finally_depth;
|
let fd = self.labels.finally_depth;
|
||||||
let fod = self.labels.for_of_depth;
|
let fod = self.labels.for_of_depth;
|
||||||
let fid = self.labels.for_in_depth;
|
let fid = self.labels.for_in_depth;
|
||||||
|
let dd = self.labels.dispose_depth;
|
||||||
for name in names {
|
for name in names {
|
||||||
self.labels.label_scopes.push(LabelScope {
|
self.labels.label_scopes.push(LabelScope {
|
||||||
name,
|
name,
|
||||||
@@ -793,6 +818,7 @@ impl CompileCtx {
|
|||||||
finally_depth_at_open: fd,
|
finally_depth_at_open: fd,
|
||||||
for_of_depth_at_open: fod,
|
for_of_depth_at_open: fod,
|
||||||
for_in_depth_at_open: fid,
|
for_in_depth_at_open: fid,
|
||||||
|
dispose_depth_at_open: dd,
|
||||||
completion_reg: Some(completion_reg),
|
completion_reg: Some(completion_reg),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -921,6 +947,12 @@ impl CompileCtx {
|
|||||||
pub(crate) fn assemble_ir(
|
pub(crate) fn assemble_ir(
|
||||||
&mut self, param_layout: oxide_ir::ParamLayout, parent_ctx: Option<&CompileCtx>,
|
&mut self, param_layout: oxide_ir::ParamLayout, parent_ctx: Option<&CompileCtx>,
|
||||||
) -> IRFunction {
|
) -> IRFunction {
|
||||||
|
// 编译期不变式:每个 DISPOSE_MARK 都有配对的 DISPOSE_POP,函数体收尾
|
||||||
|
// 释放作用域全部关闭(早 return 跳过 POP 归穿越面,不在此列)。
|
||||||
|
debug_assert!(
|
||||||
|
self.labels.dispose_depth == 0,
|
||||||
|
"函数体收尾释放作用域深度须归零(mark/pop 未配对)"
|
||||||
|
);
|
||||||
let upvalue_captures = self
|
let upvalue_captures = self
|
||||||
.current_upvalue_captures
|
.current_upvalue_captures
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ pub(crate) struct LoopEntry {
|
|||||||
/// 循环出口结果寄存器:入口初始 undefined,体正常完成回写,break/continue
|
/// 循环出口结果寄存器:入口初始 undefined,体正常完成回写,break/continue
|
||||||
/// 携值写入(空携值不写,保持前次累积值)。
|
/// 携值写入(空携值不写,保持前次累积值)。
|
||||||
pub(crate) v_reg: u32,
|
pub(crate) v_reg: u32,
|
||||||
|
/// 循环打开时已打开的释放作用域数(break/continue/return 逃出时据此
|
||||||
|
/// 计算需穿越释放的作用域层数)。
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) dispose_depth_at_open: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 完成值帧:编译期帧栈元素,承载「每语句列表独立累积」语义。
|
/// 完成值帧:编译期帧栈元素,承载「每语句列表独立累积」语义。
|
||||||
@@ -83,6 +87,9 @@ pub struct LabelScope {
|
|||||||
pub(crate) for_of_depth_at_open: usize,
|
pub(crate) for_of_depth_at_open: usize,
|
||||||
/// 标签打开时已打开的 for-in 循环数。
|
/// 标签打开时已打开的 for-in 循环数。
|
||||||
pub(crate) for_in_depth_at_open: usize,
|
pub(crate) for_in_depth_at_open: usize,
|
||||||
|
/// 标签打开时已打开的释放作用域数(break 逃出时据此计算需穿越释放的层数)。
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) dispose_depth_at_open: usize,
|
||||||
/// 标签目标出口结果寄存器:迭代标签取所包裹循环的 `v_reg`;非迭代标签仅当
|
/// 标签目标出口结果寄存器:迭代标签取所包裹循环的 `v_reg`;非迭代标签仅当
|
||||||
/// 体含指向本标签的 break 时分配(体正常完成回写、break 携值写入)。
|
/// 体含指向本标签的 break 时分配(体正常完成回写、break 携值写入)。
|
||||||
pub(crate) completion_reg: Option<u32>,
|
pub(crate) completion_reg: Option<u32>,
|
||||||
@@ -96,6 +103,9 @@ pub(crate) struct SwitchEntry {
|
|||||||
pub(crate) finally_depth_at_open: usize,
|
pub(crate) finally_depth_at_open: usize,
|
||||||
pub(crate) for_of_depth_at_open: usize,
|
pub(crate) for_of_depth_at_open: usize,
|
||||||
pub(crate) for_in_depth_at_open: usize,
|
pub(crate) for_in_depth_at_open: usize,
|
||||||
|
/// switch 打开时已打开的释放作用域数(switch 内 break 逃出时据此计算穿越层数)。
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) dispose_depth_at_open: usize,
|
||||||
pub(crate) result_reg: u32,
|
pub(crate) result_reg: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +129,10 @@ pub(crate) struct LabelCtx {
|
|||||||
pub(crate) for_of_depth: usize,
|
pub(crate) for_of_depth: usize,
|
||||||
/// 当前打开的 for-in 循环数(逃出计数基数)。
|
/// 当前打开的 for-in 循环数(逃出计数基数)。
|
||||||
pub(crate) for_in_depth: usize,
|
pub(crate) for_in_depth: usize,
|
||||||
|
/// 当前打开的释放作用域数(DISPOSE_MARK 已发未 POP 的嵌套层数):
|
||||||
|
/// 编译期不变式是函数体收尾归零,break/continue/return 逃出穿越层数以此
|
||||||
|
/// 与打开点快照之差计算。
|
||||||
|
pub(crate) dispose_depth: usize,
|
||||||
pub(crate) label_counter: u32,
|
pub(crate) label_counter: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -458,10 +458,21 @@ impl Emitter {
|
|||||||
// MAKE_CELL(undefined) 实例化,此处只补未捕获名。
|
// MAKE_CELL(undefined) 实例化,此处只补未捕获名。
|
||||||
self.instantiate_var_bindings(body_stmts, &mut ctx);
|
self.instantiate_var_bindings(body_stmts, &mut ctx);
|
||||||
|
|
||||||
|
// 函数体是释放作用域:仅当体含 using 声明时发 mark/pop(无 using 的体
|
||||||
|
// 不发,防早退 return 跳过 POP 留未配对水位错乱外层对齐)。
|
||||||
|
let has_using = Self::scope_contains_using(body_stmts);
|
||||||
// 生成器:body 起点标记——调用时参数初始化(emit_params_prologue)结束后挂起于此,
|
// 生成器:body 起点标记——调用时参数初始化(emit_params_prologue)结束后挂起于此,
|
||||||
// 参数副作用/异常在 `g()` 调用时刻生效,首次 next() 从这继续执行 body。
|
// 参数副作用/异常在 `g()` 调用时刻生效,首次 next() 从这继续执行 body。
|
||||||
if is_generator {
|
if is_generator {
|
||||||
ctx.inst(Inst::suspend_body());
|
ctx.inst(Inst::suspend_body());
|
||||||
|
// 生成器体环境在 `g()` 调用时建立、首次 next() 从挂起点后继续:水位登记
|
||||||
|
// 在挂起点之后,体声明的资源只在体尾(生成器完成)释放。
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_mark();
|
||||||
|
}
|
||||||
|
} else if has_using {
|
||||||
|
// 普通函数体环境在参数 prologue 后建立:body 语句发射前登记水位。
|
||||||
|
ctx.emit_dispose_mark();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(emit) = emit_fields.as_mut() {
|
if let Some(emit) = emit_fields.as_mut() {
|
||||||
@@ -492,6 +503,10 @@ impl Emitter {
|
|||||||
|
|
||||||
// 隐式 RETURN:表达式体返回最后表达式,语句体返回 undefined。
|
// 隐式 RETURN:表达式体返回最后表达式,语句体返回 undefined。
|
||||||
// 函数尾词法上不在任何循环内,迭代器逃出计数恒 0。
|
// 函数尾词法上不在任何循环内,迭代器逃出计数恒 0。
|
||||||
|
// 函数尾正常完成:逆序释放体声明的资源(早 return 跳过本点,归穿越面)。
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_pop();
|
||||||
|
}
|
||||||
if is_expression_body {
|
if is_expression_body {
|
||||||
if let Some(reg) = last_result_reg {
|
if let Some(reg) = last_result_reg {
|
||||||
ctx.inst(Inst::ret(Operand::Reg(reg), 0, 0));
|
ctx.inst(Inst::ret(Operand::Reg(reg), 0, 0));
|
||||||
|
|||||||
@@ -11,6 +11,13 @@ impl Emitter {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
ctx.push_scope();
|
ctx.push_scope();
|
||||||
|
// 块是释放作用域:仅当块含 using 声明时入口登记水位、块尾正常完成逆序
|
||||||
|
// 释放本块声明的资源(无 using 的块不发 mark/pop,防早退跳过 POP 留
|
||||||
|
// 未配对水位)。
|
||||||
|
let has_using = Self::scope_contains_using(&block.body);
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_mark();
|
||||||
|
}
|
||||||
// 函数预声明先于 lexical:`{ let g; function g(){} }` 时 lexical 的预声明
|
// 函数预声明先于 lexical:`{ let g; function g(){} }` 时 lexical 的预声明
|
||||||
// 命中已存在的函数绑定自然报重复声明错,不被静默覆盖破坏 TDZ。
|
// 命中已存在的函数绑定自然报重复声明错,不被静默覆盖破坏 TDZ。
|
||||||
self.predeclare_block_function_declarations(&block.body, ctx, false);
|
self.predeclare_block_function_declarations(&block.body, ctx, false);
|
||||||
@@ -35,6 +42,10 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
ctx.pop_completion_list();
|
ctx.pop_completion_list();
|
||||||
ctx.block_fn_entry_mats.pop();
|
ctx.block_fn_entry_mats.pop();
|
||||||
|
// 块尾正常完成:逆序释放本块声明的资源(异常/return 穿越跳过本点)。
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_pop();
|
||||||
|
}
|
||||||
ctx.pop_scope();
|
ctx.pop_scope();
|
||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ use oxide_bytecode::module::Constant;
|
|||||||
use oxide_bytecode::opcode::OpCode;
|
use oxide_bytecode::opcode::OpCode;
|
||||||
use oxide_ir::inst::Inst;
|
use oxide_ir::inst::Inst;
|
||||||
use oxide_ir::operand::Operand;
|
use oxide_ir::operand::Operand;
|
||||||
use oxide_parser::{BindingPattern, Statement, VariableDeclaration, VariableDeclarationKind};
|
use oxide_parser::{
|
||||||
|
BindingPattern, ForStatementInit, ForStatementLeft, Statement, VariableDeclaration,
|
||||||
|
VariableDeclarationKind,
|
||||||
|
};
|
||||||
|
|
||||||
impl Emitter {
|
impl Emitter {
|
||||||
pub(crate) fn emit_variable_declaration_statement(
|
pub(crate) fn emit_variable_declaration_statement(
|
||||||
@@ -44,16 +47,8 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// using/await using 声明登记:把资源值压入 VM 释放栈(释放时机由
|
// using/await using 声明登记:把资源值压入 VM 释放栈(释放时机由
|
||||||
// 作用域出口点决定,本指令只登记)。a 槽为释放提示:0 = 同步、1 = 异步。
|
// 作用域出口点决定,本指令只登记)。
|
||||||
if matches!(decl.kind, VariableDeclarationKind::Using | VariableDeclarationKind::AwaitUsing) {
|
self.emit_dispose_register(val_reg, decl.kind, ctx);
|
||||||
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 {
|
} else {
|
||||||
let BindingPattern::BindingIdentifier(bi) = &d.id else {
|
let BindingPattern::BindingIdentifier(bi) = &d.id else {
|
||||||
return Err("destructuring declaration requires an initializer".into());
|
return Err("destructuring declaration requires an initializer".into());
|
||||||
@@ -97,4 +92,87 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 递归扫描语句列表是否含 using/await using 声明(含嵌套复合语句,不进入
|
||||||
|
/// 函数/类体——那是独立作用域,其 using 声明不属本作用域)。释放作用域的
|
||||||
|
/// mark/pop 仅在有资源声明时发射:无 using 的作用域若发 mark/pop,其早退
|
||||||
|
/// (return/break/continue/throw)跳过 POP 会留下未配对水位,错乱外层作用域
|
||||||
|
/// 的 mark/pop 对齐。
|
||||||
|
pub(crate) fn scope_contains_using(stmts: &[Statement]) -> bool {
|
||||||
|
stmts.iter().any(Self::statement_contains_using)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn statement_contains_using(stmt: &Statement) -> bool {
|
||||||
|
match stmt {
|
||||||
|
Statement::VariableDeclaration(decl) => {
|
||||||
|
matches!(decl.kind, VariableDeclarationKind::Using | VariableDeclarationKind::AwaitUsing)
|
||||||
|
}
|
||||||
|
Statement::BlockStatement(block) => Self::scope_contains_using(&block.body),
|
||||||
|
Statement::IfStatement(if_) => {
|
||||||
|
Self::statement_contains_using(&if_.consequent)
|
||||||
|
|| if_.alternate.as_ref().is_some_and(|s| Self::statement_contains_using(s))
|
||||||
|
}
|
||||||
|
Statement::WhileStatement(while_) => Self::statement_contains_using(&while_.body),
|
||||||
|
Statement::DoWhileStatement(do_) => Self::statement_contains_using(&do_.body),
|
||||||
|
Statement::ForStatement(for_) => Self::statement_contains_using(&for_.body),
|
||||||
|
Statement::ForOfStatement(for_) => Self::statement_contains_using(&for_.body),
|
||||||
|
Statement::ForInStatement(for_) => Self::statement_contains_using(&for_.body),
|
||||||
|
Statement::LabeledStatement(labeled) => Self::statement_contains_using(&labeled.body),
|
||||||
|
Statement::WithStatement(with_) => Self::statement_contains_using(&with_.body),
|
||||||
|
Statement::SwitchStatement(switch_) => {
|
||||||
|
switch_.cases.iter().any(|case| Self::scope_contains_using(&case.consequent))
|
||||||
|
}
|
||||||
|
Statement::TryStatement(try_) => {
|
||||||
|
Self::scope_contains_using(&try_.block.body)
|
||||||
|
|| try_
|
||||||
|
.handler
|
||||||
|
.as_deref()
|
||||||
|
.is_some_and(|h| Self::scope_contains_using(&h.body.body))
|
||||||
|
|| try_.finalizer.as_deref().is_some_and(|f| Self::scope_contains_using(&f.body))
|
||||||
|
}
|
||||||
|
// 函数/类声明是独立作用域,其 using 声明不属本作用域;其余语句无嵌套。
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// using/await using 声明登记释放栈:把资源值(val_reg)压入释放栈。非
|
||||||
|
/// using/await using 声明不登记。a 槽为释放提示(0 = 同步、1 = 异步)。
|
||||||
|
pub(crate) fn emit_dispose_register(
|
||||||
|
&self, val_reg: u32, kind: VariableDeclarationKind, ctx: &mut CompileCtx,
|
||||||
|
) {
|
||||||
|
if !matches!(kind, VariableDeclarationKind::Using | VariableDeclarationKind::AwaitUsing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let hint = u16::from(matches!(kind, VariableDeclarationKind::AwaitUsing));
|
||||||
|
ctx.inst(Inst::new(
|
||||||
|
OpCode::DISPOSE_REGISTER,
|
||||||
|
Operand::Reg(val_reg),
|
||||||
|
Operand::Imm(hint),
|
||||||
|
Operand::None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// C 风格 for 头是否含 using 声明(init 为 using/await using 变量声明)。
|
||||||
|
pub(crate) fn for_init_contains_using(init: &Option<ForStatementInit>) -> bool {
|
||||||
|
matches!(
|
||||||
|
init,
|
||||||
|
Some(ForStatementInit::VariableDeclaration(decl))
|
||||||
|
if matches!(
|
||||||
|
decl.kind,
|
||||||
|
VariableDeclarationKind::Using | VariableDeclarationKind::AwaitUsing
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// for-of/for-in 左侧是否含 using 声明(头为 using/await using 变量声明)。
|
||||||
|
pub(crate) fn for_left_contains_using(left: &ForStatementLeft) -> bool {
|
||||||
|
matches!(
|
||||||
|
left,
|
||||||
|
ForStatementLeft::VariableDeclaration(decl)
|
||||||
|
if matches!(
|
||||||
|
decl.kind,
|
||||||
|
VariableDeclarationKind::Using | VariableDeclarationKind::AwaitUsing
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ impl Emitter {
|
|||||||
// try block 是独立块作用域:lexical 声明(let/const/class)限定于 try 内,
|
// try block 是独立块作用域:lexical 声明(let/const/class)限定于 try 内,
|
||||||
// 与外层同名绑定互不干扰;块级预声明使声明点前读取编译为 TDZ 抛错。
|
// 与外层同名绑定互不干扰;块级预声明使声明点前读取编译为 TDZ 抛错。
|
||||||
ctx.push_scope();
|
ctx.push_scope();
|
||||||
|
// try 体是独立释放环境:仅当体含 using 声明时入口登记水位、体尾正常
|
||||||
|
// 完成逆序释放(异常穿越跳过本点,归穿越面)。
|
||||||
|
let try_has_using = Self::scope_contains_using(&ts.block.body);
|
||||||
|
if try_has_using {
|
||||||
|
ctx.emit_dispose_mark();
|
||||||
|
}
|
||||||
// 函数预声明先于 lexical:同名 let 命中已有函数绑定报重复声明错。
|
// 函数预声明先于 lexical:同名 let 命中已有函数绑定报重复声明错。
|
||||||
self.predeclare_block_function_declarations(&ts.block.body, ctx, false);
|
self.predeclare_block_function_declarations(&ts.block.body, ctx, false);
|
||||||
// try 块 lexical 声明是局部绑定:不做受限全局名检查(重复声明错在 emit 期报)。
|
// try 块 lexical 声明是局部绑定:不做受限全局名检查(重复声明错在 emit 期报)。
|
||||||
@@ -58,6 +64,10 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
ctx.pop_completion_list();
|
ctx.pop_completion_list();
|
||||||
ctx.block_fn_entry_mats.pop();
|
ctx.block_fn_entry_mats.pop();
|
||||||
|
// try 体正常完成出口:逆序释放本体声明的资源。
|
||||||
|
if try_has_using {
|
||||||
|
ctx.emit_dispose_pop();
|
||||||
|
}
|
||||||
ctx.pop_scope();
|
ctx.pop_scope();
|
||||||
ctx.inst(Inst::new(
|
ctx.inst(Inst::new(
|
||||||
OpCode::LOAD_VAR,
|
OpCode::LOAD_VAR,
|
||||||
@@ -77,6 +87,12 @@ impl Emitter {
|
|||||||
ctx.labels.set_label_pos(catch_label, ctx.insts.len());
|
ctx.labels.set_label_pos(catch_label, ctx.insts.len());
|
||||||
if let Some(catch) = &ts.handler {
|
if let Some(catch) = &ts.handler {
|
||||||
ctx.push_scope();
|
ctx.push_scope();
|
||||||
|
// catch 体是独立释放环境:仅当体含 using 声明时入口登记水位、体尾
|
||||||
|
// 正常完成逆序释放。
|
||||||
|
let catch_has_using = Self::scope_contains_using(&catch.body.body);
|
||||||
|
if catch_has_using {
|
||||||
|
ctx.emit_dispose_mark();
|
||||||
|
}
|
||||||
if let Some(param) = &catch.param {
|
if let Some(param) = &catch.param {
|
||||||
let src_reg = ctx.alloc_reg();
|
let src_reg = ctx.alloc_reg();
|
||||||
// 异常值在 VM 物理 regs[0](unwind 展开处写入),STORE_VAR a=None 读回。
|
// 异常值在 VM 物理 regs[0](unwind 展开处写入),STORE_VAR a=None 读回。
|
||||||
@@ -111,6 +127,10 @@ impl Emitter {
|
|||||||
Operand::Reg(last_catch_result.unwrap_or(result_reg)),
|
Operand::Reg(last_catch_result.unwrap_or(result_reg)),
|
||||||
Operand::None,
|
Operand::None,
|
||||||
));
|
));
|
||||||
|
// catch 体正常完成出口:逆序释放本体声明的资源。
|
||||||
|
if catch_has_using {
|
||||||
|
ctx.emit_dispose_pop();
|
||||||
|
}
|
||||||
ctx.pop_scope();
|
ctx.pop_scope();
|
||||||
}
|
}
|
||||||
if has_finally {
|
if has_finally {
|
||||||
@@ -124,6 +144,12 @@ impl Emitter {
|
|||||||
// try/catch 收敛值保持 try 体或 catch 体值,finally 值不渗入。
|
// try/catch 收敛值保持 try 体或 catch 体值,finally 值不渗入。
|
||||||
// finally block 同为独立块作用域,lexical 声明互不泄漏。
|
// finally block 同为独立块作用域,lexical 声明互不泄漏。
|
||||||
ctx.push_scope();
|
ctx.push_scope();
|
||||||
|
// finally 体是独立释放环境:仅当体含 using 声明时入口登记水位、
|
||||||
|
// 体尾正常完成逆序释放。
|
||||||
|
let finally_has_using = Self::scope_contains_using(&ts.finalizer.as_ref().unwrap().body);
|
||||||
|
if finally_has_using {
|
||||||
|
ctx.emit_dispose_mark();
|
||||||
|
}
|
||||||
// finally 块函数预声明先于 lexical,块入口物化与 try/普通块同口径。
|
// finally 块函数预声明先于 lexical,块入口物化与 try/普通块同口径。
|
||||||
self.predeclare_block_function_declarations(&ts.finalizer.as_ref().unwrap().body, ctx, false);
|
self.predeclare_block_function_declarations(&ts.finalizer.as_ref().unwrap().body, ctx, false);
|
||||||
// finally 块 lexical 声明同为局部绑定:不做受限全局名检查。
|
// finally 块 lexical 声明同为局部绑定:不做受限全局名检查。
|
||||||
@@ -142,6 +168,10 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
ctx.pop_completion_list();
|
ctx.pop_completion_list();
|
||||||
ctx.block_fn_entry_mats.pop();
|
ctx.block_fn_entry_mats.pop();
|
||||||
|
// finally 体正常完成出口:逆序释放本体声明的资源。
|
||||||
|
if finally_has_using {
|
||||||
|
ctx.emit_dispose_pop();
|
||||||
|
}
|
||||||
ctx.pop_scope();
|
ctx.pop_scope();
|
||||||
ctx.inst(Inst::new(OpCode::TRY_FINALLY_END, Operand::None, Operand::None, Operand::None));
|
ctx.inst(Inst::new(OpCode::TRY_FINALLY_END, Operand::None, Operand::None, Operand::None));
|
||||||
ctx.pop_open_try_handler();
|
ctx.pop_open_try_handler();
|
||||||
|
|||||||
@@ -44,6 +44,12 @@ impl Emitter {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
ctx.push_scope();
|
ctx.push_scope();
|
||||||
|
// C 风格 for 是整语句单一释放环境(非每迭代):仅当头/体含 using 声明
|
||||||
|
// 时入口登记水位、出口(end_label 后)逆序释放。
|
||||||
|
let has_using = Self::for_init_contains_using(&fr.init) || Self::statement_contains_using(&fr.body);
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_mark();
|
||||||
|
}
|
||||||
let start_label = ctx.next_label_id();
|
let start_label = ctx.next_label_id();
|
||||||
let update_label = ctx.next_label_id();
|
let update_label = ctx.next_label_id();
|
||||||
let end_label = ctx.next_label_id();
|
let end_label = ctx.next_label_id();
|
||||||
@@ -98,6 +104,8 @@ impl Emitter {
|
|||||||
// 捕获头名的寄存器补写在 `emit_bind_target` 经
|
// 捕获头名的寄存器补写在 `emit_bind_target` 经
|
||||||
// `for_head_store_registers` 完成(解构叶用各自的叶值,不用整头 RHS)。
|
// `for_head_store_registers` 完成(解构叶用各自的叶值,不用整头 RHS)。
|
||||||
self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, false, ctx)?;
|
self.emit_binding_pattern(&d.id, val_reg, decl.kind, is_const, false, ctx)?;
|
||||||
|
// using/await using 头声明登记释放栈(资源值即 init 表达式结果)。
|
||||||
|
self.emit_dispose_register(val_reg, decl.kind, ctx);
|
||||||
} else if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
} else if let BindingPattern::BindingIdentifier(bi) = &d.id {
|
||||||
let idx = ctx.add_constant(Constant::Undefined);
|
let idx = ctx.add_constant(Constant::Undefined);
|
||||||
let tmp = ctx.alloc_reg();
|
let tmp = ctx.alloc_reg();
|
||||||
@@ -222,6 +230,10 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
ctx.inst(Inst::jmp(start_label));
|
ctx.inst(Inst::jmp(start_label));
|
||||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||||
|
// 循环正常完成出口:逆序释放本 for 语句声明的资源。
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_pop();
|
||||||
|
}
|
||||||
ctx.pop_label_scopes(n_labeled);
|
ctx.pop_label_scopes(n_labeled);
|
||||||
ctx.pop_loop();
|
ctx.pop_loop();
|
||||||
ctx.pop_scope();
|
ctx.pop_scope();
|
||||||
|
|||||||
@@ -189,6 +189,12 @@ impl Emitter {
|
|||||||
ctx.labels.set_label_pos(continue_label, ctx.insts.len());
|
ctx.labels.set_label_pos(continue_label, ctx.insts.len());
|
||||||
let key_reg = ctx.alloc_reg();
|
let key_reg = ctx.alloc_reg();
|
||||||
ctx.inst(Inst::new(OpCode::FOR_IN_NEXT, Operand::Reg(key_reg), Operand::None, Operand::None));
|
ctx.inst(Inst::new(OpCode::FOR_IN_NEXT, Operand::Reg(key_reg), Operand::None, Operand::None));
|
||||||
|
// for-in 每迭代独立环境(规范 iterationEnv):仅当头/体含 using 声明时
|
||||||
|
// 本迭代 mark/pop。
|
||||||
|
let has_using = Self::for_left_contains_using(&fi.left) || Self::statement_contains_using(&fi.body);
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_mark();
|
||||||
|
}
|
||||||
match &fi.left {
|
match &fi.left {
|
||||||
ForStatementLeft::VariableDeclaration(decl) => {
|
ForStatementLeft::VariableDeclaration(decl) => {
|
||||||
// let/const 声明对被捕获绑定用 fresh cell(每迭代新 cell);var 单绑定。
|
// let/const 声明对被捕获绑定用 fresh cell(每迭代新 cell);var 单绑定。
|
||||||
@@ -253,6 +259,8 @@ impl Emitter {
|
|||||||
_ => self.emit_binding_pattern(&d.id, key_reg, decl.kind, is_const, fresh_cell, ctx)?,
|
_ => self.emit_binding_pattern(&d.id, key_reg, decl.kind, is_const, fresh_cell, ctx)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// using/await using 头声明登记释放栈(资源值即本迭代键)。
|
||||||
|
self.emit_dispose_register(key_reg, decl.kind, ctx);
|
||||||
}
|
}
|
||||||
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
||||||
let name = id_ref.name.as_str();
|
let name = id_ref.name.as_str();
|
||||||
@@ -284,6 +292,10 @@ impl Emitter {
|
|||||||
if let Some(env) = head_env {
|
if let Some(env) = head_env {
|
||||||
self.restore_for_head_env(env, ctx);
|
self.restore_for_head_env(env, ctx);
|
||||||
}
|
}
|
||||||
|
// 本迭代正常完成:逆序释放本迭代声明的资源。
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_pop();
|
||||||
|
}
|
||||||
ctx.inst(Inst::jmp(start_label));
|
ctx.inst(Inst::jmp(start_label));
|
||||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||||
ctx.inst(Inst::new(OpCode::FOR_IN_CLEANUP, Operand::None, Operand::None, Operand::None));
|
ctx.inst(Inst::new(OpCode::FOR_IN_CLEANUP, Operand::None, Operand::None, Operand::None));
|
||||||
|
|||||||
@@ -49,11 +49,21 @@ impl Emitter {
|
|||||||
ctx.inst(Inst::jmp_if_false(has_reg, end_label));
|
ctx.inst(Inst::jmp_if_false(has_reg, end_label));
|
||||||
let val_reg = ctx.alloc_reg();
|
let val_reg = ctx.alloc_reg();
|
||||||
ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None));
|
ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None));
|
||||||
|
// for-of 每迭代独立环境(规范 iterationEnv):仅当头/体含 using 声明时
|
||||||
|
// 本迭代 mark/pop,迭代器关闭(FOR_OF_CLOSE)与释放互不干扰。
|
||||||
|
let has_using = Self::for_left_contains_using(&fo.left) || Self::statement_contains_using(&fo.body);
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_mark();
|
||||||
|
}
|
||||||
self.emit_for_of_left_assignment(&fo.left, val_reg, ctx)?;
|
self.emit_for_of_left_assignment(&fo.left, val_reg, ctx)?;
|
||||||
let body_result = self.emit_statement(&fo.body, ctx)?;
|
let body_result = self.emit_statement(&fo.body, ctx)?;
|
||||||
if let Some(env) = head_env {
|
if let Some(env) = head_env {
|
||||||
self.restore_for_head_env(env, ctx);
|
self.restore_for_head_env(env, ctx);
|
||||||
}
|
}
|
||||||
|
// 本迭代正常完成:逆序释放本迭代声明的资源。
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_pop();
|
||||||
|
}
|
||||||
ctx.inst(Inst::jmp(start_label));
|
ctx.inst(Inst::jmp(start_label));
|
||||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||||
ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None));
|
ctx.inst(Inst::new(OpCode::FOR_OF_CLOSE, Operand::None, Operand::None, Operand::None));
|
||||||
@@ -115,11 +125,21 @@ impl Emitter {
|
|||||||
ctx.inst(Inst::jmp_if_false(has_reg, end_label));
|
ctx.inst(Inst::jmp_if_false(has_reg, end_label));
|
||||||
let val_reg = ctx.alloc_reg();
|
let val_reg = ctx.alloc_reg();
|
||||||
ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None));
|
ctx.inst(Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(val_reg), Operand::None, Operand::None));
|
||||||
|
// for-await-of 同同步 for-of:每迭代独立环境,仅当头/体含 using 声明时
|
||||||
|
// 本迭代 mark/pop。
|
||||||
|
let has_using = Self::for_left_contains_using(&fo.left) || Self::statement_contains_using(&fo.body);
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_mark();
|
||||||
|
}
|
||||||
self.emit_for_of_left_assignment(&fo.left, val_reg, ctx)?;
|
self.emit_for_of_left_assignment(&fo.left, val_reg, ctx)?;
|
||||||
let body_result = self.emit_statement(&fo.body, ctx)?;
|
let body_result = self.emit_statement(&fo.body, ctx)?;
|
||||||
if let Some(env) = head_env {
|
if let Some(env) = head_env {
|
||||||
self.restore_for_head_env(env, ctx);
|
self.restore_for_head_env(env, ctx);
|
||||||
}
|
}
|
||||||
|
// 本迭代正常完成:逆序释放本迭代声明的资源。
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_pop();
|
||||||
|
}
|
||||||
ctx.inst(Inst::jmp(start_label));
|
ctx.inst(Inst::jmp(start_label));
|
||||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||||
ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_CLOSE, Operand::None, Operand::None, Operand::None));
|
ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_CLOSE, Operand::None, Operand::None, Operand::None));
|
||||||
@@ -147,6 +167,8 @@ impl Emitter {
|
|||||||
for d in &decl.declarations {
|
for d in &decl.declarations {
|
||||||
self.emit_binding_pattern(&d.id, val_reg, decl.kind, false, fresh_cell, ctx)?;
|
self.emit_binding_pattern(&d.id, val_reg, decl.kind, false, fresh_cell, ctx)?;
|
||||||
}
|
}
|
||||||
|
// using/await using 头声明登记释放栈(资源值即本迭代值)。
|
||||||
|
self.emit_dispose_register(val_reg, decl.kind, ctx);
|
||||||
}
|
}
|
||||||
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
ForStatementLeft::AssignmentTargetIdentifier(id_ref) => {
|
||||||
let name = id_ref.name.as_str();
|
let name = id_ref.name.as_str();
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ impl Emitter {
|
|||||||
// 块函数预声明先于 lexical,使 `case 0: let g; function g(){}` 的 lexical
|
// 块函数预声明先于 lexical,使 `case 0: let g; function g(){}` 的 lexical
|
||||||
// 占位命中已存在的函数绑定,报重复声明错。
|
// 占位命中已存在的函数绑定,报重复声明错。
|
||||||
ctx.push_scope();
|
ctx.push_scope();
|
||||||
|
// CaseBlock 是单一释放环境(非逐 case):穿落不跨环境,仅当 case 体含
|
||||||
|
// using 声明时入口登记水位、出口逆序释放。
|
||||||
|
let has_using = cases.iter().any(|case| Self::scope_contains_using(&case.consequent));
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_mark();
|
||||||
|
}
|
||||||
for case in cases.iter() {
|
for case in cases.iter() {
|
||||||
self.predeclare_block_function_declarations(&case.consequent, ctx, false);
|
self.predeclare_block_function_declarations(&case.consequent, ctx, false);
|
||||||
self.predeclare_lexical_declarations(&case.consequent, ctx)?;
|
self.predeclare_lexical_declarations(&case.consequent, ctx)?;
|
||||||
@@ -84,6 +90,10 @@ impl Emitter {
|
|||||||
}
|
}
|
||||||
ctx.block_fn_entry_mats.pop();
|
ctx.block_fn_entry_mats.pop();
|
||||||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||||||
|
// CaseBlock 正常完成出口:逆序释放本块声明的资源。
|
||||||
|
if has_using {
|
||||||
|
ctx.emit_dispose_pop();
|
||||||
|
}
|
||||||
ctx.pop_scope();
|
ctx.pop_scope();
|
||||||
ctx.pop_completion_target();
|
ctx.pop_completion_target();
|
||||||
ctx.pop_switch();
|
ctx.pop_switch();
|
||||||
|
|||||||
@@ -289,6 +289,12 @@ impl Vm {
|
|||||||
OpCode::DISPOSE_REGISTER => {
|
OpCode::DISPOSE_REGISTER => {
|
||||||
self.dispatch_dispose_register(rd, a);
|
self.dispatch_dispose_register(rd, a);
|
||||||
}
|
}
|
||||||
|
OpCode::DISPOSE_MARK => {
|
||||||
|
self.dispatch_dispose_mark();
|
||||||
|
}
|
||||||
|
OpCode::DISPOSE_POP => {
|
||||||
|
self.dispatch_dispose_pop()?;
|
||||||
|
}
|
||||||
OpCode::CELL_GET => {
|
OpCode::CELL_GET => {
|
||||||
self.dispatch_cell_get(rd, a, b)?;
|
self.dispatch_cell_get(rd, a, b)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ impl Vm {
|
|||||||
f(RootGroup::SpillStack, v);
|
f(RootGroup::SpillStack, v);
|
||||||
}
|
}
|
||||||
// 释放栈持 using 声明的资源值:漏根 → sweep 释放 → 释放点解引用悬垂。
|
// 释放栈持 using 声明的资源值:漏根 → sweep 释放 → 释放点解引用悬垂。
|
||||||
for &v in &self.dispose_stack {
|
for (v, _) in &self.dispose_stack {
|
||||||
f(RootGroup::DisposeStack, v);
|
f(RootGroup::DisposeStack, *v);
|
||||||
}
|
}
|
||||||
for cell_vec in &self.cell_stack {
|
for cell_vec in &self.cell_stack {
|
||||||
for &cell_ptr in cell_vec {
|
for &cell_ptr in cell_vec {
|
||||||
|
|||||||
@@ -230,10 +230,13 @@ pub struct Vm {
|
|||||||
/// VM 级 spill 栈。`CallFrame.spill_offset` 定位本帧区:调用子函数时从边界后分配,
|
/// VM 级 spill 栈。`CallFrame.spill_offset` 定位本帧区:调用子函数时从边界后分配,
|
||||||
/// 帧恢复时截断到边界,子函数 spill 数据随帧丢弃。
|
/// 帧恢复时截断到边界,子函数 spill 数据随帧丢弃。
|
||||||
pub(crate) spill_stack: Vec<JsValue>,
|
pub(crate) spill_stack: Vec<JsValue>,
|
||||||
/// using/await using 声明的资源释放栈:DISPOSE_REGISTER 把资源值压入,
|
/// using/await using 声明的资源释放栈:DISPOSE_REGISTER 把(资源值,释放提示)
|
||||||
/// 作用域出口点逆序释放(释放点由后续指令族承担)。栈内 JsValue 是 GC 根
|
/// 压入,DISPOSE_POP 按水位逆序释放并截断。栈内 JsValue 是 GC 根
|
||||||
/// (`for_each_value` 遍历);run 边界清空,防跨 run 残留条目在下一 run 被误释放。
|
/// (`for_each_value` 遍历);run 边界清空,防跨 run 残留条目在下一 run 被误释放。
|
||||||
pub(crate) dispose_stack: Vec<JsValue>,
|
pub(crate) dispose_stack: Vec<(JsValue, u16)>,
|
||||||
|
/// 作用域水位栈:DISPOSE_MARK 压入当前释放栈深度,DISPOSE_POP 弹出并只释放
|
||||||
|
/// 水位以上条目。与释放栈同生共死(run 边界一并清空)。
|
||||||
|
pub(crate) dispose_marks: Vec<usize>,
|
||||||
/// 本次 native 调用的 spill 溢出实参区:`spill_stack[base..base+count)`。
|
/// 本次 native 调用的 spill 溢出实参区:`spill_stack[base..base+count)`。
|
||||||
/// 实参数超过寄存器窗口(253)时,窗口外的实参转存 spill 栈(GC 根),
|
/// 实参数超过寄存器窗口(253)时,窗口外的实参转存 spill 栈(GC 根),
|
||||||
/// native 侧经 `VmHost::native_arg_count`/`native_arg_at` 读取。仅在一次
|
/// native 侧经 `VmHost::native_arg_count`/`native_arg_at` 读取。仅在一次
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use oxide_runtime_api::{push_units_to, to_boolean, to_units_full, VmHost};
|
|||||||
use oxide_types::object::{JsObject, PropAttributes};
|
use oxide_types::object::{JsObject, PropAttributes};
|
||||||
use oxide_types::private_key::{
|
use oxide_types::private_key::{
|
||||||
encode_symbol_key, int_key_value, is_int_key, is_private_name_key, is_symbol_key, make_int_key, INT_KEY_COUNT,
|
encode_symbol_key, int_key_value, is_int_key, is_private_name_key, is_symbol_key, make_int_key, INT_KEY_COUNT,
|
||||||
WELL_KNOWN_SYMBOL_HAS_INSTANCE,
|
WELL_KNOWN_SYMBOL_ASYNC_DISPOSE, WELL_KNOWN_SYMBOL_DISPOSE, WELL_KNOWN_SYMBOL_HAS_INSTANCE,
|
||||||
};
|
};
|
||||||
use oxide_types::value::JsValue;
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
@@ -649,16 +649,78 @@ impl Vm {
|
|||||||
self.iters.pop_for_in();
|
self.iters.pop_for_in();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// using/await using 声明登记:把资源值(rd 槽)压入释放栈。
|
/// using/await using 声明登记:把资源值(rd 槽)与释放提示(a 槽)压入释放栈。
|
||||||
///
|
///
|
||||||
/// a 槽是释放提示(0 = 同步、1 = 异步),登记期原样保留供释放点分派;
|
/// 释放提示 0 = 同步、1 = 异步,登记期原样保留供释放点分派;
|
||||||
/// 本指令只登记,释放时机由作用域出口点决定。
|
/// 本指令只登记,释放时机由作用域出口点决定。
|
||||||
///
|
///
|
||||||
/// # 副作用
|
/// # 副作用
|
||||||
/// - 释放栈增长(栈内 JsValue 是 GC 根,run 边界清空)。
|
/// - 释放栈增长(栈内 JsValue 是 GC 根,run 边界清空)。
|
||||||
pub(crate) fn dispatch_dispose_register(&mut self, rd: usize, a: usize) {
|
pub(crate) fn dispatch_dispose_register(&mut self, rd: usize, a: usize) {
|
||||||
vm_trace!("DISPOSE_REGISTER rd={} hint={}", rd, a);
|
vm_trace!("DISPOSE_REGISTER rd={} hint={}", rd, a);
|
||||||
self.dispose_stack.push(self.regs[rd]);
|
self.dispose_stack.push((self.regs[rd], a as u16));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 作用域入口水位:把当前释放栈深度压入水位栈(DISPOSE_POP 的配对入口)。
|
||||||
|
///
|
||||||
|
/// # 副作用
|
||||||
|
/// - 水位栈增长。
|
||||||
|
pub(crate) fn dispatch_dispose_mark(&mut self) {
|
||||||
|
vm_trace!("DISPOSE_MARK depth={}", self.dispose_stack.len());
|
||||||
|
self.dispose_marks.push(self.dispose_stack.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 作用域出口:弹出水位,逆序释放水位以上资源并截断释放栈(规范
|
||||||
|
/// DisposeResources)。释放方法自身抛错按第一错误向外传播。
|
||||||
|
///
|
||||||
|
/// # 副作用
|
||||||
|
/// - 释放栈截断到水位、水位栈弹出;释放方法是任意 JS 调用(可再入,
|
||||||
|
/// 嵌套 using 声明会在其内再压释放栈)。
|
||||||
|
pub(crate) fn dispatch_dispose_pop(&mut self) -> Result<(), String> {
|
||||||
|
let mark = match self.dispose_marks.pop() {
|
||||||
|
Some(m) => m,
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
vm_trace!("DISPOSE_POP mark={} depth={}", mark, self.dispose_stack.len());
|
||||||
|
while self.dispose_stack.len() > mark {
|
||||||
|
let (value, hint) = self.dispose_stack.pop().expect("水位以上必有条目");
|
||||||
|
self.dispose_value(value, hint)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 单资源释放(规范 Dispose 语义):非对象跳过;按释放提示取方法(同步只取
|
||||||
|
/// Symbol.dispose;异步先取 Symbol.asyncDispose,未定义回退 Symbol.dispose),
|
||||||
|
/// 不可调用跳过;以资源自身为 this 零参调用。
|
||||||
|
///
|
||||||
|
/// # 边界与前提
|
||||||
|
/// - 异步方法结果为对象时须 Await 挂起,归后续任务;本期同步调用,结果丢弃。
|
||||||
|
fn dispose_value(&mut self, value: JsValue, hint: u16) -> Result<(), String> {
|
||||||
|
if !value.is_object() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let val_obj = unsafe { &*value.as_js_object_ptr() };
|
||||||
|
let dispose_key = encode_symbol_key(self.realm_id(), WELL_KNOWN_SYMBOL_DISPOSE);
|
||||||
|
let method = if hint == 1 {
|
||||||
|
let async_key = encode_symbol_key(self.realm_id(), WELL_KNOWN_SYMBOL_ASYNC_DISPOSE);
|
||||||
|
let async_method = self.ordinary_get(val_obj, async_key, value)?;
|
||||||
|
if async_method.is_undefined() {
|
||||||
|
self.ordinary_get(val_obj, dispose_key, value)?
|
||||||
|
} else {
|
||||||
|
async_method
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.ordinary_get(val_obj, dispose_key, value)?
|
||||||
|
};
|
||||||
|
if !method.is_object() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let method_obj = unsafe { &*method.as_js_object_ptr() };
|
||||||
|
if !method_obj.is_function() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let _ = self.call_function_sync(method, value, &[])?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn dispatch_for_of_init(&mut self, a: usize) -> Result<(), String> {
|
pub(crate) fn dispatch_for_of_init(&mut self, a: usize) -> Result<(), String> {
|
||||||
|
|||||||
@@ -1146,13 +1146,15 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn using_declaration_registers_dispose_stack() {
|
fn using_declaration_registers_dispose_stack() {
|
||||||
// using 声明经 DISPOSE_REGISTER 把资源值压入释放栈;run 边界(下次 run
|
// using 声明经 DISPOSE_REGISTER 把资源值压入释放栈。早 return 跳过函数
|
||||||
// 入口)清栈,上一 run 的残留条目不得跨 run 可见。
|
// 体 POP(穿越面,归后续任务),资源残留栈上;run 边界(下次 run 入口)
|
||||||
|
// 清栈,残留条目不得跨 run 可见。
|
||||||
let mut vm = Vm::new();
|
let mut vm = Vm::new();
|
||||||
let module = Arc::new(compile("function f() { using x = { a: 1 }; } f();"));
|
let module = Arc::new(compile("function f() { using x = { a: 1 }; return 42; } f();"));
|
||||||
vm.run(&module).expect("run");
|
vm.run(&module).expect("run");
|
||||||
assert_eq!(vm.dispose_stack.len(), 1, "using 声明应登记一个资源值");
|
assert_eq!(vm.dispose_stack.len(), 1, "早 return 跳过 POP,资源残留栈上");
|
||||||
assert!(vm.dispose_stack[0].is_object(), "登记值应为资源对象");
|
assert!(vm.dispose_stack[0].0.is_object(), "登记值应为资源对象");
|
||||||
|
assert_eq!(vm.dispose_stack[0].1, 0, "using 声明释放提示应为同步");
|
||||||
|
|
||||||
// 二次 run 入口清栈后重新登记:残留条目不跨 run 存活。
|
// 二次 run 入口清栈后重新登记:残留条目不跨 run 存活。
|
||||||
vm.run(&module).expect("run2");
|
vm.run(&module).expect("run2");
|
||||||
@@ -1162,4 +1164,107 @@ mod tests {
|
|||||||
vm.clear_execution_state();
|
vm.clear_execution_state();
|
||||||
assert!(vm.dispose_stack.is_empty(), "执行状态清空应清空释放栈");
|
assert!(vm.dispose_stack.is_empty(), "执行状态清空应清空释放栈");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn using_disposed_at_end_of_block() {
|
||||||
|
// using 声明在块尾正常释放:Symbol.dispose 以资源自身为 this 调用一次。
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let module = Arc::new(compile(
|
||||||
|
"var count = 0, thisOk = false; \
|
||||||
|
function make() { return { [Symbol.dispose]: function () { count++; thisOk = (this === r); } }; } \
|
||||||
|
var r = make(); \
|
||||||
|
{ using x = r; }",
|
||||||
|
));
|
||||||
|
vm.run(&module).expect("run");
|
||||||
|
assert_eq!(global_value(&mut vm, "count"), JsValue::int(1), "块尾应释放一次");
|
||||||
|
assert_eq!(global_value(&mut vm, "thisOk"), JsValue::bool(true), "释放方法 this 应为资源自身");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn using_disposed_in_reverse_order() {
|
||||||
|
// 多资源按声明逆序释放(后声明者先释放):释放序拼成数字 321。
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let module = Arc::new(compile(
|
||||||
|
"var seq = 0; \
|
||||||
|
function m(a) { return { [Symbol.dispose]: function () { seq = seq * 10 + a; } }; } \
|
||||||
|
{ using a = m(1); using b = m(2); using c = m(3); }",
|
||||||
|
));
|
||||||
|
vm.run(&module).expect("run");
|
||||||
|
assert_eq!(as_number(&global_value(&mut vm, "seq")), 321.0, "逆序释放拼成 321");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn using_disposed_at_end_of_function_body() {
|
||||||
|
// 函数体尾释放:每次调用各释放一次,早 return 不释放(归穿越面)。
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let module = Arc::new(compile(
|
||||||
|
"var count = 0; \
|
||||||
|
function make() { return { [Symbol.dispose]: function () { count++; } }; } \
|
||||||
|
function f() { using x = make(); } \
|
||||||
|
f(); f();",
|
||||||
|
));
|
||||||
|
vm.run(&module).expect("run");
|
||||||
|
assert_eq!(global_value(&mut vm, "count"), JsValue::int(2), "两次调用各释放一次");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn using_disposed_at_end_of_for_statement() {
|
||||||
|
// C 风格 for 是整语句单一环境:每迭代的块尾释放,循环尾不重复释放。
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let module = Arc::new(compile(
|
||||||
|
"var count = 0; \
|
||||||
|
function make() { return { [Symbol.dispose]: function () { count++; } }; } \
|
||||||
|
for (var i = 0; i < 3; i++) { using x = make(); }",
|
||||||
|
));
|
||||||
|
vm.run(&module).expect("run");
|
||||||
|
assert_eq!(global_value(&mut vm, "count"), JsValue::int(3), "每迭代块尾各释放一次");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn using_disposed_each_iteration_of_for_of() {
|
||||||
|
// for-of 每迭代独立环境:每迭代 mark/pop,迭代器关闭与释放互不干扰。
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let module = Arc::new(compile(
|
||||||
|
"var count = 0; \
|
||||||
|
function make() { return { [Symbol.dispose]: function () { count++; } }; } \
|
||||||
|
for (using x of [make(), make(), make()]) { }",
|
||||||
|
));
|
||||||
|
vm.run(&module).expect("run");
|
||||||
|
assert_eq!(global_value(&mut vm, "count"), JsValue::int(3), "每迭代各释放一次");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn using_disposed_at_end_of_switch_and_try() {
|
||||||
|
// switch CaseBlock 单一环境、try/catch/finally 各体独立环境:各体出口
|
||||||
|
// 释放本体检证(catch 体未触发不释放)。
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let module = Arc::new(compile(
|
||||||
|
"var count = 0; \
|
||||||
|
function make() { return { [Symbol.dispose]: function () { count++; } }; } \
|
||||||
|
switch (1) { case 1: { using a = make(); break; } } \
|
||||||
|
try { using b = make(); } catch (e) { using c = make(); } finally { using d = make(); }",
|
||||||
|
));
|
||||||
|
vm.run(&module).expect("run");
|
||||||
|
assert_eq!(
|
||||||
|
global_value(&mut vm, "count"),
|
||||||
|
JsValue::int(3),
|
||||||
|
"switch 体、try 体、finally 体各释放一次(catch 未触发)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn await_using_prefers_async_dispose_falls_back_to_dispose() {
|
||||||
|
// await using 释放:有 Symbol.asyncDispose 用之,未定义回退 Symbol.dispose。
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let module = Arc::new(compile(
|
||||||
|
"var asyncUsed = 0, syncUsed = 0; \
|
||||||
|
var withAsync = { [Symbol.asyncDispose]: function () { asyncUsed++; } }; \
|
||||||
|
var withSync = { [Symbol.dispose]: function () { syncUsed++; } }; \
|
||||||
|
{ await using a = withAsync; } \
|
||||||
|
{ await using b = withSync; }",
|
||||||
|
));
|
||||||
|
vm.run(&module).expect("run");
|
||||||
|
assert_eq!(global_value(&mut vm, "asyncUsed"), JsValue::int(1), "asyncDispose 优先");
|
||||||
|
assert_eq!(global_value(&mut vm, "syncUsed"), JsValue::int(1), "asyncDispose 未定义回退 dispose");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ impl Vm {
|
|||||||
save_stack: Vec::new(),
|
save_stack: Vec::new(),
|
||||||
spill_stack: Vec::new(),
|
spill_stack: Vec::new(),
|
||||||
dispose_stack: Vec::new(),
|
dispose_stack: Vec::new(),
|
||||||
|
dispose_marks: Vec::new(),
|
||||||
native_overflow_base: 0,
|
native_overflow_base: 0,
|
||||||
native_overflow_count: 0,
|
native_overflow_count: 0,
|
||||||
try_stack: Vec::new(),
|
try_stack: Vec::new(),
|
||||||
@@ -293,6 +294,7 @@ impl Vm {
|
|||||||
save_stack: Vec::new(),
|
save_stack: Vec::new(),
|
||||||
spill_stack: Vec::new(),
|
spill_stack: Vec::new(),
|
||||||
dispose_stack: Vec::new(),
|
dispose_stack: Vec::new(),
|
||||||
|
dispose_marks: Vec::new(),
|
||||||
native_overflow_base: 0,
|
native_overflow_base: 0,
|
||||||
native_overflow_count: 0,
|
native_overflow_count: 0,
|
||||||
try_stack: Vec::new(),
|
try_stack: Vec::new(),
|
||||||
@@ -518,8 +520,9 @@ impl Vm {
|
|||||||
self.save_stack.clear();
|
self.save_stack.clear();
|
||||||
self.spill_stack.clear();
|
self.spill_stack.clear();
|
||||||
// 释放栈条目持资源值(session 对象指针):跨 run/reset 残留会在下一 run
|
// 释放栈条目持资源值(session 对象指针):跨 run/reset 残留会在下一 run
|
||||||
// 被误释放或悬垂,随执行状态一并清空。
|
// 被误释放或悬垂,随执行状态一并清空;水位栈同清,防陈旧水位误截断。
|
||||||
self.dispose_stack.clear();
|
self.dispose_stack.clear();
|
||||||
|
self.dispose_marks.clear();
|
||||||
self.native_overflow_base = 0;
|
self.native_overflow_base = 0;
|
||||||
self.native_overflow_count = 0;
|
self.native_overflow_count = 0;
|
||||||
// native pack 实参区上界与溢出描述符同构:仅一次 native 调用期间有效,
|
// native pack 实参区上界与溢出描述符同构:仅一次 native 调用期间有效,
|
||||||
|
|||||||
Reference in New Issue
Block a user