mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: for-await-of 逃出关闭异步 await return() 结算后恢复帧
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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")。
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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),
|
||||
},
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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\""
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user