From 9ab19fb472cceb657427e7df0b74829eca210acf Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 27 Sep 2026 19:48:19 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20for-await-of=20=E9=80=83=E5=87=BA?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E5=BC=82=E6=AD=A5=20await=20return()=20?= =?UTF-8?q?=E7=BB=93=E7=AE=97=E5=90=8E=E6=81=A2=E5=A4=8D=E5=B8=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_emit/src/emit_ctx.rs | 10 +- crates/oxide_vm/src/async_func.rs | 173 +++++++++++++++++++++++- crates/oxide_vm/src/generator.rs | 12 +- crates/oxide_vm/src/suspended.rs | 65 ++++++++- crates/oxide_vm/src/vm/dispatch_loop.rs | 31 ++++- crates/oxide_vm/src/vm/gc_hooks.rs | 32 +++++ crates/oxide_vm/src/vm/mod.rs | 15 ++ crates/oxide_vm/src/vm_dispatch_ctrl.rs | 169 ++++++++++++++++------- crates/oxide_vm/src/vm_runtime.rs | 5 + crates/oxide_vm/src/vm_support.rs | 4 + crates/oxide_vm/tests/async_tests.rs | 6 +- 11 files changed, 457 insertions(+), 65 deletions(-) diff --git a/crates/oxide_emit/src/emit_ctx.rs b/crates/oxide_emit/src/emit_ctx.rs index 9a910943..0dd3d3ff 100644 --- a/crates/oxide_emit/src/emit_ctx.rs +++ b/crates/oxide_emit/src/emit_ctx.rs @@ -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 { diff --git a/crates/oxide_vm/src/async_func.rs b/crates/oxide_vm/src/async_func.rs index 6fc188ba..86220c59 100644 --- a/crates/oxide_vm/src/async_func.rs +++ b/crates/oxide_vm/src/async_func.rs @@ -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")。 diff --git a/crates/oxide_vm/src/generator.rs b/crates/oxide_vm/src/generator.rs index 19cefe17..1e86d18e 100644 --- a/crates/oxide_vm/src/generator.rs +++ b/crates/oxide_vm/src/generator.rs @@ -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), } } diff --git a/crates/oxide_vm/src/suspended.rs b/crates/oxide_vm/src/suspended.rs index 9f49541e..83f0ab70 100644 --- a/crates/oxide_vm/src/suspended.rs +++ b/crates/oxide_vm/src/suspended.rs @@ -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, pub pending_error_kind: Option<&'static str>, pub pending_completion: Option, + /// 逃出 for-await-of 的异步关闭在途状态(随挂起快照跨微任务存活)。 + pub pending_async_escape: Option, } 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] diff --git a/crates/oxide_vm/src/vm/dispatch_loop.rs b/crates/oxide_vm/src/vm/dispatch_loop.rs index 8ed9a963..f692bab3 100644 --- a/crates/oxide_vm/src/vm/dispatch_loop.rs +++ b/crates/oxide_vm/src/vm/dispatch_loop.rs @@ -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), }, diff --git a/crates/oxide_vm/src/vm/gc_hooks.rs b/crates/oxide_vm/src/vm/gc_hooks.rs index 6ed3a10b..d1232e69 100644 --- a/crates/oxide_vm/src/vm/gc_hooks.rs +++ b/crates/oxide_vm/src/vm/gc_hooks.rs @@ -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); diff --git a/crates/oxide_vm/src/vm/mod.rs b/crates/oxide_vm/src/vm/mod.rs index ee5524c8..b038d0eb 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -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, +} + /// 基于寄存器的 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, + /// 逃出 for-await-of 的异步关闭在途状态:return() 的 promise 挂起等待结算, + /// 结算闭包(微任务)恢复挂起帧后继续关闭剩余条目并执行完成。 + pub(crate) pending_async_escape: Option, pub(crate) root_reg_limit: u8, pub(crate) active_reg_limit: u8, pub(crate) native_call_depth: usize, diff --git a/crates/oxide_vm/src/vm_dispatch_ctrl.rs b/crates/oxide_vm/src/vm_dispatch_ctrl.rs index 79c2cc1f..c2b5c785 100644 --- a/crates/oxide_vm/src/vm_dispatch_ctrl.rs +++ b/crates/oxide_vm/src/vm_dispatch_ctrl.rs @@ -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 { 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 { + /// `Done` = 全部关闭完成,调用方继续跳转/返回动作;`Handled` = return() 抛错且 + /// 异常已被外围 catch/finally 接管(unwind 已改写 pc),调用方须丢弃原完成动作; + /// `Suspended` = 异步关闭已登记挂起,调用方须早退(由结算闭包续);`Err` = 无 + /// 处理器,异常逃逸。 + pub(crate) fn close_escaped_iters(&mut self, completion: Completion) -> Result { + 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 = 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, + ) -> Result { + // 异步生成器走独立状态盒(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) } diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index 2d37b995..5b081f0c 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -323,6 +323,9 @@ impl Vm { /// (`n_registers ≤ 254`,含 RegAlloc 最高合法物理槽 253)。 pub(crate) fn save_inline_state(&mut self, regs_end: usize) -> Box { 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) { 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 = Vec::new(); inline_core_fields!(vm, saved, inline_restore, _window_regs); diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index 439b0ad4..71a8df5e 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -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; diff --git a/crates/oxide_vm/tests/async_tests.rs b/crates/oxide_vm/tests/async_tests.rs index 3b3c470d..56ae8b0f 100644 --- a/crates/oxide_vm/tests/async_tests.rs +++ b/crates/oxide_vm/tests/async_tests.rs @@ -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\"" ); }