mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 18:51:13 +08:00
fix: for-of/for-in 提前退出时按规范顺序完成 IteratorClose
return/break/continue 逃出 for-of 时此前完全不执行 IteratorClose: BREAK/CONTINUE/RETURN 指令现在经 ext 字携带词法逃出的迭代器层数 (for-of + for-in 各 16 位),运行时在全部 finally 穿越之后、真正 跳转/返回之前按 LIFO 关闭——与规范 §13.7.5.4(循环体完成值已含 内部 try-finally 处理,IteratorClose 在后)顺序一致。 - emit 词法:LabelCtx 增加 for-of/for-in 深度计数,loop/switch/label 打开时快照,break/continue/return 按当前深度减目标深度计算逃出层数 (switch 内 break 以 switch 打开点为基准,目标循环自身不计入) - 指令编码:brk/cont/ret 三参构造,lower 跳转路径补发 ext 字 - 运行时:dispatch_break/continue/return 读取逃出计数,无 finally 时 直接关闭后跳转/返回,有 finally 时随 Completion 悬挂由 TRY_FINALLY_END 在穿越完成后消费;生成器 .return()/.throw() 注入经 complete_generator_return 全关快照栈迭代器;return() 抛错经 unwind 替代完成值并关闭剩余迭代器 - 顺带:for-in 逃出残留(无 return() 语义,仅弹栈);for-await-of 的 异步迭代器不做同步关闭(return() promise 须 await,走异步机制) - 新增 15 项回归测试:return/labeled break/continue/switch/while/finally 顺序/抛错优先/生成器注入/for-in/for-await-of 嵌套
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
//! break/continue 语句 emit:解析标签与最近循环的跳转目标。
|
||||
//!
|
||||
//! 计算 break/continue 逃出的 finally 域数(目标打开时的 finally 深度与当前深度之差):
|
||||
//! 逃出 ≥1 个 finally → 发 BREAK/CONTINUE(运行时穿越 finally 后再跳转);
|
||||
//! 不逃出(目标在 try 域内,如 switch/循环内部的 break)→ 发普通 JMP。
|
||||
//! 计算 break/continue 逃出的 finally 域数(目标打开时的 finally 深度与当前深度之差)
|
||||
//! 与逃出的迭代器层数(for-of/for-in 深度之差,目标循环自身的关闭由 CLOSE /
|
||||
//! FOR_IN_CLEANUP 处理,不计入):逃出 ≥1 个 finally 域或 ≥1 层迭代器 → 发
|
||||
//! BREAK/CONTINUE(运行时穿越 finally、关闭逃出迭代器后再跳转);否则发普通 JMP。
|
||||
|
||||
use crate::{CompileCtx, Emitter};
|
||||
use oxide_ir::inst::Inst;
|
||||
@@ -11,21 +12,35 @@ 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) = if let Some(label) = &stmt.label {
|
||||
let (break_label, fd_at_open, fod_at_open, fid_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, scope.finally_depth_at_open)
|
||||
} else if let Some((sw_label, fd)) = ctx.current_switch() {
|
||||
(*sw_label, *fd)
|
||||
(
|
||||
scope.break_label,
|
||||
scope.finally_depth_at_open,
|
||||
scope.for_of_depth_at_open,
|
||||
scope.for_in_depth_at_open,
|
||||
)
|
||||
} else if let Some((sw_label, fd, fod, fid)) = ctx.current_switch() {
|
||||
// switch 内 break:目标为 switch 出口。逃出计数以 switch 打开时的
|
||||
// 深度快照为基准——switch 之前已打开的循环不属于本次逃出,仅当
|
||||
// case 内新打开了迭代循环(并逃出)时才需关闭。
|
||||
(*sw_label, *fd, *fod, *fid)
|
||||
} else {
|
||||
let (bl, _, fd) = ctx.current_loop().ok_or("break outside switch or loop".to_string())?;
|
||||
(*bl, *fd)
|
||||
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,
|
||||
)
|
||||
};
|
||||
let crossed = ctx.labels.finally_depth.saturating_sub(fd_at_open) as u16;
|
||||
if crossed > 0 {
|
||||
ctx.inst(Inst::brk(break_label, crossed));
|
||||
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));
|
||||
} else {
|
||||
ctx.inst(Inst::jmp(break_label));
|
||||
}
|
||||
@@ -35,7 +50,7 @@ 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) = if let Some(label) = &stmt.label {
|
||||
let (continue_label, fd_at_open, fod_at_open, fid_at_open) = if let Some(label) = &stmt.label {
|
||||
let name = label.name.as_str();
|
||||
let scope = ctx
|
||||
.find_label(name)
|
||||
@@ -45,17 +60,37 @@ impl Emitter {
|
||||
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,
|
||||
)
|
||||
} else {
|
||||
let (_, cl, fd) = ctx.current_loop().ok_or("continue outside loop".to_string())?;
|
||||
(*cl, *fd)
|
||||
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,
|
||||
)
|
||||
};
|
||||
let crossed = ctx.labels.finally_depth.saturating_sub(fd_at_open) as u16;
|
||||
if crossed > 0 {
|
||||
ctx.inst(Inst::cont(continue_label, crossed));
|
||||
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));
|
||||
} else {
|
||||
ctx.inst(Inst::jmp(continue_label));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// 计算逃出目标循环需要关闭的 for-of / for-in 迭代器层数。
|
||||
///
|
||||
/// 深度快照(`fod_at_open`/`fid_at_open`)取目标循环打开后(含自身)的深度:
|
||||
/// 当前深度与之差 = 被逃出的中间层数。目标循环自身的关闭由循环收尾指令
|
||||
/// (FOR_OF_CLOSE / FOR_IN_CLEANUP)处理,不计入——break 的跳转目标在收尾指令
|
||||
/// 之后(含其 CLOSE),continue 则目标循环继续迭代。
|
||||
fn escape_iter_counts(ctx: &CompileCtx, fod_at_open: usize, fid_at_open: usize) -> (usize, usize) {
|
||||
let for_of_count = ctx.labels.for_of_depth.saturating_sub(fod_at_open);
|
||||
let for_in_count = ctx.labels.for_in_depth.saturating_sub(fid_at_open);
|
||||
(for_of_count, for_in_count)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ impl Emitter {
|
||||
let Statement::ReturnStatement(ret) = stmt else {
|
||||
return Ok(None);
|
||||
};
|
||||
// return 逃出全部打开的迭代循环:for-of 需 IteratorClose,for-in 弹出迭代器。
|
||||
let for_of_count = ctx.labels.for_of_depth;
|
||||
let for_in_count = ctx.labels.for_in_depth;
|
||||
match &ret.argument {
|
||||
Some(expr) => {
|
||||
let r = self.emit_expression(expr, ctx)?;
|
||||
@@ -18,12 +21,18 @@ impl Emitter {
|
||||
// 弹出连续纯 catch handler(finally handler 走运行时完成穿越逐个
|
||||
// 执行)。否则 return 跳过 TRY_END 会让 handler 残留在 try_stack,
|
||||
// 后续异常 unwind 会跳回已返回函数的 catch 形成死循环。
|
||||
self.emit_return_try_cleanup(ctx);
|
||||
ctx.inst(Inst::new(OpCode::RETURN, Operand::Reg(r), Operand::None, Operand::None));
|
||||
// 逃出迭代器时(close_count>0)保留 handler:return() 抛错须经
|
||||
// unwind 被外围 catch 捕获(新错误替代完成值),弹出后无法展开。
|
||||
if for_of_count == 0 && for_in_count == 0 {
|
||||
self.emit_return_try_cleanup(ctx);
|
||||
}
|
||||
ctx.inst(Inst::ret(Operand::Reg(r), for_of_count, for_in_count));
|
||||
}
|
||||
None => {
|
||||
self.emit_return_try_cleanup(ctx);
|
||||
ctx.inst(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None));
|
||||
if for_of_count == 0 && for_in_count == 0 {
|
||||
self.emit_return_try_cleanup(ctx);
|
||||
}
|
||||
ctx.inst(Inst::ret(Operand::None, for_of_count, for_in_count));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
|
||||
Reference in New Issue
Block a user