diff --git a/crates/oxide_builtins/src/data_view.rs b/crates/oxide_builtins/src/data_view.rs index e6bfb3ed..833c2ffd 100644 --- a/crates/oxide_builtins/src/data_view.rs +++ b/crates/oxide_builtins/src/data_view.rs @@ -144,8 +144,8 @@ fn dv_buffer_writable(vm: &mut H, view: DataViewData) -> Result<(), J Ok(()) } -/// JS 调用点后的缓冲重取纪律:重读寄存器再重取载荷(epoch 晋升改写 -/// 寄存器,旧 JsValue 滞留 epoch 克隆旧盒);detached → TypeError。 +/// JS 调用点后的缓冲重取纪律:重读寄存器并重取载荷,寄存器值为唯一口径; +/// detached → TypeError。 /// 返回(寄存器重读值、live 长、存储上限)。 fn revalidate_buffer(vm: &mut H, buf_reg: u8) -> Result<(JsValue, usize, usize), JsValue> { let buffer = vm.reg(buf_reg); @@ -246,8 +246,7 @@ fn has_data_view_proto(vm: &mut H, this_val: JsValue) -> bool { /// # 边界与前提 /// - 原型读取在全部初校验之后:越界 RangeError 先于抛错的 prototype getter。 /// - NewTarget 非对象(直调/缺省面)时直接回落默认原型,不抛错。 -/// - 每个 JS 调用点后重取缓冲(重读寄存器 + 载荷重取):晋升改写寄存器, -/// 旧值滞留 epoch 克隆旧盒。 +/// - 每个 JS 调用点后重取缓冲(重读寄存器 + 载荷重取),寄存器值为唯一口径。 pub fn data_view_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if !has_data_view_proto(vm, this_val) { @@ -357,39 +356,6 @@ pub fn data_view_native_edges(obj: &JsObject) -> Vec { } } -/// 克隆 DataView 视图数据到新对象,用 `rewrite` 改写 buffer 引用。 -pub fn clone_data_view_native_with_rewrite(old_obj: &JsObject, new_obj: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - let Some(ptr) = data_view_data_ptr(old_obj) else { - return; - }; - if ptr.is_null() { - return; - } - let mut data = unsafe { *ptr }; - data.buffer = rewrite(data.buffer); - let cloned = Box::into_raw(Box::new(data)); - new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned as *const ()) })); -} - -/// 原地重写 DataView 的 buffer 引用。 -pub fn rewrite_data_view_native(obj: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - let Some(ptr) = data_view_data_ptr(obj) else { - return; - }; - if ptr.is_null() { - return; - } - unsafe { - (*ptr).buffer = rewrite((*ptr).buffer); - } -} - /// 只读核算 DataView 视图数据字节(不释放)。 pub fn data_view_native_size(obj: &JsObject) -> u64 { let Some(ptr) = data_view_data_ptr(obj) else { diff --git a/crates/oxide_builtins/src/disposable_stack.rs b/crates/oxide_builtins/src/disposable_stack.rs index 90518e14..0a7f3df2 100644 --- a/crates/oxide_builtins/src/disposable_stack.rs +++ b/crates/oxide_builtins/src/disposable_stack.rs @@ -7,8 +7,8 @@ //! `type_tag` 为 `JsObject::OBJ_TYPE_DISPOSABLE_STACK`(同步)或 //! `OBJ_TYPE_ASYNC_DISPOSABLE_STACK`(异步)时区分接收者,`hint` 以 0/1 两值区分 //! 条目释放语义(定义见 `DisposeEntry.hint`),`wrap_sync` 标记 async 栈 use 落回 -//! `@@dispose` 的条目(返回值丢弃、异常异步化)。GC 四函数 -//! (edges/rewrite/clone/drop)供 session 层跨 epoch 追踪,签名与 Map/Promise +//! `@@dispose` 的条目(返回值丢弃、异常异步化)。GC 三函数 +//! (edges/size/drop)接线 mark 边、字节账目与释放,形态与 Map/Promise //! 状态盒同构。 use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; @@ -579,60 +579,6 @@ pub fn dispose_edges(obj: &JsObject) -> Vec { unsafe { (*ptr).entries.iter().flat_map(|entry| [entry.value, entry.method]).collect() } } -/// 克隆状态盒到新对象,用 `rewrite` 改写其中的对象引用 -/// (供跨 epoch 的对象重写/克隆流程使用)。 -pub fn clone_dispose_native_with_rewrite(src: &JsObject, dst: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - if !src.is_disposable_stack_obj() && !src.is_async_disposable_stack_obj() { - return; - } - let ptr = get_capability_ptr(src); - if ptr.is_null() { - dst.set_native_data(std::ptr::null_mut()); - return; - } - // SAFETY: 同上,src 状态盒在 GC 移动期间仍存活。 - let src_cap = unsafe { &*ptr }; - let cloned = DisposeCapability { - state: src_cap.state, - entries: src_cap - .entries - .iter() - .map(|entry| DisposeEntry { - value: if entry.value.is_object() { rewrite(entry.value) } else { entry.value }, - method: if entry.method.is_object() { rewrite(entry.method) } else { entry.method }, - hint: entry.hint, - arg_style: entry.arg_style, - wrap_sync: entry.wrap_sync, - }) - .collect(), - }; - dst.set_native_data(Box::into_raw(Box::new(cloned)) as *mut u8); -} - -/// 原地重写状态盒,用 `rewrite` 改写其中的对象引用。 -pub fn rewrite_dispose_native(obj: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - if !obj.is_disposable_stack_obj() && !obj.is_async_disposable_stack_obj() { - return; - } - let ptr = get_capability_ptr(obj); - if ptr.is_null() { - return; - } - // SAFETY: 同上,改写发生在 GC 移动/清扫期间,对象仍存活。 - unsafe { - for entry in &mut (*ptr).entries { - entry.value = rewrite(entry.value); - entry.method = rewrite(entry.method); - } - } -} - /// 只读核算 DisposableStack 状态盒字节(不释放)。 pub fn disposable_stack_native_size(obj: &JsObject) -> u64 { if !obj.is_disposable_stack_obj() && !obj.is_async_disposable_stack_obj() { diff --git a/crates/oxide_builtins/src/map.rs b/crates/oxide_builtins/src/map.rs index 68339084..5ae05984 100644 --- a/crates/oxide_builtins/src/map.rs +++ b/crates/oxide_builtins/src/map.rs @@ -41,13 +41,6 @@ impl MapInner { } } - pub(crate) fn with_capacity(capacity: usize) -> Self { - Self { - slots: Vec::with_capacity(capacity), - index: HashMap::with_capacity(capacity), - } - } - /// 活表条目数(规范 SetDataSize 口径,空槽不计)。 pub(crate) fn len(&self) -> usize { self.index.len() @@ -171,54 +164,6 @@ pub fn map_native_edges(obj: &JsObject) -> Vec { unsafe { (*inner).iter().flat_map(|(key, value)| [key.0, value]).collect() } } -/// 克隆 Map 的 native 数据到新对象,用 `rewrite` 改写其中的对象引用 -/// (供跨 epoch 的对象重写/克隆流程使用)。 -pub fn clone_map_native_with_rewrite(src: &JsObject, dst: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - if !src.is_map() { - return; - } - let inner = src.native_data() as *const MapInner; - if inner.is_null() { - dst.set_native_data(std::ptr::null_mut()); - return; - } - let mut cloned = MapInner::new(); - unsafe { - for (key, value) in (*inner).iter() { - let new_key = if key.0.is_object() { SetKey(rewrite(key.0)) } else { key }; - let new_value = if value.is_object() { rewrite(value) } else { value }; - cloned.insert(new_key, new_value); - } - } - dst.set_native_data(Box::into_raw(Box::new(cloned)) as *mut u8); -} - -/// 原地重写 Map 的 native 数据,用 `rewrite` 改写其中的对象引用。 -pub fn rewrite_map_native(obj: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - if !obj.is_map() { - return; - } - let inner = obj.native_data() as *mut MapInner; - if inner.is_null() { - return; - } - unsafe { - let mut rewritten = MapInner::with_capacity((*inner).len()); - for (key, value) in (*inner).iter() { - let new_key = if key.0.is_object() { SetKey(rewrite(key.0)) } else { key }; - let new_value = if value.is_object() { rewrite(value) } else { value }; - rewritten.insert(new_key, new_value); - } - *inner = rewritten; - } -} - /// 只读核算 Map 的 native 数据字节(不释放)。 /// 槽表与辅助索引均按 capacity 核算,与 `drop_map_native` 释放口径一致, /// 供 GC 账目核算。 diff --git a/crates/oxide_builtins/src/module.rs b/crates/oxide_builtins/src/module.rs index 86f712b9..72abb1d5 100644 --- a/crates/oxide_builtins/src/module.rs +++ b/crates/oxide_builtins/src/module.rs @@ -235,79 +235,6 @@ pub fn module_ns_cell_edges(obj: &JsObject) -> Vec<*mut Cell> { out } -/// 原地重写条目表内的 JsValue 边(对象搬移/epoch 晋升后的引用重定位)。 -/// -/// # 副作用 -/// - 改写 `Value` 条目的值与共享 cell 内的值;cell 指针本身稳定不变。 -pub fn rewrite_module_ns_native(obj: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - let table = ns_table_ptr(obj); - if table.is_null() { - return; - } - // SAFETY: table 归本 ns 对象持有,原地改写其 JsValue 边。 - let table_ref = unsafe { &mut *table }; - for entry in table_ref.entries.iter_mut() { - match &mut entry.state { - ModuleNsState::Value { value, .. } => *value = rewrite(*value), - ModuleNsState::Cell(cell) => { - if !cell.is_null() { - // SAFETY: cell 生命周期见 `module_ns_export`;共享 cell 内值一并改写。 - let cell_ref = unsafe { &mut **cell }; - cell_ref.value = rewrite(cell_ref.value); - } - } - } - } -} - -/// 深拷贝条目表到 `dst`,并用 `rewrite` 改写其中的对象引用。 -/// -/// # 注意事项 -/// - `clone_for_session_epoch` 只浅拷贝 `native_data` 指针;本函数必须在新对象上 -/// 重建独立 Box,否则原件与克隆共享同一表会在释放时双放。 -/// - `Cell` 指针本身跨搬移稳定,只改写其内部值。 -pub fn clone_module_ns_native_with_rewrite(src: &JsObject, dst: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - if !src.is_module_namespace() { - return; - } - let table = src.native_data() as *const ModuleNsTable; - if table.is_null() { - dst.set_native_data(std::ptr::null_mut()); - return; - } - // SAFETY: src.native_data 指向 src 持有的 Box。 - let table_ref = unsafe { &*table }; - let mut entries = Vec::with_capacity(table_ref.entries.len()); - for entry in &table_ref.entries { - let state = match &entry.state { - ModuleNsState::Value { value, initialized } => ModuleNsState::Value { - value: if value.is_object() { rewrite(*value) } else { *value }, - initialized: *initialized, - }, - ModuleNsState::Cell(cell) => { - if !cell.is_null() { - // SAFETY: cell 生命周期见 `module_ns_export`;晋升/搬移后改写其内值。 - let cell_ref = unsafe { &mut **cell }; - cell_ref.value = rewrite(cell_ref.value); - } - ModuleNsState::Cell(*cell) - } - }; - entries.push(ModuleNsEntry { - name_si: entry.name_si, - origin: entry.origin, - state, - }); - } - dst.set_native_data(Box::into_raw(Box::new(ModuleNsTable { entries })) as *mut u8); -} - /// 只读核算条目表的 native 数据字节(不释放),与 `drop_module_ns_native` 的 /// capacity 口径一致,供 GC 账目核算。 pub fn module_ns_native_size(obj: &JsObject) -> u64 { diff --git a/crates/oxide_builtins/src/set.rs b/crates/oxide_builtins/src/set.rs index 967ed983..d7dacd9d 100644 --- a/crates/oxide_builtins/src/set.rs +++ b/crates/oxide_builtins/src/set.rs @@ -141,51 +141,6 @@ pub fn set_native_edges(obj: &JsObject) -> Vec { unsafe { (*inner).iter().map(|key| key.0).collect() } } -/// 克隆 Set 的 native 数据到新对象,用 `rewrite` 改写其中的对象引用。 -pub fn clone_set_native_with_rewrite(src: &JsObject, dst: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - if !src.is_set() { - return; - } - let inner = src.native_data() as *const SetInner; - if inner.is_null() { - dst.set_native_data(std::ptr::null_mut()); - return; - } - let mut cloned = SetInner::new(); - unsafe { - for key in (*inner).iter() { - let new_key = if key.0.is_object() { SetKey(rewrite(key.0)) } else { *key }; - cloned.insert(new_key); - } - } - dst.set_native_data(Box::into_raw(Box::new(cloned)) as *mut u8); -} - -/// 原地重写 Set 的 native 数据,用 `rewrite` 改写其中的对象引用。 -pub fn rewrite_set_native(obj: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - if !obj.is_set() { - return; - } - let inner = obj.native_data() as *mut SetInner; - if inner.is_null() { - return; - } - unsafe { - let mut rewritten = SetInner::with_capacity((*inner).len()); - for key in (*inner).iter() { - let new_key = if key.0.is_object() { SetKey(rewrite(key.0)) } else { *key }; - rewritten.insert(new_key); - } - *inner = rewritten; - } -} - /// 只读核算 Set 的 native 数据字节(不释放)。 /// 与 `drop_set_native` 释放口径一致(capacity),供 GC 账目核算。 pub fn set_native_size(obj: &JsObject) -> u64 { diff --git a/crates/oxide_builtins/src/typed_array.rs b/crates/oxide_builtins/src/typed_array.rs index a102c662..73fab1c0 100644 --- a/crates/oxide_builtins/src/typed_array.rs +++ b/crates/oxide_builtins/src/typed_array.rs @@ -181,39 +181,6 @@ pub fn typed_array_native_edges(obj: &JsObject) -> Vec { } } -/// 克隆 TypedArray 的视图数据到新对象,用 `rewrite` 改写 buffer 引用。 -pub fn clone_typed_array_native_with_rewrite(old_obj: &JsObject, new_obj: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - let Some(ptr) = typed_array_data_ptr(old_obj) else { - return; - }; - if ptr.is_null() { - return; - } - let mut data = unsafe { *ptr }; - data.buffer = rewrite(data.buffer); - let cloned = Box::into_raw(Box::new(data)); - new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned as *const ()) })); -} - -/// 原地重写 TypedArray 的 buffer 引用。 -pub fn rewrite_typed_array_native(obj: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - let Some(ptr) = typed_array_data_ptr(obj) else { - return; - }; - if ptr.is_null() { - return; - } - unsafe { - (*ptr).buffer = rewrite((*ptr).buffer); - } -} - /// 只读核算 TypedArray 视图数据字节(不释放)。 pub fn typed_array_native_size(obj: &JsObject) -> u64 { let Some(ptr) = typed_array_data_ptr(obj) else { diff --git a/crates/oxide_builtins/src/weak_map.rs b/crates/oxide_builtins/src/weak_map.rs index b8f15bc2..9718a157 100644 --- a/crates/oxide_builtins/src/weak_map.rs +++ b/crates/oxide_builtins/src/weak_map.rs @@ -1,9 +1,10 @@ //! WeakMap 条目表:弱键 → 强值。 //! -//! 键为弱引用(GC mark 不产键边,死键由收集路径按转发表判定后丢条目), -//! 值为强引用(值边进 mark 边扫描与晋升改写)。存储盒经 `Box::into_raw` 挂 -//! `JsObject.native_data`,GC 六站点(mark 边 / 移动式 sweep / 晋升克隆 / -//! 原地晋升 / drop / 字节账目)经本模块五函数族接线,口径与 Map/Set 同形。 +//! 键为弱引用(GC mark 不产键边,死键由原地 sweep 的 mark 位判定后丢条目), +//! 值为强引用(值边进 mark 边扫描,原地 sweep 弱键定夺相改写,该相值为恒等 +//! 改写)。存储盒经 `Box::into_raw` 挂 `JsObject.native_data`,GC 四站点 +//! (mark 边 / 原地 sweep 弱键定夺 / drop / 字节账目)经本模块四函数族接线, +//! 口径与 Map/Set 同形。 //! 构造体(iterable 协议)与 set/get/has/delete 四方法的品牌三分枝守卫 //! (set/delete 对非 WeakMap this 抛 TypeError,get/has 静默返 undefined/false) //! 也在本模块。 @@ -19,7 +20,7 @@ use oxide_types::value::JsValue; /// (interner 保证同符号同 si)。两臂互斥,不可弱持的值不建键。 #[derive(Clone, Copy, PartialEq, Eq)] pub enum WeakKey { - /// 对象键:裸指针仅作哈希键与成员查,生命周期由 GC 转发表判定。 + /// 对象键:裸指针仅作哈希键与成员查,生命周期由原地 sweep 的 mark 位判定。 Obj(*const JsObject), /// symbol 键:interned si 值。 Symbol(u32), @@ -120,34 +121,11 @@ pub fn weak_map_native_edges(obj: &JsObject) -> Vec { unsafe { (*inner).entries.values().copied().collect() } } -/// 克隆 WeakMap 条目表到新对象:键原样搬运(生死判定交晋升后的弱键定夺 -/// 路径),值经 `rewrite` 改写(强边)。源盒由释放路径单独释放,互不共享。 -pub fn clone_weak_map_native_with_rewrite(src: &JsObject, dst: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - if !src.is_weak_map_obj() { - return; - } - // SAFETY: 源盒指针由构造路径写入,克隆完成 native_data 重指前读取有效。 - let inner = weak_map_inner_of(src); - if inner.is_null() { - dst.set_native_data(std::ptr::null_mut()); - return; - } - let mut cloned = WeakMapInner::new(); - unsafe { - for (key, value) in (*inner).iter() { - let new_value = if value.is_object() { rewrite(value) } else { value }; - cloned.entries.insert(key, new_value); - } - } - dst.set_native_data(Box::into_raw(Box::new(cloned)) as *mut u8); -} - /// 原地重写 WeakMap 条目表的键与值:键经 `key_resolve` 判定(返回 `None` /// 即死键,条目丢弃;`Some` 改指新键),值经 `value_rewrite` 改写(强边)。 -/// 按原表整表重建,免条目级原位换键的容量抖动。 +/// 键不提前改写,键的生死统一由弱键定夺路径判定(原地 sweep 弱键定夺按 +/// mark 位定生死、死键条目丢弃);提前改写键会引入定夺序依赖。按原表 +/// 整表重建,免条目级原位换键的容量抖动。 pub fn rewrite_weak_map_native(obj: &mut JsObject, mut key_resolve: K, mut value_rewrite: V) where K: FnMut(WeakKey) -> Option, @@ -174,31 +152,6 @@ where } } -/// 原地重写 WeakMap 条目表的值边(强边):键不动,值经 `rewrite` 改写。 -/// 供晋升主改写路径使用——键的生死须待转发表收敛后由弱键定夺路径判定, -/// 本路径提前改写键会引入晋升序依赖。 -pub fn rewrite_weak_map_native_values(obj: &mut JsObject, mut rewrite: F) -where - F: FnMut(JsValue) -> JsValue, -{ - if !obj.is_weak_map_obj() { - return; - } - // native_data 同 rewrite_weak_map_native:构造路径写入的有效 Box 指针。 - let inner = obj.native_data() as *mut WeakMapInner; - if inner.is_null() { - return; - } - unsafe { - let mut rewritten = WeakMapInner::with_capacity((*inner).len()); - for (key, value) in (*inner).iter() { - let new_value = if value.is_object() { rewrite(value) } else { value }; - rewritten.entries.insert(key, new_value); - } - *inner = rewritten; - } -} - /// 只读核算 WeakMap 条目表字节(不释放),供 GC 账目核算;与 /// `drop_weak_map_native` 同口径(盒本体 + 桶容量按条目尺寸计)。 pub fn weak_map_native_size(obj: &JsObject) -> u64 { diff --git a/crates/oxide_vm/src/vm/frames.rs b/crates/oxide_vm/src/vm/frames.rs index 4521edec..353b0eed 100644 --- a/crates/oxide_vm/src/vm/frames.rs +++ b/crates/oxide_vm/src/vm/frames.rs @@ -78,9 +78,9 @@ pub struct CallFrame { pub continuation: FrameContinuation, /// 被调函数 upvalue cell 表(压帧时从 callee 对象固化,非闭包为 null 空切片)。 /// - /// SAFETY 前提:`callee` 是 GC 根(根遍历/重写/深拷贝三处均遍历该字段), - /// 其 `upvalues` Box 创建后不替换(`set_upvalues` 仅 CREATE_CLOSURE 调用一次, - /// 晋升克隆共享同一 Box),故指针跨 GC 恒有效;写路径经 `*mut` 转可变切片 + /// SAFETY 前提:`callee` 是 GC 根(根遍历经该字段),其 `upvalues` Box + /// 创建后不替换(`set_upvalues` 仅 CREATE_CLOSURE 调用一次),故指针跨 GC + /// 恒有效;写路径经 `*mut` 转可变切片 /// 与 `upvalues_slice_mut` 同口径(Box 单所有权)。 pub upvalues: *const [*mut Cell], }