fix: break/continue 携当前语句列表累积值且循环出口值按迭代回写

This commit is contained in:
2026-09-20 12:19:52 +00:00
parent c4ce29c6f2
commit 28ccbebd52
15 changed files with 429 additions and 66 deletions
@@ -1,18 +1,39 @@
//! break/continue 语句 emit:解析标签与最近循环的跳转目标。
//! break/continue 语句 emit:解析标签与最近循环的跳转目标,发射前按完成值帧
//! 栈解析携值并写入出口目标寄存器。
//!
//! 计算 break/continue 逃出的 finally 域数(目标打开时的 finally 深度与当前深度之差)
//! 与逃出的迭代器层数(for-of/for-in 深度之差,目标循环自身的关闭由 CLOSE /
//! FOR_IN_CLEANUP 处理,不计入):逃出 ≥1 个 finally 域或 ≥1 层迭代器 → 发
//! BREAK/CONTINUE(运行时穿越 finally、关闭逃出迭代器后再跳转);否则发普通 JMP。
use crate::emit_ctx::CompletionCarry;
use crate::{CompileCtx, Emitter};
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
impl Emitter {
/// break/continue 的携值写入:自完成值帧栈顶解析携值,非空携值在跳转前
/// 写入出口目标寄存器——`Value(r)` 拷寄存器、`Undefined` 物化 undefined、
/// `Empty` 不写(目标寄存器保持既有累积值,规范
/// `UpdateEmpty(break, iterationResult)` 等义)。
fn emit_completion_carry(&self, ctx: &mut CompileCtx, target_v_reg: u32) {
match ctx.resolve_completion_carry(target_v_reg) {
CompletionCarry::Value(r) => {
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(target_v_reg), Operand::Reg(r), Operand::None));
}
CompletionCarry::Undefined => {
let u = self.emit_undefined(ctx);
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(target_v_reg), Operand::Reg(u), Operand::None));
}
CompletionCarry::Empty => {}
}
}
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 (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)
@@ -22,12 +43,19 @@ impl Emitter {
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_label, fd, fod, fid)) = ctx.current_switch() {
} else if let Some(sw) = ctx.current_switch() {
// switch 内 break:目标为 switch 出口。逃出计数以 switch 打开时的
// 深度快照为基准——switch 之前已打开的循环不属于本次逃出,仅当
// case 内新打开了迭代循环(并逃出)时才需关闭。
(*sw_label, *fd, *fod, *fid)
(
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())?;
(
@@ -35,8 +63,12 @@ impl Emitter {
entry.finally_depth_at_open,
entry.for_of_depth_at_open,
entry.for_in_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 {
@@ -50,7 +82,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, fod_at_open, fid_at_open) = if let Some(label) = &stmt.label {
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)
@@ -62,6 +94,7 @@ impl Emitter {
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())?;
@@ -70,8 +103,12 @@ impl Emitter {
entry.finally_depth_at_open,
entry.for_of_depth_at_open,
entry.for_in_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 {
+59 -1
View File
@@ -1,6 +1,10 @@
//! 标签语句 emit:`emit_labeled_statement` 登记 break/continue 标签作用域。
use crate::{CompileCtx, Emitter};
use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
use oxide_parser::Statement;
impl Emitter {
@@ -15,6 +19,42 @@ impl Emitter {
)
}
/// 标签体是否含指向本标签的 `break <name>`:递归走语句树,不下钻嵌套
/// 函数体(嵌套函数是独立编译单元,其 break 无法指向外层标签)。
fn body_contains_break_to_label(stmt: &Statement, name: &str) -> bool {
match stmt {
Statement::BreakStatement(b) => b.label.as_ref().is_some_and(|l| l.name.as_str() == name),
Statement::BlockStatement(b) => b.body.iter().any(|s| Self::body_contains_break_to_label(s, name)),
Statement::IfStatement(i) => {
Self::body_contains_break_to_label(&i.consequent, name)
|| i.alternate
.as_ref()
.is_some_and(|a| Self::body_contains_break_to_label(a, name))
}
Statement::WhileStatement(w) => Self::body_contains_break_to_label(&w.body, name),
Statement::DoWhileStatement(d) => Self::body_contains_break_to_label(&d.body, name),
Statement::ForStatement(f) => Self::body_contains_break_to_label(&f.body, name),
Statement::ForInStatement(f) => Self::body_contains_break_to_label(&f.body, name),
Statement::ForOfStatement(f) => Self::body_contains_break_to_label(&f.body, name),
Statement::LabeledStatement(l) => Self::body_contains_break_to_label(&l.body, name),
Statement::SwitchStatement(s) => s
.cases
.iter()
.any(|c| c.consequent.iter().any(|st| Self::body_contains_break_to_label(st, name))),
Statement::TryStatement(t) => {
t.block.body.iter().any(|s| Self::body_contains_break_to_label(s, name))
|| t.handler
.as_ref()
.is_some_and(|h| h.body.body.iter().any(|s| Self::body_contains_break_to_label(s, name)))
|| t.finalizer
.as_ref()
.is_some_and(|f| f.body.iter().any(|s| Self::body_contains_break_to_label(s, name)))
}
Statement::WithStatement(w) => Self::body_contains_break_to_label(&w.body, name),
_ => false,
}
}
pub(crate) fn emit_labeled_statement(
&self, stmt: &oxide_parser::LabeledStatement, ctx: &mut CompileCtx,
) -> Result<Option<u32>, String> {
@@ -24,9 +64,27 @@ impl Emitter {
let body_result = if Self::is_iteration_statement(&stmt.body) {
ctx.queue_loop_label(name)?;
self.emit_statement(&stmt.body, ctx)?
} else if Self::body_contains_break_to_label(&stmt.body, name) {
// 体含指向本标签的 break:出口结果寄存器在此分配并初始 undefined,
// 体正常完成回写、break 携值就地写入(break 后死语句的寄存器不得
// 成为完成值)。
let v_reg = ctx.alloc_reg();
let undef_idx = ctx.add_constant(Constant::Undefined);
ctx.inst(Inst::load_const(Operand::Reg(v_reg), undef_idx));
let id = ctx.next_label_id();
ctx.push_label_scope(name, id, None, Some(v_reg))?;
ctx.push_completion_target(v_reg);
let r = self.emit_statement(&stmt.body, ctx)?;
if let Some(r) = r {
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(v_reg), Operand::Reg(r), Operand::None));
}
ctx.pop_completion_target();
ctx.labels.set_label_pos(id, ctx.insts.len());
ctx.pop_label_scope();
Some(v_reg)
} else {
let id = ctx.next_label_id();
ctx.push_label_scope(name, id, None)?;
ctx.push_label_scope(name, id, None, None)?;
let r = self.emit_statement(&stmt.body, ctx)?;
ctx.labels.set_label_pos(id, ctx.insts.len());
ctx.pop_label_scope();