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
+28 -5
View File
@@ -44,7 +44,8 @@ pub(crate) struct SuspendedFrame {
impl SuspendedFrame {
/// 全空帧(New 阶段构造状态盒时用)。
pub fn new_empty() -> Self { SuspendedFrame {
pub fn new_empty() -> Self {
SuspendedFrame {
regs: Box::new([JsValue::undefined(); 256]),
pc: 0,
bytecode: Arc::default(),
@@ -233,9 +234,16 @@ impl SuspendedFrame {
self.exception_value = self.exception_value.map(&mut rewrite);
self.pending_exception = self.pending_exception.map(&mut rewrite);
self.pending_completion = self.pending_completion.map(|completion| match completion {
Completion::Return { value, remaining_finally } => Completion::Return {
Completion::Return {
value,
remaining_finally,
for_of_count,
for_in_count,
} => Completion::Return {
value: rewrite(value),
remaining_finally,
for_of_count,
for_in_count,
},
other => other,
});
@@ -295,6 +303,7 @@ impl SuspendedFrame {
.map(|entry| ForOfEntry {
iterator: rewrite(entry.iterator),
last_result: rewrite(entry.last_result),
is_async: entry.is_async,
})
.collect(),
delegated_iterator: self.delegated_iterator.map(&mut rewrite),
@@ -304,9 +313,16 @@ impl SuspendedFrame {
pending_exception: self.pending_exception.map(&mut rewrite),
pending_error_kind: self.pending_error_kind,
pending_completion: self.pending_completion.map(|completion| match completion {
Completion::Return { value, remaining_finally } => Completion::Return {
Completion::Return {
value,
remaining_finally,
for_of_count,
for_in_count,
} => Completion::Return {
value: rewrite(value),
remaining_finally,
for_of_count,
for_in_count,
},
other => other,
}),
@@ -375,16 +391,21 @@ mod tests {
frame.for_of_iters.push(ForOfEntry {
iterator: JsValue::float(6.0),
last_result: JsValue::float(7.0),
is_async: false,
});
frame.delegated_iterator = Some(JsValue::float(8.0));
frame.saved_bytecode_stack.push(Arc::from(vec![0u32]));
frame.saved_immutables_stack.push(std::ptr::slice_from_raw_parts(std::ptr::null(), 0));
frame
.saved_immutables_stack
.push(std::ptr::slice_from_raw_parts(std::ptr::null(), 0));
frame.exception_value = Some(JsValue::float(9.0));
frame.pending_exception = Some(JsValue::float(10.0));
frame.pending_error_kind = Some("Error");
frame.pending_completion = Some(Completion::Return {
value: JsValue::float(11.0),
remaining_finally: 0,
for_of_count: 0,
for_in_count: 0,
});
}
@@ -426,6 +447,8 @@ mod tests {
machine.pending_completion = Some(Completion::Return {
value: JsValue::float(46.0),
remaining_finally: 1,
for_of_count: 0,
for_in_count: 0,
});
let mut frame = SuspendedFrame::new_empty();
@@ -435,7 +458,7 @@ mod tests {
assert_eq!(frame.delegated_iterator, Some(JsValue::float(45.0)));
assert!(matches!(
frame.pending_completion,
Some(Completion::Return { value, remaining_finally: 1 }) if value == JsValue::float(46.0)
Some(Completion::Return { value, remaining_finally: 1, .. }) if value == JsValue::float(46.0)
));
// sub_modules 为空表:恢复应报"跨 run"错(保持现有三处行为)。