From 71904a644fa52ed84f8e9b81c3e4dcac326a2aa9 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Fri, 9 Oct 2026 20:50:35 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20await=20using=20=E9=87=8A=E6=94=BE?= =?UTF-8?q?=E6=8C=82=E8=B5=B7=E8=B7=A8=E5=BE=AE=E4=BB=BB=E5=8A=A1=E7=BB=AD?= =?UTF-8?q?=E8=A1=8C=E5=B9=B6=E5=90=88=E5=B9=B6=20SuppressedError?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 38 ++-- Cargo.toml | 2 +- crates/oxide_vm/src/async_func.rs | 235 +++++++++++++++++++++++- crates/oxide_vm/src/async_generator.rs | 196 +++++++++++++++++++- crates/oxide_vm/src/suspended.rs | 16 +- crates/oxide_vm/src/vm/dispatch_loop.rs | 14 ++ crates/oxide_vm/src/vm/gc_hooks.rs | 17 +- crates/oxide_vm/src/vm/mod.rs | 28 +++ crates/oxide_vm/src/vm_dispatch_ctrl.rs | 14 +- crates/oxide_vm/src/vm_dispatch_misc.rs | 229 ++++++++++++++++------- crates/oxide_vm/src/vm_runtime.rs | 26 ++- crates/oxide_vm/src/vm_support.rs | 4 + 12 files changed, 718 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2ef41f5..bf6674d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.13.18" +version = "0.13.19" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.13.18" +version = "0.13.19" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.13.18" +version = "0.13.19" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.13.18" +version = "0.13.19" dependencies = [ "ansi_term", "bumpalo", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.13.18" +version = "0.13.19" dependencies = [ "lru", "oxide_bytecode", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.13.18" +version = "0.13.19" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.13.18" +version = "0.13.19" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.13.18" +version = "0.13.19" dependencies = [ "num-bigint", "oxc_allocator", @@ -1284,7 +1284,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.13.18" +version = "0.13.19" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.13.18" +version = "0.13.19" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1305,7 +1305,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.13.18" +version = "0.13.19" dependencies = [ "bumpalo", "dashmap", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.13.18" +version = "0.13.19" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1331,7 +1331,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.13.18" +version = "0.13.19" dependencies = [ "tracing", "tracing-appender", @@ -1340,7 +1340,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.13.18" +version = "0.13.19" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1355,7 +1355,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.13.18" +version = "0.13.19" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1371,7 +1371,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.13.18" +version = "0.13.19" dependencies = [ "num-bigint", "num-traits", @@ -1384,7 +1384,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.13.18" +version = "0.13.19" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1402,7 +1402,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.13.18" +version = "0.13.19" dependencies = [ "bumpalo", "dashmap", @@ -1415,7 +1415,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.13.18" +version = "0.13.19" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 9a1d32ba..f555163d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.13.18" +version = "0.13.19" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_vm/src/async_func.rs b/crates/oxide_vm/src/async_func.rs index 1adbe527..219811cc 100644 --- a/crates/oxide_vm/src/async_func.rs +++ b/crates/oxide_vm/src/async_func.rs @@ -13,7 +13,8 @@ use oxide_runtime_api::{to_boolean, NativeResult}; use oxide_types::object::{Cell, JsObject, NativeFnPtr, PropAttributes}; use oxide_types::value::JsValue; -use crate::vm::{Completion, Vm}; +use crate::vm::{Completion, DisposeResumeKind, InlineSyncState, Vm}; +use crate::vm_dispatch_misc::DisposePhase; /// 异步函数执行阶段。 #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -66,6 +67,8 @@ pub(crate) const ASYNC_CTX_PROP: &str = "__oxide_async_ctx__"; const ASYNC_REJECT_PROP: &str = "__oxide_async_reject__"; /// 逃出关闭结算闭包上存 return() promise 的属性名(陈旧防御校验用)。 pub(crate) const ASYNC_ESCAPE_PROMISE_PROP: &str = "__oxide_async_escape_promise__"; +/// 异步释放结算闭包上存释放方法 promise 的属性名(陈旧防御校验用)。 +pub(crate) const ASYNC_DISPOSE_PROMISE_PROP: &str = "__oxide_async_dispose_promise__"; impl Vm { /// 调用异步函数返回的 capability promise:创建上下文对象 + 状态盒,立即压帧 @@ -380,6 +383,29 @@ impl Vm { JsValue::from_js_object(ptr) } + /// 构造异步释放结算闭包:携带目标异步上下文对象与释放方法 promise,区分 + /// fulfill/reject 角色。 + pub(crate) fn make_async_dispose_resume_fn( + &mut self, ctx: JsValue, promise: JsValue, reject_role: bool, + ) -> JsValue { + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); + let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); + func.set_function(true); + // SAFETY: async_dispose_resume_closure 是 NativeFn 函数项。 + func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(async_dispose_resume_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_DISPOSE_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`,其余栈段整段搬入(嵌套循环期间这些栈 @@ -544,6 +570,192 @@ impl Vm { self.restore_inline_state(saved); Ok(()) } + + /// 续 dispatch 并结算:运行 body 到下一个 AWAIT/RETURN/异常,挂起则快照新状态, + /// 完成/异常则结算 capability。恢复调用方状态。 + fn dispatch_and_finish_async( + &mut self, state_ptr: *mut AsyncState, saved: Box, + prev_ctx: Option, prev_dispatch: bool, prev_gen_ctx: Option, + prev_gen_dispatch: bool, + ) -> Result<(), String> { + let result = self.dispatch(); + 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 using` 释放挂起的异步函数:释放方法 promise 结算后继续释放剩余 + /// 条目并执行完成。 + /// + /// # 步骤 + /// 1. 陈旧防御:状态盒 `pending_async_dispose.dispose_promise` 须与本闭包 promise + /// 匹配,不匹配则不处理(防同一 promise 被双结算)。 + /// 2. 把挂起快照灌回 VM,取出水位 / 合并错误 / 恢复类别并清除 + /// `pending_async_dispose`。 + /// 3. 拒绝角色:拒绝值合并进合并错误。 + /// 4. 从水位续释放循环(复用批量释放核心);再挂起则重新登记 + 快照 + 返回。 + /// 5. 循环后执行类别:`Pop` 有合并错误则展开否则续行;`Unwind` 重压 handler 再入 + /// unwind;`Escape` 有合并错误则丢弃完成值展开否则执行完成。 + /// 6. 再挂起则快照新状态;完成 / 异常则结算 capability。恢复调用方状态。 + pub(crate) fn resume_async_dispose( + &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); + // 清上一轮残留的挂起信号(防陈旧信号误消费)。 + self.async_suspended = false; + 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 }; + // 陈旧防御:dispose_promise 须匹配,陈旧闭包不处理。 + let is_stale = !matches!( + state.suspended.pending_async_dispose.as_ref(), + Some(p) if p.dispose_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_dispose。 + let (mark, completion, kind) = match self.pending_async_dispose.take() { + Some(p) => (p.mark, p.completion, p.kind), + // 陈旧防御已早退,正常路径此处必为 Some;防御性兜底。 + None => { + 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 completion = if is_reject { + oxide_builtins::disposable_stack::merge_dispose_error(self, completion, value) + } else { + completion + }; + + // 从水位续释放循环(复用批量释放核心);再挂起则重新登记 + 快照 + 返回。 + if matches!(self.dispose_above_core(mark, kind, completion)?, DisposePhase::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 kind { + DisposeResumeKind::Pop => { + // 有合并错误则展开(无处理器则 reject capability),否则续行。 + if self.exception_value.is_some() && self.unwind().is_err() { + let exc = self.last_uncaught_value.take().unwrap_or(JsValue::undefined()); + 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(()); + } + self.dispatch_and_finish_async( + state_ptr, saved, prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch, + ) + } + DisposeResumeKind::Unwind(handler) => { + // 重压 handler 再入 unwind(handler 已弹出,不重压则 catch/finally 丢失)。 + if let Some(h) = handler { + self.try_stack.push(h); + } + if self.unwind().is_err() { + let exc = self.last_uncaught_value.take().unwrap_or(JsValue::undefined()); + 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(()); + } + self.dispatch_and_finish_async( + state_ptr, saved, prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch, + ) + } + DisposeResumeKind::Escape(completion_val) => { + if self.exception_value.is_some() { + // 有合并错误:丢弃完成值,展开到外围处理器(无处理器则 reject)。 + if self.unwind().is_err() { + let exc = self.last_uncaught_value.take().unwrap_or(JsValue::undefined()); + 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(()); + } + self.dispatch_and_finish_async( + state_ptr, saved, prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch, + ) + } else { + // 无合并错误:执行完成。 + match completion_val { + Completion::Return { value: ret_val, .. } => { + let result = self.do_return(ret_val)?; + if let Some(result) = result { + self.finish_async(state_ptr, Ok(result))?; + 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(()); + } + self.dispatch_and_finish_async( + state_ptr, saved, prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch, + ) + } + Completion::Break { target_pc, .. } | Completion::Continue { target_pc, .. } => { + self.pc = target_pc; + self.dispatch_and_finish_async( + state_ptr, saved, prev_ctx, prev_dispatch, prev_gen_ctx, prev_gen_dispatch, + ) + } + } + } + } + } + } } /// await 恢复闭包:读自身 prop 的异步上下文与角色,恢复异步帧继续执行。 @@ -590,6 +802,27 @@ fn async_escape_close_closure(vm: &mut Vm, args: &[u8]) -> NativeResult { } } +/// 异步释放结算闭包:读自身 prop 的异步上下文、释放方法 promise 与角色,恢复挂起 +/// 帧并继续释放剩余条目、执行完成。 +fn async_dispose_resume_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, "dispose resume 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_DISPOSE_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_dispose(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/async_generator.rs b/crates/oxide_vm/src/async_generator.rs index 9771fd17..2da8e767 100644 --- a/crates/oxide_vm/src/async_generator.rs +++ b/crates/oxide_vm/src/async_generator.rs @@ -21,7 +21,8 @@ use oxide_types::object::{Cell, JsObject, NativeFnPtr, PropAttributes}; use oxide_types::value::JsValue; use crate::generator::{DelegateOutcome, GeneratorResumeMode}; -use crate::vm::{Completion, Vm}; +use crate::vm::{Completion, DisposeResumeKind, Vm}; +use crate::vm_dispatch_misc::DisposePhase; /// 异步生成器执行阶段。 #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -93,6 +94,8 @@ const AG_YIELD_PROMISE_PROP: &str = "__oxide_async_gen_yield_promise__"; const AG_YIELD_RAW_PROP: &str = "__oxide_async_gen_yield_raw__"; /// 委托步进取续闭包上存协议步的属性名。 const AG_STEP_PROP: &str = "__oxide_async_gen_delegate_step__"; +/// 异步释放结算闭包上存释放方法 promise 的属性名(陈旧防御校验用)。 +const AG_DISPOSE_PROMISE_PROP: &str = "__oxide_async_gen_dispose_promise__"; impl Vm { /// 调用异步生成器函数返回的迭代器对象:创建异步生成器实例、挂状态并执行 @@ -793,6 +796,29 @@ impl Vm { JsValue::from_js_object(ptr) } + /// 构造异步释放结算闭包(异步生成器):携带目标异步生成器上下文对象与 + /// 释放方法 promise,区分 fulfill/reject 角色。 + pub(crate) fn make_async_gen_dispose_resume_fn( + &mut self, ctx: JsValue, promise: JsValue, reject_role: bool, + ) -> JsValue { + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); + let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); + func.set_function(true); + // SAFETY: async_gen_dispose_closure 是 NativeFn 函数项。 + func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(async_gen_dispose_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(crate::async_func::ASYNC_CTX_PROP).0; + self.set_or_create_prop_value(obj, ctx_si, ctx); + let role_si = self.kernel_core.perm_interner().intern(AG_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(AG_DISPOSE_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) + } + /// 恢复逃出 for-await-of 的异步生成器:return() 的 promise 结算后继续关闭剩余 /// 条目并执行完成。 /// @@ -916,6 +942,151 @@ impl Vm { } } } + + /// 恢复 `await using` 释放挂起的异步生成器:释放方法 promise 结算后继续释放 + /// 剩余条目并执行完成。 + /// + /// # 步骤 + /// 1. 陈旧防御:状态盒 `pending_async_dispose.dispose_promise` 须与本闭包 promise + /// 匹配,不匹配则不处理(防同一 promise 被双结算)。 + /// 2. 把挂起快照灌回 VM,取出水位 / 合并错误 / 恢复类别并清除 + /// `pending_async_dispose`。 + /// 3. 拒绝角色:拒绝值合并进合并错误。 + /// 4. 从水位续释放循环(复用批量释放核心);再挂起则重新登记 + 快照 + 返回。 + /// 5. 循环后执行类别:`Pop` 有合并错误则展开否则续行;`Unwind` 重压 handler 再入 + /// unwind;`Escape` 有合并错误则丢弃完成值展开否则执行完成。 + /// 6. 再挂起则快照新状态;完成 / 异常则结算当前请求。恢复调用方状态。 + pub(crate) fn resume_async_gen_dispose( + &mut self, ctx: JsValue, promise: JsValue, is_reject: bool, value: JsValue, + ) -> Result<(), String> { + let state_ptr = self.async_gen_state_ptr(ctx); + let saved = self.save_inline_state(256); + // 清上一轮残留的挂起信号(防陈旧信号误消费)。 + self.generator_suspended = None; + self.async_gen_suspended = false; + let prev_ctx = self.async_context.take(); + let prev_gd = self.generator_dispatch; + let prev_ad = self.async_dispatch; + let prev_gen_ctx = self.async_gen_context.take(); + let prev_agd = self.async_gen_dispatch; + self.generator_dispatch = true; + self.async_dispatch = true; + self.async_gen_dispatch = true; + self.async_context = Some(ctx); + self.async_gen_context = Some(ctx); + self.native_call_depth += 1; + + { + let state = unsafe { &mut *state_ptr }; + // 陈旧防御:dispose_promise 须匹配,陈旧闭包不处理。 + let is_stale = !matches!( + state.suspended.pending_async_dispose.as_ref(), + Some(p) if p.dispose_promise == promise + ); + if is_stale { + self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + 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_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + self.restore_inline_state(saved); + return Err("async generator suspended across runs is no longer valid".into()); + } + state.phase = AsyncGenPhase::Running; + } + + // 取出水位 / 合并错误 / 恢复类别并清除 pending_async_dispose。 + let (mark, completion, kind) = match self.pending_async_dispose.take() { + Some(p) => (p.mark, p.completion, p.kind), + // 陈旧防御已早退,正常路径此处必为 Some;防御性兜底。 + None => { + self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + self.restore_inline_state(saved); + return Ok(()); + } + }; + + // 拒绝角色:拒绝值合并进合并错误。 + let completion = if is_reject { + oxide_builtins::disposable_stack::merge_dispose_error(self, completion, value) + } else { + completion + }; + + // 从水位续释放循环(复用批量释放核心);再挂起则重新登记 + 快照 + 返回。 + if matches!(self.dispose_above_core(mark, kind, completion)?, DisposePhase::Suspended) { + unsafe { (*state_ptr).phase = AsyncGenPhase::AwaitSuspended }; + self.snapshot_async_generator(state_ptr)?; + self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + self.restore_inline_state(saved); + return Ok(()); + } + + // 循环后执行类别。 + match kind { + DisposeResumeKind::Pop => { + // 有合并错误则展开(无处理器则 reject 当前请求),否则续行。 + if self.exception_value.is_some() && self.unwind().is_err() { + let exc = self.last_uncaught_value.take().unwrap_or(JsValue::undefined()); + self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + return self.finish_async_gen_exc(state_ptr, saved, ctx, exc); + } + let result = self.dispatch(); + self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + self.post_dispatch_async_gen(state_ptr, saved, ctx, result) + } + DisposeResumeKind::Unwind(handler) => { + // 重压 handler 再入 unwind(handler 已弹出,不重压则 catch/finally 丢失)。 + if let Some(h) = handler { + self.try_stack.push(h); + } + if self.unwind().is_err() { + let exc = self.last_uncaught_value.take().unwrap_or(JsValue::undefined()); + self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + return self.finish_async_gen_exc(state_ptr, saved, ctx, exc); + } + let result = self.dispatch(); + self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + self.post_dispatch_async_gen(state_ptr, saved, ctx, result) + } + DisposeResumeKind::Escape(completion_val) => { + if self.exception_value.is_some() { + // 有合并错误:丢弃完成值,展开到外围处理器(无处理器则 reject)。 + if self.unwind().is_err() { + let exc = self.last_uncaught_value.take().unwrap_or(JsValue::undefined()); + self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + return self.finish_async_gen_exc(state_ptr, saved, ctx, exc); + } + let result = self.dispatch(); + self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + self.post_dispatch_async_gen(state_ptr, saved, ctx, result) + } else { + // 无合并错误:执行完成。 + match completion_val { + Completion::Return { value: ret_val, .. } => { + let result = self.do_return(ret_val)?; + if let Some(result) = result { + self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + return self.finish_async_gen_ok(state_ptr, saved, ctx, result); + } + // 嵌套帧未弹空:续 dispatch。 + let result = self.dispatch(); + self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + self.post_dispatch_async_gen(state_ptr, saved, ctx, result) + } + Completion::Break { target_pc, .. } | Completion::Continue { target_pc, .. } => { + self.pc = target_pc; + let result = self.dispatch(); + self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd); + self.post_dispatch_async_gen(state_ptr, saved, ctx, result) + } + } + } + } + } + } } /// await 恢复闭包:读自身 prop 的异步生成器上下文与角色,恢复异步生成器继续执行。 @@ -1243,6 +1414,29 @@ fn async_gen_escape_closure(vm: &mut Vm, args: &[u8]) -> NativeResult { } } +/// 异步释放结算闭包(异步生成器):读自身 prop 的异步生成器上下文、释放方法 +/// promise 与角色,恢复挂起帧并继续释放剩余条目、执行完成。 +fn async_gen_dispose_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, "dispose resume handler is invalid")); + } + let callee_obj = unsafe { &*callee.as_js_object_ptr() }; + let ctx_si = vm.kernel_core.perm_interner().intern(crate::async_func::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(AG_REJECT_PROP).0; + let is_reject = vm + .resolve_property(callee_obj, role_si) + .is_some_and(oxide_runtime_api::to_boolean); + let promise_si = vm.kernel_core.perm_interner().intern(AG_DISPOSE_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_gen_dispose(ctx, promise, is_reject, value) { + Ok(()) => NativeResult::Ok(JsValue::undefined()), + Err(e) => NativeResult::Err(oxide_builtins::error::create_from_text(vm, &e)), + } +} + /// 委托结算值读取:done 与 value 的 getter 抛错经 `last_uncaught_value` 取原值。 enum DelegateResult { /// 结算值读取成功(done 标志与委托值)。 diff --git a/crates/oxide_vm/src/suspended.rs b/crates/oxide_vm/src/suspended.rs index 9f016fc4..3e36fd51 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, PendingAsyncEscape, TryHandler, Vm}; +use crate::vm::{CallFrame, Completion, ForInIter, PendingAsyncDispose, PendingAsyncEscape, TryHandler, Vm}; use crate::vm_state::ForOfEntry; /// 挂起执行上下文快照(三份状态结构体共享的执行核心)。 @@ -42,6 +42,8 @@ pub(crate) struct SuspendedFrame { pub pending_completion: Option, /// 逃出 for-await-of 的异步关闭在途状态(随挂起快照跨微任务存活)。 pub pending_async_escape: Option, + /// `await using` 资源释放的异步挂起在途状态(随挂起快照跨微任务存活)。 + pub pending_async_dispose: Option, } impl SuspendedFrame { @@ -69,6 +71,7 @@ impl SuspendedFrame { pending_error_kind: None, pending_completion: None, pending_async_escape: None, + pending_async_dispose: None, } } @@ -109,6 +112,7 @@ impl SuspendedFrame { 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); + self.pending_async_dispose = std::mem::take(&mut vm.pending_async_dispose); Ok(()) } @@ -173,6 +177,7 @@ impl SuspendedFrame { 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); + vm.pending_async_dispose = std::mem::take(&mut self.pending_async_dispose); Ok(()) } @@ -231,6 +236,15 @@ impl SuspendedFrame { f(v); } } + if let Some(pend) = &self.pending_async_dispose { + f(pend.dispose_promise); + if let Some(v) = pend.completion { + f(v); + } + if let crate::vm::DisposeResumeKind::Escape(Completion::Return { value, .. }) = pend.kind { + f(value); + } + } for iter in &self.for_in_iters { if iter.is_null() { continue; diff --git a/crates/oxide_vm/src/vm/dispatch_loop.rs b/crates/oxide_vm/src/vm/dispatch_loop.rs index d1c9f090..6c428b1d 100644 --- a/crates/oxide_vm/src/vm/dispatch_loop.rs +++ b/crates/oxide_vm/src/vm/dispatch_loop.rs @@ -118,6 +118,14 @@ impl Vm { } loop { steps += 1; + // 异步释放挂起:unwind 经 dispose_above 挂起时须让内嵌 dispatch 返回 + // (挂起信号由 register_dispose_suspend 置位,此处统一消费,覆盖 THROW + // 与各错误处理器路径;AWAIT/BREAK/CONTINUE/RETURN/DISPOSE_POP 各臂已 + // 就地返回,此处为兜底)。 + if self.async_suspended || self.async_gen_suspended { + self.profiling.set_instruction_count(steps); + return Ok(JsValue::undefined()); + } // 执行期 GC 安全点:仅在顶层 dispatch(native_call_depth == 0) // 的指令边界触发——嵌套 dispatch(builtin 经 call_function_sync 重入 // 执行 JS 回调、generator/async 恢复)期间,调用方寄存器窗口副本存于 @@ -294,6 +302,12 @@ impl Vm { } OpCode::DISPOSE_POP => { self.dispatch_dispose_pop()?; + // `await using` 释放的异步挂起:挂起时须像 AWAIT 一样让内嵌 + // dispatch 返回,由恢复方快照状态。 + if self.async_suspended || self.async_gen_suspended { + self.profiling.set_instruction_count(steps); + return Ok(JsValue::undefined()); + } } OpCode::CELL_GET => { self.dispatch_cell_get(rd, a, b)?; diff --git a/crates/oxide_vm/src/vm/gc_hooks.rs b/crates/oxide_vm/src/vm/gc_hooks.rs index 75cbb51d..c61612fb 100644 --- a/crates/oxide_vm/src/vm/gc_hooks.rs +++ b/crates/oxide_vm/src/vm/gc_hooks.rs @@ -8,7 +8,7 @@ use super::frames::Completion; use super::Vm; use crate::session_gc::SessionGc; -/// GC 根组来源:`for_each_value` 枚举的 26 个根组,变体顺序与根清单 +/// GC 根组来源:`for_each_value` 枚举的 27 个根组,变体顺序与根清单 /// 的枚举顺序一致。 /// /// 变体下标是逐组计数数组(`SessionGc::root_counts`)的下标;`COUNT` 与 @@ -31,6 +31,7 @@ pub(crate) enum RootGroup { AsyncContext, AsyncGenContext, PendingAsyncEscape, + PendingAsyncDispose, InlineCallee, TemplateObjects, NumberToStringCache, @@ -45,7 +46,7 @@ pub(crate) enum RootGroup { impl RootGroup { /// 根组总数(与变体数同源,绑定逐组计数数组的长度)。 - pub const COUNT: usize = 26; + pub const COUNT: usize = 27; } impl Vm { @@ -53,7 +54,7 @@ impl Vm { /// 覆盖执行核心的全部 JsValue 持有点:regs/帧/各栈段/cell/在途异常与完成/ /// 挂起信号/迭代器/微任务/global/Worker 对象注册表。 /// - /// 闭包接收根组来源(`RootGroup`)与根值:组是枚举顺序中的 26 根组之一, + /// 闭包接收根组来源(`RootGroup`)与根值:组是枚举顺序中的 27 根组之一, /// 调用方按组计数(如 `SessionGc` 的逐组计数)。 pub(crate) fn for_each_value(&self, mut f: impl FnMut(RootGroup, JsValue)) { for value in &self.regs { @@ -121,6 +122,16 @@ impl Vm { f(RootGroup::PendingAsyncEscape, v); } } + // 在途异步释放的 promise/合并错误/完成值都是 GC 根。 + if let Some(pend) = &self.pending_async_dispose { + f(RootGroup::PendingAsyncDispose, pend.dispose_promise); + if let Some(v) = pend.completion { + f(RootGroup::PendingAsyncDispose, v); + } + if let super::DisposeResumeKind::Escape(Completion::Return { value, .. }) = pend.kind { + f(RootGroup::PendingAsyncDispose, value); + } + } f(RootGroup::InlineCallee, self.inline_callee.unwrap_or(JsValue::undefined())); // 标签模板对象缓存:命中的模板对象是 GC 根(未根 → sweep 搬移/回收悬垂)。 for &cached in self.template_objects.values() { diff --git a/crates/oxide_vm/src/vm/mod.rs b/crates/oxide_vm/src/vm/mod.rs index 623b7d18..113ce7c3 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -159,6 +159,31 @@ pub(crate) struct PendingAsyncEscape { pub(crate) remaining: Vec, } +/// `await using` 资源释放的异步挂起在途状态:释放方法的 promise 挂起等待结算, +/// 结算闭包(微任务)恢复挂起帧后继续释放剩余条目并执行完成。 +#[derive(Debug)] +pub(crate) struct PendingAsyncDispose { + /// 正在等待的释放方法 promise(结算闭包陈旧防御校验用)。 + pub(crate) dispose_promise: JsValue, + /// 作用域水位:恢复后继续释放水位以上条目(LIFO)。 + pub(crate) mark: usize, + /// 在途合并错误(SuppressedError 链跨挂起累积)。 + pub(crate) completion: Option, + /// 释放循环结束后的恢复动作类别。 + pub(crate) kind: DisposeResumeKind, +} + +/// 释放循环结束后的恢复动作类别(三穿越点)。 +#[derive(Debug, Clone, Copy)] +pub(crate) enum DisposeResumeKind { + /// 作用域出口正常完成:有合并错误则展开,否则续行。 + Pop, + /// 异常穿越:handler 已弹出,恢复时重压后再入 unwind。 + Unwind(Option), + /// return/break/continue 穿越:有合并错误则丢弃完成值并展开,否则执行完成。 + Escape(Completion), +} + /// 基于寄存器的 JS 虚拟机:持有执行状态、寄存器文件、调用栈与 session 内存。 /// /// 执行入口为 [`Vm::run`](见 `vm_runtime` 模块);内存模型为统一入口分配 + @@ -268,6 +293,9 @@ pub struct Vm { /// 逃出 for-await-of 的异步关闭在途状态:return() 的 promise 挂起等待结算, /// 结算闭包(微任务)恢复挂起帧后继续关闭剩余条目并执行完成。 pub(crate) pending_async_escape: Option, + /// `await using` 资源释放的异步挂起在途状态:释放方法的 promise 挂起等待 + /// 结算,结算闭包(微任务)恢复挂起帧后继续释放剩余条目并执行完成。 + pub(crate) pending_async_dispose: 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 cbab6942..683f0fe0 100644 --- a/crates/oxide_vm/src/vm_dispatch_ctrl.rs +++ b/crates/oxide_vm/src/vm_dispatch_ctrl.rs @@ -1,4 +1,5 @@ use crate::vm::{Completion, FrameArgs, FrameContinuation, PendingAsyncEscape, TryHandler, Vm}; +use crate::vm_dispatch_misc::DisposePhase; use crate::vm_trace; use oxide_bytecode::opcode; use oxide_runtime_api::VmHost; @@ -1037,10 +1038,15 @@ impl Vm { } } // 逃出资源释放(规范序:IteratorClose 先、DisposeResources 后):逆序释放 - // 顶部 dispose_count 条。释放方法抛错时新错误替代完成值,展开到外围处理器。 - if self.dispose_top_n(completion.dispose_count()) { - self.unwind()?; - return Ok(CloseEscapeOutcome::Handled); + // 顶部 dispose_count 条。释放方法抛错时新错误替代完成值,展开到外围处理器; + // 异步释放挂起时登记在途状态并让出(结算闭包续)。 + match self.dispose_top_n(completion.dispose_count(), completion)? { + DisposePhase::Threw => { + self.unwind()?; + return Ok(CloseEscapeOutcome::Handled); + } + DisposePhase::Suspended => return Ok(CloseEscapeOutcome::Suspended), + DisposePhase::Clean => {} } // 异步条目逐层关闭:首个有 return 方法的登记挂起,其余入 remaining;无 // return 方法时继续下一层。 diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index 1f54d6f7..f2abc510 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -1,5 +1,9 @@ -use crate::vm::{ForInIter, FrameArgs, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH}; +use crate::vm::{ + Completion, DisposeResumeKind, ForInIter, FrameArgs, FrameContinuation, PendingAsyncDispose, + TryHandler, Vm, MAX_PROTO_CHAIN_DEPTH, +}; use crate::vm_trace; +use oxide_builtins::iterator::is_callable; use oxide_runtime_api::{push_units_to, to_boolean, to_units_full, VmHost}; use oxide_types::object::{JsObject, PropAttributes}; use oxide_types::private_key::{ @@ -9,11 +13,23 @@ use oxide_types::private_key::{ use oxide_types::value::JsValue; /// 单资源释放结局(规范 Dispose 语义):`Done` = 释放成功;`NoMethod` = 无释放方法 -/// (非对象 / 方法未定义 / 不可调用,跳过);`Err(v)` = 释放方法抛错(v 为原始异常值)。 +/// (非对象 / 方法未定义 / 不可调用,跳过);`Err(v)` = 释放方法抛错(v 为原始异常值); +/// `Suspended(promise)` = 异步释放已挂起(promise 为释放方法结果经 PromiseResolve 包装 +/// 后登记的结算 promise,调用方据此登记 `PendingAsyncDispose`)。 enum DisposeOutcome { Done, NoMethod, Err(JsValue), + Suspended(JsValue), +} + +/// 批量释放(`dispose_above` / `dispose_top_n`)结局:`Clean` = 全部正常; +/// `Threw` = 有释放方法抛错(合并错误已写入 `exception_value`);`Suspended` = +/// 异步挂起已登记(`PendingAsyncDispose` 已置,调用方须快照并早退)。 +pub(crate) enum DisposePhase { + Clean, + Threw, + Suspended, } impl Vm { @@ -705,11 +721,23 @@ impl Vm { let mut completion: Option = None; while self.dispose_stack.len() > mark { let (value, hint) = self.dispose_stack.pop().expect("水位以上必有条目"); - match self.dispose_value(value, hint) { + match self.dispose_value(value, hint)? { DisposeOutcome::Done | DisposeOutcome::NoMethod => {} DisposeOutcome::Err(exc) => { completion = oxide_builtins::disposable_stack::merge_dispose_error(self, completion, exc); } + DisposeOutcome::Suspended(promise) => { + // 异步挂起:登记在途状态(水位已在循环前弹出,恢复后续循环不 + // 重复弹),置挂起信号让派发循环退出由恢复方快照。 + self.pending_async_dispose = Some(PendingAsyncDispose { + dispose_promise: promise, + mark, + completion, + kind: DisposeResumeKind::Pop, + }); + self.disposing = false; + return Ok(()); + } } } self.disposing = false; @@ -721,41 +749,52 @@ impl Vm { Ok(()) } - /// 逆序释放 `depth` 以上资源并截断释放栈到 `depth`,同步弹出水位栈中 - /// `>= depth` 的基线条目(保两栈一致,防异常路径基线泄漏)。 + /// 批量释放核心:逆序释放 `depth` 以上条目并截断释放栈到 `depth`,同步弹出 + /// 水位栈中 `>= depth` 的基线条目(保两栈一致,防异常路径基线泄漏)。 /// /// # 步骤 - /// 1. 在途异常作 completion 种子(异常穿越时被折叠进 suppressed)。 + /// 1. 在途异常作 completion 种子(`seed`,异常穿越时被折叠进 suppressed)。 /// 2. 逐条弹出并释放(`dispose_value`);多个释放方法抛错时按规范折叠成 /// SuppressedError 链(后抛为 error、前值为 suppressed),循环不因错中断。 - /// 3. 收尾写回 completion:有合并错误则替代在途异常并更新错误类型;无合并 + /// 3. 异步挂起时登记 `PendingAsyncDispose`(携给定 `kind` 与在途合并错误)并 + /// 返回 `Suspended`。 + /// 4. 收尾写回 completion:有合并错误则替代在途异常并更新错误类型;无合并 /// 错误则还原在途异常(类型不变)。 - /// 4. 截断释放栈后,弹出水位栈中 `>= depth` 的基线条目。 + /// 5. 截断释放栈后,弹出水位栈中 `>= depth` 的基线条目。 /// - /// # 返回值 - /// `true` = 有释放方法抛错(合并错误已写入 `exception_value`);`false` = 全部正常。 - /// - /// # 副作用 - /// - 释放栈截断到 `depth`、水位栈弹出 `>= depth` 的基线。 - /// - 有释放方法抛错时改写 `exception_value`/`pending_error_kind`。 - pub(crate) fn dispose_above(&mut self, depth: usize) -> bool { - // 重入守卫:释放方法抛错触发的嵌套 unwind 会重入本函数,置位时直接返回, - // 防重复释放与合并(合并链会被二次折叠)。 + /// # 边界与前提 + /// - 重入守卫:释放方法抛错触发的嵌套 unwind 会重入本函数,`disposing` 置位时 + /// 直接返回(防重复释放与合并,合并链会被二次折叠)。 + /// - 挂起路径基线水位暂不弹(恢复续循环后统一弹)。 + pub(crate) fn dispose_above_core( + &mut self, depth: usize, kind: DisposeResumeKind, seed: Option, + ) -> Result { if self.disposing { - return false; + return Ok(DisposePhase::Clean); } self.disposing = true; - let mut completion: Option = self.exception_value; + let mut completion: Option = seed; let mut threw = false; let truncated = self.dispose_stack.len() > depth; while self.dispose_stack.len() > depth { let (value, hint) = self.dispose_stack.pop().expect("水位以上必有条目"); - match self.dispose_value(value, hint) { + match self.dispose_value(value, hint)? { DisposeOutcome::Done | DisposeOutcome::NoMethod => {} DisposeOutcome::Err(exc) => { completion = oxide_builtins::disposable_stack::merge_dispose_error(self, completion, exc); threw = true; } + DisposeOutcome::Suspended(promise) => { + // 异步挂起:登记在途状态,基线水位暂不弹(恢复续循环后统一弹)。 + self.pending_async_dispose = Some(PendingAsyncDispose { + dispose_promise: promise, + mark: depth, + completion, + kind, + }); + self.disposing = false; + return Ok(DisposePhase::Suspended); + } } } if let Some(exc) = completion { @@ -772,77 +811,139 @@ impl Vm { } } self.disposing = false; - threw + Ok(if threw { DisposePhase::Threw } else { DisposePhase::Clean }) + } + + /// 异常穿越释放:逆序释放 `depth` 以上资源。在途异常作 completion 种子(被折叠 + /// 进 suppressed)。挂起时携 `Unwind(handler)`(handler 已弹出,恢复时重压再入 + /// unwind)。 + pub(crate) fn dispose_above( + &mut self, depth: usize, handler: Option, + ) -> Result { + self.dispose_above_core(depth, DisposeResumeKind::Unwind(handler), self.exception_value) } /// 释放栈顶部 `n` 条资源(逃出释放:return/break/continue 完成值逃出作用域时 - /// 逆序释放被逃出的资源)。`n` 超过栈长时按全量释放。 - /// - /// # 返回值 - /// 同 `dispose_above`:`true` = 有释放方法抛错。 - pub(crate) fn dispose_top_n(&mut self, n: usize) -> bool { + /// 逆序释放被逃出的资源)。`n` 超过栈长时按全量释放。在途 completion 初值为 + /// None(完成值非 throw),挂起时携 `Escape(completion)` 供恢复后执行。 + pub(crate) fn dispose_top_n( + &mut self, n: usize, completion: Completion, + ) -> Result { let depth = self.dispose_stack.len().saturating_sub(n); - self.dispose_above(depth) + self.dispose_above_core(depth, DisposeResumeKind::Escape(completion), None) } - /// 单资源释放(规范 Dispose 语义):非对象跳过;按释放提示取方法(同步只取 - /// Symbol.dispose;异步先取 Symbol.asyncDispose,未定义回退 Symbol.dispose), - /// 不可调用跳过;以资源自身为 this 零参调用。 + /// 单资源释放(规范 Dispose 语义):按释放提示取方法(同步只取 Symbol.dispose; + /// 异步先取 Symbol.asyncDispose,未定义回退 Symbol.dispose),以资源自身为 this + /// 零参调用。 /// /// # 返回值 /// `Done` = 释放成功;`NoMethod` = 无释放方法(跳过);`Err(v)` = 释放方法抛错 - /// (v 为原始异常值,经 `last_uncaught_value` 恢复,无则按错误文本建普通 Error)。 + /// (v 为原始异常值,经 `last_uncaught_value` 恢复,无则按错误文本建普通 Error); + /// `Suspended(promise)` = 异步释放已挂起(promise 为释放方法结果经 PromiseResolve + /// 包装后登记的结算 promise,调用方据此登记 `PendingAsyncDispose`)。 /// /// # 边界与前提 /// - 取方法 / 调用方法任一步抛错均归入 `Err`(getter 抛错与调用抛错同处置)。 - /// - 异步方法结果为对象时须 Await 挂起,归后续任务;本期同步调用,结果丢弃。 - fn dispose_value(&mut self, value: JsValue, hint: u16) -> DisposeOutcome { + /// - 异步提示恒挂起:方法未定义时结果取 undefined,仍经 PromiseResolve 包成新 + /// promise 让出一次微任务(规范 `await using` 的 Await 步无条件执行)。 + fn dispose_value(&mut self, value: JsValue, hint: u16) -> Result { + // 取方法抛错(getter 抛错)归入 Err 结局(合并进链)。 + let method = match self.lookup_dispose_method(value, hint) { + Ok(m) => m, + Err(exc) => return Ok(DisposeOutcome::Err(exc)), + }; + // 异步提示且有异步上下文:调用方法(未定义时结果取 undefined),结果无条件 + // Await 挂起。无异步上下文(顶层 await using)回退同步释放。 + if hint == 1 && (self.async_context.is_some() || self.async_gen_context.is_some()) { + let result = if is_callable(method) { + match self.call_function_sync(method, value, &[]) { + Ok(v) => v, + Err(e) => return Ok(DisposeOutcome::Err(self.dispose_exc(e))), + } + } else { + JsValue::undefined() + }; + let promise = match self.promise_resolve(result) { + Ok(p) => p, + Err(exc) => { + let (p, _, _) = self.new_promise_capability(); + let _ = self.reject_promise(p, exc); + p + } + }; + self.register_dispose_suspend(promise)?; + return Ok(DisposeOutcome::Suspended(promise)); + } + // 同步(或无异步上下文的异步提示):不可调用跳过;调用成功 Done,抛错 Err。 + if !is_callable(method) { + return Ok(DisposeOutcome::NoMethod); + } + match self.call_function_sync(method, value, &[]) { + Ok(_) => Ok(DisposeOutcome::Done), + Err(e) => Ok(DisposeOutcome::Err(self.dispose_exc(e))), + } + } + + /// 取释放方法:异步提示先取 Symbol.asyncDispose,未定义回退 Symbol.dispose; + /// 同步提示只取 Symbol.dispose。非对象取 undefined。getter 抛错时返回原始 + /// 异常值(经 `last_uncaught_value` 恢复,无则按错误文本建普通 Error)。 + fn lookup_dispose_method(&mut self, value: JsValue, hint: u16) -> Result { if !value.is_object() { - return DisposeOutcome::NoMethod; + return Ok(JsValue::undefined()); } let val_obj = unsafe { &*value.as_js_object_ptr() }; let dispose_key = encode_symbol_key(self.realm_id(), WELL_KNOWN_SYMBOL_DISPOSE); let method = if hint == 1 { let async_key = encode_symbol_key(self.realm_id(), WELL_KNOWN_SYMBOL_ASYNC_DISPOSE); - let async_method = match self.ordinary_get(val_obj, async_key, value) { - Ok(m) => m, - Err(e) => return self.dispose_thrown(e), - }; + let async_method = self + .ordinary_get(val_obj, async_key, value) + .map_err(|e| self.dispose_exc(e))?; if async_method.is_undefined() { - match self.ordinary_get(val_obj, dispose_key, value) { - Ok(m) => m, - Err(e) => return self.dispose_thrown(e), - } + self.ordinary_get(val_obj, dispose_key, value).map_err(|e| self.dispose_exc(e))? } else { async_method } } else { - match self.ordinary_get(val_obj, dispose_key, value) { - Ok(m) => m, - Err(e) => return self.dispose_thrown(e), - } + self.ordinary_get(val_obj, dispose_key, value).map_err(|e| self.dispose_exc(e))? }; - if !method.is_object() { - return DisposeOutcome::NoMethod; - } - let method_obj = unsafe { &*method.as_js_object_ptr() }; - if !method_obj.is_function() { - return DisposeOutcome::NoMethod; - } - match self.call_function_sync(method, value, &[]) { - Ok(_) => DisposeOutcome::Done, - Err(e) => self.dispose_thrown(e), - } + Ok(method) } - /// 释放方法抛错时恢复原始异常值(经 `last_uncaught_value`,无则按错误文本建 - /// 普通 Error),归入 `Err` 结局。 - fn dispose_thrown(&mut self, e: String) -> DisposeOutcome { - let exc = self - .last_uncaught_value + /// 登记异步释放挂起:按派发上下文(异步生成器 / 普通异步函数)构造结算闭包, + /// 登记 fulfill/reject 恢复反应并置挂起信号。 + /// + /// # 边界与前提 + /// - 异步生成器派发(`async_gen_dispatch`)走异步生成器状态盒与结算闭包,置 + /// `async_gen_suspended`;普通异步函数走异步上下文,置 `async_suspended`。 + /// - 无对应上下文(非异步上下文内的异步释放)返回 Err,不静默降级。 + fn register_dispose_suspend(&mut self, promise: JsValue) -> Result<(), String> { + if self.async_gen_dispatch { + let Some(ctx) = self.async_gen_context else { + return Err("async dispose in async generator without context".into()); + }; + let fulfill_fn = self.make_async_gen_dispose_resume_fn(ctx, promise, false); + let reject_fn = self.make_async_gen_dispose_resume_fn(ctx, promise, true); + let _ = self.perform_promise_then(promise, fulfill_fn, reject_fn); + self.async_gen_suspended = true; + return Ok(()); + } + let Some(ctx) = self.async_context else { + return Err("async dispose outside async function".into()); + }; + let fulfill_fn = self.make_async_dispose_resume_fn(ctx, promise, false); + let reject_fn = self.make_async_dispose_resume_fn(ctx, promise, true); + let _ = self.perform_promise_then(promise, fulfill_fn, reject_fn); + self.async_suspended = true; + Ok(()) + } + + /// 释放路径抛错时恢复原始异常值(经 `last_uncaught_value`,无则按错误文本建 + /// 普通 Error)。 + fn dispose_exc(&mut self, e: String) -> JsValue { + self.last_uncaught_value .take() - .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)); - DisposeOutcome::Err(exc) + .unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e)) } pub(crate) fn dispatch_for_of_init(&mut self, a: usize) -> Result<(), String> { diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index 590e5e03..803e2bf1 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -5,6 +5,7 @@ use oxide_bytecode::module::CompiledModule; use oxide_bytecode::opcode::OpCode; use crate::vm::{CallFrame, FrameArgs, FrameContinuation, InlineSyncState, TableGen, Vm}; +use crate::vm_dispatch_misc::DisposePhase; use crate::{vm_debug, vm_info, vm_trace, vm_warn}; use oxide_types::object::{Cell, JsObject}; use oxide_types::value::JsValue; @@ -329,9 +330,10 @@ 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)。 + // 固化不变量:内联 state-swap 边界不携带在途异步逃出/释放(挂起态快照前 + // 已搬入状态盒,settle 前恒为 None)。 debug_assert!(self.pending_async_escape.is_none()); + debug_assert!(self.pending_async_dispose.is_none()); let window = regs_end.min(254); let mut window_regs = self.inline_reg_pool.take().unwrap_or_default(); window_regs.clear(); @@ -344,8 +346,9 @@ impl Vm { /// 单回,窗口外寄存器 callee 未触碰无需恢复。 pub(crate) fn restore_inline_state(&mut self, mut saved: Box) { vm_trace!("restore_inline_state: pc={}", saved.pc); - // 固化不变量:内联 state-swap 边界不携带在途异步逃出(与 save 侧同)。 + // 固化不变量:内联 state-swap 边界不携带在途异步逃出/释放(与 save 侧同)。 debug_assert!(self.pending_async_escape.is_none()); + debug_assert!(self.pending_async_dispose.is_none()); let vm = self; let _window_regs: Vec = Vec::new(); inline_core_fields!(vm, saved, inline_restore, _window_regs); @@ -871,8 +874,14 @@ impl Vm { // 对该处理器作用域内被中断的 for-of 循环执行 IteratorClose。 self.close_for_of_above(handler.for_of_depth); // 异常穿越释放:逆序释放该作用域内登记的资源(规范序 IteratorClose 先、 - // DisposeResources 后)。释放方法抛错时新错误替代在途异常继续向外展开。 - self.dispose_above(handler.dispose_depth); + // DisposeResources 后)。释放方法抛错时新错误替代在途异常继续向外展开; + // 异步释放挂起时登记在途状态并让出(恢复方重压 handler 再入本函数)。 + if matches!( + self.dispose_above(handler.dispose_depth, Some(handler))?, + DisposePhase::Suspended + ) { + return Ok(()); + } if let Some(finally_pc) = handler.finally_pc { if handler.finally_active { // 新异常在 finally 体内抛出:覆盖在途异常与完成,继续向外展开, @@ -902,8 +911,11 @@ impl Vm { } } self.close_for_of_above(0); - // 未捕获兜底:释放栈上全部资源(异常穿越全作用域)。 - self.dispose_above(0); + // 未捕获兜底:释放栈上全部资源(异常穿越全作用域)。无 handler(未捕获路径); + // 异步释放挂起时登记在途状态并让出(恢复方直接再入本函数走未捕获路径)。 + if matches!(self.dispose_above(0, None)?, DisposePhase::Suspended) { + return Ok(()); + } while let Some(frame) = self.frames.pop() { self.cell_stack.pop(); self.restore_frame(frame); diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index 0248ef6c..00fba9c4 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -144,6 +144,7 @@ impl Vm { pending_error_kind: None, pending_completion: None, pending_async_escape: None, + pending_async_dispose: None, root_reg_limit: 0, active_reg_limit: 0, native_call_depth: 0, @@ -308,6 +309,7 @@ impl Vm { pending_error_kind: None, pending_completion: None, pending_async_escape: None, + pending_async_dispose: None, root_reg_limit: 0, active_reg_limit: 0, native_call_depth: 0, @@ -543,6 +545,8 @@ impl Vm { self.pending_completion = None; // 在途异步逃出是执行期状态:跨 run/reset 不保留,残留将悬垂。 self.pending_async_escape = None; + // 在途异步释放同属执行期状态:跨 run/reset 不保留,残留将悬垂。 + self.pending_async_dispose = None; self.generator_suspended = None; self.delegated_iterator = None; self.generator_dispatch = false;