feat: await using 释放挂起跨微任务续行并合并 SuppressedError

This commit is contained in:
2026-10-09 20:50:35 +00:00
parent 328ccafbc0
commit 71904a644f
12 changed files with 718 additions and 101 deletions
Generated
+19 -19
View File
@@ -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",
+1 -1
View File
@@ -23,7 +23,7 @@ members = [
]
[workspace.package]
version = "0.13.18"
version = "0.13.19"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+234 -1
View File
@@ -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<InlineSyncState>,
prev_ctx: Option<JsValue>, prev_dispatch: bool, prev_gen_ctx: Option<JsValue>,
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")。
+195 -1
View File
@@ -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 标志与委托值)。
+15 -1
View File
@@ -11,7 +11,7 @@ use oxide_bytecode::opcode;
use oxide_types::object::Cell;
use oxide_types::value::JsValue;
use crate::vm::{CallFrame, Completion, ForInIter, 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<Completion>,
/// 逃出 for-await-of 的异步关闭在途状态(随挂起快照跨微任务存活)。
pub pending_async_escape: Option<PendingAsyncEscape>,
/// `await using` 资源释放的异步挂起在途状态(随挂起快照跨微任务存活)。
pub pending_async_dispose: Option<PendingAsyncDispose>,
}
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;
+14
View File
@@ -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)?;
+14 -3
View File
@@ -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() {
+28
View File
@@ -159,6 +159,31 @@ pub(crate) struct PendingAsyncEscape {
pub(crate) remaining: Vec<JsValue>,
}
/// `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<JsValue>,
/// 释放循环结束后的恢复动作类别。
pub(crate) kind: DisposeResumeKind,
}
/// 释放循环结束后的恢复动作类别(三穿越点)。
#[derive(Debug, Clone, Copy)]
pub(crate) enum DisposeResumeKind {
/// 作用域出口正常完成:有合并错误则展开,否则续行。
Pop,
/// 异常穿越:handler 已弹出,恢复时重压后再入 unwind。
Unwind(Option<TryHandler>),
/// 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<PendingAsyncEscape>,
/// `await using` 资源释放的异步挂起在途状态:释放方法的 promise 挂起等待
/// 结算,结算闭包(微任务)恢复挂起帧后继续释放剩余条目并执行完成。
pub(crate) pending_async_dispose: Option<PendingAsyncDispose>,
pub(crate) root_reg_limit: u8,
pub(crate) active_reg_limit: u8,
pub(crate) native_call_depth: usize,
+10 -4
View File
@@ -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 方法时继续下一层。
+165 -64
View File
@@ -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<JsValue> = 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<JsValue>,
) -> Result<DisposePhase, String> {
if self.disposing {
return false;
return Ok(DisposePhase::Clean);
}
self.disposing = true;
let mut completion: Option<JsValue> = self.exception_value;
let mut completion: Option<JsValue> = 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<TryHandler>,
) -> Result<DisposePhase, String> {
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<DisposePhase, String> {
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<DisposeOutcome, String> {
// 取方法抛错(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<JsValue, JsValue> {
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> {
+19 -7
View File
@@ -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<InlineSyncState> {
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<InlineSyncState>) {
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<JsValue> = 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);
+4
View File
@@ -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;