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
+15 -3
View File
@@ -647,21 +647,33 @@ impl Vm {
/// - `Ok(Some(value))`:直接完成,无需再 dispatch(`value` 为返回值);
/// - `Ok(None)`:进入 finally 穿越,调用方须继续 `dispatch()`。
pub(crate) fn complete_generator_return(&mut self, value: JsValue) -> Result<Option<JsValue>, String> {
self.pop_frame_catch_handlers();
// 纯 catch handler 的清理延后到完成消费处:record_completion 在 finally
// 穿越路径弹出逃出的 catch-only handler;无 finally 时 do_return 弹帧前
// 兜底清理。若提前弹出,return() 抛错经 unwind 展开时将找不到外围 catch。
let crossed = self
.try_stack
.iter()
.filter(|h| h.frame_depth == self.frames.len() && h.finally_pc.is_some())
.count();
// .return()/.throw() 注入时生成器挂起快照中打开的全部迭代器一并逃出
// (栈上迭代器属于本生成器),完成恢复处统一关闭。
let for_of_count = self.iters.for_of_iters.len();
let for_in_count = self.iters.for_in_iters.len();
if let Some(finally_pc) = self.record_completion(crate::vm::Completion::Return {
value,
remaining_finally: crossed,
for_of_count,
for_in_count,
}) {
self.pc = finally_pc;
return Ok(None);
}
// 无 finally:直接交付返回(弹出生成器帧后 frames 为空 → Some(value))。
self.do_return(value)
// 无 finally:关闭逃出迭代器后直接交付返回(弹出生成器帧后 frames 为空 → Some(value))。
match self.close_escaped_iters(for_of_count, for_in_count) {
Ok(true) => self.do_return(value),
Ok(false) => Ok(None),
Err(e) => Err(e),
}
}
}