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 2e16b63d9b
commit 744899aae9
23 changed files with 893 additions and 204 deletions
@@ -14,7 +14,7 @@ impl Emitter {
let start_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
ctx.labels.set_label_pos(start_label, ctx.insts.len());
ctx.push_loop(end_label, start_label);
ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::Plain);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
self.emit_statement(&dw.body, ctx)?;
let test_reg = self.emit_expression(&dw.test, ctx)?;
+1 -1
View File
@@ -40,7 +40,7 @@ impl Emitter {
let start_label = ctx.next_label_id();
let update_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
ctx.push_loop(end_label, update_label);
ctx.push_loop(end_label, update_label, crate::emit_ctx::LoopKind::Plain);
let n_labeled = ctx.take_pending_loop_labels(end_label, update_label);
// 循环头声明中被嵌套函数捕获的 let/const 绑定:每迭代 fresh cell。
let mut fresh_bindings: Vec<(String, u8)> = Vec::new();
@@ -17,7 +17,7 @@ impl Emitter {
let obj_reg = self.emit_expression(&fi.right, ctx)?;
ctx.inst(Inst::new(OpCode::FOR_IN_INIT, Operand::None, Operand::Reg(obj_reg), Operand::None));
ctx.labels.set_label_pos(start_label, ctx.insts.len());
ctx.push_loop(end_label, start_label);
ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::ForIn);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
let done_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::FOR_IN_DONE, Operand::Reg(done_reg), Operand::None, Operand::None));
@@ -29,7 +29,7 @@ impl Emitter {
let iter_src_reg = self.emit_expression(&fo.right, ctx)?;
ctx.inst(Inst::new(OpCode::FOR_OF_INIT, Operand::None, Operand::Reg(iter_src_reg), Operand::None));
ctx.labels.set_label_pos(start_label, ctx.insts.len());
ctx.push_loop(end_label, start_label);
ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::ForOf);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
let has_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::FOR_OF_DONE, Operand::Reg(has_reg), Operand::None, Operand::None));
@@ -64,7 +64,7 @@ impl Emitter {
Operand::None,
));
ctx.labels.set_label_pos(start_label, ctx.insts.len());
ctx.push_loop(end_label, start_label);
ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::ForAwaitOf);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
let next_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::FOR_AWAIT_OF_NEXT, Operand::Reg(next_reg), Operand::None, Operand::None));
@@ -12,7 +12,7 @@ impl Emitter {
let start_label = ctx.next_label_id();
let end_label = ctx.next_label_id();
ctx.labels.set_label_pos(start_label, ctx.insts.len());
ctx.push_loop(end_label, start_label);
ctx.push_loop(end_label, start_label, crate::emit_ctx::LoopKind::Plain);
let n_labeled = ctx.take_pending_loop_labels(end_label, start_label);
let test_reg = self.emit_expression(&wh.test, ctx)?;
ctx.inst(Inst::jmp_if_false(test_reg, end_label));