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:
2026-08-16 09:31:39 +08:00
parent b885b672b9
commit a8378694be
23 changed files with 893 additions and 204 deletions
+37 -5
View File
@@ -368,14 +368,40 @@ impl Inst {
/// break 完成:label 指向循环/switch 出口,`crossed` 为逃出的 finally 域数
/// (emit 词法计算,运行时据此逐个穿越 finally;0 表示未逃出,不产生完成语义)。
/// 编码:rd 槽放 crossed(≤255),label 放 b 槽。
pub fn brk(label: LabelId, crossed: u16) -> Self {
Self::new(OpCode::BREAK, Operand::Imm(crossed), Operand::None, Operand::Label(label))
/// `for_of_count`/`for_in_count` 为逃出的迭代器层数,编码进 ext 字——
/// BREAK/CONTINUE 的 a/b 槽被跳转 offset 占用,无空闲位。
/// 编码:rd 槽放 crossed(≤255),label 放 b 槽,ext[0] 打包两个逃出计数。
pub fn brk(label: LabelId, crossed: u16, for_of_count: usize, for_in_count: usize) -> Self {
Self::with_ext(
OpCode::BREAK,
Operand::Imm(crossed),
Operand::None,
Operand::Label(label),
&[pack_escape_counts(for_of_count, for_in_count)],
)
}
/// continue 完成:同 break,label 指向循环继续目标。
pub fn cont(label: LabelId, crossed: u16) -> Self {
Self::new(OpCode::CONTINUE, Operand::Imm(crossed), Operand::None, Operand::Label(label))
pub fn cont(label: LabelId, crossed: u16, for_of_count: usize, for_in_count: usize) -> Self {
Self::with_ext(
OpCode::CONTINUE,
Operand::Imm(crossed),
Operand::None,
Operand::Label(label),
&[pack_escape_counts(for_of_count, for_in_count)],
)
}
/// return 完成:`src` 为返回值寄存器(None = 隐式 undefined,reg 0)。
/// `for_of_count`/`for_in_count` 为逃出的迭代器层数,编码进 ext 字。
pub fn ret(src: Operand, for_of_count: usize, for_in_count: usize) -> Self {
Self::with_ext(
OpCode::RETURN,
src,
Operand::None,
Operand::None,
&[pack_escape_counts(for_of_count, for_in_count)],
)
}
/// 条件寄存器为 false 时跳转。
@@ -409,6 +435,12 @@ impl Inst {
}
}
/// 把逃出的 for-of / for-in 层数打包进一个 ext 字:低 16 位 for-of,高 16 位 for-in。
/// 层数受寄存器上限约束(每层循环消耗若干 vreg),16 位覆盖远超实际可能。
fn pack_escape_counts(for_of_count: usize, for_in_count: usize) -> u32 {
((for_in_count.min(0xFFFF) as u32) << 16) | (for_of_count.min(0xFFFF) as u32)
}
/// spread 调用系 ext 构造:首字打包 `nstatic | (nspread << 8)`,后续按源码求值序排列
/// 每个实参字(静态实参 = 寄存器号,spread 源 = `0x8000_0000 | 寄存器号`)。
fn spread_ext(words: &[u32]) -> SmallVec<[u32; 4]> {
+3
View File
@@ -142,6 +142,9 @@ fn encode_inst(inst: &Inst, instrs: &mut Vec<u32>, jumps: &mut Vec<(usize, u32)>
_ => unreachable!("is_jump_op mismatch"),
};
instrs.push(instr);
// BREAK/CONTINUE 携带逃出迭代器层数的 ext 字;offset 回填只改写首条指令,
// ext 字随指令长度计入后续偏移(inst_to_instr 已含 ext)。
instrs.extend_from_slice(&inst.ext);
jumps.push((start, label));
return Ok(());
}