feat: 异常与 return 穿越释放资源,dispose_count 门控

This commit is contained in:
2026-10-09 19:14:36 +00:00
parent 48ffb54c4f
commit ae73fb8da5
20 changed files with 264 additions and 152 deletions
+1 -1
View File
@@ -316,7 +316,7 @@ impl Emitter {
module.insts.extend(field_ctx.insts);
module.constants = field_ctx.constants;
module.n_registers = field_ctx.max_regs.max(1);
module.insts.push(Inst::ret(Operand::Reg(undef_reg), 0, 0));
module.insts.push(Inst::ret(Operand::Reg(undef_reg), 0, 0, 0));
}
module
};
-3
View File
@@ -50,7 +50,6 @@ pub(crate) struct LoopEntry {
pub(crate) v_reg: u32,
/// 循环打开时已打开的释放作用域数(break/continue/return 逃出时据此
/// 计算需穿越释放的作用域层数)。
#[allow(dead_code)]
pub(crate) dispose_depth_at_open: usize,
}
@@ -88,7 +87,6 @@ pub struct LabelScope {
/// 标签打开时已打开的 for-in 循环数。
pub(crate) for_in_depth_at_open: usize,
/// 标签打开时已打开的释放作用域数(break 逃出时据此计算需穿越释放的层数)。
#[allow(dead_code)]
pub(crate) dispose_depth_at_open: usize,
/// 标签目标出口结果寄存器:迭代标签取所包裹循环的 `v_reg`;非迭代标签仅当
/// 体含指向本标签的 break 时分配(体正常完成回写、break 携值写入)。
@@ -104,7 +102,6 @@ pub(crate) struct SwitchEntry {
pub(crate) for_of_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,
}
+3 -3
View File
@@ -509,18 +509,18 @@ impl Emitter {
}
if is_expression_body {
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, 0));
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
let undef_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
ctx.inst(Inst::ret(Operand::Reg(undef_reg), 0, 0));
ctx.inst(Inst::ret(Operand::Reg(undef_reg), 0, 0, 0));
}
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
let undef_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
ctx.inst(Inst::ret(Operand::Reg(undef_reg), 0, 0));
ctx.inst(Inst::ret(Operand::Reg(undef_reg), 0, 0, 0));
}
// 调用契约参数段只含固定形参:rest 是函数体内普通变量,不在 VM 实参传递区。
+3 -3
View File
@@ -938,9 +938,9 @@ impl Emitter {
let undef_idx = ctx.add_constant(Constant::Undefined);
let r = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(r), undef_idx));
ctx.inst(Inst::ret(Operand::Reg(r), 0, 0));
ctx.inst(Inst::ret(Operand::Reg(r), 0, 0, 0));
} else {
ctx.inst(Inst::ret(Operand::Reg(ns_reg), 0, 0));
ctx.inst(Inst::ret(Operand::Reg(ns_reg), 0, 0, 0));
}
Ok(())
}
@@ -1003,7 +1003,7 @@ impl Emitter {
let kind_reg = self.load_string_const(kind, &mut ctx);
let content_reg = self.load_string_const(content, &mut ctx);
let val_reg = self.emit_module_call(&mut ctx, "__moduleData", &[kind_reg, content_reg])?;
ctx.inst(Inst::ret(Operand::Reg(val_reg), 0, 0));
ctx.inst(Inst::ret(Operand::Reg(val_reg), 0, 0, 0));
Ok(ctx.assemble_ir(ParamLayout { base: 0, count: 0 }, None))
}
@@ -33,46 +33,51 @@ impl Emitter {
pub(crate) fn emit_break_statement(
&self, stmt: &oxide_parser::BreakStatement, ctx: &mut CompileCtx,
) -> Result<Option<u32>, String> {
let (break_label, fd_at_open, fod_at_open, fid_at_open, target_v_reg) = if let Some(label) = &stmt.label {
let name = label.name.as_str();
let scope = ctx
.find_label(name)
.ok_or_else(|| format!("SyntaxError: Undefined label '{name}'"))?;
(
scope.break_label,
scope.finally_depth_at_open,
scope.for_of_depth_at_open,
scope.for_in_depth_at_open,
scope.completion_reg,
)
} else if let Some(sw) = ctx.current_switch() {
// switch 内 break:目标为 switch 出口。逃出计数以 switch 打开时的
// 深度快照为基准——switch 之前已打开的循环不属于本次逃出,仅当
// case 内新打开了迭代循环(并逃出)时才需关闭。
(
sw.break_label,
sw.finally_depth_at_open,
sw.for_of_depth_at_open,
sw.for_in_depth_at_open,
Some(sw.result_reg),
)
} else {
let entry = ctx.current_loop().ok_or("break outside switch or loop".to_string())?;
(
entry.break_label,
entry.finally_depth_at_open,
entry.for_of_depth_at_open,
entry.for_in_depth_at_open,
Some(entry.v_reg),
)
};
let (break_label, fd_at_open, fod_at_open, fid_at_open, dd_at_open, target_v_reg) =
if let Some(label) = &stmt.label {
let name = label.name.as_str();
let scope = ctx
.find_label(name)
.ok_or_else(|| format!("SyntaxError: Undefined label '{name}'"))?;
(
scope.break_label,
scope.finally_depth_at_open,
scope.for_of_depth_at_open,
scope.for_in_depth_at_open,
scope.dispose_depth_at_open,
scope.completion_reg,
)
} else if let Some(sw) = ctx.current_switch() {
// switch 内 break:目标为 switch 出口。逃出计数以 switch 打开时的
// 深度快照为基准——switch 之前已打开的循环不属于本次逃出,仅当
// case 内新打开了迭代循环(并逃出)时才需关闭。
(
sw.break_label,
sw.finally_depth_at_open,
sw.for_of_depth_at_open,
sw.for_in_depth_at_open,
sw.dispose_depth_at_open,
Some(sw.result_reg),
)
} else {
let entry = ctx.current_loop().ok_or("break outside switch or loop".to_string())?;
(
entry.break_label,
entry.finally_depth_at_open,
entry.for_of_depth_at_open,
entry.for_in_depth_at_open,
entry.dispose_depth_at_open,
Some(entry.v_reg),
)
};
if let Some(target_v_reg) = target_v_reg {
self.emit_completion_carry(ctx, target_v_reg);
}
let crossed = ctx.labels.finally_depth.saturating_sub(fd_at_open) as u16;
let (for_of_count, for_in_count) = escape_iter_counts(ctx, fod_at_open, fid_at_open);
if crossed > 0 || for_of_count > 0 || for_in_count > 0 {
ctx.inst(Inst::brk(break_label, crossed, for_of_count, for_in_count));
let dispose_count = ctx.labels.dispose_depth.saturating_sub(dd_at_open);
if crossed > 0 || for_of_count > 0 || for_in_count > 0 || dispose_count > 0 {
ctx.inst(Inst::brk(break_label, crossed, for_of_count, for_in_count, dispose_count));
} else {
ctx.inst(Inst::jmp(break_label));
}
@@ -82,37 +87,41 @@ impl Emitter {
pub(crate) fn emit_continue_statement(
&self, stmt: &oxide_parser::ContinueStatement, ctx: &mut CompileCtx,
) -> Result<Option<u32>, String> {
let (continue_label, fd_at_open, fod_at_open, fid_at_open, target_v_reg) = if let Some(label) = &stmt.label {
let name = label.name.as_str();
let scope = ctx
.find_label(name)
.ok_or_else(|| format!("SyntaxError: Undefined label '{name}'"))?;
(
scope.continue_label.ok_or_else(|| {
format!("SyntaxError: Illegal continue statement: '{name}' does not denote an iteration statement")
})?,
scope.finally_depth_at_open,
scope.for_of_depth_at_open,
scope.for_in_depth_at_open,
scope.completion_reg,
)
} else {
let entry = ctx.current_loop().ok_or("continue outside loop".to_string())?;
(
entry.continue_label,
entry.finally_depth_at_open,
entry.for_of_depth_at_open,
entry.for_in_depth_at_open,
Some(entry.v_reg),
)
};
let (continue_label, fd_at_open, fod_at_open, fid_at_open, dd_at_open, target_v_reg) =
if let Some(label) = &stmt.label {
let name = label.name.as_str();
let scope = ctx
.find_label(name)
.ok_or_else(|| format!("SyntaxError: Undefined label '{name}'"))?;
(
scope.continue_label.ok_or_else(|| {
format!("SyntaxError: Illegal continue statement: '{name}' does not denote an iteration statement")
})?,
scope.finally_depth_at_open,
scope.for_of_depth_at_open,
scope.for_in_depth_at_open,
scope.dispose_depth_at_open,
scope.completion_reg,
)
} else {
let entry = ctx.current_loop().ok_or("continue outside loop".to_string())?;
(
entry.continue_label,
entry.finally_depth_at_open,
entry.for_of_depth_at_open,
entry.for_in_depth_at_open,
entry.dispose_depth_at_open,
Some(entry.v_reg),
)
};
if let Some(target_v_reg) = target_v_reg {
self.emit_completion_carry(ctx, target_v_reg);
}
let crossed = ctx.labels.finally_depth.saturating_sub(fd_at_open) as u16;
let (for_of_count, for_in_count) = escape_iter_counts(ctx, fod_at_open, fid_at_open);
if crossed > 0 || for_of_count > 0 || for_in_count > 0 {
ctx.inst(Inst::cont(continue_label, crossed, for_of_count, for_in_count));
let dispose_count = ctx.labels.dispose_depth.saturating_sub(dd_at_open);
if crossed > 0 || for_of_count > 0 || for_in_count > 0 || dispose_count > 0 {
ctx.inst(Inst::cont(continue_label, crossed, for_of_count, for_in_count, dispose_count));
} else {
ctx.inst(Inst::jmp(continue_label));
}
@@ -12,9 +12,11 @@ impl Emitter {
let Statement::ReturnStatement(ret) = stmt else {
return Ok(None);
};
// return 逃出全部打开的迭代循环:for-of 需 IteratorClose,for-in 弹出迭代器。
// return 逃出全部打开的迭代循环与释放作用域:for-of 需 IteratorClose,
// for-in 弹出迭代器,释放栈顶部资源需 DisposeResources。
let for_of_count = ctx.labels.for_of_depth;
let for_in_count = ctx.labels.for_in_depth;
let dispose_count = ctx.labels.dispose_depth;
match &ret.argument {
Some(expr) => {
let r = self.emit_expression(expr, ctx)?;
@@ -22,15 +24,15 @@ impl Emitter {
// 弹出连续纯 catch handler(finally handler 走运行时完成穿越逐个
// 执行)。否则 return 跳过 TRY_END 会让 handler 残留在 try_stack,
// 后续异常 unwind 会跳回已返回函数的 catch 形成死循环。
// 逃出迭代器时(close_count>0)保留 handler:return() 抛错须经
// unwind 被外围 catch 捕获(新错误替代完成值),弹出后无法展开。
if for_of_count == 0 && for_in_count == 0 {
// 逃出迭代器或资源时(close_count>0)保留 handler:return() 抛错
// 须经 unwind 被外围 catch 捕获(新错误替代完成值),弹出后无法展开。
if for_of_count == 0 && for_in_count == 0 && dispose_count == 0 {
self.emit_return_try_cleanup(ctx);
}
ctx.inst(Inst::ret(Operand::Reg(r), for_of_count, for_in_count));
ctx.inst(Inst::ret(Operand::Reg(r), for_of_count, for_in_count, dispose_count));
}
None => {
if for_of_count == 0 && for_in_count == 0 {
if for_of_count == 0 && for_in_count == 0 && dispose_count == 0 {
self.emit_return_try_cleanup(ctx);
}
// 裸 return 须显式物化 undefined 为完成值:RETURN 从 rd 槽读值,
@@ -38,7 +40,7 @@ impl Emitter {
let undef_idx = ctx.add_constant(Constant::Undefined);
let undef_reg = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(undef_reg), undef_idx));
ctx.inst(Inst::ret(Operand::Reg(undef_reg), for_of_count, for_in_count));
ctx.inst(Inst::ret(Operand::Reg(undef_reg), for_of_count, for_in_count, dispose_count));
}
}
Ok(None)