From 9493b889b6ea713dab370cc722e06e246341f643 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Fri, 11 Sep 2026 08:50:59 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=80=89=E6=8B=A9=E6=80=A7=E9=87=8D?= =?UTF-8?q?=E5=BB=BA=E5=90=8E=E8=A2=AB=E6=9B=BF=E6=8D=A2=E5=AE=B6=E6=97=8F?= =?UTF-8?q?=E7=9A=84=E5=B1=9E=E6=80=A7=E5=8C=BA=E6=97=A0=E4=BA=BA=E9=87=8A?= =?UTF-8?q?=E6=94=BE=E2=80=94=E2=80=94=E9=87=8D=E5=BB=BA=E7=82=B9=E6=8C=89?= =?UTF-8?q?=E8=84=8F=E6=A0=87=E8=AE=B0=E6=81=B0=E5=A5=BD=E9=87=8A=E6=94=BE?= =?UTF-8?q?=E4=B8=80=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_kernel/src/builtin.rs | 232 ++++++++++++++++++++++++++++- 1 file changed, 230 insertions(+), 2 deletions(-) diff --git a/crates/oxide_kernel/src/builtin.rs b/crates/oxide_kernel/src/builtin.rs index 7de128a1..cbd149f9 100644 --- a/crates/oxide_kernel/src/builtin.rs +++ b/crates/oxide_kernel/src/builtin.rs @@ -700,6 +700,183 @@ impl BuiltinWorld { self.leaked_objects.borrow_mut().append(&mut from.leaked_objects.borrow_mut()); } + /// 释放选择性重建中被替换家族的旧 P 对象属性区。 + /// + /// # 边界与前提 + /// - 仅覆盖脏标记命中的家族字段(含 `dirty.object` 连带的迭代器原型族、 + /// `dirty.stubs` 连带的 BigInt 对);未替换字段由新 world 沿用同一 Arc, + /// 其属性区归 session 收尾(`teardown_heap_data`)释放,此处不碰。 + /// - Function/Object 对除外:保留方法 wrapper(登记表对象)的原型链仍经 + /// 裸指针指向旧 function_proto/object_proto,执行期属性查找可达其属性 + /// 区——该 4 对象已由 `rebuild_with_dirty` 保活(本体与属性区同泄漏), + /// 此处不得释放。 + /// - 须在旧 world 被替换前调用(full_reset 安全点无并发读者);与登记表 + /// 对象(wrapper)集不相交,不双放。 + /// + /// # 副作用 + /// 每个被替换 P 对象的属性区四区释放并置空(幂等,重入为 no-op)。 + pub fn release_replaced_family_heaps(&self, dirty: &BuiltinDirtySet) { + let release = |p: &P| { + // SAFETY: p 是本 world 的 P 对象,属性区仅此一处释放并置空(幂等); + // full_reset 安全点无并发读者。 + unsafe { + (&mut *p.as_mut_ptr()).release_raw_heap(); + } + }; + if dirty.object { + // object 家族重建连带重建迭代器原型族(链于 Object.prototype 之下), + // 旧 9 对象一并被替换。 + for p in [ + &self.iterator_proto, + &self.array_iterator_proto, + &self.map_iterator_proto, + &self.set_iterator_proto, + &self.string_iterator_proto, + &self.regexp_string_iterator_proto, + &self.iterator_helper_proto, + &self.disposable_stack_proto, + &self.async_disposable_stack_proto, + ] { + release(p); + } + } + if dirty.array { + release(&self.array_proto); + release(&self.array_constructor); + } + if dirty.string { + release(&self.string_proto); + release(&self.string_constructor); + } + if dirty.number { + release(&self.number_proto); + release(&self.number_constructor); + } + if dirty.boolean { + release(&self.boolean_proto); + release(&self.boolean_constructor); + } + if dirty.error_family { + release(&self.error_proto); + release(&self.error_constructor); + release(&self.type_error_proto); + release(&self.reference_error_proto); + release(&self.range_error_proto); + release(&self.syntax_error_proto); + release(&self.uri_error_proto); + release(&self.eval_error_proto); + release(&self.suppressed_error_proto); + } + if dirty.symbol_family { + release(&self.symbol_proto); + release(&self.symbol_constructor); + release(&self.sym_match); + release(&self.sym_replace); + release(&self.sym_search); + release(&self.sym_split); + release(&self.sym_iterator); + release(&self.sym_to_primitive); + release(&self.sym_has_instance); + release(&self.sym_match_all); + release(&self.sym_async_iterator); + release(&self.sym_to_string_tag); + release(&self.sym_species); + release(&self.sym_async_dispose); + release(&self.sym_dispose); + } + if dirty.math { + release(&self.math_object); + } + if dirty.json { + release(&self.json_object); + } + if dirty.date { + release(&self.date_proto); + release(&self.date_constructor); + } + if dirty.set { + release(&self.set_proto); + release(&self.set_constructor); + } + if dirty.map { + release(&self.map_proto); + release(&self.map_constructor); + } + if dirty.regexp { + release(&self.regexp_proto); + release(&self.regexp_constructor); + } + if dirty.array_buffer { + release(&self.array_buffer_proto); + release(&self.array_buffer_constructor); + } + if dirty.data_view { + release(&self.data_view_proto); + release(&self.data_view_constructor); + } + if dirty.typed_array_family { + for p in [ + &self.typed_array_proto, + &self.typed_array_constructor, + &self.int8array_constructor, + &self.int8array_proto, + &self.uint8array_constructor, + &self.uint8array_proto, + &self.uint8clampedarray_constructor, + &self.uint8clampedarray_proto, + &self.int16array_constructor, + &self.int16array_proto, + &self.uint16array_constructor, + &self.uint16array_proto, + &self.int32array_constructor, + &self.int32array_proto, + &self.uint32array_constructor, + &self.uint32array_proto, + &self.float32array_constructor, + &self.float32array_proto, + &self.float64array_constructor, + &self.float64array_proto, + &self.bigint64array_constructor, + &self.bigint64array_proto, + &self.biguint64array_constructor, + &self.biguint64array_proto, + ] { + release(p); + } + } + if dirty.temporal { + for p in [ + &self.temporal_object, + &self.temporal_now_object, + &self.instant_constructor, + &self.instant_proto, + &self.plain_date_constructor, + &self.plain_date_proto, + &self.plain_time_constructor, + &self.plain_time_proto, + &self.duration_constructor, + &self.duration_proto, + &self.zoned_date_time_constructor, + &self.zoned_date_time_proto, + &self.plain_date_time_constructor, + &self.plain_date_time_proto, + ] { + release(p); + } + } + if dirty.stubs { + // stubs 家族重建连带重建 BigInt 对。 + release(&self.bigint_proto); + release(&self.bigint_constructor); + for p in &self.stub_objects { + release(p); + } + } + if dirty.console { + release(&self.console_object); + } + } + /// 释放本 world 拥有的全部手工堆数据。 /// /// # 口径 @@ -711,8 +888,10 @@ impl BuiltinWorld { /// # 注意事项 /// 仅由 session 收尾调用(`KernelSession` 的 `Drop` 与 session 替换前), /// 幂等:登记表按值取走,属性区释放后置空。选择性重建(dirty rebuild) - /// 不走本路径——旧 world 整体丢弃,登记表与属性区同泄漏(改造前口径), - /// 保留对象的引用不受影响,不引入悬垂。 + /// 不走本路径:登记表整体并入新 world(`inherit_leaked_objects`),仍由 + /// session 收尾统一释放;被替换家族的旧 P 字段属性区在重建点 + /// (`release_replaced_family_heaps`)恰好释放一次,与本路径对象集不相交, + /// 不双放。 pub fn teardown_heap_data(&self) { for ptr in self.leaked_objects.borrow_mut().drain(..) { if ptr.is_null() { @@ -1142,6 +1321,10 @@ impl BuiltinWorld { std::mem::forget(current.object_proto.clone()); std::mem::forget(current.object_constructor.clone()); } + // 被替换家族的旧 P 字段属性区释放站:本体随旧 world Arc 归零释放, + // 属性区无 Drop 口径,须在此恰好释放一次(Function/Object 对除外, + // 见上方保活)。 + current.release_replaced_family_heaps(dirty); let (object_proto, object_constructor) = if dirty.object { make_named_pair(string_forge, shape_forge, labels, "Object") } else { @@ -2023,4 +2206,49 @@ mod tests { assert!(std::ptr::eq(ctor_proto, rebuilt.array_proto.as_ptr() as *mut JsObject)); assert!(std::ptr::eq(proto_ctor, rebuilt.array_constructor.as_ptr() as *mut JsObject)); } + + /// 选择性重建的释放面动态自测:被替换家族的旧属性区恰好释放一次(置空可断言), + /// 保活家族与未脏家族不释放,登记表并入新 world。 + #[test] + fn selective_reset_releases_replaced_family_heap() { + use crate::kernel::{KernelConfig, KernelCore, KernelSession}; + let core = KernelCore::new(KernelConfig::minimal()); + let mut session = KernelSession::new(&core); + // 持有旧 world Arc:被替换对象本体在断言期仍可读(属性区指针可检查)。 + let old_world = std::sync::Arc::clone(&session.builtin_world); + let array_proto = old_world.array_proto.as_ptr() as *mut JsObject; + let object_proto = old_world.object_proto.as_ptr() as *mut JsObject; + let fn_proto = old_world.function_proto.as_ptr() as *mut JsObject; + + // 旧原型各造一个命名属性区(绑定后的驻留态),登记表放一个泄漏 wrapper。 + let wrapper = Box::into_raw(Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))); + old_world.track_leaked_object(wrapper); + unsafe { + (&mut *array_proto).ensure_hash_props().push(JsValue::int(1)); + (&mut *object_proto).ensure_hash_props().push(JsValue::int(2)); + (&mut *fn_proto).ensure_hash_props().push(JsValue::int(3)); + } + + unsafe { + (&mut *array_proto).bump_generation(); + (&mut *fn_proto).bump_generation(); + } + let dirty = session.selective_reset(&core); + assert!(dirty.array); + assert!(dirty.function); + + // 被替换家族(array):属性区四区已释放置空。 + let old_array = unsafe { &*array_proto }; + assert!(old_array.hash_props_raw().is_null()); + assert!(old_array.array_elements_raw().is_null()); + assert!(old_array.array_elements_meta_raw().is_null()); + assert!(old_array.prop_meta_raw().is_null()); + // 保活家族(function):本体与属性区均保活——保留 wrapper 原型链执行期可读。 + assert!(!unsafe { &*fn_proto }.hash_props_raw().is_null()); + // 未脏家族(object):沿用同一对象,属性区不受影响。 + assert!(!unsafe { &*object_proto }.hash_props_raw().is_null()); + assert!(std::ptr::eq(object_proto, session.builtin_world.object_proto.as_ptr() as *mut JsObject)); + // 登记表并入新 world:保留 wrapper 仍须由 session 收尾统一释放。 + assert!(session.builtin_world.leaked_objects.borrow().contains(&wrapper)); + } }