feat: for-await-of 多层逃出逐层 LIFO 关闭并接通异步生成器路径

This commit is contained in:
2026-09-27 20:53:13 +00:00
parent 7f5ca71b9b
commit b6580036da
5 changed files with 388 additions and 39 deletions
+33 -8
View File
@@ -65,7 +65,7 @@ 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__";
pub(crate) const ASYNC_ESCAPE_PROMISE_PROP: &str = "__oxide_async_escape_promise__";
impl Vm {
/// 调用异步函数返回的 capability promise:创建上下文对象 + 状态盒,立即压帧
@@ -389,14 +389,16 @@ impl Vm {
Ok(())
}
/// 恢复逃出 for-await-of 的异步函数:return() 的 promise 结算后执行完成。
/// 恢复逃出 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。恢复调用方状态。
/// 2. 把挂起快照灌回 VM,取出完成与剩余待关条目并清除 pending_async_escape。
/// 3. 多层逃出:remaining 非空时逐层关闭(每层一轮结算),空了才执行完成。
/// 4. 执行完成:Return 走 do_return,Break/Continue 跳转 target_pc 续 dispatch。
/// 5. 再挂起则快照新状态;完成/异常则结算 capability。恢复调用方状态。
///
/// # 边界与前提
/// - 关闭 promise 拒绝时(reject 角色),拒绝值替代在途完成:抛拒绝原因,
@@ -406,6 +408,8 @@ impl Vm {
) -> 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();
@@ -438,8 +442,29 @@ impl Vm {
state.phase = AsyncPhase::Running;
}
// 取出完成并清除 pending_async_escape。
let completion = self.pending_async_escape.take().unwrap().completion;
// 取出完成与剩余待关条目并清除 pending_async_escape。
let (completion, mut remaining) = match self.pending_async_escape.take() {
Some(p) => (p.completion, p.remaining),
// 陈旧防御已早退,正常路径此处必为 Some;防御性兜底。
None => return Ok(()),
};
// 多层逃出:remaining 非空时逐层关闭(每层一轮结算),空了才执行完成
// (或抛拒绝原因)。挂起信号由 register 置位,快照后返回续下一轮结算。
while let Some(next_iter) = remaining.first().cloned() {
remaining.remove(0);
match self.register_async_escape_close(next_iter, completion, remaining.clone()) {
Ok(true) => {
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(());
}
Ok(false) => continue,
Err(e) => return Err(e),
}
}
// 关闭拒绝:拒绝值替代在途完成。抛拒绝原因,外围 catch/finally 接管;
// 无处理器时异步函数以拒绝原因拒绝。
@@ -479,7 +504,7 @@ impl Vm {
return Ok(());
}
// 单层路径:remaining 恒空,直接执行完成。
// remaining 已空,执行完成。
match completion {
Completion::Return { value: ret_val, .. } => {
let result = self.do_return(ret_val)?;
+189 -7
View File
@@ -21,7 +21,7 @@ use oxide_types::object::{Cell, JsObject, NativeFnPtr, PropAttributes};
use oxide_types::value::JsValue;
use crate::generator::{DelegateOutcome, GeneratorResumeMode};
use crate::vm::Vm;
use crate::vm::{Completion, Vm};
/// 异步生成器执行阶段。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -429,13 +429,20 @@ impl Vm {
}
let result = self.dispatch();
self.async_gen_dispatch = prev_agd;
self.async_gen_context = prev_gen_ctx;
self.generator_dispatch = prev_gd;
self.async_dispatch = prev_ad;
self.async_context = prev_ctx;
self.native_call_depth -= 1;
self.restore_async_gen_flags(prev_ctx, prev_gen_ctx, prev_gd, prev_ad, prev_agd);
self.post_dispatch_async_gen(state_ptr, saved, gen_val, result)
}
/// 异步生成器恢复的 dispatch 后处理:YIELD 让出(快照 + 让出值 unwrap 结算)、
/// AWAIT 挂起(快照)、完成/异常(结算当前请求)。
///
/// # 前提
/// 调度标志已恢复(调用方先恢复再调本函数);本函数内部按结果快照新状态并
/// 恢复调用方内联状态。
fn post_dispatch_async_gen(
&mut self, state_ptr: *mut AsyncGeneratorState, saved: Box<crate::vm::InlineSyncState>, gen_val: JsValue,
result: Result<JsValue, String>,
) -> Result<(), String> {
// YIELD 让出:快照挂起状态,让出值经 promise unwrap 后结算当前请求。
if let Some(value) = self.generator_suspended.take() {
// AsyncGeneratorYield:让出值 PromiseResolve 包装后 await 展开
@@ -758,6 +765,154 @@ impl Vm {
self.add_fn_name_length(obj, "", 1);
JsValue::from_js_object(ptr)
}
/// 构造逃出关闭结算闭包(异步生成器):携带目标异步生成器上下文对象与
/// return() promise,区分 fulfill/reject 角色。
pub(crate) fn make_async_gen_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_gen_escape_closure 是 NativeFn 函数项。
func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(async_gen_escape_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(crate::async_func::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)
}
/// 恢复逃出 for-await-of 的异步生成器:return() 的 promise 结算后继续关闭剩余
/// 条目并执行完成。
///
/// # 步骤
/// 1. 陈旧防御:状态盒 pending_async_escape.close_promise 须与本闭包 promise
/// 匹配,不匹配则不处理(防同一 promise 被双结算)。
/// 2. 把挂起快照灌回 VM,取出完成与剩余待关条目并清除 pending_async_escape。
/// 3. 多层逃出:remaining 非空时逐层关闭(每层一轮结算),空了才执行完成。
/// 4. 执行完成:Return 走 do_return 完成生成器,Break/Continue 跳转 target_pc
/// 续 dispatch。
/// 5. 再挂起则快照新状态;完成/异常则结算当前请求。恢复调用方状态。
///
/// # 边界与前提
/// - 关闭 promise 拒绝时(reject 角色),拒绝值替代在途完成:抛拒绝原因,
/// 外围 catch/finally 接管;无处理器时当前请求以拒绝原因拒绝。
pub(crate) fn resume_async_gen_escape(
&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 };
// 陈旧防御: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_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_escape。
let (completion, mut remaining) = match self.pending_async_escape.take() {
Some(p) => (p.completion, p.remaining),
// 陈旧防御已早退,正常路径此处必为 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(());
}
};
// 多层逃出:remaining 非空时逐层关闭(每层一轮结算),空了才执行完成
// (或抛拒绝原因)。挂起信号由 register 置位,快照后返回续下一轮结算。
while let Some(next_iter) = remaining.first().cloned() {
remaining.remove(0);
match self.register_async_escape_close(next_iter, completion, remaining.clone()) {
Ok(true) => {
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(());
}
Ok(false) => continue,
Err(e) => return Err(e),
}
}
// 关闭拒绝:拒绝值替代在途完成。抛拒绝原因,外围 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.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);
return self.post_dispatch_async_gen(state_ptr, saved, ctx, result);
}
// remaining 已空,执行完成。
match completion {
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 的异步生成器上下文与角色,恢复异步生成器继续执行。
@@ -1058,6 +1213,33 @@ fn async_gen_delegate_return_await_closure(vm: &mut Vm, args: &[u8]) -> NativeRe
}
}
/// 逃出关闭结算闭包(异步生成器):读自身 prop 的异步生成器上下文、return()
/// promise 与角色,恢复挂起帧并继续关闭剩余条目、执行完成。
fn async_gen_escape_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(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(crate::async_func::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_gen_escape(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 标志与委托值)。
+42 -24
View File
@@ -945,9 +945,9 @@ impl Vm {
/// 逃出关闭:按 LIFO 弹出逃出的 for-in 迭代器(无 return() 语义),再关闭逃出的
/// for-of 迭代器。同步迭代器同步关闭;异步迭代器(for-await-of)同步调 return()
/// 得 promise,登记结算闭包后挂起。逃出路径非 suppress:return() 抛错经
/// raise_call_error 展开,剩余迭代器由 unwind 的 close_for_of_above 以 suppress
/// 继续关闭——新错误替代原完成值向外传播。
/// 得 promise,登记结算闭包后挂起,剩余异步条目入 remaining 逐层续关。逃出路径
/// 非 suppress:return() 抛错经 raise_call_error 展开,剩余迭代器由 unwind 的
/// close_for_of_above 以 suppress 继续关闭——新错误替代原完成值向外传播。
///
/// # 返回值
/// `Done` = 全部关闭完成,调用方继续跳转/返回动作;`Handled` = return() 抛错且
@@ -960,16 +960,14 @@ impl Vm {
for _ in 0..for_in_count {
self.iters.pop_for_in();
}
// 单层路径:首个异步条目登记关闭,其余异步条目丢弃(多层路径填 remaining)。
let mut first_async: Option<JsValue> = None;
// 多层路径:异步条目按 LIFO 收集(前元素先关),同步条目当场关闭。
let mut async_iters: Vec<JsValue> = Vec::new();
for _ in 0..for_of_count {
let Some(entry) = self.iters.pop_for_of() else {
break;
};
if entry.is_async {
if first_async.is_none() {
first_async = Some(entry.iterator);
}
async_iters.push(entry.iterator);
continue;
}
let pc_before = self.pc;
@@ -984,13 +982,14 @@ impl Vm {
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) => {}
// 异步条目逐层关闭:首个有 return 方法的登记挂起,其余入 remaining;无
// return 方法时继续下一层。
let mut remaining = async_iters;
while let Some(iterator) = remaining.first().cloned() {
remaining.remove(0);
match self.register_async_escape_close(iterator, completion, remaining.clone()) {
Ok(true) => return Ok(CloseEscapeOutcome::Suspended),
Ok(false) => continue,
Err(e) => return Err(e),
}
}
@@ -998,19 +997,20 @@ impl Vm {
}
/// 登记逃出 for-await-of 的异步关闭:同步调 return() 得 promise,登记结算闭包,
/// 置 pending_async_escape。迭代器无 return 方法时不登记(完成直接继续)。
/// 置 pending_async_escape 与挂起信号。迭代器无 return 方法时不登记(完成直接
/// 继续)。
///
/// # 边界与前提
/// - 异步生成器派发(`async_gen_dispatch`)走异步生成器状态盒与结算闭包,置
/// `async_gen_suspended`;普通异步函数走异步上下文,置 `async_suspended`。
/// - 同步生成器无异步上下文:不登记挂起(完成直接继续)。
///
/// # 返回值
/// `Ok(true)` = 已登记挂起;`Ok(false)` = 无 return 方法,无需挂起;`Err` = 关闭
/// 过程出错。
fn register_async_escape_close(
pub(crate) fn register_async_escape_close(
&mut self, iterator: JsValue, completion: Completion, remaining: Vec<JsValue>,
) -> Result<bool, String> {
// 异步生成器走独立状态盒(AsyncGeneratorState),本路径的结算闭包按
// AsyncState 恢复——不登记挂起(异步迭代器保持既有跳过行为,完成直接继续)。
if self.async_gen_dispatch {
return Ok(false);
}
if !iterator.is_object() {
return Ok(false);
}
@@ -1037,8 +1037,25 @@ impl Vm {
}
}
};
// 同步生成器无异步上下文:不登记挂起(异步迭代器保持既有跳过行为,完成直接继续)。
let Some(ctx) = self.async_context.or(self.async_gen_context) else {
// 异步生成器派发:异步生成器状态盒与结算闭包,置 async_gen_suspended。
if self.async_gen_dispatch {
let Some(ctx) = self.async_gen_context else {
return Ok(false);
};
let fulfill_fn = self.make_async_gen_escape_close_fn(ctx, promise, false);
let reject_fn = self.make_async_gen_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,
});
self.async_gen_suspended = true;
return Ok(true);
}
// 普通异步函数:异步上下文与结算闭包,置 async_suspended。同步生成器无
// 异步上下文:不登记挂起(完成直接继续)。
let Some(ctx) = self.async_context else {
return Ok(false);
};
let fulfill_fn = self.make_async_escape_close_fn(ctx, promise, false);
@@ -1049,6 +1066,7 @@ impl Vm {
completion,
remaining,
});
self.async_suspended = true;
Ok(true)
}