mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: for-await-of 多层逃出逐层 LIFO 关闭并接通异步生成器路径
This commit is contained in:
@@ -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)?;
|
||||
|
||||
@@ -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 标志与委托值)。
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -360,3 +360,29 @@ fn t18_throw_close_non_object_result_typeerror() {
|
||||
"\"caught:IteratorResult is not an object:true\""
|
||||
);
|
||||
}
|
||||
|
||||
// T19:异步生成器体内 for-await-of 多层 return 逃出——LIFO 顺序关闭(内层先),
|
||||
// 完成值经 next() 的 promise 交付({value:42, done:true})。
|
||||
#[test]
|
||||
fn t19_async_gen_multi_layer_return_escape_lifo() {
|
||||
let source = "let log=[];\
|
||||
const mk=(n)=>({[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:n,done:false})},return(){log.push('c'+n);return Promise.resolve({done:true})}}}});\
|
||||
async function* g() { for await (const a of mk(1)) { for await (const b of mk(2)) { log.push('body'); return 42; } } }\
|
||||
(async function run() { const it = g(); const r = await it.next(); \
|
||||
return log.join(',') + '|' + r.done + ':' + r.value; })()"
|
||||
.to_string();
|
||||
assert_eq!(eval(&source), "\"body,c2,c1|true:42\"");
|
||||
}
|
||||
|
||||
// T20:异步生成器体内 for-await-of break 逃出——关闭迭代器后 body 继续,
|
||||
// 后续 yield 正常交付(验证 break 跳转 target_pc 正确、生成器未误完成)。
|
||||
#[test]
|
||||
fn t20_async_gen_break_escape_continues() {
|
||||
let source = "let log=[];\
|
||||
const it={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})},return(){log.push('close');return Promise.resolve({done:true})}}}};\
|
||||
async function* g() { for await (const x of it) { log.push('body'); break; } yield 'after'; }\
|
||||
(async function run() { const it = g(); const r = await it.next(); \
|
||||
return log.join(',') + '|' + r.value + ':' + r.done; })()"
|
||||
.to_string();
|
||||
assert_eq!(eval(&source), "\"body,close|after:false\"");
|
||||
}
|
||||
|
||||
@@ -281,6 +281,104 @@ fn for_await_of_close_throw_object_identity_preserved() {
|
||||
);
|
||||
}
|
||||
|
||||
// ── 逃出关闭测试矩阵(E1-E10):多层 LIFO 关闭与 Completion 边角 ──
|
||||
//
|
||||
// for-await-of 的 next() 经 await 结算,循环体在 N 层嵌套时深 N 个微任务 tick;
|
||||
// 读取 log 的 then 回调须比最深的结算闭包更晚入队,故用足够长的 then 链延迟读取。
|
||||
|
||||
/// 延迟 N 个微任务 tick 后读取 log(N 层逃出需 N+2 个 tick 让全部结算闭包跑完)。
|
||||
fn delay_read(n: usize) -> String {
|
||||
let mut chain = "Promise.resolve()".to_string();
|
||||
for _ in 0..n {
|
||||
chain.push_str(".then(()=>{})");
|
||||
}
|
||||
chain.push_str(".then(()=>log.join(','))");
|
||||
chain
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_break_escape_middle_layer() {
|
||||
// E2b:break 逃出中间 for-await-of 层——内层迭代器关闭,外层继续迭代。
|
||||
let source = "let log=[];\
|
||||
const mk=(n)=>({[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:n,done:false})},return(){log.push('c'+n);return Promise.resolve({done:true})}}}});\
|
||||
(async()=>{ for await (const a of mk(1)) { for await (const b of mk(2)) { log.push('body'); break; } log.push('after'); break; } })();\
|
||||
".to_string()
|
||||
+ &delay_read(4);
|
||||
assert_eq!(eval(&source), "\"body,c2,after,c1\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_labeled_continue_closes_inner() {
|
||||
// E3:labeled continue 逃出内层 for-await-of——内层迭代器每轮关闭,外层继续。
|
||||
// 外层迭代器有限(两轮后 done),验证 continue 不关闭外层、外层继续迭代。
|
||||
let source = "let log=[];\
|
||||
let outerN=0;\
|
||||
const outer={[Symbol.asyncIterator](){return{next(){outerN++;if(outerN>2)return Promise.resolve({value:undefined,done:true});return Promise.resolve({value:outerN,done:false});},return(){log.push('cO');return Promise.resolve({done:true})}}}};\
|
||||
const inner={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})},return(){log.push('cI');return Promise.resolve({done:true})}}}};\
|
||||
(async()=>{ outer: for await (const a of outer) { for await (const b of inner) { log.push('body'); continue outer; } log.push('after-inner'); } })();\
|
||||
".to_string()
|
||||
+ &delay_read(6);
|
||||
assert_eq!(eval(&source), "\"body,cI,body,cI\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_multi_layer_return_escape_lifo() {
|
||||
// E4:多层 return 逃出(两层 for-await-of)——LIFO 顺序关闭(内层先)。
|
||||
// 内层 return() 先被调(c2),外层后(c1),关闭顺序验证 LIFO。
|
||||
let source = "let log=[];\
|
||||
const mk=(n)=>({[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:n,done:false})},return(){log.push('c'+n);return Promise.resolve({done:true})}}}});\
|
||||
(async()=>{ for await (const a of mk(1)) { for await (const b of mk(2)) { log.push('body'); return 42; } } })();\
|
||||
".to_string()
|
||||
+ &delay_read(4);
|
||||
assert_eq!(eval(&source), "\"body,c2,c1\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_multi_layer_labeled_break_escape_lifo() {
|
||||
// E5:多层 labeled break 逃出——LIFO 顺序关闭(内层先),跳转目标正确。
|
||||
let source = "let log=[];\
|
||||
const mk=(n)=>({[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:n,done:false})},return(){log.push('c'+n);return Promise.resolve({done:true})}}}});\
|
||||
(async()=>{ outer: for await (const a of mk(1)) { for await (const b of mk(2)) { log.push('body'); break outer; } } log.push('after'); })();\
|
||||
".to_string()
|
||||
+ &delay_read(4);
|
||||
assert_eq!(eval(&source), "\"body,c2,c1,after\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_escape_close_reject_outer_catch() {
|
||||
// E6:逃出时 return() 拒绝——拒绝原因替代完成值,外围 catch 捕获。
|
||||
let source = "let log=[];\
|
||||
const it={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})},return(){return Promise.reject('rej');}}}};\
|
||||
(async()=>{ try { for await (const x of it) { log.push('body'); return 1; } } catch (e) { log.push('caught:'+e); } log.push('done'); })();\
|
||||
".to_string()
|
||||
+ &delay_read(3);
|
||||
assert_eq!(eval(&source), "\"body,caught:rej,done\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_escape_no_return_method() {
|
||||
// E8:迭代器无 return 方法——不登记挂起,完成直接继续。
|
||||
let source = "let log=[];\
|
||||
const it={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})}}}};\
|
||||
(async()=>{ for await (const x of it) { log.push('body'); return 1; } })();\
|
||||
"
|
||||
.to_string()
|
||||
+ &delay_read(2);
|
||||
assert_eq!(eval(&source), "\"body\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_await_of_escape_settlement_precedes_then() {
|
||||
// E10:微任务时序——结算闭包(执行完成)先于后续 then 回调。
|
||||
// break 逃出后,完成跳转(log.push('after'))在结算闭包内执行,须先于 then 回调。
|
||||
let source = "let log=[];\
|
||||
const it={[Symbol.asyncIterator](){return{next(){return Promise.resolve({value:1,done:false})},return(){return Promise.resolve({done:true})}}}};\
|
||||
(async()=>{ for await (const x of it) { log.push('body'); break; } log.push('after'); })();\
|
||||
".to_string()
|
||||
+ &delay_read(3);
|
||||
assert_eq!(eval(&source), "\"body,after\"");
|
||||
}
|
||||
|
||||
// ── class/object 生成器方法(异步) ──
|
||||
|
||||
// class async 生成器方法:yield 顺序与 done 收敛。顶层 then 链驱动
|
||||
|
||||
Reference in New Issue
Block a user