fix: try/finally 支持 break/continue/return 穿越 finally(completion 机制)
This commit is contained in:
@@ -143,6 +143,8 @@ pub struct LabelScope {
|
||||
pub(crate) name: String,
|
||||
pub(crate) break_label: LabelId,
|
||||
pub(crate) continue_label: Option<LabelId>,
|
||||
/// 标签打开时嵌套的 finally 域数:break/continue 跨越 finally 的计数依据。
|
||||
pub(crate) finally_depth_at_open: usize,
|
||||
}
|
||||
|
||||
/// 单函数编译上下文:执行流 + 作用域 + 闭包捕获的聚合状态。
|
||||
@@ -233,6 +235,7 @@ impl CompileCtx {
|
||||
switch_stack: Vec::new(),
|
||||
label_scopes: Vec::new(),
|
||||
pending_loop_labels: Vec::new(),
|
||||
finally_depth: 0,
|
||||
label_counter: 0,
|
||||
},
|
||||
scopes: ScopeCtx {
|
||||
@@ -375,39 +378,52 @@ impl CompileCtx {
|
||||
}
|
||||
|
||||
pub(crate) fn push_loop(&mut self, break_label: LabelId, continue_label: LabelId) {
|
||||
self.labels.loop_stack.push((break_label, continue_label));
|
||||
let fd = self.labels.finally_depth;
|
||||
self.labels.loop_stack.push((break_label, continue_label, fd));
|
||||
}
|
||||
|
||||
pub(crate) fn pop_loop(&mut self) {
|
||||
self.labels.loop_stack.pop();
|
||||
}
|
||||
|
||||
pub(crate) fn current_loop(&self) -> Option<&(LabelId, LabelId)> {
|
||||
pub(crate) fn current_loop(&self) -> Option<&(LabelId, LabelId, usize)> {
|
||||
self.labels.loop_stack.last()
|
||||
}
|
||||
|
||||
pub(crate) fn push_switch(&mut self, break_label: LabelId) {
|
||||
self.labels.switch_stack.push(break_label);
|
||||
let fd = self.labels.finally_depth;
|
||||
self.labels.switch_stack.push((break_label, fd));
|
||||
}
|
||||
|
||||
pub(crate) fn pop_switch(&mut self) {
|
||||
self.labels.switch_stack.pop();
|
||||
}
|
||||
|
||||
pub(crate) fn current_switch(&self) -> Option<&LabelId> {
|
||||
pub(crate) fn current_switch(&self) -> Option<&(LabelId, usize)> {
|
||||
self.labels.switch_stack.last()
|
||||
}
|
||||
|
||||
/// 进入/离开一个 try/finally 域:break/continue 跨越 finally 计数用。
|
||||
pub(crate) fn push_finally_domain(&mut self) {
|
||||
self.labels.finally_depth += 1;
|
||||
}
|
||||
|
||||
pub(crate) fn pop_finally_domain(&mut self) {
|
||||
self.labels.finally_depth -= 1;
|
||||
}
|
||||
|
||||
pub(crate) fn push_label_scope(
|
||||
&mut self, name: &str, break_label: LabelId, continue_label: Option<LabelId>,
|
||||
) -> Result<(), String> {
|
||||
if self.labels.label_scopes.iter().any(|s| s.name == name) {
|
||||
return Err(format!("SyntaxError: Label '{name}' has already been declared"));
|
||||
}
|
||||
let fd = self.labels.finally_depth;
|
||||
self.labels.label_scopes.push(LabelScope {
|
||||
name: name.to_string(),
|
||||
break_label,
|
||||
continue_label,
|
||||
finally_depth_at_open: fd,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@@ -437,11 +453,13 @@ impl CompileCtx {
|
||||
pub(crate) fn take_pending_loop_labels(&mut self, break_label: LabelId, continue_label: LabelId) -> usize {
|
||||
let names = std::mem::take(&mut self.labels.pending_loop_labels);
|
||||
let count = names.len();
|
||||
let fd = self.labels.finally_depth;
|
||||
for name in names {
|
||||
self.labels.label_scopes.push(LabelScope {
|
||||
name,
|
||||
break_label,
|
||||
continue_label: Some(continue_label),
|
||||
finally_depth_at_open: fd,
|
||||
});
|
||||
}
|
||||
count
|
||||
|
||||
@@ -13,12 +13,16 @@ use oxide_ir::operand::LabelId;
|
||||
pub(crate) struct LabelCtx {
|
||||
/// label id → 指令下标。id 连续递增,Vec 索引即 id;写入前须扩容。
|
||||
pub(crate) label_pos: Vec<Option<usize>>,
|
||||
pub(crate) loop_stack: Vec<(LabelId, LabelId)>,
|
||||
pub(crate) switch_stack: Vec<LabelId>,
|
||||
/// 每个条目记录循环打开时嵌套的 finally 域数(break/continue 跨越 finally 计数用)。
|
||||
pub(crate) loop_stack: Vec<(LabelId, LabelId, usize)>,
|
||||
/// 每个条目记录 switch 打开时嵌套的 finally 域数。
|
||||
pub(crate) switch_stack: Vec<(LabelId, usize)>,
|
||||
/// 活动标签语句作用域(解析 `break label` / `continue label`)。
|
||||
pub(crate) label_scopes: Vec<LabelScope>,
|
||||
/// 等待绑定到下一个循环 continue 目标的标签名。
|
||||
pub(crate) pending_loop_labels: Vec<String>,
|
||||
/// 当前打开(正在 emit)的 try/finally 域数。
|
||||
pub(crate) finally_depth: usize,
|
||||
pub(crate) label_counter: u32,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
//! break/continue 语句 emit:解析标签与最近循环的跳转目标。
|
||||
//!
|
||||
//! 计算 break/continue 逃出的 finally 域数(目标打开时的 finally 深度与当前深度之差):
|
||||
//! 逃出 ≥1 个 finally → 发 BREAK/CONTINUE(运行时穿越 finally 后再跳转);
|
||||
//! 不逃出(目标在 try 域内,如 switch/循环内部的 break)→ 发普通 JMP。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
@@ -7,38 +11,51 @@ impl Emitter {
|
||||
pub(crate) fn emit_break_statement(
|
||||
&self, stmt: &oxide_parser::BreakStatement, ctx: &mut CompileCtx,
|
||||
) -> Result<Option<u32>, String> {
|
||||
let break_label = if let Some(label) = &stmt.label {
|
||||
let (break_label, fd_at_open) = 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
|
||||
} else if let Some(sw_label) = ctx.current_switch() {
|
||||
*sw_label
|
||||
(scope.break_label, scope.finally_depth_at_open)
|
||||
} else if let Some((sw_label, fd)) = ctx.current_switch() {
|
||||
(*sw_label, *fd)
|
||||
} else {
|
||||
let (bl, _) = ctx.current_loop().ok_or("break outside switch or loop".to_string())?;
|
||||
*bl
|
||||
let (bl, _, fd) = ctx.current_loop().ok_or("break outside switch or loop".to_string())?;
|
||||
(*bl, *fd)
|
||||
};
|
||||
ctx.inst(Inst::jmp(break_label));
|
||||
let crossed = ctx.labels.finally_depth.saturating_sub(fd_at_open) as u16;
|
||||
if crossed > 0 {
|
||||
ctx.inst(Inst::brk(break_label, crossed));
|
||||
} else {
|
||||
ctx.inst(Inst::jmp(break_label));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub(crate) fn emit_continue_statement(
|
||||
&self, stmt: &oxide_parser::ContinueStatement, ctx: &mut CompileCtx,
|
||||
) -> Result<Option<u32>, String> {
|
||||
let continue_label = if let Some(label) = &stmt.label {
|
||||
let (continue_label, fd_at_open) = 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.continue_label.ok_or_else(|| {
|
||||
format!("SyntaxError: Illegal continue statement: '{name}' does not denote an iteration statement")
|
||||
})?,
|
||||
scope.finally_depth_at_open,
|
||||
)
|
||||
} else {
|
||||
let (_, cl) = ctx.current_loop().ok_or("continue outside loop".to_string())?;
|
||||
*cl
|
||||
let (_, cl, fd) = ctx.current_loop().ok_or("continue outside loop".to_string())?;
|
||||
(*cl, *fd)
|
||||
};
|
||||
ctx.inst(Inst::jmp(continue_label));
|
||||
let crossed = ctx.labels.finally_depth.saturating_sub(fd_at_open) as u16;
|
||||
if crossed > 0 {
|
||||
ctx.inst(Inst::cont(continue_label, crossed));
|
||||
} else {
|
||||
ctx.inst(Inst::jmp(continue_label));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ impl Emitter {
|
||||
let has_finally = ts.finalizer.is_some();
|
||||
let result_reg = ctx.alloc_reg();
|
||||
if has_finally {
|
||||
// finally 域跨整个语句(try 体 + catch 体 + finally 体):期间 break/continue
|
||||
// 逃出语句时须穿越本 finally,词法上记录域深度供跨越计数。
|
||||
ctx.push_finally_domain();
|
||||
ctx.inst(Inst::try_finally_begin(finally_label));
|
||||
}
|
||||
if has_catch {
|
||||
@@ -81,6 +84,7 @@ impl Emitter {
|
||||
Operand::None,
|
||||
));
|
||||
ctx.inst(Inst::new(OpCode::TRY_FINALLY_END, Operand::None, Operand::None, Operand::None));
|
||||
ctx.pop_finally_domain();
|
||||
}
|
||||
ctx.labels.set_label_pos(try_end_label, ctx.insts.len());
|
||||
Ok(Some(result_reg))
|
||||
|
||||
Reference in New Issue
Block a user