diff --git a/crates/oxide_builtins/src/array_buffer.rs b/crates/oxide_builtins/src/array_buffer.rs index 3e697b89..4f76e060 100644 --- a/crates/oxide_builtins/src/array_buffer.rs +++ b/crates/oxide_builtins/src/array_buffer.rs @@ -12,8 +12,8 @@ pub(crate) const MAX_ARRAY_BUFFER_LENGTH: usize = 1 << 30; /// ArrayBuffer 载荷:字节缓冲与状态位。`data` 为 `None` 即缓冲区已 detach; /// `max_byte_length` 为存储态上限:0 即定长缓冲(resizable 判据),非 0 存 /// 请求上限 + 1(上限 0 与定长必须可分);`immutable` 为字节缓冲写守卫标志。 -/// 载荷经 `Box::into_raw` 存于对象 `native_fn` 槽,GC 三自由函数 -/// (clone/size/drop)对整结构体操作,克隆须连标志位一并拷贝。 +/// 载荷经 `Box::into_raw` 存于对象 `native_fn` 槽,GC 两自由函数 +/// (size/drop)对整结构体操作。 #[derive(Clone)] pub(crate) struct ArrayBufferPayload { pub(crate) data: Option>, @@ -115,20 +115,6 @@ pub(crate) fn array_buffer_payload_ptr(obj: &JsObject) -> Option<*mut ArrayBuffe obj.native_fn().map(|ptr| ptr.as_ptr() as *mut ArrayBufferPayload) } -/// 克隆 ArrayBuffer 载荷(字节缓冲 + 状态位)到新对象(跨 epoch 克隆流程用)。 -pub fn clone_array_buffer_native(old_obj: &JsObject, new_obj: &mut JsObject) { - let Some(ptr) = array_buffer_payload_ptr(old_obj) else { - return; - }; - if ptr.is_null() { - return; - } - // SAFETY: ptr 非空且指向存活载荷盒(克隆/晋升臂由 GC 持有源对象)。 - let cloned = unsafe { &*ptr }.clone(); - let cloned_ptr = Box::into_raw(Box::new(cloned)); - new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned_ptr as *const ()) })); -} - /// 只读核算 ArrayBuffer 载荷字节(不释放):结构体尺寸 + 附着缓冲容量, /// detached 载荷仅计结构体尺寸。 pub fn array_buffer_native_size(obj: &JsObject) -> u64 { @@ -259,20 +245,6 @@ pub fn drop_shared_array_buffer_native(obj: &mut JsObject) -> u64 { bytes } -/// 克隆 SharedArrayBuffer 载荷(字节缓冲)到新对象(跨 epoch 克隆流程用)。 -pub fn clone_shared_array_buffer_native(old_obj: &JsObject, new_obj: &mut JsObject) { - let Some(ptr) = shared_array_buffer_payload_ptr(old_obj) else { - return; - }; - if ptr.is_null() { - return; - } - // SAFETY: ptr 非空且指向存活载荷盒(克隆/晋升臂由 GC 持有源对象)。 - let cloned = unsafe { &*ptr }.clone(); - let cloned_ptr = Box::into_raw(Box::new(cloned)); - new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned_ptr as *const ()) })); -} - /// `SharedArrayBuffer(length [, options])`:构造语义仅 `new`;length 缺省 0, /// 经 ToIndex 传播式(强转副作用原值上抛);`maxByteLength` 选项经传播式 /// Get + ToIndex,`resizable` 键恒忽略(SAB 面不读);分配期上界校验在原型 @@ -1187,7 +1159,7 @@ mod tests { use super::*; use oxide_vm::vm::Vm; - /// 手工构造带载荷盒的 ArrayBuffer 对象(不经 Vm,只验 GC 三自由函数)。 + /// 手工构造带载荷盒的 ArrayBuffer 对象(不经 Vm,只验 GC 两自由函数)。 fn ab_object_with_payload(payload: ArrayBufferPayload) -> JsObject { let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()); obj.type_tag = JsObject::OBJ_TYPE_ARRAY_BUFFER; @@ -1197,47 +1169,6 @@ mod tests { obj } - /// 克隆目标对象与真实 GC 克隆体同形:AB 类型标签 + 空 native_fn 槽。 - fn ab_clone_target() -> JsObject { - let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()); - obj.type_tag = JsObject::OBJ_TYPE_ARRAY_BUFFER; - obj - } - - #[test] - fn payload_clone_propagates_detached() { - let old = ab_object_with_payload(ArrayBufferPayload { - data: None, - max_byte_length: 0, - immutable: false, - }); - let mut new_obj = ab_clone_target(); - clone_array_buffer_native(&old, &mut new_obj); - let ptr = array_buffer_payload_ptr(&new_obj).expect("克隆体应携带载荷盒"); - // SAFETY: ptr 由 clone_array_buffer_native 新建的存活盒。 - let cloned = unsafe { &*ptr }; - assert!(cloned.data.is_none()); - unsafe { drop(Box::from_raw(ptr)) }; - } - - #[test] - fn payload_clone_copies_flags() { - let old = ab_object_with_payload(ArrayBufferPayload { - data: Some(vec![1, 2, 3]), - max_byte_length: 42, - immutable: true, - }); - let mut new_obj = ab_clone_target(); - clone_array_buffer_native(&old, &mut new_obj); - let ptr = array_buffer_payload_ptr(&new_obj).expect("克隆体应携带载荷盒"); - // SAFETY: ptr 由 clone_array_buffer_native 新建的存活盒。 - let cloned = unsafe { &*ptr }; - assert_eq!(cloned.max_byte_length, 42); - assert!(cloned.immutable); - assert_eq!(cloned.data.as_ref().expect("附着缓冲应随克隆").as_slice(), &[1, 2, 3]); - unsafe { drop(Box::from_raw(ptr)) }; - } - #[test] fn detached_size_and_drop_idempotent() { let mut obj = ab_object_with_payload(ArrayBufferPayload { diff --git a/crates/oxide_builtins/src/regexp.rs b/crates/oxide_builtins/src/regexp.rs index 664f1577..0813ac0c 100644 --- a/crates/oxide_builtins/src/regexp.rs +++ b/crates/oxide_builtins/src/regexp.rs @@ -560,27 +560,6 @@ pub fn drop_regexp_native(obj: &mut JsObject) -> u64 { bytes } -/// 深拷贝已编译正则到新对象(GC 搬移 / 晋升流程用)。 -/// -/// 与 drop 配对:新对象获得独立的 `Box`,源对象保留自己的指针, -/// 两侧各自释放恰好一次,杜绝跨 arena 克隆后的指针别名双释放。 -/// 作用于全部持编译正则的对象形态(RegExp / matchAll 载体)。 -pub fn clone_regexp_native(old_obj: &JsObject, new_obj: &mut JsObject) { - if !old_obj.holds_compiled_regex() { - return; - } - let Some(ptr) = old_obj.native_fn() else { - return; - }; - let regex_ptr = ptr.as_ptr() as *const regress::Regex; - if regex_ptr.is_null() { - return; - } - // SAFETY: old_obj 的 native_fn 槽存 `Box` 指针,对象存活期间有效。 - let cloned_ptr = Box::into_raw(Box::new(unsafe { (&*regex_ptr).clone() })); - new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned_ptr as *const ()) })); -} - /// `RegExp.prototype.test(string)`:判断是否匹配。global/sticky 从 lastIndex /// 开始匹配,sticky 要求匹配恰在 lastIndex 起点。 /// diff --git a/crates/oxide_vm/src/promise/gc_edges.rs b/crates/oxide_vm/src/promise/gc_edges.rs index d2f65172..dee9b1dc 100644 --- a/crates/oxide_vm/src/promise/gc_edges.rs +++ b/crates/oxide_vm/src/promise/gc_edges.rs @@ -1,13 +1,11 @@ -//! Promise 状态盒的 session GC 支撑:值边、改写、深拷贝迁移、 -//! 结算链克隆链维护与字节核算 / 释放。 +//! Promise 状态盒的 session GC 支撑:值边、字节核算与释放三类接线。 //! -//! 结算链指针是裸指针边、不在 JsValue 改写面:晋升场景由 -//! `migrate_settlement_to_newest_clone` 接链,搬移场景按转发表重定位。 +//! 结算链指针是裸指针边、不是 JsValue。 use oxide_types::object::JsObject; use oxide_types::value::JsValue; -use super::{promise_state_mut, promise_state_ref, PromiseReaction, PromiseState, PromiseStateKind}; +use super::{promise_state_ref, PromiseReaction, PromiseState}; /// Promise 状态盒内全部值边(GC mark 边):对象/字符串/BigInt 均产出, /// 消费侧按值类型分发到对象栈与存活集。 @@ -33,133 +31,6 @@ pub(crate) fn promise_native_edges(obj: &JsObject) -> Vec { edges } -/// 用转发函数重写状态盒中的所有 JsValue(promote 用)。 -/// -/// 结算链指针是裸指针边、不在本函数改写面:晋升克隆新分配于 session arena -/// (原件地址不变),改写场景指针天然有效;结算链接链由 -/// `migrate_settlement_to_newest_clone` 负责。 -#[allow(dead_code)] -pub(crate) fn rewrite_promise_native(obj: &JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue) { - let Some(state) = promise_state_mut(obj) else { - return; - }; - state.result = rewrite(state.result); - state.resolve_fn = rewrite(state.resolve_fn); - state.reject_fn = rewrite(state.reject_fn); - for r in &mut state.reactions { - r.promise = rewrite(r.promise); - r.resolve = rewrite(r.resolve); - r.reject = rewrite(r.reject); - r.handler = rewrite(r.handler); - } -} - -/// 深拷贝状态盒到新对象(promote 用):新对象持独立 Box,源盒可安全释放。 -/// -/// 源的反应**迁移**(非复制)进新对象:源盒随 epoch 释放,留在源上的反应会 -/// 永久丢失、原件侧消费者永不触发;每条反应的 JsValue 随状态盒其余字段同一 -/// pass 晋升/改写。结算链指针按原值保留(接链由 -/// `migrate_settlement_to_newest_clone` 负责)。 -#[allow(dead_code)] -pub(crate) fn clone_promise_native_with_rewrite( - old: &JsObject, new: &mut JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue, -) { - // 先取全部 Copy 字段,避免与随后的可变 take 别名校验冲突。 - let (state_kind, prev_clone, result, resolve_fn, reject_fn, already_resolved) = { - let Some(state) = promise_state_ref(old) else { - return; - }; - ( - state.state, - state.promoted_clone, - state.result, - state.resolve_fn, - state.reject_fn, - state.already_resolved, - ) - }; - - // 源反应迁入新对象:源盒随 epoch 释放,反应留源即永久丢失。 - let mut reactions: Vec = Vec::new(); - if let Some(src) = promise_state_mut(old) { - for r in std::mem::take(&mut src.reactions) { - reactions.push(rewrite_reaction(r, &mut rewrite)); - } - } - - let cloned = PromiseState { - state: state_kind, - result: rewrite(result), - reactions, - resolve_fn: rewrite(resolve_fn), - reject_fn: rewrite(reject_fn), - already_resolved, - // 结算链指针按原值保留(不改写源指针):晋升后继步 - // `migrate_settlement_to_newest_clone` 负责接链与更新源指针, - // 搬移后继步按转发表重定位。 - promoted_clone: prev_clone, - }; - new.set_native_data(Box::into_raw(Box::new(cloned)) as *mut u8); -} - -/// 晋升路径专属:新克隆取代前一克隆成为结算枢纽——前一克隆的残留反应取回归并 -/// 进新克隆,旧克隆的结算改链到新克隆,原件的传导指针指向新克隆。任一结算 -/// 入口(原件 / 旧克隆 / 新克隆)都沿链落到携全部反应的最新克隆,反应恰触发 -/// 一次。 -/// -/// # 边界与前提 -/// - 仅 epoch 原件晋升后调用(`promote_object_inner` 的 Promise 分支,紧随 -/// `clone_promise_native_with_rewrite`);session 搬移(GC sweep)不改结算 -/// 拓扑,不得调用。 -/// - 源非 pending 时无事可做直接返回(已结算原件不再传导)。 -#[allow(dead_code)] -pub(crate) fn migrate_settlement_to_newest_clone( - old: &JsObject, new: &mut JsObject, mut rewrite: impl FnMut(JsValue) -> JsValue, -) { - // 链上的前克隆 = 源当前指向的克隆(克隆步只保留不改写源指针)。 - let (pending, prev) = { - let Some(src) = promise_state_ref(old) else { - return; - }; - (src.state == PromiseStateKind::Pending, src.promoted_clone) - }; - if !pending { - return; - } - // 前克隆残留并入本克隆(旧克隆腾空,防双触发),并把它的结算改链到新克隆。 - if !prev.is_null() { - // SAFETY: prev 是早前晋升产生的 session 克隆,session arena 存活期内有效。 - if let Some(prev_state) = promise_state_mut(unsafe { &*prev }) { - if let Some(dst) = promise_state_mut(new) { - for r in std::mem::take(&mut prev_state.reactions) { - dst.reactions.push(rewrite_reaction(r, &mut rewrite)); - } - } - prev_state.promoted_clone = new as *mut JsObject; - } - } - // 新克隆即最新:自身无后继,原件传导指针指向它。 - if let Some(dst) = promise_state_mut(new) { - dst.promoted_clone = std::ptr::null_mut(); - } - if let Some(src) = promise_state_mut(old) { - src.promoted_clone = new as *mut JsObject; - } -} - -/// 改写单条反应的四条 JsValue 引用(派生 promise 能力与处理器),源反应迁移 -/// 与再晋升合并共用。 -#[allow(dead_code)] -fn rewrite_reaction JsValue>(r: PromiseReaction, rewrite: &mut F) -> PromiseReaction { - PromiseReaction { - promise: rewrite(r.promise), - resolve: rewrite(r.resolve), - reject: rewrite(r.reject), - handler: rewrite(r.handler), - is_fulfill: r.is_fulfill, - } -} - /// 只读核算 Promise 状态盒字节(不释放),供 GC 账目核算。 pub(crate) fn promise_native_size(obj: &JsObject) -> u64 { if !obj.is_promise_obj() { diff --git a/crates/oxide_vm/src/promise/mod.rs b/crates/oxide_vm/src/promise/mod.rs index 841aeff0..0103b440 100644 --- a/crates/oxide_vm/src/promise/mod.rs +++ b/crates/oxide_vm/src/promise/mod.rs @@ -4,8 +4,7 @@ //! 是携带目标 promise 的 native 闭包函数(函数对象 prop 存 promise 引用)。 //! 反应(reaction)与 thenable 委托以 `Microtask` 入队 `Vm::job_queue`, //! 由 `run()` 末尾的 drain 循环 FIFO 执行。所有盒内 JsValue 由 session GC -//! 经 Promise 对象边追踪(见 `promise_native_edges` / `rewrite_promise_native` / -//! `clone_promise_native_with_rewrite` / `drop_promise_native`)。 +//! 经 Promise 对象边追踪(见 `promise_native_edges` / `drop_promise_native`)。 use oxide_types::object::JsObject; use oxide_types::value::JsValue; @@ -156,15 +155,3 @@ fn promise_state_ref(obj: &JsObject) -> Option<&PromiseState> { Some(unsafe { &*ptr }) } } - -#[expect(clippy::mut_from_ref)] -#[allow(dead_code)] -fn promise_state_mut(obj: &JsObject) -> Option<&mut PromiseState> { - let ptr = obj.native_data() as *mut PromiseState; - if ptr.is_null() { - None - } else { - // SAFETY: 同上,改写发生在 GC 移动/清扫期间,对象仍存活。 - Some(unsafe { &mut *ptr }) - } -}