feat: for-await-of 逃出关闭异步 await return() 结算后恢复帧

This commit is contained in:
2026-09-27 19:48:19 +00:00
parent c9f8597491
commit 9ab19fb472
11 changed files with 457 additions and 65 deletions
+5 -5
View File
@@ -20,12 +20,12 @@ pub(crate) enum LoopKind {
}
impl LoopKind {
/// 是否计入 for-of 逃出关闭计数。for-await-of 的逃出(labeled break /
/// continue / return)需要异步 await 迭代器 return() 的 promise,走
/// 异步挂起机制另行实现——此处只计同步 for-of,避免运行时同步调用异步
/// 迭代器的 return()。
/// 是否计入 for-of 逃出关闭计数。同步 for-of 与 for-await-of 都计入:
/// for-await-of 的逃出(labeled break / continue / return)经运行时异步关闭
/// 机制 await 迭代器 return() 的 promise,逃出计数须含其条目,运行时据此
/// 弹出并登记异步关闭。
pub(crate) fn is_for_of(self) -> bool {
matches!(self, LoopKind::ForOf)
matches!(self, LoopKind::ForOf | LoopKind::ForAwaitOf)
}
pub(crate) fn is_for_in(self) -> bool {
+172 -1
View File
@@ -13,7 +13,7 @@ use oxide_runtime_api::{to_boolean, NativeResult};
use oxide_types::object::{Cell, JsObject, NativeFnPtr, PropAttributes};
use oxide_types::value::JsValue;
use crate::vm::Vm;
use crate::vm::{Completion, Vm};
/// 异步函数执行阶段。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -64,6 +64,8 @@ pub(crate) enum AsyncResumeMode {
pub(crate) const ASYNC_CTX_PROP: &str = "__oxide_async_ctx__";
/// 恢复闭包上区分 reject 角色的属性名。
const ASYNC_REJECT_PROP: &str = "__oxide_async_reject__";
/// 逃出关闭结算闭包上存 return() promise 的属性名(陈旧防御校验用)。
const ASYNC_ESCAPE_PROMISE_PROP: &str = "__oxide_async_escape_promise__";
impl Vm {
/// 调用异步函数返回的 capability promise:创建上下文对象 + 状态盒,立即压帧
@@ -356,6 +358,27 @@ impl Vm {
JsValue::from_js_object(ptr)
}
/// 构造逃出关闭结算闭包:携带目标异步上下文对象与 return() promise,区分
/// fulfill/reject 角色。
pub(crate) fn make_async_escape_close_fn(&mut self, ctx: JsValue, promise: JsValue, reject_role: bool) -> JsValue {
let fn_proto = self.session.builtin_world().fn_proto_val();
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
func.set_function(true);
// SAFETY: async_escape_close_closure 是 NativeFn 函数项。
func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(async_escape_close_closure as *const ()) }));
func.set_native_arg_count(1);
let ptr = self.alloc_object(func);
let obj = unsafe { &mut *ptr };
let ctx_si = self.kernel_core.perm_interner().intern(ASYNC_CTX_PROP).0;
self.set_or_create_prop_value(obj, ctx_si, ctx);
let role_si = self.kernel_core.perm_interner().intern(ASYNC_REJECT_PROP).0;
self.set_or_create_prop_value(obj, role_si, JsValue::bool(reject_role));
let promise_si = self.kernel_core.perm_interner().intern(ASYNC_ESCAPE_PROMISE_PROP).0;
self.set_or_create_prop_value(obj, promise_si, promise);
self.add_fn_name_length(obj, "", 1);
JsValue::from_js_object(ptr)
}
/// 把当前 VM 执行状态(异步 body 刚在嵌套 dispatch 中让出)快照进状态盒。
///
/// 异步帧弹出存 `suspended.frame`,其余栈段整段搬入(嵌套循环期间这些栈
@@ -365,6 +388,133 @@ impl Vm {
state.phase = AsyncPhase::Suspended;
Ok(())
}
/// 恢复逃出 for-await-of 的异步函数:return() 的 promise 结算后执行完成。
///
/// # 步骤
/// 1. 陈旧防御:状态盒 pending_async_escape.close_promise 须与本闭包 promise
/// 匹配,不匹配则不处理(防同一 promise 被双结算)。
/// 2. 把挂起快照灌回 VM,取出完成并清除 pending_async_escape。
/// 3. 执行完成:Return 走 do_return,Break/Continue 跳转 target_pc 续 dispatch。
/// 4. 再挂起则快照新状态;完成/异常则结算 capability。恢复调用方状态。
///
/// # 边界与前提
/// - 关闭 promise 拒绝时(reject 角色),拒绝值替代在途完成:抛拒绝原因,
/// 外围 catch/finally 接管;无处理器时异步函数以拒绝原因拒绝。
fn resume_async_escape(
&mut self, ctx: JsValue, promise: JsValue, is_reject: bool, value: JsValue,
) -> Result<(), String> {
let state_ptr = self.async_state_ptr(ctx);
let saved = self.save_inline_state(256);
let prev_ctx = self.async_context.take();
let prev_dispatch = self.async_dispatch;
let prev_gen_ctx = self.async_gen_context.take();
let prev_gen_dispatch = self.async_gen_dispatch;
self.async_dispatch = true;
self.async_gen_dispatch = false;
self.async_context = Some(ctx);
self.native_call_depth += 1;
{
let state = unsafe { &mut *state_ptr };
// 陈旧防御:close_promise 须匹配,陈旧闭包不处理。
let is_stale = !matches!(
state.suspended.pending_async_escape.as_ref(),
Some(p) if p.close_promise == promise
);
if is_stale {
self.restore_async_flags(prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch);
self.native_call_depth -= 1;
self.restore_inline_state(saved);
return Ok(());
}
let restore_res = state.suspended.restore_into(self, state.callee);
if restore_res.is_err() {
self.restore_async_flags(prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch);
self.native_call_depth -= 1;
self.restore_inline_state(saved);
return Err("async function suspended across runs is no longer valid".into());
}
state.phase = AsyncPhase::Running;
}
// 取出完成并清除 pending_async_escape。
let completion = self.pending_async_escape.take().unwrap().completion;
// 关闭拒绝:拒绝值替代在途完成。抛拒绝原因,外围 catch/finally 接管;
// 无处理器时异步函数以拒绝原因拒绝。
if is_reject {
self.exception_value = Some(value);
self.pending_error_kind = Some(self.thrown_error_kind(value));
if self.unwind().is_err() {
let exc = self.last_uncaught_value.take().unwrap_or(value);
self.finish_async(state_ptr, Err(exc))?;
self.restore_async_flags(prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch);
self.native_call_depth -= 1;
self.restore_inline_state(saved);
return Ok(());
}
let result = self.dispatch();
// 处理器体内再挂起(await):快照新状态后退出。
if std::mem::take(&mut self.async_suspended) {
self.snapshot_async(unsafe { &mut *state_ptr })?;
self.restore_async_flags(prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch);
self.native_call_depth -= 1;
self.restore_inline_state(saved);
return Ok(());
}
match result {
Ok(value) => self.finish_async(state_ptr, Ok(value))?,
Err(e) => {
let exc = self
.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e));
self.finish_async(state_ptr, Err(exc))?;
}
}
self.restore_async_flags(prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch);
self.native_call_depth -= 1;
self.restore_inline_state(saved);
return Ok(());
}
// 单层路径:remaining 恒空,直接执行完成。
match completion {
Completion::Return { value: ret_val, .. } => {
let result = self.do_return(ret_val)?;
if let Some(result) = result {
self.finish_async(state_ptr, Ok(result))?;
}
}
Completion::Break { target_pc, .. } | Completion::Continue { target_pc, .. } => {
self.pc = target_pc;
let result = self.dispatch();
// 完成跳转后 body 继续执行到下一个 await/return/异常。
if std::mem::take(&mut self.async_suspended) {
self.snapshot_async(unsafe { &mut *state_ptr })?;
self.restore_async_flags(prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch);
self.native_call_depth -= 1;
self.restore_inline_state(saved);
return Ok(());
}
match result {
Ok(value) => self.finish_async(state_ptr, Ok(value))?,
Err(e) => {
let exc = self
.last_uncaught_value
.take()
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e));
self.finish_async(state_ptr, Err(exc))?;
}
}
}
}
self.restore_async_flags(prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch);
self.native_call_depth -= 1;
self.restore_inline_state(saved);
Ok(())
}
}
/// await 恢复闭包:读自身 prop 的异步上下文与角色,恢复异步帧继续执行。
@@ -390,6 +540,27 @@ fn async_await_resume_closure(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
}
/// 逃出关闭结算闭包:读自身 prop 的异步上下文、return() promise 与角色,恢复挂起
/// 帧并执行完成。
fn async_escape_close_closure(vm: &mut Vm, args: &[u8]) -> NativeResult {
let callee = vm.reg(254);
if !callee.is_object() {
return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "escape close handler is invalid"));
}
let callee_obj = unsafe { &*callee.as_js_object_ptr() };
let ctx_si = vm.kernel_core.perm_interner().intern(ASYNC_CTX_PROP).0;
let ctx = vm.resolve_property(callee_obj, ctx_si).unwrap_or(JsValue::undefined());
let role_si = vm.kernel_core.perm_interner().intern(ASYNC_REJECT_PROP).0;
let is_reject = vm.resolve_property(callee_obj, role_si).is_some_and(to_boolean);
let promise_si = vm.kernel_core.perm_interner().intern(ASYNC_ESCAPE_PROMISE_PROP).0;
let promise = vm.resolve_property(callee_obj, promise_si).unwrap_or(JsValue::undefined());
let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
match vm.resume_async_escape(ctx, promise, is_reject, value) {
Ok(()) => NativeResult::Ok(JsValue::undefined()),
Err(e) => NativeResult::Err(oxide_builtins::error::create_from_text(vm, &e)),
}
}
/// 初始化/重建异步函数内建对象:`%AsyncFunction.prototype%`(+ 占位 `%AsyncFunction%`)。
///
/// 异步函数对象的 [[Prototype]] 指向此原型(`f.constructor.name` → "AsyncFunction")。
+7 -5
View File
@@ -728,19 +728,21 @@ impl Vm {
// (栈上迭代器属于本生成器),完成恢复处统一关闭。
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 {
let completion = crate::vm::Completion::Return {
value,
remaining_finally: crossed,
for_of_count,
for_in_count,
}) {
};
if let Some(finally_pc) = self.record_completion(completion) {
self.pc = finally_pc;
return Ok(None);
}
// 无 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),
match self.close_escaped_iters(completion) {
Ok(crate::vm_dispatch_ctrl::CloseEscapeOutcome::Done) => self.do_return(value),
Ok(crate::vm_dispatch_ctrl::CloseEscapeOutcome::Handled) => Ok(None),
Ok(crate::vm_dispatch_ctrl::CloseEscapeOutcome::Suspended) => Ok(None),
Err(e) => Err(e),
}
}
+64 -1
View File
@@ -11,7 +11,7 @@ use oxide_bytecode::opcode;
use oxide_types::object::Cell;
use oxide_types::value::JsValue;
use crate::vm::{CallFrame, Completion, ForInIter, TryHandler, Vm};
use crate::vm::{CallFrame, Completion, ForInIter, PendingAsyncEscape, TryHandler, Vm};
use crate::vm_state::ForOfEntry;
/// 挂起执行上下文快照(三份状态结构体共享的执行核心)。
@@ -40,6 +40,8 @@ pub(crate) struct SuspendedFrame {
pub pending_exception: Option<JsValue>,
pub pending_error_kind: Option<&'static str>,
pub pending_completion: Option<Completion>,
/// 逃出 for-await-of 的异步关闭在途状态(随挂起快照跨微任务存活)。
pub pending_async_escape: Option<PendingAsyncEscape>,
}
impl SuspendedFrame {
@@ -66,6 +68,7 @@ impl SuspendedFrame {
pending_exception: None,
pending_error_kind: None,
pending_completion: None,
pending_async_escape: None,
}
}
@@ -106,6 +109,7 @@ impl SuspendedFrame {
self.pending_exception = vm.pending_exception.take();
self.pending_error_kind = vm.pending_error_kind.take();
self.pending_completion = vm.pending_completion.take();
self.pending_async_escape = std::mem::take(&mut vm.pending_async_escape);
Ok(())
}
@@ -162,6 +166,7 @@ impl SuspendedFrame {
vm.pending_exception = self.pending_exception.take();
vm.pending_error_kind = self.pending_error_kind.take();
vm.pending_completion = self.pending_completion.take();
vm.pending_async_escape = std::mem::take(&mut self.pending_async_escape);
Ok(())
}
@@ -211,6 +216,15 @@ impl SuspendedFrame {
if let Some(Completion::Return { value, .. }) = self.pending_completion {
f(value);
}
if let Some(pend) = &self.pending_async_escape {
f(pend.close_promise);
if let Completion::Return { value, .. } = pend.completion {
f(value);
}
for &v in &pend.remaining {
f(v);
}
}
for iter in &self.for_in_iters {
if iter.is_null() {
continue;
@@ -271,6 +285,27 @@ impl SuspendedFrame {
},
other => other,
});
self.pending_async_escape = self.pending_async_escape.as_mut().map(|pend| {
let completion = match pend.completion {
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,
};
PendingAsyncEscape {
close_promise: rewrite(pend.close_promise),
completion,
remaining: pend.remaining.iter().copied().map(&mut rewrite).collect(),
}
});
for iter in &mut self.for_in_iters {
if iter.is_null() {
continue;
@@ -353,6 +388,24 @@ impl SuspendedFrame {
},
other => other,
}),
pending_async_escape: self.pending_async_escape.as_ref().map(|p| PendingAsyncEscape {
close_promise: rewrite(p.close_promise),
completion: match p.completion {
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,
},
remaining: p.remaining.iter().copied().map(&mut rewrite).collect(),
}),
}
}
@@ -437,6 +490,16 @@ mod tests {
for_of_count: 0,
for_in_count: 0,
});
frame.pending_async_escape = Some(PendingAsyncEscape {
close_promise: JsValue::float(12.0),
completion: Completion::Return {
value: JsValue::float(13.0),
remaining_finally: 0,
for_of_count: 0,
for_in_count: 0,
},
remaining: vec![JsValue::float(14.0)],
});
}
#[test]
+29 -2
View File
@@ -385,10 +385,21 @@ impl Vm {
OpCode::BREAK => {
self.dispatch_break(instr)?;
// 逃出 for-await-of 的异步关闭可能经 await 挂起(return() 的
// promise),挂起时须像 AWAIT 一样让内嵌 dispatch 返回,由恢复方
// 快照状态。
if self.async_suspended || self.async_gen_suspended {
self.profiling.set_instruction_count(steps);
return Ok(JsValue::undefined());
}
}
OpCode::CONTINUE => {
self.dispatch_continue(instr)?;
if self.async_suspended || self.async_gen_suspended {
self.profiling.set_instruction_count(steps);
return Ok(JsValue::undefined());
}
}
OpCode::JMP_IF_FALSE => {
@@ -550,7 +561,15 @@ impl Vm {
OpCode::RETURN => match self.dispatch_return(instr) {
Ok(Some(result)) => return Ok(result),
Ok(None) => {}
Ok(None) => {
// 逃出 for-await-of 的异步关闭可能经 await 挂起(return() 的
// promise),挂起时须像 AWAIT 一样让内嵌 dispatch 返回,由恢复
// 方快照状态。
if self.async_suspended || self.async_gen_suspended {
self.profiling.set_instruction_count(steps);
return Ok(JsValue::undefined());
}
}
Err(e) => return Err(e),
},
@@ -815,7 +834,15 @@ impl Vm {
OpCode::TRY_FINALLY_END => match self.dispatch_try_finally_end() {
Ok(Some(result)) => return Ok(result),
Ok(None) => {}
Ok(None) => {
// 逃出 for-await-of 的异步关闭可能经 await 挂起(return() 的
// promise),挂起时须像 AWAIT 一样让内嵌 dispatch 返回,由恢复
// 方快照状态。
if self.async_suspended || self.async_gen_suspended {
self.profiling.set_instruction_count(steps);
return Ok(JsValue::undefined());
}
}
Err(e) => return Err(e),
},
+32
View File
@@ -62,6 +62,16 @@ impl Vm {
f(self.delegated_iterator.unwrap_or(JsValue::undefined()));
f(self.async_context.unwrap_or(JsValue::undefined()));
f(self.async_gen_context.unwrap_or(JsValue::undefined()));
// 在途异步逃出的 promise/完成值/剩余迭代器都是 GC 根。
if let Some(pend) = &self.pending_async_escape {
f(pend.close_promise);
if let Completion::Return { value, .. } = pend.completion {
f(value);
}
for &v in &pend.remaining {
f(v);
}
}
f(self.inline_callee.unwrap_or(JsValue::undefined()));
// 标签模板对象缓存:命中的模板对象是 GC 根(未根 → sweep 搬移/回收悬垂)。
for &cached in self.template_objects.values() {
@@ -148,6 +158,28 @@ impl Vm {
self.delegated_iterator = self.delegated_iterator.map(&mut rewrite);
self.async_context = self.async_context.map(&mut rewrite);
self.async_gen_context = self.async_gen_context.map(&mut rewrite);
// 在途异步逃出的 promise/完成值/剩余迭代器随 sweep 重写(与 mark 段一一对应)。
self.pending_async_escape = self.pending_async_escape.as_mut().map(|pend| {
let completion = match pend.completion {
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,
};
super::PendingAsyncEscape {
close_promise: rewrite(pend.close_promise),
completion,
remaining: pend.remaining.iter().copied().map(&mut rewrite).collect(),
}
});
self.inline_callee = self.inline_callee.map(&mut rewrite);
for entry in &mut self.iters.for_of_iters {
entry.iterator = rewrite(entry.iterator);
+15
View File
@@ -145,6 +145,18 @@ pub(crate) fn format_error_message(name: &str, msg: &str) -> String {
}
}
/// 逃出 for-await-of 的异步关闭在途状态:return() 的 promise 挂起等待结算,
/// 结算闭包(微任务)恢复挂起帧后继续关闭剩余条目并执行完成。
#[derive(Debug)]
pub(crate) struct PendingAsyncEscape {
/// 正在等待的 return() promise(结算闭包陈旧防御校验用)。
pub(crate) close_promise: JsValue,
/// 悬挂的完成(Break/Continue/Return),结算后执行。
pub(crate) completion: Completion,
/// 剩余待关闭的异步迭代器(LIFO 序,前元素先关)。单层路径恒空,多层路径填充。
pub(crate) remaining: Vec<JsValue>,
}
/// 基于寄存器的 JS 虚拟机:持有执行状态、寄存器文件、调用栈与 session 内存。
///
/// 执行入口为 [`Vm::run`](见 `vm_runtime` 模块);内存模型为 epoch arena +
@@ -235,6 +247,9 @@ pub struct Vm {
pub(crate) pending_error_kind: Option<&'static str>,
/// 控制流完成(break/continue/return)暂存,finally 执行后由 TRY_FINALLY_END 恢复。
pub(crate) pending_completion: Option<Completion>,
/// 逃出 for-await-of 的异步关闭在途状态:return() 的 promise 挂起等待结算,
/// 结算闭包(微任务)恢复挂起帧后继续关闭剩余条目并执行完成。
pub(crate) pending_async_escape: Option<PendingAsyncEscape>,
pub(crate) root_reg_limit: u8,
pub(crate) active_reg_limit: u8,
pub(crate) native_call_depth: usize,
+121 -48
View File
@@ -1,10 +1,20 @@
use crate::vm::{Completion, FrameArgs, FrameContinuation, TryHandler, Vm};
use crate::vm::{Completion, FrameArgs, FrameContinuation, PendingAsyncEscape, TryHandler, Vm};
use crate::vm_trace;
use oxide_bytecode::opcode;
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::private_key::make_private_name_id;
use oxide_types::value::JsValue;
/// 逃出关闭结果:`Done` = 全部关闭完成,调用方继续跳转/返回动作;
/// `Handled` = return() 抛错且异常已被外围 catch/finally 接管(unwind 已改写
/// pc),调用方须丢弃原完成动作;`Suspended` = 异步关闭已登记挂起,调用方
/// 须早退(由结算闭包续)。
pub(crate) enum CloseEscapeOutcome {
Done,
Handled,
Suspended,
}
impl Vm {
pub(crate) fn dispatch_call(&mut self, rd: usize, a: usize, b: usize) -> Result<bool, String> {
let callee_reg = rd;
@@ -578,19 +588,21 @@ impl Vm {
let offset = opcode::offset16(instr) as isize;
let target_pc = ((self.pc as isize) + offset - 1) as usize;
let crossed = opcode::rd(instr) as usize;
if let Some(finally_pc) = self.record_completion(Completion::Break {
let completion = Completion::Break {
target_pc,
remaining_finally: crossed,
for_of_count,
for_in_count,
}) {
};
if let Some(finally_pc) = self.record_completion(completion) {
self.pc = finally_pc;
} else {
// 无 finally 穿越:关闭逃出迭代器后跳转;return() 抛错被接管时
// unwind 已设 pc,跳过跳转。
match self.close_escaped_iters(for_of_count, for_in_count) {
Ok(true) => self.pc = target_pc,
Ok(false) => {}
match self.close_escaped_iters(completion) {
Ok(CloseEscapeOutcome::Done) => self.pc = target_pc,
Ok(CloseEscapeOutcome::Handled) => {}
Ok(CloseEscapeOutcome::Suspended) => {}
Err(e) => return Err(e),
}
}
@@ -603,19 +615,21 @@ impl Vm {
let offset = opcode::offset16(instr) as isize;
let target_pc = ((self.pc as isize) + offset - 1) as usize;
let crossed = opcode::rd(instr) as usize;
if let Some(finally_pc) = self.record_completion(Completion::Continue {
let completion = Completion::Continue {
target_pc,
remaining_finally: crossed,
for_of_count,
for_in_count,
}) {
};
if let Some(finally_pc) = self.record_completion(completion) {
self.pc = finally_pc;
} else {
// 无 finally 穿越:关闭逃出迭代器后跳转;return() 抛错被接管时
// unwind 已设 pc,跳过跳转。
match self.close_escaped_iters(for_of_count, for_in_count) {
Ok(true) => self.pc = target_pc,
Ok(false) => {}
match self.close_escaped_iters(completion) {
Ok(CloseEscapeOutcome::Done) => self.pc = target_pc,
Ok(CloseEscapeOutcome::Handled) => {}
Ok(CloseEscapeOutcome::Suspended) => {}
Err(e) => return Err(e),
}
}
@@ -706,20 +720,22 @@ impl Vm {
.iter()
.filter(|h| h.frame_depth == self.frames.len() && h.finally_pc.is_some())
.count();
if let Some(finally_pc) = self.record_completion(Completion::Return {
let completion = Completion::Return {
value: result,
remaining_finally: crossed,
for_of_count,
for_in_count,
}) {
};
if let Some(finally_pc) = self.record_completion(completion) {
self.pc = finally_pc;
return Ok(None);
}
// 无 finally 穿越:关闭逃出迭代器后返回;return() 抛错被接管时原完成值
// 被新错误替代,跳过实际返回。
match self.close_escaped_iters(for_of_count, for_in_count) {
Ok(true) => self.do_return(result),
Ok(false) => Ok(None),
match self.close_escaped_iters(completion) {
Ok(CloseEscapeOutcome::Done) => self.do_return(result),
Ok(CloseEscapeOutcome::Handled) => Ok(None),
Ok(CloseEscapeOutcome::Suspended) => Ok(None),
Err(e) => Err(e),
}
}
@@ -906,62 +922,54 @@ impl Vm {
}
}
match c {
Completion::Break {
target_pc,
for_of_count,
for_in_count,
..
}
| Completion::Continue {
target_pc,
for_of_count,
for_in_count,
..
} => {
Completion::Break { target_pc, .. } | Completion::Continue { target_pc, .. } => {
// 全部 finally 穿越完成、真正跳转前关闭逃出迭代器——与规范顺序
// (循环体完成值已含内部 try-finally 处理,IteratorClose 在后)一致。
// return() 抛错被接管时 unwind 已设 pc,跳过跳转。
match self.close_escaped_iters(for_of_count, for_in_count) {
Ok(true) => self.pc = target_pc,
Ok(false) => {}
match self.close_escaped_iters(c) {
Ok(CloseEscapeOutcome::Done) => self.pc = target_pc,
Ok(CloseEscapeOutcome::Handled) => {}
Ok(CloseEscapeOutcome::Suspended) => {}
Err(e) => return Err(e),
}
Ok(None)
}
Completion::Return {
value,
for_of_count,
for_in_count,
..
} => match self.close_escaped_iters(for_of_count, for_in_count) {
Ok(true) => self.do_return(value),
Ok(false) => Ok(None),
Completion::Return { value, .. } => match self.close_escaped_iters(c) {
Ok(CloseEscapeOutcome::Done) => self.do_return(value),
Ok(CloseEscapeOutcome::Handled) => Ok(None),
Ok(CloseEscapeOutcome::Suspended) => Ok(None),
Err(e) => Err(e),
},
}
}
/// 逃出关闭:按 LIFO 弹出逃出的 for-in 迭代器(无 return() 语义),再关闭逃出的
/// for-of 迭代器。异步迭代器条目(for-await-of)跳过——其 return() 返回 promise
/// 须 await 后结算,走独立异步关闭机制。逃出路径非 suppress:return() 抛错经
/// for-of 迭代器。同步迭代器同步关闭;异步迭代器(for-await-of)同步调 return()
/// 得 promise,登记结算闭包后挂起。逃出路径非 suppress:return() 抛错经
/// raise_call_error 展开,剩余迭代器由 unwind 的 close_for_of_above 以 suppress
/// 继续关闭——新错误替代原完成值向外传播。
///
/// # 返回值
/// `Ok(true)` = 全部关闭完成,调用方继续跳转/返回动作;`Ok(false)` = return()
/// 抛错且异常已被外围 catch/finally 接管(unwind 已改写 pc),调用方须丢弃
/// 原完成动作;`Err` = 无处理器,异常逃逸。
pub(crate) fn close_escaped_iters(&mut self, for_of_count: usize, for_in_count: usize) -> Result<bool, String> {
/// `Done` = 全部关闭完成,调用方继续跳转/返回动作;`Handled` = return() 抛错且
/// 异常已被外围 catch/finally 接管(unwind 已改写 pc),调用方须丢弃原完成动作;
/// `Suspended` = 异步关闭已登记挂起,调用方须早退(由结算闭包续);`Err` = 无
/// 处理器,异常逃逸。
pub(crate) fn close_escaped_iters(&mut self, completion: Completion) -> Result<CloseEscapeOutcome, String> {
let for_in_count = completion.for_in_count();
let for_of_count = completion.for_of_count();
for _ in 0..for_in_count {
self.iters.pop_for_in();
}
// 单层路径:首个异步条目登记关闭,其余异步条目丢弃(多层路径填 remaining)。
let mut first_async: Option<JsValue> = None;
for _ in 0..for_of_count {
let Some(entry) = self.iters.pop_for_of() else {
break;
};
// 异步迭代器不在此关闭:for-await-of 的逃出(labeled/return)由
// 异步关闭机制处理,此处同步关闭会跳过 return() promise 的等待。
if entry.is_async {
if first_async.is_none() {
first_async = Some(entry.iterator);
}
continue;
}
let pc_before = self.pc;
@@ -973,9 +981,74 @@ impl Vm {
// 在途异常等 finally 穿越。正常关闭时 pc 不变(call_function_sync 保存
// 并恢复调用方 pc)。
if self.pc != pc_before || self.pending_exception.is_some() {
return Ok(false);
return Ok(CloseEscapeOutcome::Handled);
}
}
if let Some(iterator) = first_async {
match self.register_async_escape_close(iterator, completion, Vec::new()) {
Ok(true) => {
self.async_suspended = true;
return Ok(CloseEscapeOutcome::Suspended);
}
Ok(false) => {}
Err(e) => return Err(e),
}
}
Ok(CloseEscapeOutcome::Done)
}
/// 登记逃出 for-await-of 的异步关闭:同步调 return() 得 promise,登记结算闭包,
/// 置 pending_async_escape。迭代器无 return 方法时不登记(完成直接继续)。
///
/// # 返回值
/// `Ok(true)` = 已登记挂起;`Ok(false)` = 无 return 方法,无需挂起;`Err` = 关闭
/// 过程出错。
fn register_async_escape_close(
&mut self, iterator: JsValue, completion: Completion, remaining: Vec<JsValue>,
) -> Result<bool, String> {
// 异步生成器走独立状态盒(AsyncGeneratorState),本路径的结算闭包按
// AsyncState 恢复——不登记挂起(异步迭代器保持既有跳过行为,完成直接继续)。
if self.async_gen_dispatch {
return Ok(false);
}
if !iterator.is_object() {
return Ok(false);
}
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
let return_si = self.kernel_core.perm_interner().intern("return").0;
let return_fn = self.ordinary_get(iter_obj, return_si, iterator)?;
if !return_fn.is_object() || !unsafe { &*return_fn.as_js_object_ptr() }.is_function() {
return Ok(false);
}
let inner = match self.call_function_sync(return_fn, iterator, &[]) {
Ok(v) => v,
// return() 抛出:恢复原始异常值并展开,使外围 try/catch 可捕获。
Err(e) => return self.raise_call_error(&e).map(|_| false),
};
let promise = if self.is_promise_value(inner) {
inner
} else {
match self.promise_resolve(inner) {
Ok(p) => p,
Err(exc) => {
let (p, _, _) = self.new_promise_capability();
let _ = self.reject_promise(p, exc);
p
}
}
};
// 同步生成器无异步上下文:不登记挂起(异步迭代器保持既有跳过行为,完成直接继续)。
let Some(ctx) = self.async_context.or(self.async_gen_context) else {
return Ok(false);
};
let fulfill_fn = self.make_async_escape_close_fn(ctx, promise, false);
let reject_fn = self.make_async_escape_close_fn(ctx, promise, true);
let _ = self.perform_promise_then(promise, fulfill_fn, reject_fn);
self.pending_async_escape = Some(PendingAsyncEscape {
close_promise: promise,
completion,
remaining,
});
Ok(true)
}
+5
View File
@@ -323,6 +323,9 @@ impl Vm {
/// (`n_registers ≤ 254`,含 RegAlloc 最高合法物理槽 253)。
pub(crate) fn save_inline_state(&mut self, regs_end: usize) -> Box<InlineSyncState> {
vm_trace!("save_inline_state: pc={} depth={}", self.pc, self.frames.len());
// 固化不变量:内联 state-swap 边界不携带在途异步逃出(挂起态快照前已搬入
// 状态盒,settle 前恒为 None)。
debug_assert!(self.pending_async_escape.is_none());
let window = regs_end.min(254);
let mut window_regs = self.inline_reg_pool.take().unwrap_or_default();
window_regs.clear();
@@ -335,6 +338,8 @@ impl Vm {
/// 单回,窗口外寄存器 callee 未触碰无需恢复。
pub(crate) fn restore_inline_state(&mut self, saved: Box<InlineSyncState>) {
vm_trace!("restore_inline_state: pc={}", saved.pc);
// 固化不变量:内联 state-swap 边界不携带在途异步逃出(与 save 侧同)。
debug_assert!(self.pending_async_escape.is_none());
let vm = self;
let _window_regs: Vec<JsValue> = Vec::new();
inline_core_fields!(vm, saved, inline_restore, _window_regs);
+4
View File
@@ -92,6 +92,7 @@ impl Vm {
pending_exception: None,
pending_error_kind: None,
pending_completion: None,
pending_async_escape: None,
root_reg_limit: 0,
active_reg_limit: 0,
native_call_depth: 0,
@@ -229,6 +230,7 @@ impl Vm {
pending_exception: None,
pending_error_kind: None,
pending_completion: None,
pending_async_escape: None,
root_reg_limit: 0,
active_reg_limit: 0,
native_call_depth: 0,
@@ -536,6 +538,8 @@ impl Vm {
self.pending_exception = None;
self.pending_error_kind = None;
self.pending_completion = None;
// 在途异步逃出是执行期状态:跨 run/reset 不保留,残留将悬垂。
self.pending_async_escape = None;
self.generator_suspended = None;
self.delegated_iterator = None;
self.generator_dispatch = false;
+3 -3
View File
@@ -226,8 +226,8 @@ fn for_await_of_body_throw_closes_and_original_error_wins() {
#[test]
fn for_await_of_return_escape_defers_async_close() {
// return 逃出 for-await-of:异步迭代器 return() 的 promise 须 await 后结算,
// 走异步关闭机制(后续阶段);阶段 1 同步逃出路径不得同步调用异步 return()。
// 断言循环体已执行且没有同步副作用泄露。
// 走异步关闭机制。return() 同步调用(log.push('close') 同步执行),promise 经
// 微任务结算后完成返回。断言循环体与关闭副作用都已执行。
assert_eq!(
eval(
"let log=[];\
@@ -235,7 +235,7 @@ fn for_await_of_return_escape_defers_async_close() {
(async()=>{ for await (const x of it) { log.push('body'); return 1; } })();\
Promise.resolve().then(()=>log.join(','))"
),
"\"body\""
"\"body,close\""
);
}