Files
OxideJS-New/crates/oxide_builtins/src/disposable_stack.rs
T

609 lines
27 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.
//! DisposableStack(同步资源栈)与 AsyncDisposableStack(异步资源栈)内置对象实现。
//!
//! 状态盒 `DisposeCapability` 以 `Box::into_raw` 存入对象 `native_data`:`state`
//! 记录栈状态,`entries` 按入栈序存待释放资源。同步栈 dispose 只用
//! Pending/Disposed 两态;Disposing 为异步资源栈 disposeAsync 执行期标记
//! (当前状态机只置 Disposed,变体语义预留)。两栈共用本模块:方法按
//! `type_tag` 为 `JsObject::OBJ_TYPE_DISPOSABLE_STACK`(同步)或
//! `OBJ_TYPE_ASYNC_DISPOSABLE_STACK`(异步)时区分接收者,`hint` 以 0/1 两值区分
//! 条目释放语义(定义见 `DisposeEntry.hint`),`wrap_sync` 标记 async 栈 use 落回
//! `@@dispose` 的条目(返回值丢弃、异常异步化)。GC 三函数
//! (edges/size/drop)接线 mark 边、字节账目与释放,形态与 Map/Promise
//! 状态盒同构。
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject;
use oxide_types::private_key::encode_symbol_key;
use oxide_types::value::JsValue;
use oxide_runtime_api::{NativeResult, ProtoKind, VmHost};
/// 分配空状态盒并存入新资源栈对象(proto 与 type_tag 按栈类型传入)。
fn alloc_disposable_stack<H: VmHost>(vm: &mut H, proto_val: JsValue, type_tag: u8) -> *mut JsObject {
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto_val);
obj.type_tag = type_tag;
obj.set_native_data(Box::into_raw(Box::new(DisposeCapability {
state: DisposeState::Pending,
entries: Vec::new(),
})) as *mut u8);
vm.alloc_object(obj)
}
macro_rules! native_try {
($expr:expr) => {
match $expr {
Ok(value) => value,
Err(err) => return NativeResult::Err(err),
}
};
}
/// 取出栈对象 native-data 槽中的 `DisposeCapability` 可变指针。
///
/// # 边界与前提
/// - this 非对象 → TypeError;
/// - type_tag 非 24/25 → TypeError(无槽对象);
/// - native_data 为空 → TypeError(防御,正常构造路径不会出现)。
pub fn require_dispose_capability<H: VmHost>(
vm: &mut H, this_val: JsValue, type_tag: u8,
) -> Result<*mut DisposeCapability, JsValue> {
if !this_val.is_object() {
return Err(crate::error::create_type_error(vm, "called on non-DisposableStack object"));
}
let obj_ptr = this_val.as_js_object_ptr();
if obj_ptr.is_null() {
return Err(crate::error::create_type_error(vm, "DisposableStack internal state invalid"));
}
// SAFETY: obj_ptr 是 JsObject 的非空、对齐指针;对象在 native 执行期间
// 被根保持、不被回收,指针在本调用内有效。
let obj = unsafe { &*obj_ptr };
if obj.type_tag != type_tag {
return Err(crate::error::create_type_error(
vm,
"DisposableStack.prototype method called on incompatible receiver",
));
}
let cap_ptr = get_capability_ptr(obj);
if cap_ptr.is_null() {
return Err(crate::error::create_type_error(vm, "DisposableStack internal state invalid"));
}
Ok(cap_ptr)
}
fn disposed_reference_error<H: VmHost>(vm: &mut H) -> JsValue {
crate::error::create_reference_error(vm, "DisposableStack has been disposed")
}
/// 接收者原型链是否命中对应栈类型内建原型(上限 16 层,普通调用接收者不命中)。
fn receiver_intrinsic_proto(this_val: JsValue, stack_proto_val: JsValue) -> bool {
if !this_val.is_object() {
return false;
}
let this_ptr = this_val.as_js_object_ptr();
if this_ptr.is_null() {
return false;
}
let stack_proto = stack_proto_val.as_js_object_ptr();
// SAFETY: this_ptr 是 is_object 校验过的当前 session 存活对象指针。
let mut proto = unsafe { &*this_ptr }.proto();
for _ in 0..16 {
if !proto.is_object() {
return false;
}
let proto_ptr = proto.as_js_object_ptr();
if proto_ptr.is_null() {
return false;
}
if std::ptr::eq(proto_ptr, stack_proto) {
return true;
}
// SAFETY: 同上,proto 链指针本 session 存活。
proto = unsafe { &*proto_ptr }.proto();
}
false
}
/// 资源栈构造器公共实现:构造形态校验后经 GpFC 推导原型,建带空状态盒的栈对象。
///
/// # 步骤
/// 1. 非构造形态 → TypeError。构造形态标记由 NEW/SUPER/construct_with 三入口置位、
/// 普通调用入口清零;标记未置位的直接 native 调用形态以接收者原型链命中
/// 对应内建原型视同构造形态。
/// 2. GetPrototypeFromConstructor:传播式读 new.target 的 "prototype"(访问器
/// 副作用与异常原值传播);非对象结果回落本栈类型内建原型。
/// 3. 建空状态盒对象,proto 与 type_tag 按栈类型传入。
fn stack_constructor_impl<H: VmHost>(vm: &mut H, args: &[u8], proto_val: JsValue, type_tag: u8) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !vm.constructing_native() && !receiver_intrinsic_proto(this_val, proto_val) {
return NativeResult::Err(crate::error::create_type_error(vm, "DisposableStack must be called with new"));
}
let new_target = vm.reg(255);
let gpf_proto = if new_target.is_object() {
let nt_ptr = new_target.as_js_object_ptr();
// SAFETY: is_object 保证指针非空且对象本 session 存活。
let nt_obj = unsafe { &*nt_ptr };
let proto_si = vm.perm_intern("prototype");
let proto = match vm.ordinary_get(nt_obj, proto_si, new_target) {
Ok(v) => v,
Err(err) => return NativeResult::Err(crate::iterator::engine_error(vm, &err)),
};
if proto.is_object() {
proto
} else {
proto_val
}
} else {
proto_val
};
let stack = alloc_disposable_stack(vm, gpf_proto, type_tag);
NativeResult::Ok(JsValue::from_js_object(stack))
}
/// `DisposableStack` 构造函数:创建带空状态盒的 DisposableStack 对象。
pub fn disposable_stack_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let proto_val = JsValue::from_js_object(vm.builtin_proto(ProtoKind::DisposableStackProto));
stack_constructor_impl(vm, args, proto_val, JsObject::OBJ_TYPE_DISPOSABLE_STACK)
}
/// `AsyncDisposableStack` 构造函数:创建带空状态盒的 AsyncDisposableStack 对象。
pub fn async_disposable_stack_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let proto_val = JsValue::from_js_object(vm.builtin_proto(ProtoKind::AsyncDisposableStackProto));
stack_constructor_impl(vm, args, proto_val, JsObject::OBJ_TYPE_ASYNC_DISPOSABLE_STACK)
}
/// `DisposableStack.prototype.use(value)`:把资源的 `value[@@dispose]` 方法入栈。
///
/// # 步骤
/// 1. 校验栈对象与状态;Disposed 抛 ReferenceError。
/// 2. value 为 null/undefined → 直接返回 value 不入栈。
/// 3. value 非对象 → TypeError。
/// 4. 读 `value[@@dispose]`(只读一次,getter 抛错透传原值);缺/null/undefined/
/// 非 callable → TypeError。
/// 5. 入栈 `{value, method, sync, receiver 模式}`,返回 value。
pub fn disposable_stack_use<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let cap = native_try!(require_dispose_capability(vm, this_val, JsObject::OBJ_TYPE_DISPOSABLE_STACK));
// SAFETY: cap 由 require 校验,native 执行期间对象存活。
let cap_ref = unsafe { &mut *cap };
if cap_ref.state != DisposeState::Pending {
return NativeResult::Err(disposed_reference_error(vm));
}
let value = vm.reg(if args.len() > 1 { args[1] } else { 0 });
if value.is_null() || value.is_undefined() {
return NativeResult::Ok(value);
}
if !value.is_object() {
return NativeResult::Err(crate::error::create_type_error(vm, "value is not an Object"));
}
let value_obj = unsafe { &*value.as_js_object_ptr() };
let dispose_key = oxide_types::private_key::encode_symbol_key(vm.realm_id(), 12);
let method = match vm.ordinary_get(value_obj, dispose_key, value) {
Ok(m) => m,
Err(err) => return NativeResult::Err(crate::iterator::engine_error(vm, &err)),
};
if method.is_null() || method.is_undefined() || !crate::iterator::is_callable(method) {
return NativeResult::Err(crate::error::create_type_error(vm, "value[Symbol.dispose] is not callable"));
}
cap_ref.entries.push(DisposeEntry {
value,
method,
hint: 0,
arg_style: false,
wrap_sync: false,
});
NativeResult::Ok(value)
}
/// `DisposableStack.prototype.adopt(value, onDispose)`:把 value 与其释放回调入栈。
///
/// # 步骤
/// 1. 校验栈对象与状态;Disposed 抛 ReferenceError。
/// 2. onDispose 非 callable → TypeError。
/// 3. 入栈 `{value, onDispose, sync, arg-style}`(dispose 时 `Call(onDispose, undefined, «value»)`),
/// 返回 value。
pub fn disposable_stack_adopt<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let cap = native_try!(require_dispose_capability(vm, this_val, JsObject::OBJ_TYPE_DISPOSABLE_STACK));
// SAFETY: cap 由 require 校验,native 执行期间对象存活。
let cap_ref = unsafe { &mut *cap };
if cap_ref.state != DisposeState::Pending {
return NativeResult::Err(disposed_reference_error(vm));
}
let value = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let on_dispose = vm.reg(if args.len() > 2 { args[2] } else { 0 });
if !crate::iterator::is_callable(on_dispose) {
return NativeResult::Err(crate::error::create_type_error(vm, "onDispose is not callable"));
}
cap_ref.entries.push(DisposeEntry {
value,
method: on_dispose,
hint: 0,
arg_style: true,
wrap_sync: false,
});
NativeResult::Ok(value)
}
/// `AsyncDisposableStack.prototype.use(value)`:把资源的 `value[@@asyncDispose]`
/// 方法入栈(缺失时回落 `@@dispose`)。
///
/// # 步骤
/// 1. 校验栈对象(type_tag=25)与状态;Disposed 抛 ReferenceError。
/// 2. value 为 null/undefined → 入栈 method=undefined 的 async 条目并返回原值
/// (与同步栈相反:须记录资源求值,保证 disposeAsync 仍执行 Await)。
/// 3. value 非对象 → TypeError。
/// 4. 读 `value[@@asyncDispose]`(键 11,getter 抛错透传原值);结果缺失
/// (null/undefined)→ 回落读 `value[@@dispose]`(键 12);两者均缺 →
/// TypeError;任一读取结果非 callable → TypeError(不回落)。
/// 5. 键 11 得 callable → 入栈 `{value, method, async, receiver 模式, wrap_sync=false}`;
/// 键 12 得 callable → 入栈 `{value, method, async, receiver 模式, wrap_sync=true}`
/// (同步方法返回值丢弃、异常异步化)。返回 value。
pub fn async_disposable_stack_use<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let cap = native_try!(require_dispose_capability(vm, this_val, JsObject::OBJ_TYPE_ASYNC_DISPOSABLE_STACK));
// SAFETY: cap 由 require 校验,native 执行期间对象存活。
let cap_ref = unsafe { &mut *cap };
if cap_ref.state != DisposeState::Pending {
return NativeResult::Err(disposed_reference_error(vm));
}
let value = vm.reg(if args.len() > 1 { args[1] } else { 0 });
if value.is_null() || value.is_undefined() {
cap_ref.entries.push(DisposeEntry {
value,
method: JsValue::undefined(),
hint: 1,
arg_style: false,
wrap_sync: false,
});
return NativeResult::Ok(value);
}
if !value.is_object() {
return NativeResult::Err(crate::error::create_type_error(vm, "value is not an Object"));
}
let value_obj = unsafe { &*value.as_js_object_ptr() };
let async_dispose_key = encode_symbol_key(vm.realm_id(), 11);
let method = match vm.ordinary_get(value_obj, async_dispose_key, value) {
Ok(m) => m,
Err(err) => return NativeResult::Err(crate::iterator::engine_error(vm, &err)),
};
if method.is_null() || method.is_undefined() {
// 键 11 缺失:回落读 @@dispose(读取顺序测试断言键 11 先、键 12 后)。
let dispose_key = encode_symbol_key(vm.realm_id(), 12);
let sync_method = match vm.ordinary_get(value_obj, dispose_key, value) {
Ok(m) => m,
Err(err) => return NativeResult::Err(crate::iterator::engine_error(vm, &err)),
};
if sync_method.is_null() || sync_method.is_undefined() {
return NativeResult::Err(crate::error::create_type_error(
vm,
"value has no callable Symbol.asyncDispose or Symbol.dispose method",
));
}
if !crate::iterator::is_callable(sync_method) {
return NativeResult::Err(crate::error::create_type_error(vm, "value[Symbol.dispose] is not callable"));
}
cap_ref.entries.push(DisposeEntry {
value,
method: sync_method,
hint: 1,
arg_style: false,
wrap_sync: true,
});
return NativeResult::Ok(value);
}
if !crate::iterator::is_callable(method) {
return NativeResult::Err(crate::error::create_type_error(vm, "value[Symbol.asyncDispose] is not callable"));
}
cap_ref.entries.push(DisposeEntry {
value,
method,
hint: 1,
arg_style: false,
wrap_sync: false,
});
NativeResult::Ok(value)
}
/// `AsyncDisposableStack.prototype.adopt(value, onDisposeAsync)`:把 value 与其
/// 异步释放回调入栈。
///
/// # 步骤
/// 1. 校验栈对象(type_tag=25)与状态;Disposed 抛 ReferenceError。
/// 2. onDisposeAsync 非 callable → TypeError。
/// 3. 入栈 `{value, onDisposeAsync, async, arg-style}`(dispose 时
/// `Call(onDisposeAsync, undefined, «value»)`),返回 value。
pub fn async_disposable_stack_adopt<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let cap = native_try!(require_dispose_capability(vm, this_val, JsObject::OBJ_TYPE_ASYNC_DISPOSABLE_STACK));
// SAFETY: cap 由 require 校验,native 执行期间对象存活。
let cap_ref = unsafe { &mut *cap };
if cap_ref.state != DisposeState::Pending {
return NativeResult::Err(disposed_reference_error(vm));
}
let value = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let on_dispose = vm.reg(if args.len() > 2 { args[2] } else { 0 });
if !crate::iterator::is_callable(on_dispose) {
return NativeResult::Err(crate::error::create_type_error(vm, "onDisposeAsync is not callable"));
}
cap_ref.entries.push(DisposeEntry {
value,
method: on_dispose,
hint: 1,
arg_style: true,
wrap_sync: false,
});
NativeResult::Ok(value)
}
/// `AsyncDisposableStack.prototype.defer(onDisposeAsync)`:把无参数异步释放回调入栈。
///
/// # 步骤
/// 1. 校验栈对象(type_tag=25)与状态;Disposed 抛 ReferenceError。
/// 2. onDisposeAsync 非 callable → TypeError。
/// 3. 入栈 `{undefined, onDisposeAsync, async, receiver 模式}`(dispose 时
/// `Call(onDisposeAsync, undefined, «»)`),返回 undefined。
pub fn async_disposable_stack_defer<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let cap = native_try!(require_dispose_capability(vm, this_val, JsObject::OBJ_TYPE_ASYNC_DISPOSABLE_STACK));
// SAFETY: cap 由 require 校验,native 执行期间对象存活。
let cap_ref = unsafe { &mut *cap };
if cap_ref.state != DisposeState::Pending {
return NativeResult::Err(disposed_reference_error(vm));
}
let on_dispose = vm.reg(if args.len() > 1 { args[1] } else { 0 });
if !crate::iterator::is_callable(on_dispose) {
return NativeResult::Err(crate::error::create_type_error(vm, "onDisposeAsync is not callable"));
}
cap_ref.entries.push(DisposeEntry {
value: JsValue::undefined(),
method: on_dispose,
hint: 1,
arg_style: false,
wrap_sync: false,
});
NativeResult::Ok(JsValue::undefined())
}
/// `DisposableStack.prototype.defer(onDispose)`:把无参数释放回调入栈。
///
/// # 步骤
/// 1. 校验栈对象与状态;Disposed 抛 ReferenceError。
/// 2. onDispose 非 callable → TypeError。
/// 3. 入栈 `{undefined, onDispose, sync, receiver 模式}`(dispose 时 `Call(onDispose, undefined, «»)`),
/// 返回 undefined。
pub fn disposable_stack_defer<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let cap = native_try!(require_dispose_capability(vm, this_val, JsObject::OBJ_TYPE_DISPOSABLE_STACK));
// SAFETY: cap 由 require 校验,native 执行期间对象存活。
let cap_ref = unsafe { &mut *cap };
if cap_ref.state != DisposeState::Pending {
return NativeResult::Err(disposed_reference_error(vm));
}
let on_dispose = vm.reg(if args.len() > 1 { args[1] } else { 0 });
if !crate::iterator::is_callable(on_dispose) {
return NativeResult::Err(crate::error::create_type_error(vm, "onDispose is not callable"));
}
cap_ref.entries.push(DisposeEntry {
value: JsValue::undefined(),
method: on_dispose,
hint: 0,
arg_style: false,
wrap_sync: false,
});
NativeResult::Ok(JsValue::undefined())
}
/// `DisposableStack.prototype.dispose()`:逆序执行全部释放回调并置栈为 Disposed。
///
/// # 步骤
/// 1. 校验栈对象;state 非 Pending → 直接返回 undefined(幂等,Disposing/Disposed 同返)。
/// 2. 先置 state=Disposed 再执行(防执行中 use/adopt 重入入栈)。
/// 3. 逆序逐条调用:arg-style 走 `Call(method, undefined, «value»)`,否则
/// `Call(method, value, «»)`;method 为 undefined 时跳过(防御)。
/// 4. 单错原样抛;多错用 SuppressedError 链式合并(error=后抛、suppressed=前值),
/// 循环不因错误中断,剩余条目继续执行。
///
/// # 边界与前提
/// - 回调抛原始值(含 primitive)经 take_uncaught_value 恢复并存入 SuppressedError;
/// - 无 uncaught 时回退按错误文本建普通 Error。
pub fn disposable_stack_dispose<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let cap = native_try!(require_dispose_capability(vm, this_val, JsObject::OBJ_TYPE_DISPOSABLE_STACK));
// SAFETY: cap 由 require 校验,native 执行期间对象存活。
let cap_ref = unsafe { &mut *cap };
if cap_ref.state != DisposeState::Pending {
return NativeResult::Ok(JsValue::undefined());
}
// 先置位再执行:执行中调用 use/adopt/defer 一律命中 Disposed 分支。
cap_ref.state = DisposeState::Disposed;
let mut completion: Option<JsValue> = None;
for entry in cap_ref.entries.iter().rev() {
if entry.method.is_undefined() {
continue;
}
if let Err(err_val) = call_entry_method(vm, entry) {
completion = merge_dispose_error(vm, completion, err_val);
}
}
// 条目逐条执行完毕,清空释放引用(规范 DisposeResources 逐条移除)。
cap_ref.entries.clear();
match completion {
Some(err_val) => NativeResult::Err(err_val),
None => NativeResult::Ok(JsValue::undefined()),
}
}
/// move 公共实现:把全部 entries 转移到新资源栈。
///
/// # 步骤
/// 1. 校验栈对象;state 非 Pending → ReferenceError。
/// 2. `mem::take` 转移 entries;建新栈(proto 固定为栈类型原型,非子类原型)
/// 并装入转移的 entries。
/// 3. 源栈置 Disposed(不再执行任何释放)。
fn stack_move_impl<H: VmHost>(vm: &mut H, this_val: JsValue, proto_val: JsValue, type_tag: u8) -> NativeResult {
let cap = native_try!(require_dispose_capability(vm, this_val, type_tag));
// SAFETY: cap 由 require 校验,native 执行期间对象存活。
let cap_ref = unsafe { &mut *cap };
if cap_ref.state != DisposeState::Pending {
return NativeResult::Err(disposed_reference_error(vm));
}
let taken = std::mem::take(&mut cap_ref.entries);
let new_stack = alloc_disposable_stack(vm, proto_val, type_tag);
// SAFETY: alloc_disposable_stack 刚分配的对象,native_data 为新建空状态盒。
unsafe {
let new_obj = &mut *new_stack;
let new_cap = new_obj.native_data() as *mut DisposeCapability;
(*new_cap).entries = taken;
}
cap_ref.state = DisposeState::Disposed;
NativeResult::Ok(JsValue::from_js_object(new_stack))
}
/// `DisposableStack.prototype.move()`:把全部 entries 转移到新 DisposableStack。
pub fn disposable_stack_move<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let proto_val = JsValue::from_js_object(vm.builtin_proto(ProtoKind::DisposableStackProto));
stack_move_impl(vm, this_val, proto_val, JsObject::OBJ_TYPE_DISPOSABLE_STACK)
}
/// `AsyncDisposableStack.prototype.move()`:把全部 entries 转移到新
/// AsyncDisposableStack(proto 固定为 AsyncDisposableStack.prototype,非子类)。
pub fn async_disposable_stack_move<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let proto_val = JsValue::from_js_object(vm.builtin_proto(ProtoKind::AsyncDisposableStackProto));
stack_move_impl(vm, this_val, proto_val, JsObject::OBJ_TYPE_ASYNC_DISPOSABLE_STACK)
}
/// `get DisposableStack.prototype.disposed`:栈是否已 dispose/move。
pub fn disposable_stack_disposed_getter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let cap = native_try!(require_dispose_capability(vm, this_val, JsObject::OBJ_TYPE_DISPOSABLE_STACK));
// SAFETY: cap 由 require 校验,native 执行期间对象存活。
let state = unsafe { (*cap).state };
NativeResult::Ok(JsValue::bool(state != DisposeState::Pending))
}
/// `get AsyncDisposableStack.prototype.disposed`:栈是否已 disposeAsync/move。
pub fn async_disposable_stack_disposed_getter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let cap = native_try!(require_dispose_capability(vm, this_val, JsObject::OBJ_TYPE_ASYNC_DISPOSABLE_STACK));
// SAFETY: cap 由 require 校验,native 执行期间对象存活。
let state = unsafe { (*cap).state };
NativeResult::Ok(JsValue::bool(state != DisposeState::Pending))
}
/// 按条目调用约定执行释放回调,失败时恢复本次回调抛出的原始异常值。
///
/// # 边界与前提
/// - `arg_style` 走 `Call(method, undefined, «value»)`,否则 `Call(method, value, «»)`;
/// - 无 uncaught 时回退按错误文本建普通 Error。
pub fn call_entry_method<H: VmHost>(vm: &mut H, entry: &DisposeEntry) -> Result<JsValue, JsValue> {
let call_result = if entry.arg_style {
vm.call_function_sync(entry.method, JsValue::undefined(), &[entry.value])
} else {
vm.call_function_sync(entry.method, entry.value, &[])
};
call_result.map_err(|err| {
vm.take_uncaught_value()
.unwrap_or_else(|| crate::error::create_kind_error(vm, "Error", &err))
})
}
/// 把本次释放错误合并进完成记录:空 → 原样;已有错误 → SuppressedError 链
/// (error=后抛、suppressed=前值,逆序处理中后处理者=更早入栈者)。
pub fn merge_dispose_error<H: VmHost>(vm: &mut H, completion: Option<JsValue>, err: JsValue) -> Option<JsValue> {
Some(match completion {
None => err,
Some(prev) => crate::error::create_suppressed_error(vm, err, prev),
})
}
/// 栈生命周期状态:Pending 可入栈,Disposed 后所有操作抛 ReferenceError;
/// Disposing 为异步资源栈 disposeAsync 执行期标记(当前状态机只置 Disposed,
/// 变体语义预留)。
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DisposeState {
Pending,
#[allow(dead_code)]
Disposing,
Disposed,
}
/// 单条待释放资源:dispose 时按 `arg_style` 选择调用约定,按 `hint`/`wrap_sync`
/// 决定异步语义(disposeAsync 状态机消费)。
#[derive(Clone, Copy)]
pub struct DisposeEntry {
/// use() 的资源值(dispose 时作 receiver);adopt() 的资源值(作参数);defer() 恒 undefined。
pub value: JsValue,
/// 释放方法:use() 为 @@asyncDispose/@@dispose 函数;adopt()/defer() 为用户回调。
pub method: JsValue,
/// 0=sync-dispose,1=async-dispose(异步资源栈用)。
pub hint: u8,
/// true=adopt 闭包语义(`Call(method, undefined, «value»)`);
/// false=use/defer(`Call(method, value, «»)`)。
pub arg_style: bool,
/// true=方法取自 @@dispose(async 栈 use 回落):返回值丢弃、异常异步化。
pub wrap_sync: bool,
}
/// 状态盒:栈状态 + 按入栈序的资源条目。
pub struct DisposeCapability {
pub state: DisposeState,
pub entries: Vec<DisposeEntry>,
}
/// 取对象 `native_data` 中存放的状态盒指针。
///
/// # 注意事项
/// - 仅在对象存活期内调用(GC 边收集与绑定层);指针由分配入口
/// `alloc_disposable_stack` 保证有效。
pub fn get_capability_ptr(obj: &JsObject) -> *mut DisposeCapability {
obj.native_data() as *mut DisposeCapability
}
/// 收集栈内全部 value/method 边(GC mark 边):对象/字符串/BigInt 均产出,
/// 消费侧按值类型分发到对象栈与存活集。
pub fn dispose_edges(obj: &JsObject) -> Vec<JsValue> {
if !obj.is_disposable_stack_obj() && !obj.is_async_disposable_stack_obj() {
return Vec::new();
}
let ptr = get_capability_ptr(obj);
if ptr.is_null() {
return Vec::new();
}
// SAFETY: native_data 持有 `alloc_capability` 写入的有效 Box 指针,本函数
// 只在对象存活期间被 GC/绑定层调用。
unsafe { (*ptr).entries.iter().flat_map(|entry| [entry.value, entry.method]).collect() }
}
/// 只读核算 DisposableStack 状态盒字节(不释放)。
pub fn disposable_stack_native_size(obj: &JsObject) -> u64 {
if !obj.is_disposable_stack_obj() && !obj.is_async_disposable_stack_obj() {
return 0;
}
let ptr = get_capability_ptr(obj);
if ptr.is_null() {
return 0;
}
unsafe {
(std::mem::size_of::<DisposeCapability>() + (*ptr).entries.capacity() * std::mem::size_of::<DisposeEntry>())
as u64
}
}
/// 释放状态盒(对象被回收时),返回释放的字节数供泄漏统计。
pub fn drop_dispose_native(obj: &mut JsObject) -> u64 {
let bytes = disposable_stack_native_size(obj);
if bytes == 0 {
return 0;
}
let ptr = get_capability_ptr(obj);
// SAFETY: ptr 非空(disposable_stack_native_size 已验证),Box::from_raw 恰好释放一次。
unsafe {
let cap = Box::from_raw(ptr);
drop(cap);
obj.set_native_data(std::ptr::null_mut());
}
bytes
}