Files
OxideJS-New/crates/oxide_vm/src/async_disposable.rs
T

327 lines
16 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! AsyncDisposableStack.prototype.disposeAsync 的 VM 层实现。
//!
//! disposeAsync 返回 `%Promise%` 能力,逆序释放栈内条目。循环体以原生函数
//! 闭包实现(续链闭包),跨 await 的状态(游标/错误合并/needsAwait/hasAwaited)
//! 存函数对象属性槽;栈对象引用存入闭包槽保活状态盒(跨 await 不被 GC 回收)。
//! 2026 新版 DisposeResources 语义:null/undefined 型 async 条目的 Await 延迟到
//! 循环末单次执行;sync 条目遇待决 Await 先插队执行 Await;`@@dispose` 来源的
//! 方法(wrap_sync)返回值丢弃、同步异常异步化为拒绝再 await。
//!
//! 状态机仿 AsyncFromSync 续链模式:每次 await 经 `promise_resolve` +
//! `perform_promise_then` 注册同一 native(不同 reject_role 槽)的两个闭包实例,
//! 派生的中间 promise 不可观察。
use oxide_builtins::disposable_stack::{merge_dispose_error, require_dispose_capability, DisposeState};
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_runtime_api::NativeResult;
use oxide_types::object::{JsObject, NativeFnPtr};
use oxide_types::value::JsValue;
use crate::vm::Vm;
/// 续链闭包上保存状态盒所在栈对象的属性名(保活状态盒)。
const STACK_PROP: &str = "__oxide_ads_stack__";
/// 续链闭包上保存能力 resolve 的属性名。
const RESOLVE_PROP: &str = "__oxide_ads_resolve__";
/// 续链闭包上保存能力 reject 的属性名。
const REJECT_PROP: &str = "__oxide_ads_reject__";
/// 续链闭包上保存下一 entry 下标的属性名(逆序遍历 0→len)。
const INDEX_PROP: &str = "__oxide_ads_index__";
/// 续链闭包上保存已合并错误值的属性名(无错时为 undefined)。
const COMPLETION_PROP: &str = "__oxide_ads_completion__";
/// 续链闭包上保存是否已有错误值的属性名(区分"无错"与"错误值为 undefined")。
const HAS_COMPLETION_PROP: &str = "__oxide_ads_has_completion__";
/// 续链闭包上保存 needsAwait 标志的属性名(末尾补 Await / sync 插队判断)。
const NEEDS_AWAIT_PROP: &str = "__oxide_ads_needs_await__";
/// 续链闭包上保存 hasAwaited 标志的属性名(真实 async 方法是否已 await)。
const HAS_AWAITED_PROP: &str = "__oxide_ads_has_awaited__";
/// 续链闭包上区分 reject 角色的属性名(reject 处理器先合并入参为错误)。
const REJECT_ROLE_PROP: &str = "__oxide_ads_reject_role__";
/// `AsyncDisposableStack.prototype.disposeAsync()`:逆序释放全部资源并返回能力。
///
/// # 步骤
/// 1. 新建 promise 能力;this 非 AsyncDisposableStack(type_tag=25)或无状态盒 →
/// reject 该能力(**reject 而非抛**)并返回。
/// 2. 已 Disposed → 同步 fulfill undefined(幂等,当前轮即入队反应)。
/// 3. 同步置 state=Disposed(防重入;执行中的二次调用命中步骤 2)。
/// 4. 建续链闭包并同步执行循环至首次 await 或完成:完成时 fulfill/reject 能力
/// 同步结算(空栈/纯 sync 满足微任务序断言);首次 await 注册续链后返回。
pub(crate) fn dispose_async(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let (promise, resolve, reject) = vm.new_promise_capability();
let cap = match require_dispose_capability(vm, this_val, JsObject::OBJ_TYPE_ASYNC_DISPOSABLE_STACK) {
Ok(c) => c,
Err(err) => {
let _ = vm.call_function_sync(reject, JsValue::undefined(), &[err]);
return NativeResult::Ok(promise);
}
};
// SAFETY: cap 由 require 校验,native 执行期间对象存活。
let state = unsafe { (*cap).state };
if state == DisposeState::Disposed {
let _ = vm.call_function_sync(resolve, JsValue::undefined(), &[JsValue::undefined()]);
return NativeResult::Ok(promise);
}
// 先置位再执行:执行中/后续调用 disposeAsync 均命中 Disposed 分支。
unsafe { (*cap).state = DisposeState::Disposed };
let entries_len = unsafe { (*cap).entries.len() };
let continue_fn = make_dispose_continue_fn(vm, this_val, resolve, reject, false, None);
// 逆序游标初始化为条目数:INDEX 槽表示下一待处理位置(len → 0 递减)。
let continue_obj = unsafe { &mut *continue_fn.as_js_object_ptr() };
write_prop(vm, continue_obj, INDEX_PROP, JsValue::int(entries_len as i32));
run_dispose_loop(vm, &continue_fn, JsValue::undefined(), false);
NativeResult::Ok(promise)
}
/// 构造 disposeAsync 续链闭包:native 函数对象,状态存属性槽。
///
/// # 步骤
/// 1. 以 `dispose_async_continue` 建原生函数对象(length=1)。
/// 2. 写栈对象/能力闭包/角色槽;`state_from` 给出当前循环状态(游标/错误合并/
/// await 标志)时逐槽复制,保证续链从上次让出点继续。
fn make_dispose_continue_fn(
vm: &mut Vm, stack: JsValue, resolve: JsValue, reject: JsValue, reject_role: bool, state_from: Option<&JsObject>,
) -> JsValue {
let fn_proto = vm.session.builtin_world().fn_proto_val();
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto);
func.set_function(true);
// SAFETY: dispose_async_continue 是 NativeFn 函数项。
func.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(dispose_async_continue as *const ()) }));
func.set_native_arg_count(1);
let ptr = vm.alloc_object(func);
let obj = unsafe { &mut *ptr };
let stack_si = vm.kernel_core.perm_interner().intern(STACK_PROP).0;
vm.set_or_create_prop_value(obj, stack_si, stack);
let resolve_si = vm.kernel_core.perm_interner().intern(RESOLVE_PROP).0;
vm.set_or_create_prop_value(obj, resolve_si, resolve);
let reject_si = vm.kernel_core.perm_interner().intern(REJECT_PROP).0;
vm.set_or_create_prop_value(obj, reject_si, reject);
if let Some(src) = state_from {
let index_si = vm.kernel_core.perm_interner().intern(INDEX_PROP).0;
if let Some(v) = vm.resolve_property(src, index_si) {
vm.set_or_create_prop_value(obj, index_si, v);
}
let completion_si = vm.kernel_core.perm_interner().intern(COMPLETION_PROP).0;
if let Some(v) = vm.resolve_property(src, completion_si) {
vm.set_or_create_prop_value(obj, completion_si, v);
}
let has_completion_si = vm.kernel_core.perm_interner().intern(HAS_COMPLETION_PROP).0;
if let Some(v) = vm.resolve_property(src, has_completion_si) {
vm.set_or_create_prop_value(obj, has_completion_si, v);
}
let needs_await_si = vm.kernel_core.perm_interner().intern(NEEDS_AWAIT_PROP).0;
if let Some(v) = vm.resolve_property(src, needs_await_si) {
vm.set_or_create_prop_value(obj, needs_await_si, v);
}
let has_awaited_si = vm.kernel_core.perm_interner().intern(HAS_AWAITED_PROP).0;
if let Some(v) = vm.resolve_property(src, has_awaited_si) {
vm.set_or_create_prop_value(obj, has_awaited_si, v);
}
} else {
let index_si = vm.kernel_core.perm_interner().intern(INDEX_PROP).0;
vm.set_or_create_prop_value(obj, index_si, JsValue::int(0));
let completion_si = vm.kernel_core.perm_interner().intern(COMPLETION_PROP).0;
vm.set_or_create_prop_value(obj, completion_si, JsValue::undefined());
let has_completion_si = vm.kernel_core.perm_interner().intern(HAS_COMPLETION_PROP).0;
vm.set_or_create_prop_value(obj, has_completion_si, JsValue::bool(false));
let needs_await_si = vm.kernel_core.perm_interner().intern(NEEDS_AWAIT_PROP).0;
vm.set_or_create_prop_value(obj, needs_await_si, JsValue::bool(false));
let has_awaited_si = vm.kernel_core.perm_interner().intern(HAS_AWAITED_PROP).0;
vm.set_or_create_prop_value(obj, has_awaited_si, JsValue::bool(false));
}
let role_si = vm.kernel_core.perm_interner().intern(REJECT_ROLE_PROP).0;
vm.set_or_create_prop_value(obj, role_si, JsValue::bool(reject_role));
vm.add_fn_name_length(obj, "", 1);
JsValue::from_js_object(ptr)
}
/// 续链闭包回调:读角色槽区分 fulfill/reject,把 await 结果交给循环主体。
fn dispose_async_continue(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,
"disposeAsync continuation handler is invalid",
));
}
let callee_obj = unsafe { &*callee.as_js_object_ptr() };
let role_si = vm.kernel_core.perm_interner().intern(REJECT_ROLE_PROP).0;
let reject_role = vm
.resolve_property(callee_obj, role_si)
.map(|v| v.is_bool() && v.as_bool())
.unwrap_or(false);
let arg = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
run_dispose_loop(vm, &callee, arg, reject_role);
NativeResult::Ok(JsValue::undefined())
}
/// disposeAsync 循环主体:逆序遍历 entries,按 2026 DisposeResources 语义释放。
///
/// # 步骤
/// 1. 读全部状态槽;reject 角色先把 await 拒绝原因合并进完成记录。
/// 2. 逐条逆序处理:
/// - 游标越界:needsAwait 且未 await 过 → 末尾补一次 Await(undefined);
/// 否则清空 entries 并结算能力(有错 reject / 无错 fulfill)。
/// - sync 条目遇待决 needsAwait → 先插队 Await(undefined) 再执行。
/// - method 为 undefined(async-null 条目)→ 置 needsAwait,继续。
/// - 调用释放回调:sync 抛错 → 合并后继续;async 条目调用成功 → 记
/// hasAwaited 并 await(wrap_sync 丢弃返回值 await undefined);wrap_sync
/// 同步抛错 → 建 rejected promise 异步化后 await;真实 async 方法同步抛错
/// → 直接合并(规范不异步化)。
/// 3. 每次 await 前更新状态槽,注册同一 native 的 fulfill/reject 双闭包续链。
fn run_dispose_loop(vm: &mut Vm, callee: &JsValue, arg: JsValue, reject_role: bool) {
if !callee.is_object() {
return;
}
// SAFETY: callee 是当前 native 调用的函数对象,native 执行期间存活。
let callee_obj = unsafe { &mut *callee.as_js_object_ptr() };
let stack = read_prop(vm, callee_obj, STACK_PROP);
let resolve = read_prop(vm, callee_obj, RESOLVE_PROP);
let reject = read_prop(vm, callee_obj, REJECT_PROP);
if !stack.is_object() || resolve.is_undefined() || reject.is_undefined() {
return;
}
let mut index = read_int(vm, callee_obj, INDEX_PROP);
let mut completion = read_prop(vm, callee_obj, COMPLETION_PROP);
let mut has_completion = read_bool(vm, callee_obj, HAS_COMPLETION_PROP);
let mut needs_await = read_bool(vm, callee_obj, NEEDS_AWAIT_PROP);
let mut has_awaited = read_bool(vm, callee_obj, HAS_AWAITED_PROP);
if reject_role {
let merged = merge_dispose_error(vm, if has_completion { Some(completion) } else { None }, arg);
(completion, has_completion) = match merged {
Some(err) => (err, true),
None => (JsValue::undefined(), false),
};
write_prop(vm, callee_obj, HAS_COMPLETION_PROP, JsValue::bool(has_completion));
write_prop(vm, callee_obj, COMPLETION_PROP, completion);
}
loop {
let stack_ptr = stack.as_js_object_ptr();
if stack_ptr.is_null() {
return;
}
// SAFETY: stack 对象由闭包槽保活,native 执行期间 epoch 不重置。
let stack_obj = unsafe { &*stack_ptr };
if !stack_obj.is_async_disposable_stack_obj() {
return;
}
let cap = oxide_builtins::disposable_stack::get_capability_ptr(stack_obj);
if cap.is_null() {
return;
}
// SAFETY: cap 是构造时写入的有效 Box 指针,状态盒随栈对象存活。
let cap_ref = unsafe { &mut *cap };
if index == 0 {
if needs_await && !has_awaited {
needs_await = false;
write_prop(vm, callee_obj, NEEDS_AWAIT_PROP, JsValue::bool(needs_await));
try_await(vm, callee_obj, JsValue::undefined());
return;
}
cap_ref.entries.clear();
let _ = if has_completion {
vm.call_function_sync(reject, JsValue::undefined(), &[completion])
} else {
vm.call_function_sync(resolve, JsValue::undefined(), &[JsValue::undefined()])
};
return;
}
let entry = cap_ref.entries[index - 1];
if entry.hint == 0 && needs_await && !has_awaited {
// 插队 await:不推进游标,await 续链后重读同一位置执行该 sync 条目。
needs_await = false;
write_prop(vm, callee_obj, NEEDS_AWAIT_PROP, JsValue::bool(needs_await));
try_await(vm, callee_obj, JsValue::undefined());
return;
}
index -= 1;
write_prop(vm, callee_obj, INDEX_PROP, JsValue::int(index as i32));
if entry.method.is_undefined() {
needs_await = true;
write_prop(vm, callee_obj, NEEDS_AWAIT_PROP, JsValue::bool(needs_await));
continue;
}
match oxide_builtins::disposable_stack::call_entry_method(vm, &entry) {
Err(err) => {
if entry.hint == 1 && entry.wrap_sync {
has_awaited = true;
write_prop(vm, callee_obj, HAS_AWAITED_PROP, JsValue::bool(has_awaited));
let (rejected, _, _) = vm.new_promise_capability();
let _ = vm.reject_promise(rejected, err);
try_await(vm, callee_obj, rejected);
return;
}
let merged = merge_dispose_error(vm, if has_completion { Some(completion) } else { None }, err);
(completion, has_completion) = match merged {
Some(err_val) => (err_val, true),
None => (JsValue::undefined(), false),
};
write_prop(vm, callee_obj, HAS_COMPLETION_PROP, JsValue::bool(has_completion));
write_prop(vm, callee_obj, COMPLETION_PROP, completion);
}
Ok(value) => {
if entry.hint == 1 {
has_awaited = true;
write_prop(vm, callee_obj, HAS_AWAITED_PROP, JsValue::bool(has_awaited));
let awaited = if entry.wrap_sync { JsValue::undefined() } else { value };
try_await(vm, callee_obj, awaited);
return;
}
}
}
}
}
/// 读闭包对象属性槽的 bool 值(缺失回退 false)。
fn read_bool(vm: &Vm, obj: &JsObject, prop: &str) -> bool {
let v = read_prop(vm, obj, prop);
v.is_bool() && v.as_bool()
}
/// 读闭包对象属性槽的 int 值(缺失回退 0)。
fn read_int(vm: &Vm, obj: &JsObject, prop: &str) -> usize {
let v = read_prop(vm, obj, prop);
if v.is_int() && v.as_int() >= 0 {
v.as_int() as usize
} else {
0
}
}
/// 读闭包对象属性槽值(缺失回退 undefined)。
fn read_prop(vm: &Vm, obj: &JsObject, prop: &str) -> JsValue {
let si = vm.kernel_core.perm_interner().intern(prop).0;
vm.resolve_property(obj, si).unwrap_or(JsValue::undefined())
}
/// 写闭包对象属性槽值。
fn write_prop(vm: &mut Vm, obj: &mut JsObject, prop: &str, val: JsValue) {
let si = vm.kernel_core.perm_interner().intern(prop).0;
vm.set_or_create_prop_value(obj, si, val);
}
/// 等待 `value`:PromiseResolve 包装后注册续链;失败(constructor getter 抛错)
/// 直接拒绝能力(规范 Await abrupt → IfAbruptRejectPromise)。
///
/// # 副作用
/// - 注册的 fulfill/reject 续链闭包持全套状态槽;派生 promise 不可观察。
fn try_await(vm: &mut Vm, callee_obj: &JsObject, value: JsValue) {
let promise = match vm.promise_resolve(value) {
Ok(p) => p,
Err(exc) => {
let reject = read_prop(vm, callee_obj, REJECT_PROP);
let _ = vm.call_function_sync(reject, JsValue::undefined(), &[exc]);
return;
}
};
let stack = read_prop(vm, callee_obj, STACK_PROP);
let resolve = read_prop(vm, callee_obj, RESOLVE_PROP);
let reject = read_prop(vm, callee_obj, REJECT_PROP);
let fulfill_fn = make_dispose_continue_fn(vm, stack, resolve, reject, false, Some(callee_obj));
let reject_fn = make_dispose_continue_fn(vm, stack, resolve, reject, true, Some(callee_obj));
let _ = vm.perform_promise_then(promise, fulfill_fn, reject_fn);
}