From c6bc93a0767534c37bd03fb753d8c8b8a07384d2 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 7 Oct 2026 01:35:16 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20realm=20=E4=B8=8E=20Vm=20=E7=94=9F?= =?UTF-8?q?=E5=91=BD=E5=91=A8=E6=9C=9F=E8=A7=A3=E8=80=A6=EF=BC=8CVm=20?= =?UTF-8?q?=E6=8C=81=20Arc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/iterator.rs | 31 ++- crates/oxide_builtins/src/map.rs | 9 +- crates/oxide_builtins/src/object.rs | 6 +- crates/oxide_builtins/src/set.rs | 9 +- crates/oxide_builtins/src/string/regex.rs | 6 +- crates/oxide_builtins/src/typed_array.rs | 58 ++-- crates/oxide_cli/src/bench/js_stress.rs | 2 + crates/oxide_runtime_api/src/coercion.rs | 29 +- crates/oxide_runtime_api/src/host.rs | 6 +- crates/oxide_test262/src/runner.rs | 3 +- crates/oxide_vm/src/async_disposable.rs | 2 +- crates/oxide_vm/src/async_from_sync.rs | 8 +- crates/oxide_vm/src/async_func.rs | 18 +- crates/oxide_vm/src/async_generator.rs | 31 ++- crates/oxide_vm/src/dispatch/call.rs | 20 +- crates/oxide_vm/src/dispatch/misc.rs | 24 +- crates/oxide_vm/src/dispatch/property.rs | 12 +- crates/oxide_vm/src/generator.rs | 19 +- crates/oxide_vm/src/promise/aggregate.rs | 42 +-- crates/oxide_vm/src/promise/constructor.rs | 38 +-- crates/oxide_vm/src/promise/reactions.rs | 8 +- crates/oxide_vm/src/promise/settlement.rs | 8 +- crates/oxide_vm/src/realm.rs | 142 +++++++++- crates/oxide_vm/src/session_gc.rs | 77 +++--- crates/oxide_vm/src/session_gc/tests.rs | 261 +++++++++--------- crates/oxide_vm/src/test262_host.rs | 4 +- crates/oxide_vm/src/vm/call.rs | 13 +- crates/oxide_vm/src/vm/dispatch_loop.rs | 20 +- crates/oxide_vm/src/vm/gc_hooks.rs | 47 ++-- crates/oxide_vm/src/vm/mod.rs | 24 +- crates/oxide_vm/src/vm/vmhost.rs | 10 +- crates/oxide_vm/src/vm_dispatch_ctrl.rs | 4 +- crates/oxide_vm/src/vm_dispatch_misc.rs | 10 +- crates/oxide_vm/src/vm_props.rs | 13 +- crates/oxide_vm/src/vm_runtime.rs | 8 +- crates/oxide_vm/src/vm_support.rs | 223 +++++---------- crates/oxide_vm/src/vm_support/tests.rs | 200 +++++++------- crates/oxide_vm/tests/builtins_error_tests.rs | 24 +- crates/oxide_vm/tests/session_arena_tests.rs | 3 +- 39 files changed, 796 insertions(+), 676 deletions(-) diff --git a/crates/oxide_builtins/src/iterator.rs b/crates/oxide_builtins/src/iterator.rs index 23bcbd12..338e8c1b 100644 --- a/crates/oxide_builtins/src/iterator.rs +++ b/crates/oxide_builtins/src/iterator.rs @@ -67,12 +67,16 @@ pub fn iterator_constructor(vm: &mut H, args: &[u8]) -> NativeResult /// 重建内置对象族,换新的 `Iterator` 函数对象;缓存旧指针会指向已释放对象。 /// lookup 失败(极端:global 无 `Iterator`)返回 undefined,不 panic。 pub fn iterator_constructor_getter(vm: &mut H, _args: &[u8]) -> NativeResult { - let global = vm.session().global_object(); + let (shape_id, global_ptr) = { + let session = vm.session(); + let global = session.global_object(); + (global.shape_id(), global.as_ptr() as *mut JsObject) + }; let si = vm.kernel_core().perm_interner().intern("Iterator").0; - let Some(pos) = vm.kernel_core().shape_forge().lookup_position(global.shape_id(), si) else { + let Some(pos) = vm.kernel_core().shape_forge().lookup_position(shape_id, si) else { return NativeResult::Ok(JsValue::undefined()); }; - NativeResult::Ok(global.get_prop_at(pos)) + NativeResult::Ok(unsafe { &*global_ptr }.get_prop_at(pos)) } /// `%IteratorPrototype%` 上 `Symbol.toStringTag` 访问器的 getter:返回 `"Iterator"`。 @@ -1486,15 +1490,18 @@ fn builtin_iterator_default_intact(vm: &mut H, value: JsValue, method if !method.is_object() { return false; } - let world = vm.session().builtin_world(); - let (anchor_ptr, anchor_name) = if is_array_value(value) { - (world.array_proto.as_ptr() as *mut JsObject, "values") - } else if is_typed_array_value(value) { - (world.typed_array_proto.as_ptr() as *mut JsObject, "values") - } else if is_map_value(value) { - (world.map_proto.as_ptr() as *mut JsObject, "entries") - } else { - (world.set_proto.as_ptr() as *mut JsObject, "values") + let (anchor_ptr, anchor_name) = { + let session = vm.session(); + let world = session.builtin_world(); + if is_array_value(value) { + (world.array_proto.as_ptr() as *mut JsObject, "values") + } else if is_typed_array_value(value) { + (world.typed_array_proto.as_ptr() as *mut JsObject, "values") + } else if is_map_value(value) { + (world.map_proto.as_ptr() as *mut JsObject, "entries") + } else { + (world.set_proto.as_ptr() as *mut JsObject, "values") + } }; let anchor_obj = unsafe { &*anchor_ptr }; let anchor_si = vm.kernel_core().perm_interner().intern(anchor_name).0; diff --git a/crates/oxide_builtins/src/map.rs b/crates/oxide_builtins/src/map.rs index 6e284c34..e37dded2 100644 --- a/crates/oxide_builtins/src/map.rs +++ b/crates/oxide_builtins/src/map.rs @@ -455,10 +455,11 @@ pub fn map_size(vm: &mut H, args: &[u8]) -> NativeResult { pub fn map_entries(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let _inner = native_try!(get_map_inner(vm, this_val)); + let proto_ptr = vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject; NativeResult::Ok(crate::iterator::make_collection_iterator( vm, this_val, - JsValue::from_js_object(vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject), + JsValue::from_js_object(proto_ptr), crate::iterator::MapSetMode::MapEntries, )) } @@ -467,10 +468,11 @@ pub fn map_entries(vm: &mut H, args: &[u8]) -> NativeResult { pub fn map_values(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let _inner = native_try!(get_map_inner(vm, this_val)); + let proto_ptr = vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject; NativeResult::Ok(crate::iterator::make_collection_iterator( vm, this_val, - JsValue::from_js_object(vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject), + JsValue::from_js_object(proto_ptr), crate::iterator::MapSetMode::MapValues, )) } @@ -479,10 +481,11 @@ pub fn map_values(vm: &mut H, args: &[u8]) -> NativeResult { pub fn map_keys(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let _inner = native_try!(get_map_inner(vm, this_val)); + let proto_ptr = vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject; NativeResult::Ok(crate::iterator::make_collection_iterator( vm, this_val, - JsValue::from_js_object(vm.session().builtin_world().map_iterator_proto.as_ptr() as *mut JsObject), + JsValue::from_js_object(proto_ptr), crate::iterator::MapSetMode::MapKeys, )) } diff --git a/crates/oxide_builtins/src/object.rs b/crates/oxide_builtins/src/object.rs index ec40cb0a..4555b954 100644 --- a/crates/oxide_builtins/src/object.rs +++ b/crates/oxide_builtins/src/object.rs @@ -1568,10 +1568,8 @@ pub fn object_from_entries(vm: &mut H, args: &[u8]) -> NativeResult { if entries_ptr.is_null() { return NativeResult::Err(crate::error::create_type_error(vm, "Object.fromEntries: argument must be iterable")); } - let obj = vm.alloc_object(JsObject::new_empty( - EMPTY_SHAPE_ID, - JsValue::from_js_object(vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject), - )); + let proto_ptr = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject; + let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr))); let target_val = JsValue::from_js_object(obj); // entry 数组的元素存元素区(元素区之外才是命名属性区),按元素区长度迭代。 let n: usize = unsafe { (*entries_ptr).array_prop_count } as usize; diff --git a/crates/oxide_builtins/src/set.rs b/crates/oxide_builtins/src/set.rs index e744172e..79ddecae 100644 --- a/crates/oxide_builtins/src/set.rs +++ b/crates/oxide_builtins/src/set.rs @@ -315,10 +315,11 @@ pub fn set_size(vm: &mut H, args: &[u8]) -> NativeResult { pub fn set_entries(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let _inner = native_try!(get_set_inner(vm, this_val)); + let proto_ptr = vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject; NativeResult::Ok(crate::iterator::make_collection_iterator( vm, this_val, - JsValue::from_js_object(vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject), + JsValue::from_js_object(proto_ptr), crate::iterator::MapSetMode::SetEntries, )) } @@ -327,10 +328,11 @@ pub fn set_entries(vm: &mut H, args: &[u8]) -> NativeResult { pub fn set_values(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let _inner = native_try!(get_set_inner(vm, this_val)); + let proto_ptr = vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject; NativeResult::Ok(crate::iterator::make_collection_iterator( vm, this_val, - JsValue::from_js_object(vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject), + JsValue::from_js_object(proto_ptr), crate::iterator::MapSetMode::SetValues, )) } @@ -340,10 +342,11 @@ pub fn set_keys(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let _inner = native_try!(get_set_inner(vm, this_val)); // Set 的 keys() 是 values() 的别名——返回同样的逐元素迭代器。 + let proto_ptr = vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject; NativeResult::Ok(crate::iterator::make_collection_iterator( vm, this_val, - JsValue::from_js_object(vm.session().builtin_world().set_iterator_proto.as_ptr() as *mut JsObject), + JsValue::from_js_object(proto_ptr), crate::iterator::MapSetMode::SetValues, )) } diff --git a/crates/oxide_builtins/src/string/regex.rs b/crates/oxide_builtins/src/string/regex.rs index e923c8b0..027cad1b 100644 --- a/crates/oxide_builtins/src/string/regex.rs +++ b/crates/oxide_builtins/src/string/regex.rs @@ -931,10 +931,8 @@ fn match_all_construct_invoke(vm: &mut H, this_val: JsValue, pattern_ // regexp = RegExpCreate(pattern, "g")。 let g_str = vm.new_string("g"); - let rx_val = match vm.construct_ctor( - JsValue::from_js_object(vm.session().builtin_world().regexp_constructor.as_ptr() as *mut JsObject), - &[pattern_val, g_str], - ) { + let ctor_ptr = vm.session().builtin_world().regexp_constructor.as_ptr() as *mut JsObject; + let rx_val = match vm.construct_ctor(JsValue::from_js_object(ctor_ptr), &[pattern_val, g_str]) { Ok(v) => v, Err(e) => return NativeResult::Err(e), }; diff --git a/crates/oxide_builtins/src/typed_array.rs b/crates/oxide_builtins/src/typed_array.rs index 73fab1c0..9bf4bcca 100644 --- a/crates/oxide_builtins/src/typed_array.rs +++ b/crates/oxide_builtins/src/typed_array.rs @@ -58,19 +58,22 @@ fn to_index(vm: &mut H, value: JsValue, msg: &str) -> Result(vm: &mut H, kind: TypedArrayKind) -> *mut JsObject { - let world = vm.session().builtin_world(); - match kind { - TypedArrayKind::Int8 => world.int8array_proto.as_ptr() as *mut JsObject, - TypedArrayKind::Uint8 => world.uint8array_proto.as_ptr() as *mut JsObject, - TypedArrayKind::Uint8Clamped => world.uint8clampedarray_proto.as_ptr() as *mut JsObject, - TypedArrayKind::Int16 => world.int16array_proto.as_ptr() as *mut JsObject, - TypedArrayKind::Uint16 => world.uint16array_proto.as_ptr() as *mut JsObject, - TypedArrayKind::Int32 => world.int32array_proto.as_ptr() as *mut JsObject, - TypedArrayKind::Uint32 => world.uint32array_proto.as_ptr() as *mut JsObject, - TypedArrayKind::Float32 => world.float32array_proto.as_ptr() as *mut JsObject, - TypedArrayKind::Float64 => world.float64array_proto.as_ptr() as *mut JsObject, - TypedArrayKind::BigInt64 => world.bigint64array_proto.as_ptr() as *mut JsObject, - TypedArrayKind::BigUint64 => world.biguint64array_proto.as_ptr() as *mut JsObject, + { + let session = vm.session(); + let world = session.builtin_world(); + match kind { + TypedArrayKind::Int8 => world.int8array_proto.as_ptr() as *mut JsObject, + TypedArrayKind::Uint8 => world.uint8array_proto.as_ptr() as *mut JsObject, + TypedArrayKind::Uint8Clamped => world.uint8clampedarray_proto.as_ptr() as *mut JsObject, + TypedArrayKind::Int16 => world.int16array_proto.as_ptr() as *mut JsObject, + TypedArrayKind::Uint16 => world.uint16array_proto.as_ptr() as *mut JsObject, + TypedArrayKind::Int32 => world.int32array_proto.as_ptr() as *mut JsObject, + TypedArrayKind::Uint32 => world.uint32array_proto.as_ptr() as *mut JsObject, + TypedArrayKind::Float32 => world.float32array_proto.as_ptr() as *mut JsObject, + TypedArrayKind::Float64 => world.float64array_proto.as_ptr() as *mut JsObject, + TypedArrayKind::BigInt64 => world.bigint64array_proto.as_ptr() as *mut JsObject, + TypedArrayKind::BigUint64 => world.biguint64array_proto.as_ptr() as *mut JsObject, + } } } @@ -100,19 +103,22 @@ fn ta_gpf_proto(vm: &mut H, kind: TypedArrayKind) -> Result(vm: &mut H, kind: TypedArrayKind) -> JsValue { - let world = vm.session().builtin_world(); - let ctor = match kind { - TypedArrayKind::Int8 => world.int8array_constructor.clone(), - TypedArrayKind::Uint8 => world.uint8array_constructor.clone(), - TypedArrayKind::Uint8Clamped => world.uint8clampedarray_constructor.clone(), - TypedArrayKind::Int16 => world.int16array_constructor.clone(), - TypedArrayKind::Uint16 => world.uint16array_constructor.clone(), - TypedArrayKind::Int32 => world.int32array_constructor.clone(), - TypedArrayKind::Uint32 => world.uint32array_constructor.clone(), - TypedArrayKind::Float32 => world.float32array_constructor.clone(), - TypedArrayKind::Float64 => world.float64array_constructor.clone(), - TypedArrayKind::BigInt64 => world.bigint64array_constructor.clone(), - TypedArrayKind::BigUint64 => world.biguint64array_constructor.clone(), + let ctor = { + let session = vm.session(); + let world = session.builtin_world(); + match kind { + TypedArrayKind::Int8 => world.int8array_constructor.clone(), + TypedArrayKind::Uint8 => world.uint8array_constructor.clone(), + TypedArrayKind::Uint8Clamped => world.uint8clampedarray_constructor.clone(), + TypedArrayKind::Int16 => world.int16array_constructor.clone(), + TypedArrayKind::Uint16 => world.uint16array_constructor.clone(), + TypedArrayKind::Int32 => world.int32array_constructor.clone(), + TypedArrayKind::Uint32 => world.uint32array_constructor.clone(), + TypedArrayKind::Float32 => world.float32array_constructor.clone(), + TypedArrayKind::Float64 => world.float64array_constructor.clone(), + TypedArrayKind::BigInt64 => world.bigint64array_constructor.clone(), + TypedArrayKind::BigUint64 => world.biguint64array_constructor.clone(), + } }; JsValue::from_js_object(ctor.as_ptr() as *mut JsObject) } diff --git a/crates/oxide_cli/src/bench/js_stress.rs b/crates/oxide_cli/src/bench/js_stress.rs index 2a979413..371a32f1 100644 --- a/crates/oxide_cli/src/bench/js_stress.rs +++ b/crates/oxide_cli/src/bench/js_stress.rs @@ -107,6 +107,8 @@ pub fn run_js_stress_bench(config: &BenchConfig, kernel: &Arc, pool: let gc_bytes_freed = gc_stats.last_collection_bytes_freed; let gc_objects_scanned = gc_stats.last_collection_objects_scanned; let gc_collection_us = gc_stats.last_collection_duration_us; + // 统计已读尽:释放读守卫,使下方强制 GC 可借 vm。 + drop(gc_stats); let session_objects = vm.session_object_count().saturating_sub(pre_session); let session_bytes = vm.session_bytes_allocated(); let epoch_objects = vm.epoch_object_count().saturating_sub(pre_epoch); diff --git a/crates/oxide_runtime_api/src/coercion.rs b/crates/oxide_runtime_api/src/coercion.rs index 0eb2b922..d33d47c5 100644 --- a/crates/oxide_runtime_api/src/coercion.rs +++ b/crates/oxide_runtime_api/src/coercion.rs @@ -803,19 +803,22 @@ pub fn to_object(val: JsValue, host: &mut H) -> Result &Arc; - fn session(&self) -> &KernelSession; + /// 只读访问当前 session(builtin world 与 global object)。 + /// + /// 返回 `Ref` 守卫(session 入 `RefCell` 后无法再给稳定 `&`):调用方 + /// 在单表达式内消费,不跨 `borrow_mut` 长存。 + fn session(&self) -> std::cell::Ref<'_, KernelSession>; // 属性解析 fn property_key_si(&mut self, val: JsValue) -> u32; diff --git a/crates/oxide_test262/src/runner.rs b/crates/oxide_test262/src/runner.rs index 5b65ba87..7c243f8d 100644 --- a/crates/oxide_test262/src/runner.rs +++ b/crates/oxide_test262/src/runner.rs @@ -200,7 +200,8 @@ fn run_test_inner( /// 读取异步测试捕获的 `$DONE` 输出字符串。 pub(crate) fn read_async_output(vm: &Vm) -> String { let si = vm.kernel_core().perm_interner().intern("$__test262_async_result").0; - let global = vm.session().global_object(); + let session = vm.session(); + let global = session.global_object(); let val = vm .kernel_core() .shape_forge() diff --git a/crates/oxide_vm/src/async_disposable.rs b/crates/oxide_vm/src/async_disposable.rs index 24d197aa..36b22562 100644 --- a/crates/oxide_vm/src/async_disposable.rs +++ b/crates/oxide_vm/src/async_disposable.rs @@ -83,7 +83,7 @@ pub(crate) fn dispose_async(vm: &mut Vm, args: &[u8]) -> NativeResult { 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.realm.session.builtin_world().fn_proto_val(); + let fn_proto = vm.realm.session.borrow().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 函数项。 diff --git a/crates/oxide_vm/src/async_from_sync.rs b/crates/oxide_vm/src/async_from_sync.rs index f35e8701..1fa5f60f 100644 --- a/crates/oxide_vm/src/async_from_sync.rs +++ b/crates/oxide_vm/src/async_from_sync.rs @@ -81,7 +81,7 @@ impl Vm { let return_fn = make_wrapper_fn(self, async_from_sync_return as *const ()); let throw_fn = make_wrapper_fn(self, async_from_sync_throw as *const ()); let self_fn = make_wrapper_fn(self, async_from_sync_symbol_async_iterator as *const ()); - let object_proto = self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let object_proto = self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject; let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto))); let obj = unsafe { &mut *ptr }; let inner_si = self.kernel_core.perm_interner().intern(INNER_PROP).0; @@ -100,7 +100,7 @@ impl Vm { /// 构造包装器方法 native 函数对象(name/length 属性由 `add_fn_name_length` 补全)。 fn make_wrapper_fn(vm: &mut Vm, native_fn: *const ()) -> JsValue { - let fn_proto = vm.realm.session.builtin_world().fn_proto_val(); + let fn_proto = vm.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: native_fn 来自 NativeFn 函数项。 @@ -235,7 +235,7 @@ fn close_sync_iterator(vm: &mut Vm, inner: JsValue) { /// 构造 valueWrapper 拒绝时关闭迭代器的闭包:携带目标 reject 与内层同步迭代器。 fn make_reject_close_fn(vm: &mut Vm, reject: JsValue, inner: JsValue) -> JsValue { - let fn_proto = vm.realm.session.builtin_world().fn_proto_val(); + let fn_proto = vm.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: async_from_sync_reject_close 是 NativeFn 函数项。 @@ -271,7 +271,7 @@ fn async_from_sync_reject_close(vm: &mut Vm, args: &[u8]) -> NativeResult { /// 构造 AsyncFromSyncIteratorContinuation 的 value unwrap 闭包:携带目标 resolve 与 /// done 标志,valueWrapper settle 后把解开值包成 `{value, done}` 结算。 fn make_unwrap_fn(vm: &mut Vm, resolve: JsValue, done: bool) -> JsValue { - let fn_proto = vm.realm.session.builtin_world().fn_proto_val(); + let fn_proto = vm.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: async_from_sync_unwrap 是 NativeFn 函数项。 diff --git a/crates/oxide_vm/src/async_func.rs b/crates/oxide_vm/src/async_func.rs index 638474aa..f353fe62 100644 --- a/crates/oxide_vm/src/async_func.rs +++ b/crates/oxide_vm/src/async_func.rs @@ -88,7 +88,7 @@ impl Vm { &mut self, callee: JsValue, this_value: JsValue, args: &[JsValue], promise: JsValue, resolve: JsValue, reject: JsValue, ) -> JsValue { - let proto_val = JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject); let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); let obj = unsafe { &mut *ptr }; obj.type_tag = JsObject::OBJ_TYPE_ASYNC; @@ -342,7 +342,7 @@ impl Vm { /// 构造 await 恢复闭包:携带目标异步上下文对象,区分 fulfill/reject 角色。 fn make_async_await_resume_fn(&mut self, ctx: JsValue, reject_role: bool) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: async_await_resume_closure 是 NativeFn 函数项。 @@ -361,7 +361,7 @@ impl Vm { /// 构造逃出关闭结算闭包:携带目标异步上下文对象与 return() promise,区分 /// fulfill/reject 角色。 pub(crate) fn make_async_escape_close_fn(&mut self, ctx: JsValue, promise: JsValue, reject_role: bool) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: async_escape_close_closure 是 NativeFn 函数项。 @@ -596,7 +596,7 @@ fn async_escape_close_closure(vm: &mut Vm, args: &[u8]) -> NativeResult { pub(crate) fn init_async_intrinsics(vm: &mut Vm) { let sf = vm.kernel_core.perm_interner().as_ref(); let sh = vm.kernel_core.shape_forge().as_ref(); - let fn_proto_val = vm.realm.session.builtin_world().fn_proto_val(); + let fn_proto_val = vm.realm.session.borrow().builtin_world().fn_proto_val(); // %AsyncFunction% 构造器:proto = Function.prototype,动态编译异步函数。 // 选择性重建复用:前轮构造器按键迁移(prototype 槽在下方 P 槽换入后重指 @@ -607,13 +607,13 @@ pub(crate) fn init_async_intrinsics(vm: &mut Vm) { let af_ctor_fn_ptr = unsafe { NativeFnPtr::from_raw(oxide_builtins::function::async_function_constructor:: as *const ()) }; let (af_ctor_ptr, af_ctor_is_new) = - match vm.realm.session.builtin_world().find_fn_wrapper(af_reuse_key, af_ctor_fn_ptr, 1) { + match vm.realm.session.borrow().builtin_world().find_fn_wrapper(af_reuse_key, af_ctor_fn_ptr, 1) { Some(ptr) => (ptr, false), None => { // [[Prototype]] = Function 构造器本体(与 Function 构造器同链, // `Object.getPrototypeOf(AsyncFunction) === Function` 语义)。 let fn_ctor_val = - JsValue::from_js_object(vm.realm.session.builtin_world().function_constructor.as_ptr() as *mut JsObject); + JsValue::from_js_object(vm.realm.session.borrow().builtin_world().function_constructor.as_ptr() as *mut JsObject); let mut af_ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_ctor_val)); af_ctor.set_function(true); af_ctor.set_native_arg_count(1); @@ -637,7 +637,7 @@ pub(crate) fn init_async_intrinsics(vm: &mut Vm) { af_ctor.set_shape_id(tag_shape); let af_ctor_ptr = Box::into_raw(af_ctor); // 登记进 world 释放表(带复用键):session 收尾统一释放构造器本体与属性区。 - vm.realm.session.builtin_world().track_fn_wrapper(af_ctor_ptr, af_reuse_key); + vm.realm.session.borrow().builtin_world().track_fn_wrapper(af_ctor_ptr, af_reuse_key); (af_ctor_ptr, true) } }; @@ -663,11 +663,11 @@ pub(crate) fn init_async_intrinsics(vm: &mut Vm) { // 使其与动态异步函数使用的 [[Prototype]] 同一对象,保证 // `AsyncFunction.prototype === (async () => {}).__proto__`。 // 复用构造器 prototype 槽原位改指新 P 原型(旧原型已随 P 换出释放)。 - Vm::swap_intrinsic_proto(&mut vm.realm.async_function_proto, *af_proto); + Vm::swap_intrinsic_proto(&mut vm.realm.async_function_proto.borrow_mut(), *af_proto); // SAFETY: af_ctor_ptr 为 Box 原分配(已登记释放表、生命周期覆盖 session),本 Vm 独占。 unsafe { let ctor_mut = &mut *af_ctor_ptr; - let proto_val = JsValue::from_js_object(vm.realm.async_function_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(vm.realm.async_function_proto.borrow().as_ptr() as *mut JsObject); let name_val = JsValue::perm_string(sf.string_ptr(sf.intern("AsyncFunction").0)); if af_ctor_is_new { let lpos = ctor_mut.push_prop(JsValue::int(1)); diff --git a/crates/oxide_vm/src/async_generator.rs b/crates/oxide_vm/src/async_generator.rs index f15a466c..e1e717cb 100644 --- a/crates/oxide_vm/src/async_generator.rs +++ b/crates/oxide_vm/src/async_generator.rs @@ -103,7 +103,7 @@ impl Vm { pub(crate) fn create_async_generator_object( &mut self, callee: JsValue, this_value: JsValue, args: &[JsValue], ) -> Result { - let gen_proto_val = JsValue::from_js_object(self.realm.async_generator_proto.as_ptr() as *mut JsObject); + let gen_proto_val = JsValue::from_js_object(self.realm.async_generator_proto.borrow().as_ptr() as *mut JsObject); let obj = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, gen_proto_val)); let obj_ref = unsafe { &mut *obj }; obj_ref.type_tag = JsObject::OBJ_TYPE_ASYNC_GENERATOR; @@ -681,7 +681,7 @@ impl Vm { /// 构造 await 恢复闭包:携带目标异步生成器上下文对象,区分 fulfill/reject 角色。 pub(crate) fn make_async_gen_await_resume_fn(&mut self, ctx: JsValue, reject_role: bool) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: async_gen_await_resume_closure 是 NativeFn 函数项。 @@ -702,7 +702,7 @@ impl Vm { fn make_async_gen_yield_unwrap_fn( &mut self, ctx: JsValue, promise: JsValue, raw: bool, reject_role: bool, ) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: async_gen_yield_unwrap_closure 是 NativeFn 函数项。 @@ -726,7 +726,7 @@ impl Vm { pub(crate) fn make_delegate_step_closure( &mut self, ctx: JsValue, step: DelegateStep, reject_role: bool, ) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: async_gen_delegate_step_closure 是 NativeFn 函数项。 @@ -747,7 +747,7 @@ impl Vm { /// 构造委托 return 请求值 Await 恢复闭包:携带目标异步生成器上下文与 /// fulfill/reject 角色,结算后以展开值完成生成器(拒绝时以拒绝原因终止)。 fn make_delegate_return_await_closure(&mut self, ctx: JsValue, reject_role: bool) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: async_gen_delegate_return_await_closure 是 NativeFn 函数项。 @@ -770,7 +770,7 @@ impl Vm { pub(crate) fn make_async_gen_escape_close_fn( &mut self, ctx: JsValue, promise: JsValue, reject_role: bool, ) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: async_gen_escape_closure 是 NativeFn 函数项。 @@ -1469,7 +1469,7 @@ fn async_dispose_settle_closure(vm: &mut Vm, args: &[u8]) -> NativeResult { impl Vm { /// 构造 `@@asyncDispose` 的 unwrap 闭包(无捕获状态,恒返回 undefined)。 fn make_async_dispose_unwrap_fn(&mut self) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: async_dispose_unwrap_closure 是 NativeFn 函数项。 @@ -1483,7 +1483,7 @@ impl Vm { /// 构造 `@@asyncDispose` 结算闭包:捕获能力 promise 与结算角色, /// 反应触发时以反应值结算能力 promise。 fn make_async_dispose_settle_fn(&mut self, promise: JsValue, reject_role: bool) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: async_dispose_settle_closure 是 NativeFn 函数项。 @@ -1507,8 +1507,9 @@ impl Vm { pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) { let sf = vm.kernel_core.perm_interner().as_ref(); let sh = vm.kernel_core.shape_forge().as_ref(); - let fn_proto_val = vm.realm.session.builtin_world().fn_proto_val(); - let world = vm.realm.session.builtin_world(); + let fn_proto_val = vm.realm.session.borrow().builtin_world().fn_proto_val(); + let session = vm.realm.session.borrow(); + let world = session.builtin_world(); // %AsyncGeneratorPrototype% 链到 %AsyncIteratorPrototype%(规范原型链), // 方法 next/return/throw 挂其自身。 let async_iterator_proto_val = JsValue::from_js_object(world.async_iterator_proto.as_ptr() as *mut JsObject); @@ -1547,7 +1548,7 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) { world, ag_label, ); - Vm::swap_intrinsic_proto(&mut vm.realm.async_generator_proto, *ag_proto); + Vm::swap_intrinsic_proto(&mut vm.realm.async_generator_proto.borrow_mut(), *ag_proto); // %AsyncGeneratorFunction.prototype%:proto = Function.prototype, // constructor = %AsyncGeneratorFunction%。占位构造器动态创建未实现, @@ -1600,7 +1601,7 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) { let proto2_si = sf.intern("prototype").0; let proto2_shape = sh.make_shape(agf_proto.shape_id(), proto2_si); agf_proto.set_shape_id(proto2_shape); - let ppos2 = agf_proto.push_prop(JsValue::from_js_object(vm.realm.async_generator_proto.as_ptr() as *mut JsObject)); + let ppos2 = agf_proto.push_prop(JsValue::from_js_object(vm.realm.async_generator_proto.borrow().as_ptr() as *mut JsObject)); agf_proto.set_data_meta(ppos2, PropAttributes::new(false, false, true)); // agf_proto[Symbol.toStringTag] = "AsyncGeneratorFunction"(数据属性,w/e/c = false/false/true)。 let tag2_key = @@ -1614,11 +1615,11 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) { // 构造器 length/name/prototype/@@toStringTag 属性(按形状序),prototype 指向 P 槽实例, // 使其与动态异步生成器函数使用的 [[Prototype]] 同一对象。复用构造器槽位已填充, // prototype 原位改指新 P 原型(旧原型已随 P 换出释放)。 - Vm::swap_intrinsic_proto(&mut vm.realm.async_generator_function_proto, *agf_proto); + Vm::swap_intrinsic_proto(&mut vm.realm.async_generator_function_proto.borrow_mut(), *agf_proto); // SAFETY: agf_ctor_ptr 为 Box 原分配(已登记释放表、生命周期覆盖 session),本 Vm 独占。 unsafe { let ctor_mut = &mut *agf_ctor_ptr; - let proto_val = JsValue::from_js_object(vm.realm.async_generator_function_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(vm.realm.async_generator_function_proto.borrow().as_ptr() as *mut JsObject); let name_val = JsValue::perm_string(sf.string_ptr(sf.intern("AsyncGeneratorFunction").0)); if agf_ctor_is_new { let lpos = ctor_mut.push_prop(JsValue::int(1)); @@ -1644,7 +1645,7 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) { // 绑定 global:AsyncGeneratorFunction 槽已存在则原位更新(full_reset 未重建 // global 时旧槽指向已弃 ctor),不存在则开新槽。 - let global_ptr = vm.realm.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = vm.realm.session.borrow().global_object().as_ptr() as *mut JsObject; // SAFETY: global 由 session 持有存活整个 session;本函数内只改其 shape/属性区, // 期间无 reset 或对象搬移。 let global = unsafe { &mut *global_ptr }; diff --git a/crates/oxide_vm/src/dispatch/call.rs b/crates/oxide_vm/src/dispatch/call.rs index 4db4f869..532c3a91 100644 --- a/crates/oxide_vm/src/dispatch/call.rs +++ b/crates/oxide_vm/src/dispatch/call.rs @@ -230,7 +230,7 @@ impl Vm { if current_cells[cell_idx].is_null() { // 占位 Cell 保持未初始化(TDZ)直到 MAKE_CELL 置位; // 若绑定为 var 则其初始化 MAKE_CELL 在函数序言先于任何读取执行。 - current_cells[cell_idx] = self.realm.gc.alloc_cell(JsValue::undefined(), false); + current_cells[cell_idx] = self.realm.gc.borrow().alloc_cell(JsValue::undefined(), false); } current_cells[cell_idx] }; @@ -275,14 +275,14 @@ impl Vm { } if current[cell_idx].is_null() { // 无占位 cell:新建。 - current[cell_idx] = self.realm.gc.alloc_cell(value, initialized); + current[cell_idx] = self.realm.gc.borrow().alloc_cell(value, initialized); } else { let existing_initialized = unsafe { &*current[cell_idx] }.is_initialized(); if !initialized && existing_initialized { // TDZ 占位而槽内是已初始化 cell:新建替换。循环体每迭代重执行 // 的 TDZ 占位不得原位翻新迭代 cell(已被本迭代前闭包捕获); // 已初始化 cell 保持身份,闭包 upvalue 保值。 - current[cell_idx] = self.realm.gc.alloc_cell(value, initialized); + current[cell_idx] = self.realm.gc.borrow().alloc_cell(value, initialized); } else { // 更新占位 cell(CREATE_CLOSURE 已建),使闭包 upvalue 指向的 // cell 值跟随初始化;初始化标志随指令 b 槽。 @@ -309,7 +309,7 @@ impl Vm { while current.len() <= cell_idx { current.push(std::ptr::null_mut()); } - current[cell_idx] = self.realm.gc.alloc_cell(value, true); + current[cell_idx] = self.realm.gc.borrow().alloc_cell(value, true); Ok(()) } @@ -334,7 +334,7 @@ impl Vm { } if current[cell_idx].is_null() { let val = self.regs[a]; - current[cell_idx] = self.realm.gc.alloc_cell(val, true); + current[cell_idx] = self.realm.gc.borrow().alloc_cell(val, true); } let c = unsafe { &*current[cell_idx] }; if !c.is_initialized() { @@ -478,7 +478,7 @@ impl Vm { } else { self.regs[rd] }; - upvals[uv_idx] = self.realm.gc.alloc_cell(val, true); + upvals[uv_idx] = self.realm.gc.borrow().alloc_cell(val, true); self.regs[rd] = unsafe { (*upvals[uv_idx]).value }; return Ok(()); } @@ -532,7 +532,7 @@ impl Vm { } vm_debug!("STORE_UPVALUE len={} wrote existing", upvals.len()); } else { - upvals[uv_idx] = self.realm.gc.alloc_cell(src_val, true); + upvals[uv_idx] = self.realm.gc.borrow().alloc_cell(src_val, true); } } Ok(()) @@ -544,7 +544,7 @@ impl Vm { vm_trace!("CREATE_REGEXP rd={}", rd); let pat_val = self.regs[a]; let flags_val = self.regs[b]; - let ctor_ptr = self.realm.session.builtin_world().regexp_constructor.as_ptr() as *mut JsObject; + let ctor_ptr = self.realm.session.borrow().builtin_world().regexp_constructor.as_ptr() as *mut JsObject; let ctor = unsafe { &*ctor_ptr }; let Some(native_fn) = ctor.native_fn() else { self.raise_error_kind("TypeError", "RegExp constructor unavailable")?; @@ -1004,7 +1004,7 @@ impl Vm { // bound 包装:解包链后转发到最内层 target([[Construct]] 语义)。 if ctor_obj.type_tag == oxide_types::object::JsObject::OBJ_TYPE_BOUND { - let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject; let new_obj = self.alloc_object(JsObject::new_empty( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), @@ -1012,7 +1012,7 @@ impl Vm { return self.dispatch_new_bound(rd, constructor, new_obj, args, 0); } - let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject; let new_obj = self.alloc_object(JsObject::new_empty( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), diff --git a/crates/oxide_vm/src/dispatch/misc.rs b/crates/oxide_vm/src/dispatch/misc.rs index 77520077..27644054 100644 --- a/crates/oxide_vm/src/dispatch/misc.rs +++ b/crates/oxide_vm/src/dispatch/misc.rs @@ -39,8 +39,11 @@ impl Vm { return Err(format!("LOAD_GLOBAL constant index {idx} is not a string key")); } let si = self.property_key_si(key_val)?; - let global = self.realm.session.global_object(); - if let Some(val) = self.resolve_property(global, si) { + let global = { + let session = self.realm.session.borrow(); + session.global_object().clone() + }; + if let Some(val) = self.resolve_property(&global, si) { self.regs[rd] = val; Ok(false) } else { @@ -67,7 +70,8 @@ impl Vm { return Err(format!("LOAD_GLOBAL_TYPEOF constant index {idx} is not a string key")); } let si = self.property_key_si(key_val)?; - let global = self.realm.session.global_object(); + let session = self.realm.session.borrow(); + let global = session.global_object(); self.regs[rd] = self.resolve_property(global, si).unwrap_or(JsValue::undefined()); Ok(()) } @@ -215,7 +219,7 @@ impl Vm { pub(crate) fn dispatch_new_object(&mut self, rd: usize, instr: u32) -> Result<(), String> { let nprops = opcode::a(instr) as usize; vm_trace!("NEW_OBJECT rd={} nprops={}", rd, nprops); - let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject; let mut shape_id = EMPTY_SHAPE_ID; if nprops > 0 { let key_idxs: Vec = self.bytecode[self.pc..self.pc + nprops].to_vec(); @@ -258,7 +262,7 @@ impl Vm { /// - 登记 session 对象表并计入堆账目;回收由 session GC mark/sweep 承担。 pub(crate) fn dispatch_new_session_object(&mut self, rd: usize) -> Result<(), String> { vm_trace!("NEW_SESSION_OBJECT rd={}", rd); - let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject; let obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr)); let obj_ptr = self.alloc_session_object(obj); self.regs[rd] = JsValue::object(obj_ptr as *mut u8); @@ -278,7 +282,7 @@ impl Vm { Some(frame) => (frame.arguments_base, frame.arguments_count), None => (self.inline_args_base, self.inline_args_count), }; - let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject; let obj_ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr))); let obj = unsafe { &mut *obj_ptr }; obj.type_tag = JsObject::OBJ_TYPE_ARGUMENTS; @@ -312,7 +316,7 @@ impl Vm { let simple = self.active_module().is_some_and(|m| m.has_simple_params); if strict || !simple { // SAFETY: 指针由绑定层在 session 构造期写入,session 存活期内有效。 - let thrower_ptr = self.realm.session.builtin_world().throw_type_error.get(); + let thrower_ptr = self.realm.session.borrow().builtin_world().throw_type_error.get(); if thrower_ptr.is_null() { // 绑定层尚未写入共享对象(正常路径不出现):退化为数据属性保调用可用。 if let Err(msg) = @@ -367,7 +371,7 @@ impl Vm { let sym_iter_si = make_well_known_symbol_key(0); // SAFETY: array_proto 是 perm 层内置对象(BuiltinWorld 持有),地址稳定且 // 跨 epoch/session 存活,本调用期间无 GC 搬移。 - let array_proto = self.realm.session.builtin_world().array_proto.as_ptr(); + let array_proto = self.realm.session.borrow().builtin_world().array_proto.as_ptr(); let iter_val = match self.ordinary_get(unsafe { &*array_proto }, sym_iter_si, JsValue::undefined()) { Ok(v) => v, Err(msg) => return self.raise_error_kind("TypeError", &msg), @@ -397,7 +401,7 @@ impl Vm { }; let n = count as usize; let rest_len = n.saturating_sub(fixed_count); - let proto_ptr = self.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject; let obj_ptr = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), rest_len)); let obj = unsafe { &mut *obj_ptr }; @@ -417,7 +421,7 @@ impl Vm { pub(crate) fn dispatch_new_array(&mut self, rd: usize, instr: u32) { let n = opcode::imm16(instr) as usize; vm_trace!("NEW_ARRAY rd={} n={}", rd, n); - let proto_ptr = self.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject; let obj = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), n)); self.regs[rd] = JsValue::object(obj as *mut u8); } diff --git a/crates/oxide_vm/src/dispatch/property.rs b/crates/oxide_vm/src/dispatch/property.rs index 90c88f21..c0519924 100644 --- a/crates/oxide_vm/src/dispatch/property.rs +++ b/crates/oxide_vm/src/dispatch/property.rs @@ -88,27 +88,27 @@ impl Vm { } return Ok(Some(JsValue::undefined())); } - let proto_ptr = self.realm.session.builtin_world().string_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.borrow().builtin_world().string_proto.as_ptr() as *mut JsObject; let proto = unsafe { &*proto_ptr }; return self.ordinary_get(proto, prop_name_si, val).map(Some); } if val.is_int() || val.is_double() { - let proto_ptr = self.realm.session.builtin_world().number_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.borrow().builtin_world().number_proto.as_ptr() as *mut JsObject; let proto = unsafe { &*proto_ptr }; return self.ordinary_get(proto, prop_name_si, val).map(Some); } if val.is_bool() { - let proto_ptr = self.realm.session.builtin_world().boolean_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.borrow().builtin_world().boolean_proto.as_ptr() as *mut JsObject; let proto = unsafe { &*proto_ptr }; return self.ordinary_get(proto, prop_name_si, val).map(Some); } if val.is_bigint() { - let proto_ptr = self.realm.session.builtin_world().bigint_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.borrow().builtin_world().bigint_proto.as_ptr() as *mut JsObject; let proto = unsafe { &*proto_ptr }; return self.ordinary_get(proto, prop_name_si, val).map(Some); } if val.is_symbol() { - let proto_ptr = self.realm.session.builtin_world().symbol_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.borrow().builtin_world().symbol_proto.as_ptr() as *mut JsObject; let proto = unsafe { &*proto_ptr }; return self.ordinary_get(proto, prop_name_si, val).map(Some); } @@ -790,7 +790,7 @@ impl Vm { return Err(format!("DELETE_GLOBAL_PROP_C constant index {idx} is not a string key")); } let si = self.property_key_si(key_val)?; - let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject; // SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。 let obj = unsafe { &mut *global_ptr }; // 统一走共享删除逻辑(与 Reflect.deleteProperty 同源);不可配置返回 diff --git a/crates/oxide_vm/src/generator.rs b/crates/oxide_vm/src/generator.rs index 9c486dcd..3d378fc3 100644 --- a/crates/oxide_vm/src/generator.rs +++ b/crates/oxide_vm/src/generator.rs @@ -102,7 +102,7 @@ impl Vm { pub(crate) fn create_generator_object( &mut self, callee: JsValue, this_value: JsValue, args: &[JsValue], ) -> Result { - let gen_proto_val = JsValue::from_js_object(self.realm.generator_proto.as_ptr() as *mut JsObject); + let gen_proto_val = JsValue::from_js_object(self.realm.generator_proto.borrow().as_ptr() as *mut JsObject); let obj = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, gen_proto_val)); let obj_ref = unsafe { &mut *obj }; obj_ref.type_tag = JsObject::OBJ_TYPE_GENERATOR; @@ -797,12 +797,13 @@ fn generator_step_result(vm: &mut Vm, step: Result) -> Na pub(crate) fn init_generator_intrinsics(vm: &mut Vm) { let sf = vm.kernel_core.perm_interner().as_ref(); let sh = vm.kernel_core.shape_forge().as_ref(); - let fn_proto_val = vm.realm.session.builtin_world().fn_proto_val(); - let world = vm.realm.session.builtin_world(); + let fn_proto_val = vm.realm.session.borrow().builtin_world().fn_proto_val(); + let session = vm.realm.session.borrow(); + let world = session.builtin_world(); // %GeneratorPrototype%:proto = %IteratorPrototype%(生成器是迭代器,继承 // @@iterator 与 Iterator helper 方法),方法 next/return/throw。 let iterator_proto_val = - JsValue::from_js_object(vm.realm.session.builtin_world().iterator_proto.as_ptr() as *mut JsObject); + JsValue::from_js_object(vm.realm.session.borrow().builtin_world().iterator_proto.as_ptr() as *mut JsObject); let mut gen_proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, iterator_proto_val)); // 站点标签:与 AsyncGenerator 原型的同名方法槽(next/return/throw)区分复用键。 let gen_label = sf.intern("GeneratorPrototype").0; @@ -836,7 +837,7 @@ pub(crate) fn init_generator_intrinsics(vm: &mut Vm) { world, gen_label, ); - Vm::swap_intrinsic_proto(&mut vm.realm.generator_proto, *gen_proto); + Vm::swap_intrinsic_proto(&mut vm.realm.generator_proto.borrow_mut(), *gen_proto); // %GeneratorFunction.prototype%:proto = Function.prototype,constructor = %GeneratorFunction%。 let mut gf_proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto_val)); @@ -888,7 +889,7 @@ pub(crate) fn init_generator_intrinsics(vm: &mut Vm) { let proto2_si = sf.intern("prototype").0; let proto2_shape = sh.make_shape(gf_proto.shape_id(), proto2_si); gf_proto.set_shape_id(proto2_shape); - let ppos2 = gf_proto.push_prop(JsValue::from_js_object(vm.realm.generator_proto.as_ptr() as *mut JsObject)); + let ppos2 = gf_proto.push_prop(JsValue::from_js_object(vm.realm.generator_proto.borrow().as_ptr() as *mut JsObject)); gf_proto.set_data_meta(ppos2, oxide_types::object::PropAttributes::new(false, false, true)); // gf_proto[Symbol.toStringTag] = "GeneratorFunction"(数据属性,w/e/c = false/false/true)。 let tag2_key = @@ -902,11 +903,11 @@ pub(crate) fn init_generator_intrinsics(vm: &mut Vm) { // 构造器 length/name/prototype/@@toStringTag 属性(按形状序),prototype 指向 // P 槽实例,使其与动态生成器函数使用的 [[Prototype]] 同一对象。复用构造器槽位 // 已填充,prototype 原位改指新 P 原型(旧原型已随 P 换出释放)。 - Vm::swap_intrinsic_proto(&mut vm.realm.generator_function_proto, *gf_proto); + Vm::swap_intrinsic_proto(&mut vm.realm.generator_function_proto.borrow_mut(), *gf_proto); // SAFETY: gf_ctor_ptr 为 Box 原分配(已登记释放表、生命周期覆盖 session),本 Vm 独占。 unsafe { let ctor_mut = &mut *gf_ctor_ptr; - let proto_val = JsValue::from_js_object(vm.realm.generator_function_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(vm.realm.generator_function_proto.borrow().as_ptr() as *mut JsObject); let name_val = JsValue::perm_string(sf.string_ptr(sf.intern("GeneratorFunction").0)); if gf_ctor_is_new { let lpos = ctor_mut.push_prop(JsValue::int(1)); @@ -932,7 +933,7 @@ pub(crate) fn init_generator_intrinsics(vm: &mut Vm) { // 绑定 global:GeneratorFunction 槽已存在则原位更新(full_reset 未重建 global // 时旧槽指向已弃 ctor),不存在则开新槽。 - let global_ptr = vm.realm.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = vm.realm.session.borrow().global_object().as_ptr() as *mut JsObject; // SAFETY: global 由 session 持有存活整个 session;本函数内只改其 shape/属性区, // 期间无 reset 或对象搬移。 let global = unsafe { &mut *global_ptr }; diff --git a/crates/oxide_vm/src/promise/aggregate.rs b/crates/oxide_vm/src/promise/aggregate.rs index 917a1272..7957bc54 100644 --- a/crates/oxide_vm/src/promise/aggregate.rs +++ b/crates/oxide_vm/src/promise/aggregate.rs @@ -28,7 +28,7 @@ impl Vm { /// 创建聚合静态方法(all/race/allSettled/any)的共享记录对象:剩余计数 /// 初始 1,结果数组(race 无)与能力 resolve/reject 闭包全部存为自身属性。 fn make_agg_record(&mut self, values: JsValue, resolve: JsValue, reject: JsValue) -> JsValue { - let proto_val = JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject); let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); // SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;本次 native 调用内不搬移,借出期间无别名。 let obj = unsafe { &mut *ptr }; @@ -46,7 +46,7 @@ impl Vm { /// 创建 keyed 聚合静态方法的共享记录对象:剩余计数(初始 1)、结果平行数组、 /// 键平行数组与能力 resolve/reject 闭包存为自身属性。 fn make_agg_record_keyed(&mut self, values: JsValue, keys: JsValue, resolve: JsValue, reject: JsValue) -> JsValue { - let proto_val = JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject); let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); // SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;本次 native 调用内不搬移,借出期间无别名。 let obj = unsafe { &mut *ptr }; @@ -67,7 +67,7 @@ impl Vm { /// 初始 false。内部状态属性非枚举且追加在 length/name 之后,保持内建函数 /// "length 先于 name" 的属性序。 fn make_agg_element_fn(&mut self, native_fn: NativeFn, record: JsValue, index: i32) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: native_fn 是 NativeFn 函数项。 @@ -99,7 +99,7 @@ impl Vm { /// 构造 allSettled 的结算记录 `{status, : value}` 普通对象。 fn make_settled_record(&mut self, status: &str, value_field: &str, value: JsValue) -> JsValue { - let proto_val = JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject); let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); // SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 status/value 属性(含 new_string 分配)期间不搬移,借出期间无别名。 let obj = unsafe { &mut *ptr }; @@ -114,7 +114,7 @@ impl Vm { /// 构造 AggregateError 实例(proto = %AggregateError.prototype%):message 非 /// undefined 时 ToString 建自身属性,errors 存为数据属性。 fn make_aggregate_error(&mut self, errors: JsValue, message: JsValue) -> JsValue { - let proto_val = JsValue::from_js_object(self.realm.aggregate_error_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(self.realm.aggregate_error_proto.borrow().as_ptr() as *mut JsObject); let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); // SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 message/errors 数据属性期间不搬移,借出期间无别名。 let obj = unsafe { &mut *ptr }; @@ -135,7 +135,7 @@ impl Vm { /// 把 errors 可迭代值收集为新数组(IterableToList)。不可迭代抛 TypeError。 fn aggregate_errors_to_list(&mut self, errors: JsValue) -> Result { - let array_proto = JsValue::from_js_object(self.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject); + let array_proto = JsValue::from_js_object(self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject); let ptr = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0)); let list = JsValue::from_js_object(ptr); let mut index = 0usize; @@ -151,7 +151,7 @@ impl Vm { /// 建普通数组对象并依次写入元素(Promise.try 转发实参的承载)。 fn make_plain_array(&mut self, elements: Vec) -> JsValue { - let array_proto = JsValue::from_js_object(self.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject); + let array_proto = JsValue::from_js_object(self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject); let ptr = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0)); // SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;元素写入不搬移对象。 let obj = unsafe { &mut *ptr }; @@ -165,7 +165,7 @@ impl Vm { /// 自身属性(GC 边走属性区,免 native_data 接线);W 被构造器以 /// (resolve, reject) 实参调用。 fn make_try_wrapper(&mut self, executor: JsValue, call_args: Vec) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: promise_try_wrapper 是 NativeFn 函数项。 @@ -192,7 +192,7 @@ impl Vm { let sf = self.kernel_core.perm_interner().as_ref(); let sh = self.kernel_core.shape_forge().as_ref(); let error_proto_val = - JsValue::from_js_object(self.realm.session.builtin_world().error_proto.as_ptr() as *mut JsObject); + JsValue::from_js_object(self.realm.session.borrow().builtin_world().error_proto.as_ptr() as *mut JsObject); // %AggregateError.prototype%:proto = %Error.prototype%,constructor/name/message 数据属性。 let mut proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); @@ -214,7 +214,7 @@ impl Vm { // %AggregateError% 构造器:proto = %Error%(NativeError 构造器继承 Error 构造器),length 2。 let error_ctor_val = - JsValue::from_js_object(self.realm.session.builtin_world().error_constructor.as_ptr() as *mut JsObject); + JsValue::from_js_object(self.realm.session.borrow().builtin_world().error_constructor.as_ptr() as *mut JsObject); let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_ctor_val)); ctor.set_function(true); ctor.type_tag = JsObject::OBJ_TYPE_CONSTRUCTOR; @@ -238,22 +238,22 @@ impl Vm { ctor.set_data_meta(2u32, PropAttributes::new(false, false, false)); // 固定地址后互相接线:proto.constructor ↔ ctor.prototype。 - Self::swap_intrinsic_proto(&mut self.realm.aggregate_error_proto, *proto); - Self::swap_intrinsic_proto(&mut self.realm.aggregate_error_constructor, *ctor); + Self::swap_intrinsic_proto(&mut self.realm.aggregate_error_proto.borrow_mut(), *proto); + Self::swap_intrinsic_proto(&mut self.realm.aggregate_error_constructor.borrow_mut(), *ctor); // SAFETY: aggregate_error_proto 为堆址固定的 P(Arc 透明包装),本行前刚经 swap_intrinsic_proto 落地;与 ctor_mut 分属不同对象,无别名。 - let proto_mut = unsafe { &mut *self.realm.aggregate_error_proto.as_mut_ptr() }; + let proto_mut = unsafe { &mut *self.realm.aggregate_error_proto.borrow().as_mut_ptr() }; proto_mut - .set_prop_at(0u32, JsValue::from_js_object(self.realm.aggregate_error_constructor.as_ptr() as *mut JsObject)); + .set_prop_at(0u32, JsValue::from_js_object(self.realm.aggregate_error_constructor.borrow().as_ptr() as *mut JsObject)); // SAFETY: aggregate_error_constructor 同为堆址固定的 P(Arc 透明包装);此处写 prototype 槽位(下标 2),与 proto_mut 分属不同对象,无别名。 - let ctor_mut = unsafe { &mut *self.realm.aggregate_error_constructor.as_mut_ptr() }; - ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.realm.aggregate_error_proto.as_ptr() as *mut JsObject)); + let ctor_mut = unsafe { &mut *self.realm.aggregate_error_constructor.borrow().as_mut_ptr() }; + ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.realm.aggregate_error_proto.borrow().as_ptr() as *mut JsObject)); // 绑定 global(槽已存在则更新)。 - let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject; // SAFETY: global 对象由 session 持有,存活整个 session;本函数内只改其 shape/属性区,期间无 reset 或对象搬移。 let global = unsafe { &mut *global_ptr }; let si = self.kernel_core.perm_interner().intern("AggregateError").0; - let ctor_val = JsValue::from_js_object(self.realm.aggregate_error_constructor.as_ptr() as *mut JsObject); + let ctor_val = JsValue::from_js_object(self.realm.aggregate_error_constructor.borrow().as_ptr() as *mut JsObject); if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) { global.set_prop_at(pos, ctor_val); } else { @@ -356,7 +356,7 @@ pub(super) fn promise_static_with_resolvers(vm: &mut Vm, args: &[u8]) -> NativeR Ok(t) => t, Err(err) => return NativeResult::Err(err), }; - let object_proto = JsValue::from_js_object(vm.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let object_proto = JsValue::from_js_object(vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject); let ptr = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, object_proto)); // SAFETY: ptr 由 alloc_object 新建,返回非空 arena 指针;写 promise/resolve/reject 属性期间不搬移,借出期间无别名。 let obj = unsafe { &mut *ptr }; @@ -481,7 +481,7 @@ fn perform_promise_combine( let values = if kind == AggregateKind::Race { JsValue::undefined() } else { - let array_proto = JsValue::from_js_object(vm.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject); + let array_proto = JsValue::from_js_object(vm.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject); let ptr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 0)); JsValue::from_js_object(ptr) }; @@ -1049,7 +1049,7 @@ fn aggregate_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { let this = if this_val.is_object() && new_target.is_object() { this_val.as_js_object_ptr() } else { - let proto_val = JsValue::from_js_object(vm.realm.aggregate_error_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(vm.realm.aggregate_error_proto.borrow().as_ptr() as *mut JsObject); vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)) }; // SAFETY: this 来自构造路径预分配或 alloc_object 新建,均存活且本段无别名。 diff --git a/crates/oxide_vm/src/promise/constructor.rs b/crates/oxide_vm/src/promise/constructor.rs index f8e4e740..8f4d24ad 100644 --- a/crates/oxide_vm/src/promise/constructor.rs +++ b/crates/oxide_vm/src/promise/constructor.rs @@ -22,7 +22,7 @@ use super::{is_constructor_value, PromiseState, PromiseStateKind}; impl Vm { /// 创建空 Promise 对象(proto = `%Promise.prototype%`),状态盒为 Pending。 pub(super) fn create_promise_object(&mut self) -> JsValue { - let proto_val = JsValue::from_js_object(self.realm.promise_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(self.realm.promise_proto.borrow().as_ptr() as *mut JsObject); let ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); // SAFETY: `alloc_object` 返回非空 arena 指针;此处写 type_tag 与 native_data // (Box 由 Box::into_raw 分配,随对象释放),无别名。 @@ -114,11 +114,11 @@ impl Vm { // 的 receiver 原型链判定通过。 let is_data_view = std::ptr::eq( ctor.as_js_object_ptr(), - self.realm.session.builtin_world().data_view_constructor.as_ptr() as *mut JsObject, + self.realm.session.borrow().builtin_world().data_view_constructor.as_ptr() as *mut JsObject, ); let this_val = if is_data_view { let data_view_proto_val = - JsValue::from_js_object(self.realm.session.builtin_world().data_view_proto.as_ptr() as *mut JsObject); + JsValue::from_js_object(self.realm.session.borrow().builtin_world().data_view_proto.as_ptr() as *mut JsObject); JsValue::from_js_object(self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, data_view_proto_val))) } else { let this_ptr = self.alloc_ctor_this(nt_obj, ctor_obj, new_target)?; @@ -156,7 +156,7 @@ impl Vm { &mut self, nt_obj: &JsObject, ctor_obj: &JsObject, nt_val: JsValue, ) -> Result<*mut JsObject, JsValue> { let proto_si = self.kernel_core.perm_interner().intern("prototype").0; - let object_proto = JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let object_proto = JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject); let is_native_ctor = ctor_obj.native_fn().is_some() && ctor_obj.type_tag == JsObject::OBJ_TYPE_CONSTRUCTOR; let proto_val = if is_native_ctor { match self.resolve_property(nt_obj, proto_si) { @@ -257,10 +257,11 @@ impl Vm { pub(crate) fn init_promise_intrinsics(&mut self) { let sf = self.kernel_core.perm_interner().as_ref(); let sh = self.kernel_core.shape_forge().as_ref(); - let fn_proto_val = self.realm.session.builtin_world().fn_proto_val(); - let world = self.realm.session.builtin_world(); + let fn_proto_val = self.realm.session.borrow().builtin_world().fn_proto_val(); + let session = self.realm.session.borrow(); + let world = session.builtin_world(); let object_proto_val = - JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject); // %Promise.prototype%:proto = Object.prototype,方法 then/catch/finally + @@toStringTag。 let mut proto = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, object_proto_val)); @@ -325,25 +326,28 @@ impl Vm { ("withResolvers", promise_static_with_resolvers as *const (), 0), ("try", promise_static_try as *const (), 1), ); + // world 的最后一处使用在上:显式释放 session 读守卫, + // 使后续 &mut self 的初始化调用可借 self。 + drop(session); // 固定地址后互相接线:proto.constructor ↔ ctor.prototype。 - Self::swap_intrinsic_proto(&mut self.realm.promise_proto, *proto); - Self::swap_intrinsic_proto(&mut self.realm.promise_constructor, *ctor); + Self::swap_intrinsic_proto(&mut self.realm.promise_proto.borrow_mut(), *proto); + Self::swap_intrinsic_proto(&mut self.realm.promise_constructor.borrow_mut(), *ctor); // SAFETY: `promise_proto` 为 `P`(Arc 透明包装),堆址固定且本行前刚经 // swap_intrinsic_proto 落地;与下一处 ctor_mut 指向不同对象,无别名。 - let proto_mut = unsafe { &mut *self.realm.promise_proto.as_mut_ptr() }; - proto_mut.set_prop_at(0u32, JsValue::from_js_object(self.realm.promise_constructor.as_ptr() as *mut JsObject)); + let proto_mut = unsafe { &mut *self.realm.promise_proto.borrow().as_mut_ptr() }; + proto_mut.set_prop_at(0u32, JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject)); // SAFETY: `promise_constructor` 同为堆址固定的存活 `P`(Arc 透明包装); // 此处写 prototype 槽位(下标 2),与 proto_mut 分属不同对象,无别名。 - let ctor_mut = unsafe { &mut *self.realm.promise_constructor.as_mut_ptr() }; + let ctor_mut = unsafe { &mut *self.realm.promise_constructor.borrow().as_mut_ptr() }; // prototype 槽位在 length/name 之后(下标 2)。 - ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.realm.promise_proto.as_ptr() as *mut JsObject)); + ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.realm.promise_proto.borrow().as_ptr() as *mut JsObject)); // Promise[Symbol.species] 访问器:getter 返回 receiver,派生类沿静态原型链 // 解析 @@species 得自身构造器。ctor 经本函数每次调用全新构造,同键槽不累积。 crate::bindings::bind_accessor_getter_key( &self.kernel_core, - &self.realm.session, + &self.realm.session.borrow(), ctor_mut, oxide_types::private_key::make_well_known_symbol_key(oxide_types::private_key::WELL_KNOWN_SYMBOL_SPECIES), "get [Symbol.species]", @@ -351,12 +355,12 @@ impl Vm { ); // 绑定 global:槽已存在则更新(full_reset 未重建 global 时旧槽指向已弃 ctor)。 - let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject; // SAFETY: global 对象由 session 持有,存活整个 session;本函数内只改其 // shape/属性区,期间无 reset 或对象搬移。 let global = unsafe { &mut *global_ptr }; let si = self.kernel_core.perm_interner().intern("Promise").0; - let ctor_val = JsValue::from_js_object(self.realm.promise_constructor.as_ptr() as *mut JsObject); + let ctor_val = JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject); if let Some(pos) = self.kernel_core.shape_forge().lookup_position(global.shape_id(), si) { global.set_prop_at(pos, ctor_val); } else { @@ -392,7 +396,7 @@ fn promise_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "Promise executor is not a function")); } // GetPrototypeFromConstructor:异常原值上抛;非对象结果回落内建原型。 - let promise_proto_val = JsValue::from_js_object(vm.realm.promise_proto.as_ptr() as *mut JsObject); + let promise_proto_val = JsValue::from_js_object(vm.realm.promise_proto.borrow().as_ptr() as *mut JsObject); let new_target = vm.reg(255); let proto = if new_target.is_object() { let nt_ptr = new_target.as_js_object_ptr(); diff --git a/crates/oxide_vm/src/promise/reactions.rs b/crates/oxide_vm/src/promise/reactions.rs index 00d4fb9e..87e1b832 100644 --- a/crates/oxide_vm/src/promise/reactions.rs +++ b/crates/oxide_vm/src/promise/reactions.rs @@ -145,7 +145,7 @@ impl Vm { } }; if ctor.is_undefined() { - return Ok(JsValue::from_js_object(self.realm.promise_constructor.as_ptr() as *mut JsObject)); + return Ok(JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject)); } if !ctor.is_object() { return Err(oxide_builtins::error::create_type_error( @@ -287,7 +287,7 @@ impl Vm { /// 调 onFinally 后经 PromiseResolve(ctor, 返回值) 解包,再以直通 thunk /// 调 Invoke(p, "then")——fulfill 角色透传原值,reject 角色重抛原拒绝原因。 fn make_finally_handler(&mut self, on_finally: JsValue, ctor: JsValue, reject_role: bool) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: promise_finally_handler 是 NativeFn 函数项。 @@ -309,7 +309,7 @@ impl Vm { /// 构造 finally 直通 thunk(CreateBuiltinFunction(closure, 0, "")): /// fulfill 角色返回携带值,reject 角色抛携带的拒绝原因。 fn make_finally_thunk(&mut self, reject_role: bool, value: JsValue) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: finally_thunk 是 NativeFn 函数项。 @@ -360,7 +360,7 @@ fn promise_finally_handler(vm: &mut Vm, args: &[u8]) -> NativeResult { let c_si = vm.kernel_core.perm_interner().intern(FINALLY_CTOR_PROP).0; let ctor = vm .resolve_property(callee_obj, c_si) - .unwrap_or_else(|| JsValue::from_js_object(vm.realm.promise_constructor.as_ptr() as *mut JsObject)); + .unwrap_or_else(|| JsValue::from_js_object(vm.realm.promise_constructor.borrow().as_ptr() as *mut JsObject)); let rj_si = vm.kernel_core.perm_interner().intern(FINALLY_REJECT_PROP).0; let is_reject = vm .resolve_property(callee_obj, rj_si) diff --git a/crates/oxide_vm/src/promise/settlement.rs b/crates/oxide_vm/src/promise/settlement.rs index b09bc3ee..5c6f46d5 100644 --- a/crates/oxide_vm/src/promise/settlement.rs +++ b/crates/oxide_vm/src/promise/settlement.rs @@ -36,7 +36,7 @@ impl Vm { pub(super) fn new_promise_capability_with_ctor( &mut self, ctor: JsValue, ) -> Result<(JsValue, JsValue, JsValue), JsValue> { - let intrinsic = JsValue::from_js_object(self.realm.promise_constructor.as_ptr() as *mut JsObject); + let intrinsic = JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject); if oxide_runtime_api::same_value(ctor, intrinsic) { return Ok(self.new_promise_capability()); } @@ -71,7 +71,7 @@ impl Vm { /// 构造 GetCapabilitiesExecutor:调用时把 resolve/reject 存入自身 prop。 fn make_capability_executor(&mut self) -> JsValue { - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: capability_executor 是 NativeFn 函数项。 @@ -90,7 +90,7 @@ impl Vm { /// 仍阻止二次结算。 pub(super) fn make_resolve_reject_fn(&mut self, promise: JsValue, reject_role: bool, delegated: bool) -> JsValue { let native_fn: NativeFn = if reject_role { promise_reject_closure } else { promise_resolve_closure }; - let fn_proto = self.realm.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.borrow().builtin_world().fn_proto_val(); let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, fn_proto); func.set_function(true); // SAFETY: native_fn 是 NativeFn 函数项。 @@ -314,7 +314,7 @@ impl Vm { return Err(exc); } }; - let intrinsic = JsValue::from_js_object(self.realm.promise_constructor.as_ptr() as *mut JsObject); + let intrinsic = JsValue::from_js_object(self.realm.promise_constructor.borrow().as_ptr() as *mut JsObject); if oxide_runtime_api::same_value(ctor, intrinsic) { return Ok(value); } diff --git a/crates/oxide_vm/src/realm.rs b/crates/oxide_vm/src/realm.rs index 94a7a7c0..3bba844d 100644 --- a/crates/oxide_vm/src/realm.rs +++ b/crates/oxide_vm/src/realm.rs @@ -1,44 +1,156 @@ //! 每 VM 的 realm 组合:内核会话(builtin world 与 global 对象)、 //! session GC 簿记与 10 个内建原型槽。 //! -//! `Vm` 以单个 `realm: Realm` 字段持有本组合。`kernel_core` -//! 留在 `Vm` 上不进 Realm(`active_vms` 边界守卫计数是 per-VM 语义, -//! `KernelCore` 是跨 realm 共享结构,多 realm 共享同一 kernel)。 +//! `Vm` 以单个 `realm: Arc` 字段持有本组合。可变组(session、gc、 +//! 10 个 P 字段)经 `RefCell` 内部可变性,使重置路径(full_reset / reset / +//! 收尾)在 `&Arc` 下改写;读路径经 `borrow()` 透传。Arc 计数归零时 +//! `Drop for Realm` 执行收尾(per-realm 消亡:多 Vm 共享同一 Realm 时最后 +//! 一个 Vm 析构不触发收尾,计数归零才触发)。 +//! +//! `kernel_core` 留在 `Vm` 上不进 Realm(`active_vms` 边界守卫计数是 +//! per-VM 语义,`KernelCore` 是跨 realm 共享结构,多 realm 共享同一 +//! kernel)。`iters` 同样留在 `Vm` 上:活跃 for-in / for-of 迭代器是 +//! per-VM 执行态,两个共享 Realm 的 Vm 须有独立迭代器栈。 + +use std::cell::RefCell; use oxide_kernel::kernel::KernelSession; use oxide_types::mem::P; use oxide_types::object::JsObject; +use crate::session_gc::SessionGc; use crate::vm_state::GcState; /// 每 VM 的 realm 组合:内核会话(builtin world 与 global 对象)、 /// session GC 簿记与 10 个内建原型槽。 /// +/// 可变组经 `RefCell` 内部可变性:重置路径(selective_reset / rebind / +/// record_snapshot / 整体替换、水位复位 / 表排空 / 账目清零、 +/// object_prototype 重赋)在 `&Arc` 下改写,读路径经 `borrow()` +/// 透传。 +/// /// 字段顺序按原 `Vm` 声明顺序(session 先、10 个 P 字段次之、gc 最后), /// 保 Drop 相对顺序。 pub(crate) struct Realm { - pub(crate) session: KernelSession, + pub(crate) session: RefCell, /// `%Object.prototype%`:session 的 Object 原型(global 的 `[[Prototype]]` 挂它)。 - pub(crate) object_prototype: P, + pub(crate) object_prototype: RefCell>, /// `%GeneratorPrototype%`:生成器实例的原型(next/return/throw 方法挂此)。 - pub(crate) generator_proto: P, + pub(crate) generator_proto: RefCell>, /// `%GeneratorFunction.prototype%`:生成器函数对象的原型(`constructor` 指向 /// `%GeneratorFunction%`,使 `g.constructor.name` 解析为 "GeneratorFunction")。 - pub(crate) generator_function_proto: P, + pub(crate) generator_function_proto: RefCell>, /// `%Promise%` 构造器(resolve/reject 静态方法挂此,global 的 Promise 槽指向它)。 - pub(crate) promise_constructor: P, + pub(crate) promise_constructor: RefCell>, /// `%Promise.prototype%`:Promise 实例的原型(then/catch/finally 方法挂此)。 - pub(crate) promise_proto: P, + pub(crate) promise_proto: RefCell>, /// `%AggregateError%` 构造器(Promise.any 拒绝时构造 AggregateError 用)。 - pub(crate) aggregate_error_constructor: P, + pub(crate) aggregate_error_constructor: RefCell>, /// `%AggregateError.prototype%`(proto = %Error.prototype%)。 - pub(crate) aggregate_error_proto: P, + pub(crate) aggregate_error_proto: RefCell>, /// `%AsyncFunction.prototype%`:异步函数对象的原型(`constructor` 指向 `%AsyncFunction%`)。 - pub(crate) async_function_proto: P, + pub(crate) async_function_proto: RefCell>, /// `%AsyncGeneratorPrototype%`:异步生成器实例的原型(next/return/throw/@@asyncIterator)。 - pub(crate) async_generator_proto: P, + pub(crate) async_generator_proto: RefCell>, /// `%AsyncGeneratorFunction.prototype%`:异步生成器函数对象的原型。 - pub(crate) async_generator_function_proto: P, + pub(crate) async_generator_function_proto: RefCell>, /// session 堆与 GC 簿记(对象/字符串/BigInt/cell 四表 + 水位与账目)。 - pub(crate) gc: GcState, + pub(crate) gc: RefCell, +} + +impl Drop for Realm { + fn drop(&mut self) { + // 直接 drop(test262 每测试新建即弃)不经 reset / full_reset 路径: + // 统一收尾释放全部 session 堆数据与内建原型属性区,防逐测试累积泄漏。 + // per-realm 消亡:Arc 计数归零时恰好执行一次,多 Vm 共享同一 Realm + // 时最后一个 Vm 析构不触发收尾(计数未归零),归零才触发。 + self.teardown_intrinsic_protos(); + self.teardown_session_heap_data(); + // session 字段随后按声明顺序 drop → KernelSession::drop → + // teardown_builtins(幂等),与上两组释放集合不相交(oxide_kernel + // 不变量:登记表与 retire_replaced 对象集不相交、恰好一次)。 + } +} + +impl Realm { + /// 释放本 realm 内建原型 P 对象(生成器/Promise/异步族与 Object 原型)的 + /// 堆外属性区。 + /// + /// # 注意事项 + /// 对象本体随 Arc 引用归零释放(字段 drop);属性区须在此显式释放一次 + /// (`JsObject` 无 Drop 口径)。仅释放本 realm 独占(强引用计数为 1)的 + /// 副本:`object_prototype` 与 session world 共享 Arc,session 存活期 + /// 下一测试仍经 world 引用同一对象,其属性区归 session 收尾 + /// (`teardown_builtins`)释放。 + pub(crate) fn teardown_intrinsic_protos(&self) { + for p in [ + &self.object_prototype, + &self.generator_proto, + &self.generator_function_proto, + &self.promise_constructor, + &self.promise_proto, + &self.aggregate_error_constructor, + &self.aggregate_error_proto, + &self.async_function_proto, + &self.async_generator_proto, + &self.async_generator_function_proto, + ] { + let p = p.borrow(); + if p.strong_count() != 1 { + continue; + } + // SAFETY: 独占副本的属性区仅此一处释放并置空(幂等)。 + unsafe { + (&mut *p.as_mut_ptr()).release_raw_heap(); + } + } + } + + /// 释放全部 session 堆数据:session 对象(本体 + 堆数据 + upvalue 列表) + /// + session 串 + BigInt box + upvalue cell box。 + /// + /// 供 `full_reset` 与 `Drop` 共用——统一入口路径上对象本体全部为堆载体 + /// (`Box::from_raw` 恰好释放一次),堆数据与 upvalue 列表各恰好释放一次。 + /// 独占所有权免去重:死对象已出表不重复枚举。 + pub(crate) fn teardown_session_heap_data(&self) { + // 对象逐条独占释放:本体 + 堆数据 + upvalue 列表,顺序由 + // drop_dead_session_object 收口。 + for ptr in self.gc.borrow_mut().session_object_ptrs.drain(..) { + if ptr.is_null() { + continue; + } + // SAFETY: ptr 来自 session 对象表登记,收尾时仍指向合法对象; + // 表独占持有,无其他释放点。 + SessionGc::drop_dead_session_object(ptr); + } + self.free_session_string_heap_data(); + self.free_session_bigint_heap_data(); + self.gc.borrow_mut().free_cells(); + } + + /// 释放全部 session 堆 `JsString`。仅在完全隔离重置 + /// (`full_reset` / `clear_full_reset_state`)时调用,此时没有存活的 + /// session 对象会引用它们。较轻量的 `reset()` 刻意保留它们,与 session + /// 串跨 eval 存活一致。 + fn free_session_string_heap_data(&self) { + for ptr in self.gc.borrow_mut().session_string_ptrs.drain(..) { + // SAFETY: 每个指针来自 new_string/new_cons_string 的 Box::into_raw, + // 且只在这里(或 sweep)恰好释放一次;内部连带释放 rope 扁平化产物。 + unsafe { + SessionGc::drop_session_string_box(ptr); + } + } + } + + /// 释放全部 session 堆 BigInt box。仅在完全隔离重置(`full_reset`)时 + /// 调用,此时没有存活的 session 对象/寄存器会引用它们。 + fn free_session_bigint_heap_data(&self) { + for ptr in self.gc.borrow_mut().session_bigint_ptrs.borrow_mut().drain(..) { + // SAFETY: 每个指针来自 new_bigint 的 Box::into_raw(Box::new(BigInt)), + // 且只在这里恰好释放一次。 + unsafe { + drop(Box::from_raw(ptr)); + } + } + } } diff --git a/crates/oxide_vm/src/session_gc.rs b/crates/oxide_vm/src/session_gc.rs index 2d4cd46a..0c50eb74 100644 --- a/crates/oxide_vm/src/session_gc.rs +++ b/crates/oxide_vm/src/session_gc.rs @@ -50,7 +50,7 @@ impl SessionGc { /// 清空 session 对象表的 GC mark 位。置位不随表清位消失,残留位会让下一次 /// `mark` 的 DFS 在已标对象处短路,漏扫其新增引用边。 pub(crate) fn clear_all_marks(&mut self, vm: &mut Vm) { - vm_debug!("[GC] clear_all_marks: {} session objects", vm.realm.gc.session_object_ptrs.len()); + vm_debug!("[GC] clear_all_marks: {} session objects", vm.realm.gc.borrow().session_object_ptrs.len()); self.clear_session_marks(vm); } @@ -59,7 +59,7 @@ impl SessionGc { /// 与收集前历史残留——残留 true 会让下一次 `mark` 的 DFS 在已标对象处 /// 短路,漏扫其新增引用边。 pub(crate) fn clear_session_marks(&mut self, vm: &mut Vm) { - for &ptr in &vm.realm.gc.session_object_ptrs { + for &ptr in &vm.realm.gc.borrow().session_object_ptrs { if ptr.is_null() { continue; } @@ -650,7 +650,7 @@ impl SessionGc { /// `sweep`(完整收集)与 `collect_in_run`(执行期收集)共用,保证两条 /// 收集路径的弱键定夺口径一致。返回 (存活数, 死数, 释放字节)。 fn sweep_in_place(&mut self, vm: &mut Vm) -> (u64, u64, u64) { - let old_ptrs = std::mem::take(&mut vm.realm.gc.session_object_ptrs); + let old_ptrs = std::mem::take(&mut vm.realm.gc.borrow_mut().session_object_ptrs); let mut survivors = Vec::with_capacity(old_ptrs.len()); // 弱键定夺先于死对象释放:键对象(含死键)此刻全部仍分配, @@ -689,12 +689,15 @@ impl SessionGc { } } let live = survivors.len() as u64; - vm.realm.gc.session_object_ptrs = survivors; + vm.realm.gc.borrow_mut().session_object_ptrs = survivors; // 对象口径重算:存活对象头 + 堆数据求和(串/BigInt/cell 分量不在此口径, - // 由各清扫路径按存活补回,与完整收集的最终账目一致)。 - vm.realm.gc.session_bytes_allocated = vm - .realm.gc + // 由各清扫路径按存活补回,与完整收集的最终账目一致)。读入局部变量后 + // 释放借用再写回,不跨读写持借。 + let new_bytes: usize = vm + .realm + .gc + .borrow() .session_object_ptrs .iter() .filter(|&&ptr| !ptr.is_null()) @@ -704,6 +707,7 @@ impl SessionGc { size_of::() as u64 + Self::object_heap_data_bytes(obj) }) .sum::() as usize; + vm.realm.gc.borrow_mut().session_bytes_allocated = new_bytes; // 统计按对象口径累计(串/BigInt/cell 清扫在其上累加释放字节)。 self.total_bytes_freed = self.total_bytes_freed.saturating_add(freed_bytes); @@ -737,8 +741,8 @@ impl SessionGc { /// `session_bytes_allocated` 重置为仅对象),再补回存活字符串字节。 /// 返回释放的字节数。 pub(crate) fn sweep_session_strings(&mut self, vm: &mut Vm) -> u64 { - vm_debug!("[GC] sweep strings: {} string ptrs", vm.realm.gc.session_string_ptrs.len()); - let old = std::mem::take(&mut vm.realm.gc.session_string_ptrs); + vm_debug!("[GC] sweep strings: {} string ptrs", vm.realm.gc.borrow().session_string_ptrs.len()); + let old = std::mem::take(&mut vm.realm.gc.borrow_mut().session_string_ptrs); let mut freed = 0u64; let mut live_bytes = 0usize; let mut live = Vec::with_capacity(old.len()); @@ -756,8 +760,9 @@ impl SessionGc { freed += unsafe { Self::drop_dead_session_string(ptr) }; } } - vm.realm.gc.session_string_ptrs = live; - vm.realm.gc.session_bytes_allocated = vm.realm.gc.session_bytes_allocated.saturating_add(live_bytes); + vm.realm.gc.borrow_mut().session_string_ptrs = live; + let new_bytes = vm.realm.gc.borrow().session_bytes_allocated.saturating_add(live_bytes); + vm.realm.gc.borrow_mut().session_bytes_allocated = new_bytes; self.total_bytes_freed = self.total_bytes_freed.saturating_add(freed); self.last_collection_bytes_freed = self.last_collection_bytes_freed.saturating_add(freed); @@ -769,7 +774,7 @@ impl SessionGc { /// forwarding 表与根指针重写。在字符串清扫之后运行。 /// 返回释放的字节数。 pub(crate) fn sweep_session_bigints(&mut self, vm: &mut Vm) -> u64 { - let old = vm.realm.gc.session_bigint_ptrs.borrow_mut().drain(..).collect::>(); + let old = vm.realm.gc.borrow_mut().session_bigint_ptrs.borrow_mut().drain(..).collect::>(); let mut freed = 0u64; let mut live = Vec::with_capacity(old.len()); for ptr in old { @@ -788,7 +793,7 @@ impl SessionGc { } } } - *vm.realm.gc.session_bigint_ptrs.borrow_mut() = live; + *vm.realm.gc.borrow_mut().session_bigint_ptrs.borrow_mut() = live; self.total_bytes_freed = self.total_bytes_freed.saturating_add(freed); self.last_collection_bytes_freed = self.last_collection_bytes_freed.saturating_add(freed); @@ -804,7 +809,7 @@ impl SessionGc { /// 统计、不重算存活账目。 /// 返回释放的字节数。 pub(crate) fn sweep_session_cells(&mut self, vm: &mut Vm) -> u64 { - let old = vm.realm.gc.session_cell_ptrs.borrow_mut().drain(..).collect::>(); + let old = vm.realm.gc.borrow_mut().session_cell_ptrs.borrow_mut().drain(..).collect::>(); let mut freed = 0u64; let mut live = Vec::with_capacity(old.len()); for ptr in old { @@ -822,7 +827,7 @@ impl SessionGc { } } } - *vm.realm.gc.session_cell_ptrs.borrow_mut() = live; + *vm.realm.gc.borrow_mut().session_cell_ptrs.borrow_mut() = live; self.total_bytes_freed = self.total_bytes_freed.saturating_add(freed); self.last_collection_bytes_freed = self.last_collection_bytes_freed.saturating_add(freed); @@ -831,10 +836,11 @@ impl SessionGc { /// 完整收集的触发判断:对象、字符串或 BigInt 表非空,且字节账目已达阈值。 pub(crate) fn should_collect(&self, vm: &Vm) -> bool { - (!vm.realm.gc.session_object_ptrs.is_empty() - || !vm.realm.gc.session_string_ptrs.is_empty() - || !vm.realm.gc.session_bigint_ptrs.borrow().is_empty()) - && vm.realm.gc.session_bytes_allocated >= vm.kernel_core().config().session_gc_threshold + let gc = vm.realm.gc.borrow(); + (!gc.session_object_ptrs.is_empty() + || !gc.session_string_ptrs.is_empty() + || !gc.session_bigint_ptrs.borrow().is_empty()) + && gc.session_bytes_allocated >= vm.kernel_core().config().session_gc_threshold } /// 执行期字符串回收的触发判断:账目须超过水位(上次收集后的存活字节 + @@ -842,10 +848,11 @@ impl SessionGc { /// 保证死对象超阈值即被 reset 回收;执行期走增量水位,活串超阈值时不每指令 /// 重复触发无死串可回收的白跑。 pub(crate) fn should_collect_strings(&self, vm: &Vm) -> bool { - (!vm.realm.gc.session_object_ptrs.is_empty() - || !vm.realm.gc.session_string_ptrs.is_empty() - || !vm.realm.gc.session_bigint_ptrs.borrow().is_empty()) - && vm.realm.gc.session_bytes_allocated >= vm.realm.gc.string_gc_watermark + let gc = vm.realm.gc.borrow(); + (!gc.session_object_ptrs.is_empty() + || !gc.session_string_ptrs.is_empty() + || !gc.session_bigint_ptrs.borrow().is_empty()) + && gc.session_bytes_allocated >= gc.string_gc_watermark } /// debug 兜底:mark 之后断言所有存活对象持有的字符串边都已登记进 @@ -853,7 +860,7 @@ impl SessionGc { #[cfg(debug_assertions)] fn debug_assert_marked_object_strings_live(&self, vm: &Vm) { let mut live = HashSet::with_hasher(FxBuildHasher); - for &ptr in &vm.realm.gc.session_object_ptrs { + for &ptr in &vm.realm.gc.borrow().session_object_ptrs { if ptr.is_null() { continue; } @@ -942,7 +949,9 @@ impl SessionGc { // 扣减字符串账目(保留对象账目),再补回存活串字节。 let object_bytes: usize = vm - .realm.gc + .realm + .gc + .borrow() .session_object_ptrs .iter() .filter(|&&ptr| !ptr.is_null()) @@ -951,7 +960,7 @@ impl SessionGc { size_of::() as u64 + Self::object_heap_data_bytes(obj) }) .sum::() as usize; - vm.realm.gc.session_bytes_allocated = object_bytes; + vm.realm.gc.borrow_mut().session_bytes_allocated = object_bytes; let mut freed_bytes = self.sweep_session_strings(vm); freed_bytes += self.sweep_session_bigints(vm); freed_bytes += self.sweep_session_cells(vm); @@ -962,9 +971,10 @@ impl SessionGc { self.clear_all_marks(vm); // 抬高下次触发水位:本次存活字节 + 阈值增量,避免活串超阈值时每指令重复 - // 触发无死串可回收的完整 mark + 串清扫。 + // 触发无死串可回收的完整 mark + 串清扫。读入局部变量后释放借用再写回。 let threshold = vm.kernel_core().config().session_gc_threshold; - vm.realm.gc.string_gc_watermark = vm.realm.gc.session_bytes_allocated.saturating_add(threshold); + let new_watermark = vm.realm.gc.borrow().session_bytes_allocated.saturating_add(threshold); + vm.realm.gc.borrow_mut().string_gc_watermark = new_watermark; let elapsed = start.elapsed(); self.total_collections += 1; @@ -1034,18 +1044,21 @@ impl SessionGc { // 串,串表整体计入,与 strings-only 口径一致;BigInt/cell 不在手工 // 账目内,由 `run_alloc_bytes` 公式按表长单列)。 let mut string_bytes: usize = 0; - for &ptr in &vm.realm.gc.session_string_ptrs { + for &ptr in &vm.realm.gc.borrow().session_string_ptrs { if ptr.is_null() { continue; } // SAFETY: ptr 在字符串表登记,收尾前有效。 string_bytes += size_of::() + unsafe { (*ptr).payload_bytes() }; } - vm.realm.gc.session_bytes_allocated = vm.realm.gc.session_bytes_allocated.saturating_add(string_bytes); + let new_bytes = vm.realm.gc.borrow().session_bytes_allocated.saturating_add(string_bytes); + vm.realm.gc.borrow_mut().session_bytes_allocated = new_bytes; // 抬高下次触发水位:当前分配包络 + 阈值增量——存活包络超阈值时不每指令 - // 重复触发无死对象可回收的白跑。 - vm.realm.gc.gc_watermark = vm.run_alloc_bytes().saturating_add(vm.realm.gc.gc_threshold_cached); + // 重复触发无死对象可回收的白跑。读入局部变量后释放借用再写回。 + let alloc = vm.run_alloc_bytes(); + let threshold = vm.realm.gc.borrow().gc_threshold_cached; + vm.realm.gc.borrow_mut().gc_watermark = alloc.saturating_add(threshold); let elapsed = start.elapsed(); self.total_collections += 1; diff --git a/crates/oxide_vm/src/session_gc/tests.rs b/crates/oxide_vm/src/session_gc/tests.rs index d670740e..25c96b23 100644 --- a/crates/oxide_vm/src/session_gc/tests.rs +++ b/crates/oxide_vm/src/session_gc/tests.rs @@ -14,7 +14,7 @@ use oxide_emit::module::{ModuleKind, ModuleSourceLoader, ResolvedModule}; use oxide_runtime_api::NativeResult; fn plain_object(vm: &mut Vm) -> *mut JsObject { - let proto_ptr = vm.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let proto_ptr = vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject; vm.alloc_object(JsObject::new_empty( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), @@ -127,7 +127,7 @@ fn gc_roots_contains_registers_frames_and_root_roots() { assert!(has_ptr(&roots, frame_session)); assert!(has_ptr(&roots, this_session)); assert!(has_ptr(&roots, child_session)); - assert!(has_ptr(&roots, vm.realm.session.global_object().as_ptr() as *mut JsObject)); + assert!(has_ptr(&roots, vm.realm.session.borrow().global_object().as_ptr() as *mut JsObject)); assert!(!roots.is_empty()); assert!(roots.contains(&JsValue::from_js_object(root_session))); assert_eq!(vm.exception_value, Some(JsValue::from_js_object(root_session))); @@ -149,9 +149,9 @@ fn mark_phase_reaches_cycles_and_unreachable_are_unmarked() { let unreachable_session = unreachable; vm.regs[0] = JsValue::from_js_object(root_session); - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.mark(&vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; assert!(unsafe { (*root_session).is_gc_marked() }); assert!(unsafe { (*reachable_session).is_gc_marked() }); @@ -173,17 +173,19 @@ fn sweep_preserves_cycle_and_collects_unreachable() { let root_session = root; vm.regs[0] = JsValue::from_js_object(root_session); let dead_session = dead; - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.mark(&vm); let _ = gc.sweep(&mut vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; - assert_eq!(vm.realm.gc.session_object_ptrs.len(), 2); + assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 2); // 原地 sweep 不搬移存活对象:根与子保持原址(克隆体即原对象),死对象出表。 - assert!(vm.realm.gc.session_object_ptrs.contains(&root_session)); - assert!(!vm.realm.gc.session_object_ptrs.contains(&dead_session)); + assert!(vm.realm.gc.borrow().session_object_ptrs.contains(&root_session)); + assert!(!vm.realm.gc.borrow().session_object_ptrs.contains(&dead_session)); assert!(!vm - .realm.gc + .realm + .gc + .borrow() .session_object_ptrs .iter() .any(|ptr| unsafe { (*(*ptr)).is_gc_marked() })); @@ -192,7 +194,7 @@ fn sweep_preserves_cycle_and_collects_unreachable() { #[test] fn sweep_preserves_array_elements_and_collects_dead_element_object() { let mut vm = Vm::new(); - let array_proto = vm.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject; + let array_proto = vm.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject; let arr = vm.alloc_object(JsObject::new_array( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(array_proto), @@ -212,10 +214,10 @@ fn sweep_preserves_array_elements_and_collects_dead_element_object() { } vm.regs[0] = JsValue::from_js_object(arr_session); - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.mark(&vm); let _ = gc.sweep(&mut vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; // 原地 sweep 不搬移存活对象:数组保持原址、根指针不变,死元素对象 // 原地释放并出表,存活集合只剩数组与元素 0 的对象。 @@ -227,7 +229,7 @@ fn sweep_preserves_array_elements_and_collects_dead_element_object() { assert_eq!(unsafe { (*arr_addr).get_prop_at(0).as_js_object_ptr() }, live_session); // 元素 1 引用已断开:死元素对象被回收。 assert_eq!(unsafe { (*arr_addr).get_prop_at(1) }, JsValue::undefined()); - assert_eq!(vm.realm.gc.session_object_ptrs.len(), 2); + assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 2); } fn vm_with_low_threshold() -> Vm { @@ -247,7 +249,7 @@ fn native_ok(result: NativeResult) -> JsValue { /// 分配一个原型指向 Map.prototype 的占位对象并写入寄存器,作为构造器调用的 `this`。 fn map_this(vm: &mut Vm, reg: u8) -> JsValue { - let proto = vm.realm.session.builtin_world().map_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.borrow().builtin_world().map_proto.as_ptr() as *mut JsObject; let obj = vm.alloc_object(JsObject::new_empty( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto), @@ -259,7 +261,7 @@ fn map_this(vm: &mut Vm, reg: u8) -> JsValue { /// 分配一个原型指向 DataView.prototype 的占位对象并写入寄存器,作为构造器调用的 `this`。 fn data_view_this(vm: &mut Vm, reg: u8) -> JsValue { - let proto = vm.realm.session.builtin_world().data_view_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.borrow().builtin_world().data_view_proto.as_ptr() as *mut JsObject; let obj = vm.alloc_object(JsObject::new_empty( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto), @@ -271,7 +273,7 @@ fn data_view_this(vm: &mut Vm, reg: u8) -> JsValue { /// 分配一个原型指向 Set.prototype 的占位对象并写入寄存器,作为构造器调用的 `this`。 fn set_this(vm: &mut Vm, reg: u8) -> JsValue { - let proto = vm.realm.session.builtin_world().set_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.borrow().builtin_world().set_proto.as_ptr() as *mut JsObject; let obj = vm.alloc_object(JsObject::new_empty( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto), @@ -284,7 +286,7 @@ fn set_this(vm: &mut Vm, reg: u8) -> JsValue { /// 分配一个原型指向 DisposableStack.prototype 的占位对象并写入寄存器, /// 作为构造器调用的 `this`。 fn dispose_stack_this(vm: &mut Vm, reg: u8) -> JsValue { - let proto = vm.realm.session.builtin_world().disposable_stack_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.borrow().builtin_world().disposable_stack_proto.as_ptr() as *mut JsObject; let obj = vm.alloc_object(JsObject::new_empty( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto), @@ -297,7 +299,7 @@ fn dispose_stack_this(vm: &mut Vm, reg: u8) -> JsValue { /// 置函数位标志的占位对象(通过 `is_callable` 判定),供 adopt 的 /// onDispose 槽使用;测试不调用它。 fn function_placeholder(vm: &mut Vm) -> JsValue { - let proto = vm.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject; let obj = vm.alloc_object(JsObject::new_empty( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto), @@ -312,16 +314,16 @@ fn function_placeholder(vm: &mut Vm) -> JsValue { fn reset_maybe_collect_collects_after_threshold() { let mut vm = vm_with_low_threshold(); plain_object(&mut vm); - assert!(!vm.realm.gc.session_object_ptrs.is_empty()); - let tracked_before = vm.realm.gc.session_object_ptrs.len(); + assert!(!vm.realm.gc.borrow().session_object_ptrs.is_empty()); + let tracked_before = vm.realm.gc.borrow().session_object_ptrs.len(); vm.regs[0] = JsValue::undefined(); vm.regs[1] = JsValue::undefined(); vm.reset(); - assert!(vm.realm.gc.session_object_ptrs.len() <= tracked_before); - assert_eq!(vm.realm.gc.session_object_ptrs.len(), 0); - assert_eq!(vm.realm.gc.session_bytes_allocated, 0); + assert!(vm.realm.gc.borrow().session_object_ptrs.len() <= tracked_before); + assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 0); + assert_eq!(vm.realm.gc.borrow().session_bytes_allocated, 0); } #[test] @@ -330,7 +332,7 @@ fn gc_stats_summary_includes_collection() { plain_object(&mut vm); vm.regs[0] = JsValue::undefined(); vm.maybe_collect_session_gc(); - let summary = vm.realm.gc.session_gc.stats_summary(); + let summary = vm.realm.gc.borrow().session_gc.stats_summary(); assert!(summary.contains("[GC] collection")); } @@ -343,7 +345,7 @@ fn full_collect_keeps_global_root_edges_in_place() { } let old_ptr = obj; let key = vm.kernel_core.perm_interner().intern("gcRoot").0; - let global_ptr = vm.realm.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = vm.realm.session.borrow().global_object().as_ptr() as *mut JsObject; unsafe { let global = &mut *global_ptr; vm.set_or_create_prop_value(global, key, JsValue::from_js_object(old_ptr)); @@ -351,7 +353,8 @@ fn full_collect_keeps_global_root_edges_in_place() { vm.maybe_collect_session_gc(); - let global = vm.realm.session.global_object(); + let session = vm.realm.session.borrow(); + let global = session.global_object(); let pos = vm .kernel_core .shape_forge() @@ -401,15 +404,15 @@ fn session_gc_traces_map_object_key_and_value() { let map_session = map_value.as_js_object_ptr(); vm.regs.fill(JsValue::undefined()); vm.regs[0] = JsValue::from_js_object(map_session); - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.collect(&mut vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; let live_map = unsafe { &*vm.regs[0].as_js_object_ptr() }; let edges = map::map_native_edges(live_map); assert_eq!(edges.len(), 2); assert!(edges.iter().all(|value| vm.is_session_ptr(value.as_js_object_ptr()))); - assert_eq!(vm.realm.gc.session_object_ptrs.len(), 3); + assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 3); } #[test] @@ -425,15 +428,15 @@ fn session_gc_traces_set_object_key() { let set_session = set_value.as_js_object_ptr(); vm.regs.fill(JsValue::undefined()); vm.regs[0] = JsValue::from_js_object(set_session); - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.collect(&mut vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; let live_set = unsafe { &*vm.regs[0].as_js_object_ptr() }; let edges = set::set_native_edges(live_set); assert_eq!(edges.len(), 1); assert!(vm.is_session_ptr(edges[0].as_js_object_ptr())); - assert_eq!(vm.realm.gc.session_object_ptrs.len(), 2); + assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 2); } /// 造带条目盒的 WeakMap 形 session 对象(构造体落地前的单测代用形态)。 @@ -470,9 +473,9 @@ fn weak_map_entry_survives_promotion_with_live_key() { let value_root = oxide_builtins::weak_map::weak_map_probe_get(unsafe { &*wm_session }, JsValue::from_js_object(key)); vm.regs[2] = value_root; - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.collect(&mut vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; let live_wm = unsafe { &*vm.regs[0].as_js_object_ptr() }; assert_eq!(oxide_builtins::weak_map::weak_map_entry_count(live_wm), 1, "强可达键的条目须在收集后存活"); @@ -503,9 +506,9 @@ fn weak_map_entry_dropped_when_key_unrooted() { vm.regs.fill(JsValue::undefined()); vm.regs[0] = JsValue::from_js_object(wm_session); vm.regs[2] = JsValue::from_js_object(value); - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.collect(&mut vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; let live_wm = unsafe { &*vm.regs[0].as_js_object_ptr() }; assert_eq!(oxide_builtins::weak_map::weak_map_entry_count(live_wm), 0, "死键条目须在收集时丢弃"); @@ -535,9 +538,9 @@ fn weak_map_value_edge_keeps_value_alive() { vm.regs.fill(JsValue::undefined()); vm.regs[0] = JsValue::from_js_object(wm_session); vm.regs[1] = JsValue::from_js_object(key); - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.collect(&mut vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; let live_wm = unsafe { &*vm.regs[0].as_js_object_ptr() }; // 原地 sweep 不搬移对象:键与值边原地保留,按原键读回同一对象。 @@ -547,7 +550,7 @@ fn weak_map_value_edge_keeps_value_alive() { assert!(vm.is_session_ptr(stored.as_js_object_ptr())); // key、value、wm 三对象均存活(key/value 经寄存器根、wm 经寄存器根), // 原地保留原址。 - assert_eq!(vm.realm.gc.session_object_ptrs.len(), 3); + assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 3); } /// in-run 原地 sweep·死键面:无强根的 session 键在原地 sweep 按 mark 位判死, @@ -560,7 +563,7 @@ fn weak_map_dead_key_freed_by_in_run_sweep() { // 键无强根:仅表内弱边引用。 let key = plain_object(&mut vm); // P 键(builtin 原型,不可死):对照条目须保留。 - let p_key = JsValue::from_js_object(vm.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let p_key = JsValue::from_js_object(vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject); unsafe { oxide_builtins::weak_map::weak_map_insert( &mut *wm, @@ -574,7 +577,7 @@ fn weak_map_dead_key_freed_by_in_run_sweep() { vm.maybe_collect_in_run(); assert_eq!(vm.session_gc_stats().total_collections, 1, "in-run 收集应跑一轮"); - assert!(!vm.realm.gc.session_object_ptrs.contains(&key), "死键对象须出表"); + assert!(!vm.realm.gc.borrow().session_object_ptrs.contains(&key), "死键对象须出表"); let live_wm = unsafe { &*vm.regs[0].as_js_object_ptr() }; assert_eq!( oxide_builtins::weak_map::weak_map_entry_count(live_wm), @@ -645,8 +648,8 @@ fn session_gc_traces_map_string_and_bigint_value() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.realm.gc.session_string_ptrs.contains(&str_ptr)); - assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&str_ptr)); + assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr)); } /// 盒持唯一引用的 session 串与 BigInt 经 Set native 边进入存活集: @@ -672,8 +675,8 @@ fn session_gc_traces_set_string_and_bigint_value() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.realm.gc.session_string_ptrs.contains(&str_ptr)); - assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&str_ptr)); + assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr)); } /// 盒持唯一引用的 session 串与 BigInt 经资源栈条目边进入存活集: @@ -702,8 +705,8 @@ fn session_gc_traces_dispose_stack_string_and_bigint_value() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.realm.gc.session_string_ptrs.contains(&str_ptr)); - assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&str_ptr)); + assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr)); } /// 挂起帧内期望值 BigInt 查找:挂起点唯一持有期望值(帧内死槽残留的 @@ -740,8 +743,8 @@ fn session_gc_traces_promise_string_and_bigint_result() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.realm.gc.session_string_ptrs.contains(&str_ptr)); - assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&str_ptr)); + assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr)); } /// 挂起生成器帧寄存器持唯一引用 session BigInt:yield 挂起时 b 存活 @@ -771,7 +774,7 @@ fn session_gc_traces_suspended_generator_bigint_value() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr)); } /// 挂起异步函数帧持唯一引用 session BigInt:body await 永不结算的 promise, @@ -815,7 +818,7 @@ fn session_gc_traces_suspended_async_function_bigint_value() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr)); } /// 挂起异步生成器帧持唯一引用 session BigInt:首次 next() 挂起于 yield, @@ -844,7 +847,7 @@ fn session_gc_traces_suspended_async_generator_bigint_value() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().contains(&bi_ptr)); } #[test] @@ -883,9 +886,9 @@ fn session_gc_keeps_shared_array_buffer_alive_through_view_native_edges() { let root_session = root; vm.regs.fill(JsValue::undefined()); vm.regs[0] = JsValue::from_js_object(root_session); - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.collect(&mut vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; let live_root = unsafe { &*vm.regs[0].as_js_object_ptr() }; let live_typed = live_root.get_prop_at(0); @@ -936,16 +939,16 @@ fn session_gc_rewrites_buffer_retained_only_by_data_view_native_edge() { let view_session = view.as_js_object_ptr(); vm.regs.fill(JsValue::undefined()); vm.regs[0] = JsValue::from_js_object(view_session); - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.collect(&mut vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; let live_view = vm.regs[0]; let live_view_obj = unsafe { &*live_view.as_js_object_ptr() }; let edges = data_view::data_view_native_edges(live_view_obj); assert_eq!(edges.len(), 1); assert!(vm.is_session_ptr(edges[0].as_js_object_ptr())); - assert_eq!(vm.realm.gc.session_object_ptrs.len(), 2); + assert_eq!(vm.realm.gc.borrow().session_object_ptrs.len(), 2); vm.regs[0] = live_view; vm.regs[1] = JsValue::int(0); @@ -953,9 +956,9 @@ fn session_gc_rewrites_buffer_retained_only_by_data_view_native_edge() { } fn collect(vm: &mut Vm) { - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.collect(vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; } #[test] @@ -963,12 +966,12 @@ fn session_string_collected_when_dead() { let mut vm = Vm::new(); let dead = vm.new_string("dead-string"); let dead_ptr = dead.as_string_ptr_mut(); - assert!(vm.realm.gc.session_string_ptrs.contains(&dead_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&dead_ptr)); // 没有根引用 `dead`(它只作为值包装存在于 Rust 栈上)。 collect(&mut vm); - assert!(!vm.realm.gc.session_string_ptrs.contains(&dead_ptr)); + assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&dead_ptr)); } #[test] @@ -980,7 +983,7 @@ fn session_string_survives_when_in_register() { collect(&mut vm); - assert!(vm.realm.gc.session_string_ptrs.contains(&live_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&live_ptr)); // 存活字符串永不搬移——寄存器仍指向同一 box。 assert_eq!(vm.regs[0].as_string_ptr_mut(), live_ptr); assert_eq!(unsafe { (*live_ptr).as_str() }, "live-in-reg"); @@ -1003,7 +1006,7 @@ fn session_string_survives_via_object_property() { collect(&mut vm); - assert!(vm.realm.gc.session_string_ptrs.contains(&s_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&s_ptr)); assert_eq!(unsafe { (*s_ptr).as_str() }, "prop-string"); } @@ -1013,13 +1016,13 @@ fn permanent_string_untouched_by_sweep() { let perm = vm.perm_string("perm"); let perm_ptr = perm.as_string_ptr_mut(); // 永久字符串位于 PermInterner,绝不在 session 集合中。 - assert!(!vm.realm.gc.session_string_ptrs.contains(&perm_ptr)); + assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&perm_ptr)); vm.regs[0] = perm; collect(&mut vm); // 绝不被 session 清扫释放(仍可读,仍不在 session 集合中)。 - assert!(!vm.realm.gc.session_string_ptrs.contains(&perm_ptr)); + assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&perm_ptr)); assert_eq!(unsafe { (*perm_ptr).as_str() }, "perm"); } @@ -1030,11 +1033,11 @@ fn string_sweep_byte_accounting() { let _ = dead.as_string_ptr_mut(); let expected = (size_of::() + "0123456789".len()) as u64; - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); let before = gc.total_bytes_freed; gc.collect(&mut vm); let after = gc.total_bytes_freed; - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; assert!(after >= before + expected); } @@ -1055,7 +1058,8 @@ fn compile(source: &str) -> oxide_bytecode::module::CompiledModule { } fn global_prop_opt(vm: &Vm, name: &str) -> Option { - let global = vm.realm.session.global_object(); + let session = vm.realm.session.borrow(); + let global = session.global_object(); let si = vm.kernel_core().perm_interner().intern(name).0; vm.resolve_property(global, si) } @@ -1071,7 +1075,7 @@ fn closure_cell_survives_object_sweep() { "function outer() { var counter = 0; function inc() { return ++counter; } globalThis.inc = inc; return 0; } outer(); 0", ))) .expect("run1"); - assert!(!vm.realm.gc.session_cell_ptrs.borrow().is_empty(), "run1 应分配 upvalue cell"); + assert!(!vm.realm.gc.borrow().session_cell_ptrs.borrow().is_empty(), "run1 应分配 upvalue cell"); // reset 触发对象 sweep:存活对象原地保留,死对象原地释放。 vm.reset(); @@ -1126,10 +1130,10 @@ fn cells_freed_by_full_reset_and_reallocatable() { "function outer() { var x = 1; function f() { return x; } globalThis.f = f; return f(); } outer()", ))) .expect("run1"); - assert!(!vm.realm.gc.session_cell_ptrs.borrow().is_empty()); + assert!(!vm.realm.gc.borrow().session_cell_ptrs.borrow().is_empty()); vm.full_reset(); - assert!(vm.realm.gc.session_cell_ptrs.borrow().is_empty(), "full_reset 应释放全部 cell"); + assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().is_empty(), "full_reset 应释放全部 cell"); let result = vm .run(&Arc::new(compile( @@ -1186,13 +1190,13 @@ fn live_closure_cell_survives_full_collect() { let cells = obj.upvalues_slice(); assert_eq!(cells.len(), 1, "inc 应捕获 counter 一个 cell"); let cell_ptr = cells[0]; - assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应登记在表"); + assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应登记在表"); vm.regs.fill(JsValue::undefined()); collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应跨完整收集存活"); + assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应跨完整收集存活"); } /// 活跃帧 cell_stack 根:内层函数 upvalue cell 在活跃帧 cell_stack 上(低阈值使 @@ -1245,14 +1249,14 @@ fn suspended_state_box_cell_root_survives_collect() { .copied() .find(|p| !p.is_null()) .expect("挂起帧应持有 x cell"); - assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "cell 应登记在表"); + assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "cell 应登记在表"); vm.regs.fill(JsValue::undefined()); vm.regs[0] = JsValue::from_js_object(g_session); collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "挂起帧 cell 应跨完整收集存活"); + assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "挂起帧 cell 应跨完整收集存活"); // 恢复后读值:cell 未被误释放。 let read = vm.run(&Arc::new(compile("globalThis.g.next().value"))).expect("read"); @@ -1312,7 +1316,7 @@ fn module_ns_cell_root_survives_collect() { .into_iter() .next() .expect("依赖 ns 应持有 x 的 cell"); - assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "cell 应登记在表"); + assert!(vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "cell 应登记在表"); vm.regs.fill(JsValue::undefined()); vm.regs[0] = ns_val; @@ -1320,7 +1324,7 @@ fn module_ns_cell_root_survives_collect() { // 只断言表成员——sweep 已释放的指针解引用即 UB。 assert!( - vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), + vm.realm.gc.borrow().session_cell_ptrs.borrow().contains(&cell_ptr), "模块 ns cell 应跨完整收集存活" ); @@ -1346,16 +1350,16 @@ fn allocation_does_not_trigger_gc_before_safe_point() { let seed_ptr = seed.as_string_ptr_mut(); let returned = vm.new_string_owned("returned".repeat(16)); - assert_eq!(vm.realm.gc.session_gc.total_collections, 0); - assert!(vm.realm.gc.session_string_ptrs.contains(&seed_ptr)); - assert!(vm.realm.gc.session_string_ptrs.contains(&returned.as_string_ptr_mut())); + assert_eq!(vm.realm.gc.borrow().session_gc.total_collections, 0); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&seed_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&returned.as_string_ptr_mut())); // 显式触发后:无根引用的 seed 被回收,入根的 returned 存活(安全点语义)。 vm.regs[0] = returned; vm.maybe_collect_session_strings(); - assert!(vm.realm.gc.session_gc.total_collections >= 1); - assert!(!vm.realm.gc.session_string_ptrs.contains(&seed_ptr)); - assert!(vm.realm.gc.session_string_ptrs.contains(&returned.as_string_ptr_mut())); + assert!(vm.realm.gc.borrow().session_gc.total_collections >= 1); + assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&seed_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&returned.as_string_ptr_mut())); } #[test] @@ -1388,10 +1392,10 @@ fn strings_only_collection_preserves_all_root_kinds() { vm.maybe_collect_session_strings(); - assert!(vm.realm.gc.session_string_ptrs.contains(®_str.as_string_ptr_mut())); - assert!(vm.realm.gc.session_string_ptrs.contains(&prop_str.as_string_ptr_mut())); - assert!(vm.realm.gc.session_string_ptrs.contains(&epoch_str.as_string_ptr_mut())); - assert!(!vm.realm.gc.session_string_ptrs.contains(&dead_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(®_str.as_string_ptr_mut())); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&prop_str.as_string_ptr_mut())); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&epoch_str.as_string_ptr_mut())); + assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&dead_ptr)); // 存活串内容可读,地址稳定。 assert_eq!(unsafe { (*reg_str.as_string_ptr_mut()).as_str() }, "reg-root".repeat(16)); assert_eq!(unsafe { (*prop_str.as_string_ptr_mut()).as_str() }, "prop-root".repeat(16)); @@ -1404,7 +1408,7 @@ fn strings_only_collection_does_not_move_objects() { let obj = plain_object(&mut vm); let obj_session = obj; vm.regs[0] = JsValue::from_js_object(obj_session); - let before: Vec<_> = vm.realm.gc.session_object_ptrs.clone(); + let before: Vec<_> = vm.realm.gc.borrow().session_object_ptrs.clone(); let s = vm.new_string_owned("x".repeat(64)); vm.regs[1] = s; @@ -1412,7 +1416,7 @@ fn strings_only_collection_does_not_move_objects() { vm.maybe_collect_session_strings(); // 对象指针逐一相同:不搬移、不重写根。 - assert_eq!(vm.realm.gc.session_object_ptrs, before); + assert_eq!(vm.realm.gc.borrow().session_object_ptrs, before); assert_eq!(vm.regs[0].as_js_object_ptr(), obj_session); assert_eq!(vm.session_object_count(), 1); } @@ -1435,10 +1439,10 @@ fn multiple_strings_only_cycles_keep_objects_alive() { } // 分配不自动触发,每轮显式触发一次回收。 vm.maybe_collect_session_strings(); - assert!(vm.realm.gc.session_object_ptrs.contains(&obj_session)); + assert!(vm.realm.gc.borrow().session_object_ptrs.contains(&obj_session)); assert_eq!(unsafe { (*obj_session).get_prop_at(0) }, live); } - assert!(vm.realm.gc.session_string_ptrs.contains(&live.as_string_ptr_mut())); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&live.as_string_ptr_mut())); } #[test] @@ -1458,15 +1462,15 @@ fn strings_only_then_full_collect_keeps_object_strings_live() { // 第一轮 strings-only:对象被 mark 置位。收尾清残留,否则该位残留至完整 // 收集会使 mark DFS 在对象处短路、漏标其属性中的串;随后校验串跨完整收集存活。 vm.maybe_collect_session_strings(); - assert!(vm.realm.gc.session_string_ptrs.contains(&s_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&s_ptr)); // 完整收集(reset 的 maybe_collect 路径):对象搬移、字符串地址稳定。 - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.collect(&mut vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; // 存活对象经重写仍可达其串:串未被误释放,内容可读。 - assert!(vm.realm.gc.session_string_ptrs.contains(&s_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&s_ptr)); assert_eq!(unsafe { (*s_ptr).as_str() }, "kept".repeat(8)); assert_eq!(unsafe { (*vm.regs[0].as_js_object_ptr()).get_prop_at(0) }, s); } @@ -1487,8 +1491,8 @@ fn strings_only_collection_byte_accounting_matches_survivors() { // 账目 = 对象头 + 存活串(size_of::() + len),死串不再计入。 let expected = size_of::() + (size_of::() + unsafe { (*live_ptr).payload_bytes() }); - assert_eq!(vm.realm.gc.session_bytes_allocated, expected); - assert!(!vm.realm.gc.session_string_ptrs.contains(&dead.as_string_ptr_mut())); + assert_eq!(vm.realm.gc.borrow().session_bytes_allocated, expected); + assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&dead.as_string_ptr_mut())); } #[test] @@ -1498,9 +1502,10 @@ fn run_alloc_formula_lists_bigint_and_cell_via_tables() { vm.regs[0] = JsValue::from_js_object(obj); // 带堆区对象:元素向量非空(账目含头 + 向量容量),深层等式在双计形态 // 下不成立,钉向量面互不相交。 + let array_proto_ptr = vm.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject; let arr = vm.alloc_object(JsObject::new_array( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, - JsValue::from_js_object(vm.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject), + JsValue::from_js_object(array_proto_ptr), 2, )); vm.regs[3] = JsValue::from_js_object(arr); @@ -1509,30 +1514,30 @@ fn run_alloc_formula_lists_bigint_and_cell_via_tables() { let b = vm.new_bigint(num_bigint::BigInt::from(7_i128)); vm.regs[2] = b; // 未根 cell:收集后不可达,随 sweep 出表。 - vm.realm.gc.alloc_cell(JsValue::int(9), true); + vm.realm.gc.borrow().alloc_cell(JsValue::int(9), true); // 公式 = 手工账目 + BigInt 表长 × 尺寸 + cell 表长 × 尺寸,三分量两两不相交。 assert_eq!( vm.run_alloc_bytes(), - vm.realm.gc.session_bytes_allocated - + vm.realm.gc.session_bigint_ptrs.borrow().len() * size_of::() - + vm.realm.gc.session_cell_ptrs.borrow().len() * size_of::() + vm.realm.gc.borrow().session_bytes_allocated + + vm.realm.gc.borrow().session_bigint_ptrs.borrow().len() * size_of::() + + vm.realm.gc.borrow().session_cell_ptrs.borrow().len() * size_of::() ); // BigInt 不在手工账目内:账目 = 两对象(头 + 堆数据)+ 串,数组带元素向量。 let string_bytes = size_of::() + unsafe { (*s.as_string_ptr_mut()).payload_bytes() }; let arr_heap_bytes = SessionGc::object_heap_data_bytes(unsafe { &*arr }); assert!(arr_heap_bytes > 0, "数组元素向量应计入对象堆数据"); assert_eq!( - vm.realm.gc.session_bytes_allocated, + vm.realm.gc.borrow().session_bytes_allocated, 2 * size_of::() + arr_heap_bytes as usize + string_bytes ); // 完整收集后:死 cell 出表、存活 BigInt 留表,账目保持对象 + 串口径。 vm.collect_session_gc(); - assert_eq!(vm.realm.gc.session_cell_ptrs.borrow().len(), 0); - assert_eq!(vm.realm.gc.session_bigint_ptrs.borrow().len(), 1); + assert_eq!(vm.realm.gc.borrow().session_cell_ptrs.borrow().len(), 0); + assert_eq!(vm.realm.gc.borrow().session_bigint_ptrs.borrow().len(), 1); assert_eq!( - vm.realm.gc.session_bytes_allocated, + vm.realm.gc.borrow().session_bytes_allocated, 2 * size_of::() + arr_heap_bytes as usize + string_bytes ); // 深采样全量重算与轻层公式零增长下逐位相等:存活对象带堆区(元素向量), @@ -1546,8 +1551,8 @@ fn runtime_collection_below_threshold_is_noop() { let s = vm.new_string_owned("small".repeat(4)); vm.regs[0] = s; - assert_eq!(vm.realm.gc.session_gc.total_collections, 0); - assert!(vm.realm.gc.session_string_ptrs.contains(&s.as_string_ptr_mut())); + assert_eq!(vm.realm.gc.borrow().session_gc.total_collections, 0); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&s.as_string_ptr_mut())); } // ── rope(Cons)GC 传播闭包 ── @@ -1570,9 +1575,9 @@ fn rope_survives_collection_with_children_propagated() { // 父(寄存器根)+ 子节点(经传播闭包)全部存活,内容可读。 let parent_ptr = parent.as_string_ptr_mut(); - assert!(vm.realm.gc.session_string_ptrs.contains(&parent_ptr)); - assert!(vm.realm.gc.session_string_ptrs.contains(&l_ptr)); - assert!(vm.realm.gc.session_string_ptrs.contains(&r_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&parent_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&l_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&r_ptr)); assert_eq!(unsafe { (*parent_ptr).as_lossy_str() }, "left-partright-part"); } @@ -1581,14 +1586,14 @@ fn rope_children_swept_when_parent_dead() { let mut vm = Vm::new(); let (parent, l_ptr, r_ptr) = make_cons_pair(&mut vm, "left", "right"); let parent_ptr = parent.as_string_ptr_mut(); - assert!(vm.realm.gc.session_string_ptrs.contains(&parent_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&parent_ptr)); // 父与子均无根引用 → 整树回收。 collect(&mut vm); - assert!(!vm.realm.gc.session_string_ptrs.contains(&parent_ptr)); - assert!(!vm.realm.gc.session_string_ptrs.contains(&l_ptr)); - assert!(!vm.realm.gc.session_string_ptrs.contains(&r_ptr)); + assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&parent_ptr)); + assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&l_ptr)); + assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&r_ptr)); } #[test] @@ -1605,8 +1610,8 @@ fn rope_product_freed_with_parent() { // 父死 → 连带释放产物(不 double-free、不泄漏;产物从不进 session 表)。 let flat_mut = flat_ptr as *mut JsString; - assert!(!vm.realm.gc.session_string_ptrs.contains(&parent_ptr)); - assert!(!vm.realm.gc.session_string_ptrs.contains(&flat_mut)); + assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&parent_ptr)); + assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&flat_mut)); } #[test] @@ -1623,7 +1628,7 @@ fn rope_deep_chain_mark_iterative() { collect(&mut vm); - assert!(vm.realm.gc.session_string_ptrs.contains(&chain_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&chain_ptr)); assert_eq!(unsafe { (*chain_ptr).as_lossy_str() }, format!("root{}", "x".repeat(1024))); } @@ -1640,8 +1645,8 @@ fn rope_perm_child_untouched_by_sweep() { collect(&mut vm); // perm 子节点永不释放(不在 session 表);session 子节点随父存活。 - assert!(!vm.realm.gc.session_string_ptrs.contains(&perm_ptr)); - assert!(vm.realm.gc.session_string_ptrs.contains(&session_ptr)); + assert!(!vm.realm.gc.borrow().session_string_ptrs.contains(&perm_ptr)); + assert!(vm.realm.gc.borrow().session_string_ptrs.contains(&session_ptr)); assert_eq!(unsafe { (*parent.as_string_ptr_mut()).as_lossy_str() }, "perm-leafsession-leaf"); } @@ -1655,7 +1660,7 @@ fn full_reset_frees_rope_and_product() { // full_reset 清空全部 session 字符串(连带产物):无泄漏、无 double-free。 vm.full_reset(); - assert!(vm.realm.gc.session_string_ptrs.is_empty()); + assert!(vm.realm.gc.borrow().session_string_ptrs.is_empty()); } // ── 执行期两档收集 ────────────────────────────────────────────────────── @@ -1708,7 +1713,7 @@ fn active_and_suspended_for_in_do_not_block_in_run_collection() { vm.maybe_collect_in_run(); assert_eq!(vm.session_gc_stats().total_collections, 1, "无门控:活跃 for-in 存在时收集照常执行"); - assert!(!vm.realm.gc.session_object_ptrs.contains(&dead), "死 session 对象应被原地释放并出表"); + assert!(!vm.realm.gc.borrow().session_object_ptrs.contains(&dead), "死 session 对象应被原地释放并出表"); // 挂起形:生成器在 for-in 内 yield 后 run 结束,迭代器经状态盒持有 // (vm.iters 已空)——键经状态盒边入根收集标活,收集照常执行。 @@ -1725,7 +1730,9 @@ fn active_and_suspended_for_in_do_not_block_in_run_collection() { assert_eq!(vm.session_gc_stats().total_collections, 1, "挂起 for-in:收集照常执行"); // 挂起生成器对象保持原址(未释放):状态盒与迭代器一体存活。 let gen_in_session = vm - .realm.gc + .realm + .gc + .borrow() .session_object_ptrs .iter() .any(|&p| !p.is_null() && unsafe { (*p).type_tag == JsObject::OBJ_TYPE_GENERATOR }); @@ -1900,11 +1907,11 @@ fn promise_promote_migrates_reject_reactions_to_clone() { fn mark_counts_roots_by_group() { let mut vm = Vm::new(); vm.regs[0] = JsValue::from_js_object(plain_object(&mut vm)); - let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.borrow_mut().session_gc); gc.mark(&vm); - vm.realm.gc.session_gc = gc; + vm.realm.gc.borrow_mut().session_gc = gc; - let counts = &vm.realm.gc.session_gc.root_counts; + let counts = &vm.realm.gc.borrow().session_gc.root_counts; assert_eq!(counts[RootGroup::Regs as usize], 256, "寄存器组应计满 256 槽"); assert_eq!(counts[RootGroup::Global as usize], 1, "global 组应计 global 对象"); // 9 个单槽侧通道:空 VM 时各经 unwrap_or(undefined) 产出一个未定义根值。 diff --git a/crates/oxide_vm/src/test262_host.rs b/crates/oxide_vm/src/test262_host.rs index 07db89df..e08dc479 100644 --- a/crates/oxide_vm/src/test262_host.rs +++ b/crates/oxide_vm/src/test262_host.rs @@ -47,7 +47,7 @@ pub fn eval_script(vm: &mut Vm, args: &[u8]) -> NativeResult { let code = oxide_kernel::string_forge::source_escape(&units); match vm.create_plain_dynamic_script(&code) { Ok(func) => { - let global = JsValue::from_js_object(vm.realm.session.global_object().as_ptr() as *mut JsObject); + let global = JsValue::from_js_object(vm.realm.session.borrow().global_object().as_ptr() as *mut JsObject); match vm.call_function_sync(func, global, &[]) { Ok(val) => NativeResult::Ok(val), Err(e) => NativeResult::Err(oxide_builtins::error::create_from_text(vm, &e)), @@ -82,7 +82,7 @@ pub fn create_realm(vm: &mut Vm, _args: &[u8]) -> NativeResult { /// - 置强制收集旗标;下一个顶层指令边界执行 mark + 移动式 sweep + /// 字符串/BigInt 清扫,重写 session 对象指针。 pub fn gc(vm: &mut Vm, _args: &[u8]) -> NativeResult { - vm.realm.gc.pending_forced_collect = true; + vm.realm.gc.borrow_mut().pending_forced_collect = true; NativeResult::Ok(JsValue::undefined()) } diff --git a/crates/oxide_vm/src/vm/call.rs b/crates/oxide_vm/src/vm/call.rs index 7cc5f463..ec2e25c7 100644 --- a/crates/oxide_vm/src/vm/call.rs +++ b/crates/oxide_vm/src/vm/call.rs @@ -47,10 +47,13 @@ impl Vm { obj.set_session_epoch(true); obj.set_heap_alloc(true); let ptr = Box::into_raw(Box::new(obj)); - self.realm.gc.session_object_ptrs.push(ptr); - // 直 session 分配计入堆账目(与 promote 同式:对象头 + 对象堆数据)。 - self.realm.gc.session_bytes_allocated += std::mem::size_of::() + // 直 session 分配计入堆账目(与 promote 同式:对象头 + 对象堆数据): + // 表登记与账目更新合并进同一次借用。 + let bytes = std::mem::size_of::() + crate::session_gc::SessionGc::object_heap_data_bytes(unsafe { &*ptr }) as usize; + let gc = &mut self.realm.gc.borrow_mut(); + gc.session_object_ptrs.push(ptr); + gc.session_bytes_allocated += bytes; ptr } @@ -296,7 +299,7 @@ impl Vm { self.regs[254] = if sub_is_arrow { obj.captured_this() } else if !sub_is_strict && this_value.is_nullish() { - JsValue::from_js_object(self.realm.session.global_object().as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.session.borrow().global_object().as_ptr() as *mut JsObject) } else if sub_is_strict { this_value } else { @@ -402,7 +405,7 @@ mod tests { } fn native_function(vm: &mut Vm, f: crate::native::NativeFn) -> JsValue { - let proto = vm.realm.session.builtin_world().function_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.borrow().builtin_world().function_proto.as_ptr() as *mut JsObject; let mut obj = JsObject::new_empty(oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto)); obj.set_function(true); // SAFETY: f 是 NativeFn 函数项,可作为 NativeFnPtr 存储。 diff --git a/crates/oxide_vm/src/vm/dispatch_loop.rs b/crates/oxide_vm/src/vm/dispatch_loop.rs index 5f834858..f6a513c8 100644 --- a/crates/oxide_vm/src/vm/dispatch_loop.rs +++ b/crates/oxide_vm/src/vm/dispatch_loop.rs @@ -128,29 +128,31 @@ impl Vm { // 宿主 `$262.gc()` 置位的强制收集与压力模式收集在此边界消费: // 完整收集唯一安全点(无在途 dispatch 局部值),与正常触发路径 // 同一入口。压力模式开启后每个顶层边界做一次完整收集。 - if self.realm.gc.pending_forced_collect || self.realm.gc.gc_pressure_mode { - self.realm.gc.pending_forced_collect = false; + // 各字段读入局部变量(临时借用)后释放借用再调收集, + // 不跨 collect 调用持借。 + if self.realm.gc.borrow().pending_forced_collect || self.realm.gc.borrow().gc_pressure_mode { + self.realm.gc.borrow_mut().pending_forced_collect = false; self.collect_session_gc(); } // 峰值高水位:同一边界采样 session 堆账目上界 - let bytes = self.realm.gc.session_bytes_allocated; - if bytes > self.realm.gc.session_bytes_peak { - self.realm.gc.session_bytes_peak = bytes; + let bytes = self.realm.gc.borrow().session_bytes_allocated; + if bytes > self.realm.gc.borrow().session_bytes_peak { + self.realm.gc.borrow_mut().session_bytes_peak = bytes; } // 单 run 分配包络高水位:O(1) 读(手工账目 + BigInt/cell 表长)。 let alloc = self.run_alloc_bytes(); - if alloc > self.realm.gc.run_alloc_peak { - self.realm.gc.run_alloc_peak = alloc; + if alloc > self.realm.gc.borrow().run_alloc_peak { + self.realm.gc.borrow_mut().run_alloc_peak = alloc; } // 压力模式开启时跳过两档水位收集:完整收集已涵盖其全部工作, // 同边界双触发只会多一轮空转 mark。 - if bytes >= self.realm.gc.string_gc_watermark && !self.realm.gc.gc_pressure_mode { + if bytes >= self.realm.gc.borrow().string_gc_watermark && !self.realm.gc.borrow().gc_pressure_mode { self.maybe_collect_session_strings(); } // 执行期原地 sweep 收集:O(1) 包络超触发水位 → 收集(无门控: // 迭代器键引用经根收集标活,原地释放不搬移存活对象; // 边界契约见 maybe_collect_in_run)。 - if alloc >= self.realm.gc.gc_watermark && !self.realm.gc.gc_pressure_mode { + if alloc >= self.realm.gc.borrow().gc_watermark && !self.realm.gc.borrow().gc_pressure_mode { self.maybe_collect_in_run(); } } diff --git a/crates/oxide_vm/src/vm/gc_hooks.rs b/crates/oxide_vm/src/vm/gc_hooks.rs index 73e59d2e..31501cd7 100644 --- a/crates/oxide_vm/src/vm/gc_hooks.rs +++ b/crates/oxide_vm/src/vm/gc_hooks.rs @@ -150,7 +150,7 @@ impl Vm { } } } - f(RootGroup::Global, JsValue::from_js_object(self.realm.session.global_object().as_ptr() as *mut JsObject)); + f(RootGroup::Global, JsValue::from_js_object(self.realm.session.borrow().global_object().as_ptr() as *mut JsObject)); } /// GC 根统一枚举入口:遍历的字段清单与 `for_each_value` 相同。 @@ -163,17 +163,17 @@ impl Vm { /// `session_gc` 经 `mem::take` 借出后再放回:收集需要 `&mut Vm`,而 /// `SessionGc` 是 `Vm` 的内部字段,借出以避开借用冲突。 pub(crate) fn maybe_collect_session_gc(&mut self) { - let mut session_gc = std::mem::take(&mut self.realm.gc.session_gc); + let mut session_gc = std::mem::take(&mut self.realm.gc.borrow_mut().session_gc); session_gc.maybe_collect(self); - self.realm.gc.session_gc = session_gc; + self.realm.gc.borrow_mut().session_gc = session_gc; } /// 执行期字符串阈值回收:仅回收 session 字符串(跳过对象搬移)。热路径只在 /// 超阈值后进入,`mem::take` 不承担每次分配的开销。 pub(crate) fn maybe_collect_session_strings(&mut self) { - let mut session_gc = std::mem::take(&mut self.realm.gc.session_gc); + let mut session_gc = std::mem::take(&mut self.realm.gc.borrow_mut().session_gc); session_gc.maybe_collect_strings_only(self); - self.realm.gc.session_gc = session_gc; + self.realm.gc.borrow_mut().session_gc = session_gc; } /// 执行期原地 sweep 收集的 dispatch 安全点入口:仅在循环顶 @@ -187,35 +187,38 @@ impl Vm { /// - 无对象可回收时仍跑一轮(mark + 原地清扫),水位同点抬高, /// 触发间距由包络增量控制。 pub(crate) fn maybe_collect_in_run(&mut self) { - let mut session_gc = std::mem::take(&mut self.realm.gc.session_gc); + let mut session_gc = std::mem::take(&mut self.realm.gc.borrow_mut().session_gc); session_gc.collect_in_run(self); - self.realm.gc.session_gc = session_gc; + self.realm.gc.borrow_mut().session_gc = session_gc; } /// 只读访问 session GC 的统计(回收次数、存活/死亡对象数、释放字节等)。 - pub fn session_gc_stats(&self) -> &SessionGc { - &self.realm.gc.session_gc + /// + /// 返回 `Ref` 守卫(`session_gc` 入 `RefCell` 后无法再给稳定 `&`): + /// 调用方在单表达式内消费,不跨 `borrow_mut` 长存。 + pub fn session_gc_stats(&self) -> std::cell::Ref<'_, SessionGc> { + std::cell::Ref::map(self.realm.gc.borrow(), |gc| &gc.session_gc) } /// 当前 session arena 中存活(已晋升)的对象数量。 pub fn session_object_count(&self) -> usize { - self.realm.gc.session_object_ptrs.len() + self.realm.gc.borrow().session_object_ptrs.len() } /// session 当前分配的字节数(对象 + 存活字符串)。 pub fn session_bytes_allocated(&self) -> usize { - self.realm.gc.session_bytes_allocated + self.realm.gc.borrow().session_bytes_allocated } /// 执行期 session 堆账目的峰值高水位(顶层指令边界采样,全量重置清零)。 pub fn session_bytes_peak(&self) -> usize { - self.realm.gc.session_bytes_peak + self.realm.gc.borrow().session_bytes_peak } /// 本 run 累计分配字节的高水位:`run_alloc_bytes` 的顶层指令边界 /// 采样上界,run 边界(reset/full_reset)重起算。留存内存观测锚。 pub fn run_alloc_peak(&self) -> usize { - self.realm.gc.run_alloc_peak + self.realm.gc.borrow().run_alloc_peak } /// 本 run 累计分配字节:session 手工堆账目(session 对象及其属性向量 + @@ -228,9 +231,9 @@ impl Vm { /// 属性区(元素/属性向量扩容)增长对其不可见——上限判定须配合 /// [`Self::run_alloc_bytes_full`] 的深采样层。 pub(crate) fn run_alloc_bytes(&self) -> usize { - self.realm.gc.session_bytes_allocated - + self.realm.gc.session_bigint_ptrs.borrow().len() * std::mem::size_of::() - + self.realm.gc.session_cell_ptrs.borrow().len() * std::mem::size_of::() + self.realm.gc.borrow().session_bytes_allocated + + self.realm.gc.borrow().session_bigint_ptrs.borrow().len() * std::mem::size_of::() + + self.realm.gc.borrow().session_cell_ptrs.borrow().len() * std::mem::size_of::() } /// 本 run 累计分配字节(O(1) 读)。 @@ -248,7 +251,7 @@ impl Vm { /// 与 [`Self::run_alloc_bytes`] 的轻层公式逐位相等。 pub(crate) fn run_alloc_bytes_full(&self) -> u64 { let mut bytes = 0u64; - for &ptr in self.realm.gc.session_object_ptrs.iter() { + for &ptr in self.realm.gc.borrow().session_object_ptrs.iter() { if ptr.is_null() { continue; } @@ -256,15 +259,15 @@ impl Vm { let obj = unsafe { &*ptr }; bytes += std::mem::size_of::() as u64 + SessionGc::object_heap_data_bytes(obj); } - for &ptr in &self.realm.gc.session_string_ptrs { + for &ptr in &self.realm.gc.borrow().session_string_ptrs { if ptr.is_null() { continue; } // SAFETY: ptr 在字符串表登记,收尾前有效。 bytes += (std::mem::size_of::() + unsafe { (*ptr).payload_bytes() }) as u64; } - bytes += (self.realm.gc.session_bigint_ptrs.borrow().len() * std::mem::size_of::()) as u64; - bytes += (self.realm.gc.session_cell_ptrs.borrow().len() * std::mem::size_of::()) as u64; + bytes += (self.realm.gc.borrow().session_bigint_ptrs.borrow().len() * std::mem::size_of::()) as u64; + bytes += (self.realm.gc.borrow().session_cell_ptrs.borrow().len() * std::mem::size_of::()) as u64; bytes } @@ -278,9 +281,9 @@ impl Vm { /// - 须在执行外的安全点调用(无在途 builtin 局部裸指针、dispatch 未重入); /// 执行期触发仍走水位路径,本入口供事后观测(如基准测 workload 后留存堆)。 pub fn collect_session_gc(&mut self) { - let mut session_gc = std::mem::take(&mut self.realm.gc.session_gc); + let mut session_gc = std::mem::take(&mut self.realm.gc.borrow_mut().session_gc); session_gc.collect(self); - self.realm.gc.session_gc = session_gc; + self.realm.gc.borrow_mut().session_gc = session_gc; } /// 当前 epoch 中已分配并跟踪的对象数量。对象分配统一入口 Box 化后 diff --git a/crates/oxide_vm/src/vm/mod.rs b/crates/oxide_vm/src/vm/mod.rs index c9ac10a4..d4f0b65d 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -176,8 +176,10 @@ pub struct Vm { pub(crate) frames: SmallVec<[CallFrame; 16]>, pub(crate) kernel_core: Arc, /// 每 VM 的 realm 组合:内核会话(builtin world 与 global 对象)、 - /// session GC 簿记与 10 个内建原型槽(见 `realm` 模块)。 - pub(crate) realm: Realm, + /// session GC 簿记与 10 个内建原型槽(见 `realm` 模块)。以 `Arc` 持有: + /// 可变组经 `RefCell` 内部可变性在 `&Arc` 下改写,teardown 归 + /// `Drop for Realm`(per-realm 消亡:Arc 计数归零时收尾)。 + pub(crate) realm: Arc, /// `"length"` 属性键的 intern id 缓存:进程内稳定(PermInterner append-only、 /// KernelCore 不重建),属性 get/set 热路径免每次 intern(hash64 + DashMap + /// RwLock 读锁)。 @@ -382,10 +384,11 @@ impl Drop for Vm { fn drop(&mut self) { // 边界守卫计数:与构造器登记恰好配对(Rust 所有权保证恰好一次)。 self.kernel_core.note_vm_ended(); - // 直接 drop(test262 每测试新建即弃)不经 reset/full_reset 路径: - // 统一收尾释放全部 session 堆数据与内建原型属性区,防逐测试累积泄漏。 - self.teardown_intrinsic_protos(); - self.teardown_session_heap_data(); + // 活跃 for-in / for-of 迭代器是 per-VM 执行态:收尾路径逐条释放, + // 防 Vm 直接 drop 时表内残留体泄漏。 + self.iters.reset(); + // realm 收尾归 `Drop for Realm`(per-realm 消亡:Arc 计数归零时触发, + // 直接 drop 时恰好一次)。 } } @@ -419,13 +422,16 @@ impl Vm { } /// 只读访问当前 session(builtin world 与 global object)。 - pub fn session(&self) -> &KernelSession { - &self.realm.session + /// + /// 返回 `Ref` 守卫(session 入 `RefCell` 后无法再给稳定 `&`):调用方 + /// 在单表达式内消费,不跨 `borrow_mut` 长存。 + pub fn session(&self) -> std::cell::Ref<'_, KernelSession> { + self.realm.session.borrow() } /// 判定裸指针是否指向当前 session 的 `%Object.prototype%`。 pub(crate) fn is_object_prototype(&self, ptr: *const JsObject) -> bool { - let proto_ptr = self.realm.session.builtin_world().object_proto.as_ptr(); + let proto_ptr = self.realm.session.borrow().builtin_world().object_proto.as_ptr(); std::ptr::eq(ptr, proto_ptr) } diff --git a/crates/oxide_vm/src/vm/vmhost.rs b/crates/oxide_vm/src/vm/vmhost.rs index 28cb3315..9a06b6fb 100644 --- a/crates/oxide_vm/src/vm/vmhost.rs +++ b/crates/oxide_vm/src/vm/vmhost.rs @@ -45,8 +45,8 @@ impl oxide_runtime_api::VmHost for Vm { fn kernel_core(&self) -> &Arc { self.kernel_core() } - fn session(&self) -> &KernelSession { - &self.realm.session + fn session(&self) -> std::cell::Ref<'_, KernelSession> { + self.session() } fn pc(&self) -> usize { self.pc @@ -189,9 +189,9 @@ impl oxide_runtime_api::VmHost for Vm { return false; } let p = proto.as_js_object_ptr() as *const JsObject; - std::ptr::eq(p, self.realm.generator_function_proto.as_ptr()) - || std::ptr::eq(p, self.realm.async_function_proto.as_ptr()) - || std::ptr::eq(p, self.realm.async_generator_function_proto.as_ptr()) + std::ptr::eq(p, self.realm.generator_function_proto.borrow().as_ptr()) + || std::ptr::eq(p, self.realm.async_function_proto.borrow().as_ptr()) + || std::ptr::eq(p, self.realm.async_generator_function_proto.borrow().as_ptr()) } fn step_rng(&mut self) { self.step_rng() diff --git a/crates/oxide_vm/src/vm_dispatch_ctrl.rs b/crates/oxide_vm/src/vm_dispatch_ctrl.rs index 4a8d3442..454f586b 100644 --- a/crates/oxide_vm/src/vm_dispatch_ctrl.rs +++ b/crates/oxide_vm/src/vm_dispatch_ctrl.rs @@ -387,7 +387,7 @@ impl Vm { } let si = self.property_key_si(key_val)?; let value = self.regs[a]; - let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject; // SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。 let obj = unsafe { &mut *global_ptr }; // 属性已存在:accessor 形走 Set 语义(有 setter 调用之、无 setter @@ -483,7 +483,7 @@ impl Vm { } let si = self.property_key_si(key_val)?; let value = self.regs[a]; - let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject; // SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。 let obj = unsafe { &mut *global_ptr }; // 既有属性:CreateGlobalVarBinding 零动作。 diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index 1a22187b..69c71445 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -55,7 +55,7 @@ impl Vm { for i in 0..arg_count.min(256) { args.push(self.regs[first_arg_reg.wrapping_add(i as u8) as usize]); } - let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject; let new_obj = self.alloc_object(JsObject::new_empty( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), @@ -63,7 +63,7 @@ impl Vm { return self.dispatch_new_bound(rd, constructor, new_obj, args, call_window); } - let proto_ptr = &*self.realm.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject; let new_obj = self.alloc_object(JsObject::new_empty( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), @@ -998,7 +998,7 @@ impl Vm { if src.is_null() || src.is_undefined() { return self.raise_type_error("Cannot destructure property of null/undefined"); } - let proto_ptr = self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject; let rest_ptr = self.alloc_object(JsObject::new_empty( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), @@ -1088,7 +1088,7 @@ impl Vm { } // 提交到 rest 对象。 - let proto_ptr = self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject; let rest_ptr = self.alloc_object(JsObject::new_empty( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), @@ -1210,7 +1210,7 @@ impl Vm { return Ok(()); } - let proto_ptr = self.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject; let proto_val = JsValue::from_js_object(proto_ptr); // 模板对象直接分配为 session 对象:写入 session 根(存进 global/数组) // 时对象地址稳定,缓存指针不失效——分配档不同则首次写入触发搬移, diff --git a/crates/oxide_vm/src/vm_props.rs b/crates/oxide_vm/src/vm_props.rs index 7804194b..241029f8 100644 --- a/crates/oxide_vm/src/vm_props.rs +++ b/crates/oxide_vm/src/vm_props.rs @@ -239,7 +239,7 @@ impl Vm { /// - 只可挂在真实 A 侧写发生之后;写失败路径(只读 no-op / 抛错)不得 /// 调用,否则污染镜像槽。 pub(crate) fn sync_global_builtin_mirror(&mut self, obj: &JsObject, key_si: u32, val: JsValue) { - if !std::ptr::eq(obj as *const JsObject, self.realm.session.global_object().as_ptr()) { + if !std::ptr::eq(obj as *const JsObject, self.realm.session.borrow().global_object().as_ptr()) { return; } let Some(module) = self.active_module() else { @@ -264,7 +264,7 @@ impl Vm { pub(crate) fn sync_global_builtin_mirror_slot( &mut self, obj: &JsObject, shape_id: u32, slot: u32, depth: u8, val: JsValue, ) { - if !std::ptr::eq(obj as *const JsObject, self.realm.session.global_object().as_ptr()) { + if !std::ptr::eq(obj as *const JsObject, self.realm.session.borrow().global_object().as_ptr()) { return; } if depth != 0 || shape_id != obj.shape_id() { @@ -305,7 +305,8 @@ impl Vm { if map.is_empty() { return; } - let global = self.realm.session.global_object(); + let session = self.realm.session.borrow(); + let global = session.global_object(); for (name, reg) in map { if (*reg as usize) < pack_end { continue; @@ -1543,7 +1544,7 @@ mod tests { } fn native_function(vm: &mut Vm, f: crate::native::NativeFn) -> JsValue { - let proto = vm.realm.session.builtin_world().function_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.borrow().builtin_world().function_proto.as_ptr() as *mut JsObject; let mut obj = JsObject::new_empty(oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto)); obj.set_function(true); // SAFETY: f 是 NativeFn 函数项,可作为 NativeFnPtr 存储。 @@ -1552,7 +1553,7 @@ mod tests { } fn plain_object(vm: &mut Vm) -> JsValue { - let proto = vm.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject; let obj = JsObject::new_empty(oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto)); JsValue::object(vm.alloc_object(obj) as *mut u8) } @@ -1681,7 +1682,7 @@ mod tests { // ===== 大索引(越稠密上限)写降级命名属性的行为钉 ===== fn new_array_val(vm: &mut Vm, n: u32) -> JsValue { - let proto = vm.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject; let arr = JsObject::new_array(oxide_kernel::shape_forge::EMPTY_SHAPE_ID, JsValue::from_js_object(proto), n as usize); JsValue::object(vm.alloc_object(arr) as *mut u8) diff --git a/crates/oxide_vm/src/vm_runtime.rs b/crates/oxide_vm/src/vm_runtime.rs index 1085a6ed..9531fd0c 100644 --- a/crates/oxide_vm/src/vm_runtime.rs +++ b/crates/oxide_vm/src/vm_runtime.rs @@ -502,7 +502,7 @@ impl Vm { self.regs[254] = if sub.is_arrow { callee_obj.captured_this() } else if !sub.is_strict && receiver.is_nullish() { - JsValue::from_js_object(self.realm.session.global_object().as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.session.borrow().global_object().as_ptr() as *mut JsObject) } else if sub.is_strict { receiver } else { @@ -632,7 +632,7 @@ impl Vm { // 存活代际集:函数对象只经创建点直落 session 对象表登记(或晋升克隆 // 随行),扫全表即完备;native 函数哨兵 sub_module_index == 0 不计数。 let mut live_gens: Vec = Vec::new(); - for &ptr in &self.realm.gc.session_object_ptrs { + for &ptr in &self.realm.gc.borrow().session_object_ptrs { if ptr.is_null() { continue; } @@ -692,11 +692,11 @@ impl Vm { // 顶层 this:脚本为全局对象(ECMA-262 全局执行上下文); // ES module 顶层环境 GetThisBinding 返回 undefined。记录到 // top_level_this:rerun 清空寄存器文件后按此恢复顶层 this。 - let global = self.realm.session.global_object(); + let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject; let this_val = if module.is_es_module { JsValue::undefined() } else { - JsValue::from_js_object(global.as_ptr() as *mut JsObject) + JsValue::from_js_object(global_ptr) }; self.top_level_this = this_val; self.regs[254] = this_val; diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index 43fdb303..4b45a613 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -1,5 +1,6 @@ #![allow(clippy::arc_with_non_send_sync)] +use std::cell::RefCell; use std::collections::VecDeque; use std::sync::Arc; @@ -64,24 +65,24 @@ impl Vm { active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0), frames: smallvec::SmallVec::new(), kernel_core: core, - realm: Realm { - session, - object_prototype: obj_proto, - generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - promise_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - promise_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - aggregate_error_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - aggregate_error_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - async_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - async_generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - async_generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - gc: GcState { + realm: Arc::new(Realm { + session: RefCell::new(session), + object_prototype: RefCell::new(obj_proto), + generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + generator_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + promise_constructor: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + promise_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + aggregate_error_constructor: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + aggregate_error_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + async_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + async_generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + async_generator_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + gc: RefCell::new(GcState { session_gc: crate::session_gc::SessionGc::new(), session_object_ptrs: Vec::new(), session_string_ptrs: Vec::new(), - session_bigint_ptrs: std::cell::RefCell::new(Vec::new()), - session_cell_ptrs: std::cell::RefCell::new(Vec::new()), + session_bigint_ptrs: RefCell::new(Vec::new()), + session_cell_ptrs: RefCell::new(Vec::new()), session_bytes_allocated: 0, session_bytes_peak: 0, run_alloc_peak: 0, @@ -90,8 +91,8 @@ impl Vm { gc_watermark: gc_threshold, pending_forced_collect: false, gc_pressure_mode: gc_pressure_mode_from_env(), - }, - }, + }), + }), length_si, length_perm_ptr, number_to_string_cache_keys: [0u64; 16], @@ -186,7 +187,7 @@ impl Vm { vm.init_async_intrinsics(); vm.init_async_generator_intrinsics(); // Promise 全局绑定发生在快照采集之后,重录快照避免首次 full_reset 误判脏。 - vm.realm.session.record_snapshot(); + vm.realm.session.borrow_mut().record_snapshot(); // 边界守卫计数:VM 完整构造后登记,与 `Drop for Vm` 的注销恰好配对。 vm.kernel_core.note_vm_started(); vm_info!("Vm created"); @@ -211,24 +212,24 @@ impl Vm { active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0), frames: smallvec::SmallVec::new(), kernel_core: core, - realm: Realm { - session, - object_prototype: obj_proto, - generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - promise_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - promise_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - aggregate_error_constructor: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - aggregate_error_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - async_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - async_generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - async_generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - gc: GcState { + realm: Arc::new(Realm { + session: RefCell::new(session), + object_prototype: RefCell::new(obj_proto), + generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + generator_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + promise_constructor: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + promise_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + aggregate_error_constructor: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + aggregate_error_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + async_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + async_generator_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + async_generator_function_proto: RefCell::new(P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()))), + gc: RefCell::new(GcState { session_gc: crate::session_gc::SessionGc::new(), session_object_ptrs: Vec::new(), session_string_ptrs: Vec::new(), - session_bigint_ptrs: std::cell::RefCell::new(Vec::new()), - session_cell_ptrs: std::cell::RefCell::new(Vec::new()), + session_bigint_ptrs: RefCell::new(Vec::new()), + session_cell_ptrs: RefCell::new(Vec::new()), session_bytes_allocated: 0, session_bytes_peak: 0, run_alloc_peak: 0, @@ -237,8 +238,8 @@ impl Vm { gc_watermark: gc_threshold, pending_forced_collect: false, gc_pressure_mode: gc_pressure_mode_from_env(), - }, - }, + }), + }), length_si, length_perm_ptr, number_to_string_cache_keys: [0u64; 16], @@ -333,7 +334,7 @@ impl Vm { vm.init_async_intrinsics(); vm.init_async_generator_intrinsics(); // Promise 全局绑定发生在快照采集之后,重录快照避免首次 full_reset 误判脏。 - vm.realm.session.record_snapshot(); + vm.realm.session.borrow_mut().record_snapshot(); // 边界守卫计数:VM 完整构造后登记,与 `Drop for Vm` 的注销恰好配对。 vm.kernel_core.note_vm_started(); vm_info!("Vm created (pool)"); @@ -370,26 +371,28 @@ impl Vm { // 防御兜底:属性值写原语已推进 generation,常规覆盖写由快照对比发现; // 若未来出现绕过属性写原语的裸属性区改写,session 对象会被 session GC 回收, // 保留 global 将持悬垂指针,故带 session 对象时强制 bump 保证 global 重建。 - if !self.realm.gc.session_object_ptrs.is_empty() { - let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject; + if !self.realm.gc.borrow().session_object_ptrs.is_empty() { + let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject; unsafe { &mut *global_ptr }.bump_generation(); } - let dirty = self.realm.session.selective_reset(&self.kernel_core); + let dirty = self.realm.session.borrow_mut().selective_reset(&self.kernel_core); if dirty.any_builtin_dirty() { - bindings::rebind_dirty_builtins(&self.kernel_core, &mut self.realm.session, Some(&dirty)); + bindings::rebind_dirty_builtins(&self.kernel_core, &mut self.realm.session.borrow_mut(), Some(&dirty)); } if dirty.global { - let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject; let global = unsafe { &mut *global_ptr }; - bindings::bind_global_builtin_slots(&self.kernel_core, &self.realm.session, global); + bindings::bind_global_builtin_slots(&self.kernel_core, &self.realm.session.borrow_mut(), global); } - self.realm.object_prototype = P::clone(&self.realm.session.builtin_world().object_proto); + *self.realm + .object_prototype + .borrow_mut() = P::clone(&self.realm.session.borrow().builtin_world().object_proto); self.init_generator_intrinsics(); self.init_promise_intrinsics(); self.init_async_intrinsics(); self.init_async_generator_intrinsics(); // 快照须在 Promise 全局绑定之后采集:绑定会修改 global 世代。 - self.realm.session.record_snapshot(); + self.realm.session.borrow_mut().record_snapshot(); self.clear_full_reset_state(); vm_info!("full_reset completed"); } @@ -397,14 +400,16 @@ impl Vm { /// benchmark 专用重置路径:总是丢弃并重建整个 session 与内置对象。 #[doc(hidden)] pub fn full_reset_legacy_for_bench(&mut self) { - self.realm.session = KernelSession::new(&self.kernel_core); - bindings::init_kernel_builtins(&self.kernel_core, &mut self.realm.session); - self.realm.object_prototype = P::clone(&self.realm.session.builtin_world().object_proto); + *self.realm.session.borrow_mut() = KernelSession::new(&self.kernel_core); + bindings::init_kernel_builtins(&self.kernel_core, &mut self.realm.session.borrow_mut()); + *self.realm + .object_prototype + .borrow_mut() = P::clone(&self.realm.session.borrow().builtin_world().object_proto); self.init_generator_intrinsics(); self.init_promise_intrinsics(); self.init_async_intrinsics(); self.init_async_generator_intrinsics(); - self.realm.session.record_snapshot(); + self.realm.session.borrow_mut().record_snapshot(); self.clear_full_reset_state(); } @@ -435,15 +440,16 @@ impl Vm { // 否则下次命中返回悬垂指针。 self.number_to_string_cache_keys = [0u64; 16]; self.number_to_string_cache_vals = [JsValue::undefined(); 16]; - self.teardown_session_heap_data(); - self.realm.gc.session_bytes_allocated = 0; - self.realm.gc.session_bytes_peak = 0; - self.realm.gc.run_alloc_peak = 0; - self.realm.gc.string_gc_watermark = self.kernel_core.config().session_gc_threshold; + self.realm.teardown_session_heap_data(); + self.realm.gc.borrow_mut().session_bytes_allocated = 0; + self.realm.gc.borrow_mut().session_bytes_peak = 0; + self.realm.gc.borrow_mut().run_alloc_peak = 0; + self.realm.gc.borrow_mut().string_gc_watermark = self.kernel_core.config().session_gc_threshold; // 执行期收集水位同点复位(同式:阈值增量起算):旧 run 的存活包络 // 不延续到新 run 的触发判定。 - self.realm.gc.gc_watermark = self.realm.gc.gc_threshold_cached; - self.realm.gc.session_gc = crate::session_gc::SessionGc::new(); + let threshold = self.realm.gc.borrow().gc_threshold_cached; + self.realm.gc.borrow_mut().gc_watermark = threshold; + self.realm.gc.borrow_mut().session_gc = crate::session_gc::SessionGc::new(); self.symbols.reset(); self.root_reg_limit = 0; self.active_reg_limit = 0; @@ -452,61 +458,6 @@ impl Vm { self.max_steps_override = None; } - /// 释放全部 session 堆数据:session 对象(本体 + 堆数据 + upvalue 列表) - /// + session 串 + BigInt box + upvalue cell box。 - /// - /// 供 `full_reset` 与 `Drop` 共用——统一入口路径上对象本体全部为堆载体 - /// (`Box::from_raw` 恰好释放一次),堆数据与 upvalue 列表各恰好释放一次。 - /// 独占所有权免去重:死对象已出表不重复枚举。 - pub(crate) fn teardown_session_heap_data(&mut self) { - // 迭代器体是堆上 Box:收尾路径(full_reset 与 Drop 共用)逐条释放, - // 防 Vm 直接 drop 时表内残留体泄漏。 - self.iters.reset(); - // 对象逐条独占释放:本体 + 堆数据 + upvalue 列表,顺序由 - // drop_dead_session_object 收口。 - for ptr in self.realm.gc.session_object_ptrs.drain(..) { - if ptr.is_null() { - continue; - } - // SAFETY: ptr 来自 session 对象表登记,收尾时仍指向合法对象; - // 表独占持有,无其他释放点。 - crate::session_gc::SessionGc::drop_dead_session_object(ptr); - } - self.free_session_string_heap_data(); - self.free_session_bigint_heap_data(); - self.realm.gc.free_cells(); - } - - /// 释放 VM 内建原型 P 对象(生成器/Promise/异步族与 Object 原型)的堆外属性区。 - /// - /// # 注意事项 - /// 对象本体随 Arc 引用归零释放(字段 drop);属性区须在此显式释放一次 - /// (`JsObject` 无 Drop 口径)。仅释放本 VM 独占(强引用计数为 1)的副本: - /// `object_prototype` 与 session world 共享 Arc,session 存活期下一测试仍经 - /// world 引用同一对象,其属性区归 session 收尾(`teardown_builtins`)释放。 - pub(crate) fn teardown_intrinsic_protos(&mut self) { - for p in [ - &self.realm.object_prototype, - &self.realm.generator_proto, - &self.realm.generator_function_proto, - &self.realm.promise_constructor, - &self.realm.promise_proto, - &self.realm.aggregate_error_constructor, - &self.realm.aggregate_error_proto, - &self.realm.async_function_proto, - &self.realm.async_generator_proto, - &self.realm.async_generator_function_proto, - ] { - if p.strong_count() != 1 { - continue; - } - // SAFETY: 独占副本的属性区仅此一处释放并置空(幂等)。 - unsafe { - (&mut *p.as_mut_ptr()).release_raw_heap(); - } - } - } - pub(crate) fn clear_execution_state(&mut self) { // 重置契约: // - 清空寄存器文件、pc、帧/迭代器栈、保存的执行栈、try 处理器、 @@ -570,7 +521,7 @@ impl Vm { self.sampling.clear_records(); // GC 统计同属执行期状态:池化 Vm 跨 run 复用,不清零则 per-run // 指标(gc_trigger_count)跨文件累积,报告失真。 - let gc = &mut self.realm.gc.session_gc; + let gc = &mut self.realm.gc.borrow_mut().session_gc; gc.total_collections = 0; gc.total_bytes_freed = 0; gc.total_objects_scanned = 0; @@ -595,9 +546,10 @@ impl Vm { // 指针作废,下次 run 重装。 self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); // 单 run 分配包络按 run 边界重起算(与 run_alloc_bytes 起算口径同源)。 - self.realm.gc.run_alloc_peak = 0; + self.realm.gc.borrow_mut().run_alloc_peak = 0; // 执行期收集水位与包络同起算:旧 run 的存活包络不延续到新 run 的触发判定。 - self.realm.gc.gc_watermark = self.realm.gc.gc_threshold_cached; + let threshold = self.realm.gc.borrow().gc_threshold_cached; + self.realm.gc.borrow_mut().gc_watermark = threshold; self.root_reg_limit = 0; self.active_reg_limit = 0; } @@ -669,8 +621,8 @@ impl Vm { /// 水位触发(此时 builtin 局部值已落地为执行根,分配点触发会误释放 /// 仅存于局部/构造中的活串)。 fn register_session_string(&mut self, ptr: *mut JsString, bytes: usize) -> JsValue { - self.realm.gc.session_string_ptrs.push(ptr); - self.realm.gc.session_bytes_allocated += std::mem::size_of::() + bytes; + self.realm.gc.borrow_mut().session_string_ptrs.push(ptr); + self.realm.gc.borrow_mut().session_bytes_allocated += std::mem::size_of::() + bytes; JsValue::string(ptr) } @@ -738,19 +690,6 @@ impl Vm { JsValue::perm_string(self.kernel_core.perm_interner().string_ptr(id)) } - /// 释放全部 session 堆 `JsString`。仅在完全隔离重置 - /// (`full_reset` / `clear_full_reset_state`)时调用,此时没有存活的 session 对象 - /// 会引用它们。较轻量的 `reset()` 刻意保留它们,与 session 对象跨 eval 存活一致。 - fn free_session_string_heap_data(&mut self) { - for ptr in self.realm.gc.session_string_ptrs.drain(..) { - // SAFETY: 每个指针来自 new_string/new_cons_string 的 Box::into_raw, - // 且只在这里(或 sweep)恰好释放一次;内部连带释放 rope 扁平化产物。 - unsafe { - crate::session_gc::SessionGc::drop_session_string_box(ptr); - } - } - } - /// 为 BytecodeFunc 常量创建函数 JsObject。 /// 当 `is_arrow` 为 true 时,捕获当前 `this`(regs[254]),供调用时词法 this 绑定。 /// @@ -772,13 +711,13 @@ impl Vm { // 异步生成器(`async function*`)函数对象:原型为 %AsyncGeneratorFunction.prototype%。 let is_async_generator = is_generator && is_async; let proto_val = if is_async_generator { - JsValue::from_js_object(self.realm.async_generator_function_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.async_generator_function_proto.borrow().as_ptr() as *mut JsObject) } else if is_generator { - JsValue::from_js_object(self.realm.generator_function_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.generator_function_proto.borrow().as_ptr() as *mut JsObject) } else if is_async { - JsValue::from_js_object(self.realm.async_function_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.async_function_proto.borrow().as_ptr() as *mut JsObject) } else { - JsValue::from_js_object(self.realm.session.builtin_world().function_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.session.borrow().builtin_world().function_proto.as_ptr() as *mut JsObject) }; let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto_val); obj.set_function(true); @@ -803,11 +742,11 @@ impl Vm { // 原型对象自身的 [[Prototype]]:生成器为 %GeneratorPrototype%,普通函数为 Object.prototype。 // 纯异步函数(非生成器)无 `prototype` 属性:规范不为其建 prototype 子对象。 let proto_of_proto = if is_async_generator { - JsValue::from_js_object(self.realm.async_generator_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.async_generator_proto.borrow().as_ptr() as *mut JsObject) } else if is_generator { - JsValue::from_js_object(self.realm.generator_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.generator_proto.borrow().as_ptr() as *mut JsObject) } else { - JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject) }; // prototype 子对象与函数本体同走 session 分配:`f.prototype === // globalThis.f.prototype` 要求两侧同一对象,逃逸写不会克隆出第二份。 @@ -873,7 +812,7 @@ impl Vm { /// `&self` 使 `convert_immutables`(常量池 → JsValue)也能分配。 pub fn new_bigint(&self, v: num_bigint::BigInt) -> JsValue { let ptr = Box::into_raw(Box::new(v)); - self.realm.gc.session_bigint_ptrs.borrow_mut().push(ptr); + self.realm.gc.borrow_mut().session_bigint_ptrs.borrow_mut().push(ptr); JsValue::bigint(ptr) } @@ -883,18 +822,6 @@ impl Vm { unsafe { &*val.as_bigint_ptr() } } - /// 释放全部 session 堆 BigInt box。仅在完全隔离重置(`full_reset`)时调用, - /// 此时没有存活的 session 对象/寄存器会引用它们。 - fn free_session_bigint_heap_data(&mut self) { - for ptr in self.realm.gc.session_bigint_ptrs.borrow_mut().drain(..) { - // SAFETY: 每个指针来自 new_bigint 的 Box::into_raw(Box::new(BigInt)), - // 且只在这里恰好释放一次。 - unsafe { - drop(Box::from_raw(ptr)); - } - } - } - fn convert_constant(&self, constant: &Constant) -> JsValue { match constant { Constant::Number(v) => JsValue::float(*v), diff --git a/crates/oxide_vm/src/vm_support/tests.rs b/crates/oxide_vm/src/vm_support/tests.rs index dd61313d..3710d009 100644 --- a/crates/oxide_vm/src/vm_support/tests.rs +++ b/crates/oxide_vm/src/vm_support/tests.rs @@ -6,7 +6,8 @@ fn global_prop(vm: &Vm, name: &str) -> JsValue { } fn global_prop_opt(vm: &Vm, name: &str) -> Option { - let global = vm.realm.session.global_object(); + let session = vm.realm.session.borrow(); + let global = session.global_object(); let si = vm.kernel_core.perm_interner().intern(name).0; vm.kernel_core .shape_forge() @@ -68,17 +69,17 @@ fn full_reset_with_session_objects_forces_global_rebuild() { unsafe { (&*written.as_js_object_ptr()).is_session_epoch() }, "覆盖值应为 session 对象(统一入口 Box 化)" ); - let old_global = vm.realm.session.global_object.as_ptr(); + let old_global = vm.realm.session.borrow().global_object.as_ptr(); vm.full_reset(); // global 必须重建:旧 global 与其 session 子引用随 session 释放,Array 恢复内置构造器。 - assert!(!std::ptr::eq(old_global, vm.realm.session.global_object.as_ptr())); + assert!(!std::ptr::eq(old_global, vm.realm.session.borrow().global_object.as_ptr())); assert!(std::ptr::eq( global_prop(&vm, "Array").as_js_object_ptr(), - vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject + vm.realm.session.borrow().builtin_world().array_constructor.as_ptr() as *mut JsObject )); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// P 原型上方法槽的 wrapper 对象原始指针(rebuild 跨轮复用验证用)。 @@ -98,7 +99,7 @@ fn method_wrapper_ptr(vm: &Vm, proto_ptr: *const JsObject, name: &str) -> *const /// global 属性槽数(槽位原位更新跨轮不追加验证用)。 fn global_slot_count(vm: &Vm) -> usize { - let g = vm.realm.session.global_object.as_ptr() as *mut JsObject; + let g = vm.realm.session.borrow().global_object.as_ptr() as *mut JsObject; // SAFETY: global 是本 session 对象,安全点内无并发读者。 unsafe { (*g).prop_vec_len() } } @@ -110,9 +111,9 @@ fn global_slot_count(vm: &Vm) -> usize { fn full_reset_rebuild_reuses_method_wrappers_across_rounds() { let mut vm = Vm::new(); let _ = run_source(&mut vm, "0"); - let push_before = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push"); + let push_before = method_wrapper_ptr(&vm, vm.realm.session.borrow().builtin_world().array_proto.as_ptr(), "push"); let push_shape_before = unsafe { &*push_before }.shape_id(); - let registry_before = vm.realm.session.builtin_world().leaked_object_count(); + let registry_before = vm.realm.session.borrow().builtin_world().leaked_object_count(); let slots_before = global_slot_count(&vm); for i in 0..3u32 { @@ -126,10 +127,10 @@ fn full_reset_rebuild_reuses_method_wrappers_across_rounds() { vm.full_reset(); } - let push_after = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push"); + let push_after = method_wrapper_ptr(&vm, vm.realm.session.borrow().builtin_world().array_proto.as_ptr(), "push"); assert!(std::ptr::eq(push_before, push_after), "存活方法 wrapper 应复用(同 raw 指针)"); assert_eq!(push_shape_before, unsafe { &*push_after }.shape_id(), "wrapper shape 应稳定"); - let registry_after = vm.realm.session.builtin_world().leaked_object_count(); + let registry_after = vm.realm.session.borrow().builtin_world().leaked_object_count(); assert!( registry_after <= registry_before, "释放表计数跨轮不应增长: {registry_before} -> {registry_after}" @@ -161,7 +162,7 @@ fn full_reset_rebuild_keeps_global_slot_count_flat() { // Error 槽应指向本轮重建的构造器(非滞留旧指针)。 assert!(std::ptr::eq( global_prop(&vm, "Error").as_js_object_ptr(), - vm.realm.session.builtin_world().error_constructor.as_ptr() as *mut JsObject + vm.realm.session.borrow().builtin_world().error_constructor.as_ptr() as *mut JsObject )); assert!(global_prop(&vm, "TypeError").is_object()); assert_eq!(run_source(&mut vm, "new TypeError('x') instanceof TypeError"), JsValue::bool(true)); @@ -279,74 +280,75 @@ fn generator_captured_upvalue_survives_sweep() { #[test] fn full_reset_clean_keeps_session_objects() { let mut vm = Vm::new(); - let world_ptr = Arc::as_ptr(&vm.realm.session.builtin_world); - let global_ptr = vm.realm.session.global_object.as_ptr(); - let object_proto_ptr = vm.realm.session.builtin_world().object_proto.as_ptr(); + let world_ptr = Arc::as_ptr(&vm.realm.session.borrow().builtin_world); + let global_ptr = vm.realm.session.borrow().global_object.as_ptr(); + let object_proto_ptr = vm.realm.session.borrow().builtin_world().object_proto.as_ptr(); vm.full_reset(); - assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.builtin_world))); - assert!(std::ptr::eq(global_ptr, vm.realm.session.global_object.as_ptr())); - assert!(std::ptr::eq(object_proto_ptr, vm.realm.session.builtin_world().object_proto.as_ptr())); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world))); + assert!(std::ptr::eq(global_ptr, vm.realm.session.borrow().global_object.as_ptr())); + assert!(std::ptr::eq(object_proto_ptr, vm.realm.session.borrow().builtin_world().object_proto.as_ptr())); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } #[test] fn full_reset_global_dirty_rebuilds_global_and_restores_slots() { let mut vm = Vm::new(); - let world_ptr = Arc::as_ptr(&vm.realm.session.builtin_world); - let global_ptr = vm.realm.session.global_object.as_ptr(); - let global = unsafe { &mut *(vm.realm.session.global_object.as_ptr() as *mut JsObject) }; + let world_ptr = Arc::as_ptr(&vm.realm.session.borrow().builtin_world); + let global_ptr = vm.realm.session.borrow().global_object.as_ptr(); + let global = unsafe { &mut *(vm.realm.session.borrow().global_object.as_ptr() as *mut JsObject) }; bindings::bind_global_value(&vm.kernel_core, global, "userGlobal", JsValue::int(99)); - unsafe { &mut *(vm.realm.session.global_object.as_ptr() as *mut JsObject) }.bump_generation(); + unsafe { &mut *(vm.realm.session.borrow().global_object.as_ptr() as *mut JsObject) }.bump_generation(); vm.full_reset(); - assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.builtin_world))); - assert!(!std::ptr::eq(global_ptr, vm.realm.session.global_object.as_ptr())); + assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world))); + assert!(!std::ptr::eq(global_ptr, vm.realm.session.borrow().global_object.as_ptr())); assert!(global_prop_opt(&vm, "userGlobal").is_none()); assert!(std::ptr::eq( global_prop(&vm, "Array").as_js_object_ptr(), - vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject + vm.realm.session.borrow().builtin_world().array_constructor.as_ptr() as *mut JsObject )); assert!(std::ptr::eq( global_prop(&vm, "globalThis").as_js_object_ptr(), - vm.realm.session.global_object.as_ptr() as *mut JsObject + vm.realm.session.borrow().global_object.as_ptr() as *mut JsObject )); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } #[test] fn full_reset_dirty_builtin_rebinds_global_slot() { let mut vm = Vm::new(); - let old_object_proto = vm.realm.session.builtin_world().object_proto.as_ptr(); - let old_array_proto = vm.realm.session.builtin_world().array_proto.as_ptr(); + let old_object_proto = vm.realm.session.borrow().builtin_world().object_proto.as_ptr(); + let old_array_proto = vm.realm.session.borrow().builtin_world().array_proto.as_ptr(); unsafe { &mut *(old_array_proto as *mut JsObject) }.bump_generation(); vm.full_reset(); - assert!(std::ptr::eq(old_object_proto, vm.realm.session.builtin_world().object_proto.as_ptr())); - assert!(!std::ptr::eq(old_array_proto, vm.realm.session.builtin_world().array_proto.as_ptr())); + assert!(std::ptr::eq(old_object_proto, vm.realm.session.borrow().builtin_world().object_proto.as_ptr())); + assert!(!std::ptr::eq(old_array_proto, vm.realm.session.borrow().builtin_world().array_proto.as_ptr())); assert!(std::ptr::eq( global_prop(&vm, "Array").as_js_object_ptr(), - vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject + vm.realm.session.borrow().builtin_world().array_constructor.as_ptr() as *mut JsObject )); let constructor_si = vm.kernel_core.perm_interner().intern("constructor").0; - let array_proto = &*vm.realm.session.builtin_world().array_proto; + let session = vm.realm.session.borrow(); + let array_proto = &*session.builtin_world().array_proto; let constructor = vm .resolve_property(array_proto, constructor_si) .expect("Array.prototype.constructor"); assert!(std::ptr::eq( constructor.as_js_object_ptr(), - vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject + vm.realm.session.borrow().builtin_world().array_constructor.as_ptr() as *mut JsObject )); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } #[test] fn full_reset_dirty_function_keeps_call_working() { let mut vm = Vm::new(); - let function_proto = vm.realm.session.builtin_world().function_proto.as_ptr(); + let function_proto = vm.realm.session.borrow().builtin_world().function_proto.as_ptr(); unsafe { &mut *(function_proto as *mut JsObject) }.bump_generation(); vm.full_reset(); @@ -366,7 +368,7 @@ fn full_reset_dirty_function_keeps_call_working() { assert_eq!(in_map, JsValue::bool(true)); let mapped = run_source(&mut vm, "Array.prototype.map.call([1, 2], function(x){ return x + 1; }).join(',')"); assert_eq!(vm.lookup_str(mapped).as_deref(), Some("2,3")); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// Function 家族脏重建:未重建家族的方法 wrapper 是跨重置存活对象,其 proto @@ -374,9 +376,9 @@ fn full_reset_dirty_function_keeps_call_working() { #[test] fn full_reset_dirty_function_repoints_retained_wrapper_proto() { let mut vm = Vm::new(); - let old_fn_proto = vm.realm.session.builtin_world().function_proto.as_ptr() as *mut JsObject; + let old_fn_proto = vm.realm.session.borrow().builtin_world().function_proto.as_ptr() as *mut JsObject; // 保留方法 wrapper:array 家族不重建,wrapper 对象与其 proto 槽跨重置存活。 - let array_proto = vm.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject; + let array_proto = vm.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject; let push_si = vm.kernel_core.perm_interner().intern("push").0; let push = vm .resolve_property(unsafe { &*array_proto }, push_si) @@ -388,12 +390,12 @@ fn full_reset_dirty_function_repoints_retained_wrapper_proto() { vm.full_reset(); - let new_fn_proto = vm.realm.session.builtin_world().function_proto.as_ptr() as *mut JsObject; + let new_fn_proto = vm.realm.session.borrow().builtin_world().function_proto.as_ptr() as *mut JsObject; assert!(!std::ptr::eq(new_fn_proto, old_fn_proto)); // 保留 wrapper proto 槽已重指新 fn_proto,call 链走新原型。 assert!(std::ptr::eq(unsafe { (*push_ptr).proto().as_js_object_ptr() }, new_fn_proto)); assert_eq!(run_source(&mut vm, "Array.prototype.push.call([1], 2)"), JsValue::int(2)); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// 新键写脏 object/array/string/function 四家族(S2 脏源形态):full_reset 后 @@ -406,7 +408,7 @@ fn full_reset_dirty_four_families_cross_family_reads() { &mut vm, "Object.prototype['d'] = 1; Array.prototype['d'] = 2; String.prototype['d'] = 3; Function.prototype['d'] = 4;", ); - assert!(vm.realm.session.is_dirty_since_snapshot()); + assert!(vm.realm.session.borrow().is_dirty_since_snapshot()); vm.full_reset(); @@ -420,7 +422,7 @@ fn full_reset_dirty_four_families_cross_family_reads() { JsValue::bool(true) ); assert_eq!(run_source(&mut vm, "Array.prototype.constructor === Array"), JsValue::bool(true)); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// 未捕获异常侧通道是执行期状态:原生错误后可能残留原值,reset/full_reset @@ -592,32 +594,32 @@ fn dynamic_function_rethrows_to_string_exception() { #[test] fn full_reset_refreshes_object_prototype_after_object_dirty() { let mut vm = Vm::new(); - let old_object_proto = vm.realm.session.builtin_world().object_proto.as_ptr(); + let old_object_proto = vm.realm.session.borrow().builtin_world().object_proto.as_ptr(); unsafe { &mut *(old_object_proto as *mut JsObject) }.bump_generation(); vm.full_reset(); - assert!(!std::ptr::eq(old_object_proto, vm.realm.session.builtin_world().object_proto.as_ptr())); + assert!(!std::ptr::eq(old_object_proto, vm.realm.session.borrow().builtin_world().object_proto.as_ptr())); assert!(std::ptr::eq( - vm.realm.object_prototype.as_ptr(), - vm.realm.session.builtin_world().object_proto.as_ptr() + vm.realm.object_prototype.borrow().as_ptr(), + vm.realm.session.borrow().builtin_world().object_proto.as_ptr() )); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } #[test] fn full_reset_object_dirty_rebinds_iterator_family() { let mut vm = Vm::new(); - let old_object_proto = vm.realm.session.builtin_world().object_proto.as_ptr(); - let old_iterator_proto = vm.realm.session.builtin_world().iterator_proto.as_ptr(); + let old_object_proto = vm.realm.session.borrow().builtin_world().object_proto.as_ptr(); + let old_iterator_proto = vm.realm.session.borrow().builtin_world().iterator_proto.as_ptr(); // 用户修改 Object.prototype:object 家族世代递增,global 未动。 unsafe { &mut *(old_object_proto as *mut JsObject) }.bump_generation(); vm.full_reset(); // object 家族与迭代器原型全部重建(新原型链到新 Object.prototype)。 - assert!(!std::ptr::eq(old_object_proto, vm.realm.session.builtin_world().object_proto.as_ptr())); - assert!(!std::ptr::eq(old_iterator_proto, vm.realm.session.builtin_world().iterator_proto.as_ptr())); + assert!(!std::ptr::eq(old_object_proto, vm.realm.session.borrow().builtin_world().object_proto.as_ptr())); + assert!(!std::ptr::eq(old_iterator_proto, vm.realm.session.borrow().builtin_world().iterator_proto.as_ptr())); // global 保留(dirty.global=false):其 Iterator 函数对象的 prototype // 属性须对齐到重建后的 %IteratorPrototype%。 let iter_val = global_prop(&vm, "Iterator"); @@ -630,10 +632,10 @@ fn full_reset_object_dirty_rebinds_iterator_family() { .expect("Iterator should have prototype slot"); assert!(std::ptr::eq( iter_obj.get_prop_at(proto_pos).as_js_object_ptr(), - vm.realm.session.builtin_world().iterator_proto.as_ptr() as *mut JsObject + vm.realm.session.borrow().builtin_world().iterator_proto.as_ptr() as *mut JsObject )); // full_reset 后 session 干净;此后 run_source 执行才重新累积世代变化。 - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); // 迭代器家族功能完整:原型 next 就位,for-of/spread/Array.from/Iterator.from/ // Map/Set/String/yield* 全部可用,原型链与 Iterator.prototype 一致。 let r = run_source(&mut vm, "[...[1,2,3]].join(',')"); @@ -662,19 +664,19 @@ fn full_reset_object_dirty_rebinds_iterator_family() { #[test] fn full_reset_global_dirty_keeps_iterator_proto_slots_stable() { let mut vm = Vm::new(); - let arr_iter_proto = vm.realm.session.builtin_world().array_iterator_proto.as_ptr() as *mut JsObject; + let arr_iter_proto = vm.realm.session.borrow().builtin_world().array_iterator_proto.as_ptr() as *mut JsObject; let slots_before = unsafe { &*arr_iter_proto }.hash_props_vec().map_or(0, |v| v.len()); // global 世代递增(用户写 global),builtin 家族未动。 - unsafe { &mut *(vm.realm.session.global_object.as_ptr() as *mut JsObject) }.bump_generation(); + unsafe { &mut *(vm.realm.session.borrow().global_object.as_ptr() as *mut JsObject) }.bump_generation(); vm.full_reset(); // builtin 未脏 → 迭代器原型保留原对象且属性槽不膨胀(重复 full_reset 不再追加)。 - let arr_iter_proto_after = vm.realm.session.builtin_world().array_iterator_proto.as_ptr() as *mut JsObject; + let arr_iter_proto_after = vm.realm.session.borrow().builtin_world().array_iterator_proto.as_ptr() as *mut JsObject; assert!(std::ptr::eq(arr_iter_proto, arr_iter_proto_after)); let slots_after = unsafe { &*arr_iter_proto_after }.hash_props_vec().map_or(0, |v| v.len()); assert_eq!(slots_before, slots_after); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); // 迭代器功能经保留原型仍完整(run_source 起再次累积世代变化)。 let r = run_source(&mut vm, "[...[1,2,3]].join(',')"); assert_eq!(vm.lookup_str(r).as_deref(), Some("1,2,3")); @@ -862,19 +864,19 @@ fn inline_for_in_escape_restores_snapshot_and_releases_residual_body() { fn full_reset_value_overwrite_on_builtin_proto_rebuilds_family() { let mut vm = Vm::new(); let _ = run_source(&mut vm, "0"); - let old_array_proto = vm.realm.session.builtin_world().array_proto.as_ptr(); + let old_array_proto = vm.realm.session.borrow().builtin_world().array_proto.as_ptr(); assert_eq!(run_source(&mut vm, "Array.prototype.push = 9"), JsValue::int(9)); assert_eq!(run_source(&mut vm, "Array.prototype.push"), JsValue::int(9)); - assert!(vm.realm.session.is_dirty_since_snapshot(), "值覆盖应推进世代被脏检测捕获"); + assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "值覆盖应推进世代被脏检测捕获"); vm.full_reset(); - assert!(!std::ptr::eq(old_array_proto, vm.realm.session.builtin_world().array_proto.as_ptr())); + assert!(!std::ptr::eq(old_array_proto, vm.realm.session.borrow().builtin_world().array_proto.as_ptr())); let t = run_source(&mut vm, "typeof Array.prototype.push"); assert_eq!(vm.lookup_str(t).as_deref(), Some("function")); assert_eq!(run_source(&mut vm, "[1, 2].push(3)"), JsValue::int(3)); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// 对象值形态的既有槽覆盖同样须被发现:epoch 对象写入 builtin P 对象不被晋升, @@ -883,17 +885,17 @@ fn full_reset_value_overwrite_on_builtin_proto_rebuilds_family() { fn full_reset_value_overwrite_with_object_value_drops_stale_pointer() { let mut vm = Vm::new(); let _ = run_source(&mut vm, "0"); - let old_array_proto = vm.realm.session.builtin_world().array_proto.as_ptr(); + let old_array_proto = vm.realm.session.borrow().builtin_world().array_proto.as_ptr(); let _ = run_source(&mut vm, "Array.prototype.push = {}; 0"); - assert!(vm.realm.session.is_dirty_since_snapshot(), "对象值覆盖应被脏检测捕获"); + assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "对象值覆盖应被脏检测捕获"); vm.full_reset(); - assert!(!std::ptr::eq(old_array_proto, vm.realm.session.builtin_world().array_proto.as_ptr())); + assert!(!std::ptr::eq(old_array_proto, vm.realm.session.borrow().builtin_world().array_proto.as_ptr())); let t = run_source(&mut vm, "typeof Array.prototype.push"); assert_eq!(vm.lookup_str(t).as_deref(), Some("function")); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// 零 session 对象的全局内置值覆盖:`globalThis.Array = 9` 不触发 promote, @@ -905,17 +907,17 @@ fn full_reset_global_builtin_value_overwrite_restores_constructor() { let _ = run_source(&mut vm, "0"); let _ = run_source(&mut vm, "globalThis.Array = 9; 0"); - assert!(vm.realm.gc.session_object_ptrs.is_empty(), "原始值覆盖不产生 session 对象"); - assert!(vm.realm.session.is_dirty_since_snapshot(), "全局值覆盖应推进世代"); + assert!(vm.realm.gc.borrow().session_object_ptrs.is_empty(), "原始值覆盖不产生 session 对象"); + assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "全局值覆盖应推进世代"); vm.full_reset(); assert!(std::ptr::eq( global_prop(&vm, "Array").as_js_object_ptr(), - vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject + vm.realm.session.borrow().builtin_world().array_constructor.as_ptr() as *mut JsObject )); assert_eq!(run_source(&mut vm, "[1].push(2)"), JsValue::int(2)); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// IC 二次写(miss 落既有槽 + 命中直写)与复合赋值(set_member_prop 既有槽) @@ -927,19 +929,19 @@ fn full_reset_value_overwrite_via_ic_and_compound_marks_dirty() { // IC 首写 miss 落既有槽、二次写经缓存命中,两次都须 bump。 let _ = run_source(&mut vm, "Array.prototype.push = 9; Array.prototype.push = 8; 0"); - assert!(vm.realm.session.is_dirty_since_snapshot(), "IC 二次写应被脏检测捕获"); + assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "IC 二次写应被脏检测捕获"); vm.full_reset(); let t = run_source(&mut vm, "typeof Array.prototype.push"); assert_eq!(vm.lookup_str(t).as_deref(), Some("function")); // 复合赋值读改写走 set_member_prop 既有槽路径,同样须 bump。 let _ = run_source(&mut vm, "Array.prototype.push = 9; Array.prototype.push += 1; 0"); - assert!(vm.realm.session.is_dirty_since_snapshot(), "复合赋值应被脏检测捕获"); + assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "复合赋值应被脏检测捕获"); vm.full_reset(); let t = run_source(&mut vm, "typeof Array.prototype.push"); assert_eq!(vm.lookup_str(t).as_deref(), Some("function")); assert_eq!(run_source(&mut vm, "[1, 2, 3].push(4)"), JsValue::int(4)); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// wrapper 本体新增 own 属性:写入的是方法 wrapper 自身而非所属 P 对象, @@ -949,20 +951,20 @@ fn full_reset_value_overwrite_via_ic_and_compound_marks_dirty() { fn full_reset_wrapper_own_property_does_not_survive() { let mut vm = Vm::new(); let _ = run_source(&mut vm, "0"); - let push_before = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push"); + let push_before = method_wrapper_ptr(&vm, vm.realm.session.borrow().builtin_world().array_proto.as_ptr(), "push"); let _ = run_source(&mut vm, "Array.prototype.push.custom = 1; 0"); - assert!(vm.realm.session.is_dirty_since_snapshot(), "wrapper 本体写应被脏检测捕获"); + assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "wrapper 本体写应被脏检测捕获"); vm.full_reset(); assert_eq!(run_source(&mut vm, "Array.prototype.push.custom"), JsValue::undefined()); assert_eq!(run_source(&mut vm, "'custom' in Array.prototype.push"), JsValue::bool(false)); assert_eq!(run_source(&mut vm, "typeof Array.prototype.push === 'function'"), JsValue::bool(true)); - let push_after = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push"); + let push_after = method_wrapper_ptr(&vm, vm.realm.session.borrow().builtin_world().array_proto.as_ptr(), "push"); assert!(!std::ptr::eq(push_before, push_after), "被写的 wrapper 不应被复用"); assert_eq!(run_source(&mut vm, "[1, 2].push(3)"), JsValue::int(3)); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// wrapper 本体写 epoch 对象值:epoch 对象不晋升、不登记,若 wrapper 跨轮 @@ -973,13 +975,13 @@ fn full_reset_wrapper_object_property_does_not_dangle() { let _ = run_source(&mut vm, "0"); let _ = run_source(&mut vm, "Array.prototype.push.custom = {}; 0"); - assert!(vm.realm.session.is_dirty_since_snapshot(), "wrapper 对象值写应被脏检测捕获"); + assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "wrapper 对象值写应被脏检测捕获"); vm.full_reset(); assert_eq!(run_source(&mut vm, "Array.prototype.push.custom"), JsValue::undefined()); assert_eq!(run_source(&mut vm, "[1, 2].push(3)"), JsValue::int(3)); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// wrapper 既有 own 槽的值覆盖:首次写已建槽后重录快照,第二次写走 @@ -989,16 +991,16 @@ fn full_reset_wrapper_existing_slot_overwrite_marks_dirty() { let mut vm = Vm::new(); let _ = run_source(&mut vm, "0"); let _ = run_source(&mut vm, "Array.prototype.push.custom = 1; 0"); - vm.realm.session.record_snapshot(); + vm.realm.session.borrow_mut().record_snapshot(); let _ = run_source(&mut vm, "Array.prototype.push.custom = 2; 0"); - assert!(vm.realm.session.is_dirty_since_snapshot(), "wrapper 既有槽覆盖应被脏检测捕获"); + assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "wrapper 既有槽覆盖应被脏检测捕获"); vm.full_reset(); assert_eq!(run_source(&mut vm, "Array.prototype.push.custom"), JsValue::undefined()); assert_eq!(run_source(&mut vm, "typeof Array.prototype.push === 'function'"), JsValue::bool(true)); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// 不可复用宿主对象(`Reflect`)的属性写:登记面无复用键,脏时靠 global @@ -1009,13 +1011,13 @@ fn full_reset_keyless_leaked_object_property_does_not_survive() { let _ = run_source(&mut vm, "0"); let _ = run_source(&mut vm, "Reflect.custom = 1; 0"); - assert!(vm.realm.session.is_dirty_since_snapshot(), "keyless 宿主对象写应被脏检测捕获"); + assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "keyless 宿主对象写应被脏检测捕获"); vm.full_reset(); assert_eq!(run_source(&mut vm, "'custom' in Reflect"), JsValue::bool(false)); assert_eq!(run_source(&mut vm, "typeof Reflect.apply === 'function'"), JsValue::bool(true)); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// global 函数 wrapper(`parseInt`)的属性写:global 函数 wrapper 无 P 家族 @@ -1026,13 +1028,13 @@ fn full_reset_global_function_wrapper_property_does_not_survive() { let _ = run_source(&mut vm, "0"); let _ = run_source(&mut vm, "parseInt.custom = 1; 0"); - assert!(vm.realm.session.is_dirty_since_snapshot(), "global 函数 wrapper 写应被脏检测捕获"); + assert!(vm.realm.session.borrow().is_dirty_since_snapshot(), "global 函数 wrapper 写应被脏检测捕获"); vm.full_reset(); assert_eq!(run_source(&mut vm, "'custom' in parseInt"), JsValue::bool(false)); assert_eq!(run_source(&mut vm, "parseInt('42')"), JsValue::int(42)); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// wrapper 写重建后基线已刷新:后续仅 P 对象脏写的轮次复用新 wrapper、 @@ -1044,8 +1046,8 @@ fn wrapper_write_rebuild_then_p_writes_reuse_fresh_wrapper() { let _ = run_source(&mut vm, "Array.prototype.push.custom = 1; 0"); vm.full_reset(); - let push_after = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push"); - let registry_after = vm.realm.session.builtin_world().leaked_object_count(); + let push_after = method_wrapper_ptr(&vm, vm.realm.session.borrow().builtin_world().array_proto.as_ptr(), "push"); + let registry_after = vm.realm.session.borrow().builtin_world().leaked_object_count(); for i in 0..3u32 { let source = format!("Array.prototype['w{i}'] = 1; Object.prototype['w{i}'] = 2; 0"); @@ -1053,15 +1055,15 @@ fn wrapper_write_rebuild_then_p_writes_reuse_fresh_wrapper() { vm.full_reset(); } - let push_final = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push"); + let push_final = method_wrapper_ptr(&vm, vm.realm.session.borrow().builtin_world().array_proto.as_ptr(), "push"); assert!(std::ptr::eq(push_after, push_final), "P 写轮应复用 wrapper,不应每轮失效重建"); assert_eq!( - vm.realm.session.builtin_world().leaked_object_count(), + vm.realm.session.borrow().builtin_world().leaked_object_count(), registry_after, "P 写轮释放登记表不应增长" ); assert_eq!(run_source(&mut vm, "[1, 2].push(3)"), JsValue::int(3)); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// 假阳性护栏:只写用户对象的良性运行不得误判 wrapper 脏——wrapper 世代 @@ -1070,16 +1072,16 @@ fn wrapper_write_rebuild_then_p_writes_reuse_fresh_wrapper() { fn benign_user_object_writes_do_not_dirty_leaked_objects() { let mut vm = Vm::new(); let _ = run_source(&mut vm, "0"); - let world_ptr = Arc::as_ptr(&vm.realm.session.builtin_world); + let world_ptr = Arc::as_ptr(&vm.realm.session.borrow().builtin_world); let result = run_source(&mut vm, "(function () { var a = {}; a.x = 1; a.x = 2; return a.x; })()"); assert_eq!(result, JsValue::int(2)); - assert!(!vm.realm.session.is_dirty_since_snapshot(), "只写用户对象不应误判 wrapper 脏"); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot(), "只写用户对象不应误判 wrapper 脏"); vm.full_reset(); - assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.builtin_world)), "无污染不应重建 world"); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world)), "无污染不应重建 world"); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } /// 假阳性护栏:只写用户对象的良性运行不得误判 builtin 脏。 @@ -1091,16 +1093,16 @@ fn benign_user_object_writes_do_not_dirty_leaked_objects() { fn benign_run_does_not_falsely_dirty_builtins() { let mut vm = Vm::new(); let _ = run_source(&mut vm, "0"); - let world_ptr = Arc::as_ptr(&vm.realm.session.builtin_world); + let world_ptr = Arc::as_ptr(&vm.realm.session.borrow().builtin_world); run_source(&mut vm, "({ x: 1 }).x + [1, 2].length"); assert_eq!(vm.session_object_count(), 2, "良性表达式应产生两枚临时对象(对象字面量 + 数组字面量)"); - assert!(!vm.realm.session.is_dirty_since_snapshot(), "良性运行不应误判 builtin 脏"); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot(), "良性运行不应误判 builtin 脏"); vm.full_reset(); - assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.builtin_world)), "builtin world 不应重建"); - assert!(!vm.realm.session.is_dirty_since_snapshot()); + assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.borrow().builtin_world)), "builtin world 不应重建"); + assert!(!vm.realm.session.borrow().is_dirty_since_snapshot()); } #[test] diff --git a/crates/oxide_vm/tests/builtins_error_tests.rs b/crates/oxide_vm/tests/builtins_error_tests.rs index 6d55125c..be886d82 100644 --- a/crates/oxide_vm/tests/builtins_error_tests.rs +++ b/crates/oxide_vm/tests/builtins_error_tests.rs @@ -160,7 +160,8 @@ fn error_subtype_constructors_produce_named_objects() { #[test] fn error_prototype_has_name() { let vm = make_vm(); - let bw = vm.session().builtin_world(); + let session = vm.session(); + let bw = session.builtin_world(); let name_si = vm.kernel_core().perm_interner().intern("name").0; let proto_ptr = P::as_ptr(&bw.error_proto) as *mut oxide_types::object::JsObject; let si = vm @@ -173,7 +174,8 @@ fn error_prototype_has_name() { #[test] fn error_prototype_has_message() { let vm = make_vm(); - let bw = vm.session().builtin_world(); + let session = vm.session(); + let bw = session.builtin_world(); let msg_si = vm.kernel_core().perm_interner().intern("message").0; let proto_ptr = P::as_ptr(&bw.error_proto) as *mut oxide_types::object::JsObject; let si = vm @@ -186,7 +188,8 @@ fn error_prototype_has_message() { #[test] fn type_error_prototype_name() { let vm = make_vm(); - let bw = vm.session().builtin_world(); + let session = vm.session(); + let bw = session.builtin_world(); let name_si = vm.kernel_core().perm_interner().intern("name").0; let proto_ptr = P::as_ptr(&bw.type_error_proto) as *mut oxide_types::object::JsObject; let si = vm @@ -199,7 +202,8 @@ fn type_error_prototype_name() { #[test] fn reference_error_prototype_name() { let vm = make_vm(); - let bw = vm.session().builtin_world(); + let session = vm.session(); + let bw = session.builtin_world(); let name_si = vm.kernel_core().perm_interner().intern("name").0; let proto_ptr = P::as_ptr(&bw.reference_error_proto) as *mut oxide_types::object::JsObject; let si = vm @@ -212,7 +216,8 @@ fn reference_error_prototype_name() { #[test] fn range_error_prototype_name() { let vm = make_vm(); - let bw = vm.session().builtin_world(); + let session = vm.session(); + let bw = session.builtin_world(); let name_si = vm.kernel_core().perm_interner().intern("name").0; let proto_ptr = P::as_ptr(&bw.range_error_proto) as *mut oxide_types::object::JsObject; let si = vm @@ -225,7 +230,8 @@ fn range_error_prototype_name() { #[test] fn syntax_error_prototype_name() { let vm = make_vm(); - let bw = vm.session().builtin_world(); + let session = vm.session(); + let bw = session.builtin_world(); let name_si = vm.kernel_core().perm_interner().intern("name").0; let proto_ptr = P::as_ptr(&bw.syntax_error_proto) as *mut oxide_types::object::JsObject; let si = vm @@ -238,7 +244,8 @@ fn syntax_error_prototype_name() { #[test] fn uri_error_prototype_name() { let vm = make_vm(); - let bw = vm.session().builtin_world(); + let session = vm.session(); + let bw = session.builtin_world(); let name_si = vm.kernel_core().perm_interner().intern("name").0; let proto_ptr = P::as_ptr(&bw.uri_error_proto) as *mut oxide_types::object::JsObject; let si = vm @@ -251,7 +258,8 @@ fn uri_error_prototype_name() { #[test] fn eval_error_prototype_name() { let vm = make_vm(); - let bw = vm.session().builtin_world(); + let session = vm.session(); + let bw = session.builtin_world(); let name_si = vm.kernel_core().perm_interner().intern("name").0; let proto_ptr = P::as_ptr(&bw.eval_error_proto) as *mut oxide_types::object::JsObject; let si = vm diff --git a/crates/oxide_vm/tests/session_arena_tests.rs b/crates/oxide_vm/tests/session_arena_tests.rs index 16381673..d3ade289 100644 --- a/crates/oxide_vm/tests/session_arena_tests.rs +++ b/crates/oxide_vm/tests/session_arena_tests.rs @@ -15,7 +15,8 @@ fn run_source(vm: &mut Vm, source: &str) -> JsValue { fn global_prop(vm: &Vm, name: &str) -> JsValue { let si = vm.kernel_core().perm_interner().intern(name).0; - let global = vm.session().global_object(); + let session = vm.session(); + let global = session.global_object(); let pos = vm .kernel_core() .shape_forge()