This repository has been archived on 2026-09-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OxideJS/crates/oxide_emit/src/stmt/basic/break_continue.rs
T
pjh123 a8378694be 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 嵌套
2026-08-16 09:31:39 +08:00

97 lines
4.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! break/continue 语句 emit:解析标签与最近循环的跳转目标。
//!
//! 计算 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;
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) = 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,
)
} 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 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;
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));
}
Ok(None)
}
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) = 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,
)
} 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,
)
};
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));
} 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)
}