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
+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,