From 14ef287a75e6541ed3458c6e88bdca3a9862de8b Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 7 Oct 2026 00:10:27 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=8F=90=E5=8F=96=20Realm=20?= =?UTF-8?q?=E7=BB=84=E5=90=88=E7=B1=BB=E5=9E=8B=EF=BC=8Csession=20?= =?UTF-8?q?=E4=B8=8E=20GC=20=E5=8F=8A=E5=8E=9F=E5=9E=8B=E6=A7=BD=E5=BD=92?= =?UTF-8?q?=20realm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 36 +-- Cargo.toml | 2 +- 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 | 30 +-- crates/oxide_vm/src/dispatch/call.rs | 20 +- crates/oxide_vm/src/dispatch/misc.rs | 18 +- crates/oxide_vm/src/dispatch/property.rs | 12 +- crates/oxide_vm/src/generator.rs | 18 +- crates/oxide_vm/src/lib.rs | 2 + crates/oxide_vm/src/promise/aggregate.rs | 42 ++-- crates/oxide_vm/src/promise/constructor.rs | 34 +-- crates/oxide_vm/src/promise/reactions.rs | 8 +- crates/oxide_vm/src/promise/settlement.rs | 8 +- crates/oxide_vm/src/realm.rs | 44 ++++ crates/oxide_vm/src/session_gc.rs | 58 ++--- crates/oxide_vm/src/session_gc/tests.rs | 254 ++++++++++----------- crates/oxide_vm/src/test262_host.rs | 4 +- crates/oxide_vm/src/vm/call.rs | 8 +- crates/oxide_vm/src/vm/dispatch_loop.rs | 18 +- crates/oxide_vm/src/vm/gc_hooks.rs | 42 ++-- crates/oxide_vm/src/vm/mod.rs | 34 +-- crates/oxide_vm/src/vm/vmhost.rs | 8 +- 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 | 12 +- crates/oxide_vm/src/vm_runtime.rs | 6 +- crates/oxide_vm/src/vm_state.rs | 2 +- crates/oxide_vm/src/vm_support.rs | 205 +++++++++-------- crates/oxide_vm/src/vm_support/tests.rs | 198 ++++++++-------- 31 files changed, 598 insertions(+), 567 deletions(-) create mode 100644 crates/oxide_vm/src/realm.rs diff --git a/Cargo.lock b/Cargo.lock index a544a710..c119037e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.8.46" +version = "0.8.47" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.8.46" +version = "0.8.47" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.8.46" +version = "0.8.47" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.8.46" +version = "0.8.47" dependencies = [ "ansi_term", "bumpalo", @@ -1225,7 +1225,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.8.46" +version = "0.8.47" dependencies = [ "lru", "oxide_bytecode", @@ -1234,7 +1234,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.8.46" +version = "0.8.47" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1253,7 +1253,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.8.46" +version = "0.8.47" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1267,7 +1267,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.8.46" +version = "0.8.47" dependencies = [ "num-bigint", "oxc_allocator", @@ -1282,7 +1282,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.8.46" +version = "0.8.47" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1291,7 +1291,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.8.46" +version = "0.8.47" dependencies = [ "bumpalo", "dashmap", @@ -1306,7 +1306,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.8.46" +version = "0.8.47" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1318,7 +1318,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.8.46" +version = "0.8.47" dependencies = [ "tracing", "tracing-appender", @@ -1327,7 +1327,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.8.46" +version = "0.8.47" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1342,7 +1342,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.8.46" +version = "0.8.47" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1358,7 +1358,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.8.46" +version = "0.8.47" dependencies = [ "num-bigint", "num-traits", @@ -1370,7 +1370,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.8.46" +version = "0.8.47" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1387,7 +1387,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.8.46" +version = "0.8.47" dependencies = [ "bumpalo", "hashbrown 0.14.5", @@ -1399,7 +1399,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.8.46" +version = "0.8.47" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 71fdd8c0..8bdfb53e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ members = [ ] [workspace.package] -version = "0.8.46" +version = "0.8.47" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_vm/src/async_disposable.rs b/crates/oxide_vm/src/async_disposable.rs index e8605200..24d197aa 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.session.builtin_world().fn_proto_val(); + let fn_proto = vm.realm.session.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 ca03ed17..f35e8701 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.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let object_proto = self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = vm.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = vm.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = vm.realm.session.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 dfca2d11..638474aa 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.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto_val = vm.realm.session.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.session.builtin_world().find_fn_wrapper(af_reuse_key, af_ctor_fn_ptr, 1) { + match vm.realm.session.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.session.builtin_world().function_constructor.as_ptr() as *mut JsObject); + JsValue::from_js_object(vm.realm.session.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.session.builtin_world().track_fn_wrapper(af_ctor_ptr, af_reuse_key); + vm.realm.session.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.async_function_proto, *af_proto); + Vm::swap_intrinsic_proto(&mut vm.realm.async_function_proto, *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.async_function_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(vm.realm.async_function_proto.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 3a69cee9..f15a466c 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.async_generator_proto.as_ptr() as *mut JsObject); + let gen_proto_val = JsValue::from_js_object(self.realm.async_generator_proto.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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,8 @@ 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.session.builtin_world().fn_proto_val(); - let world = vm.session.builtin_world(); + let fn_proto_val = vm.realm.session.builtin_world().fn_proto_val(); + let world = vm.realm.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 +1547,7 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) { world, ag_label, ); - Vm::swap_intrinsic_proto(&mut vm.async_generator_proto, *ag_proto); + Vm::swap_intrinsic_proto(&mut vm.realm.async_generator_proto, *ag_proto); // %AsyncGeneratorFunction.prototype%:proto = Function.prototype, // constructor = %AsyncGeneratorFunction%。占位构造器动态创建未实现, @@ -1600,7 +1600,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.async_generator_proto.as_ptr() as *mut JsObject)); + let ppos2 = agf_proto.push_prop(JsValue::from_js_object(vm.realm.async_generator_proto.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 +1614,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.async_generator_function_proto, *agf_proto); + Vm::swap_intrinsic_proto(&mut vm.realm.async_generator_function_proto, *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.async_generator_function_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(vm.realm.async_generator_function_proto.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 +1644,7 @@ pub(crate) fn init_async_generator_intrinsics(vm: &mut Vm) { // 绑定 global:AsyncGeneratorFunction 槽已存在则原位更新(full_reset 未重建 // global 时旧槽指向已弃 ctor),不存在则开新槽。 - let global_ptr = vm.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = vm.realm.session.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 2a80db66..4db4f869 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.gc_state.alloc_cell(JsValue::undefined(), false); + current_cells[cell_idx] = self.realm.gc.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.gc_state.alloc_cell(value, initialized); + current[cell_idx] = self.realm.gc.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.gc_state.alloc_cell(value, initialized); + current[cell_idx] = self.realm.gc.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.gc_state.alloc_cell(value, true); + current[cell_idx] = self.realm.gc.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.gc_state.alloc_cell(val, true); + current[cell_idx] = self.realm.gc.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.gc_state.alloc_cell(val, true); + upvals[uv_idx] = self.realm.gc.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.gc_state.alloc_cell(src_val, true); + upvals[uv_idx] = self.realm.gc.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.session.builtin_world().regexp_constructor.as_ptr() as *mut JsObject; + let ctor_ptr = self.realm.session.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.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = &*self.realm.object_prototype as *const JsObject 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.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = &*self.realm.object_prototype as *const JsObject 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 3a0faa2d..77520077 100644 --- a/crates/oxide_vm/src/dispatch/misc.rs +++ b/crates/oxide_vm/src/dispatch/misc.rs @@ -39,7 +39,7 @@ 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.session.global_object(); + let global = self.realm.session.global_object(); if let Some(val) = self.resolve_property(global, si) { self.regs[rd] = val; Ok(false) @@ -67,7 +67,7 @@ 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.session.global_object(); + let global = self.realm.session.global_object(); self.regs[rd] = self.resolve_property(global, si).unwrap_or(JsValue::undefined()); Ok(()) } @@ -215,7 +215,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.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = &*self.realm.object_prototype as *const JsObject 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 +258,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.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = &*self.realm.object_prototype as *const JsObject 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 +278,7 @@ impl Vm { Some(frame) => (frame.arguments_base, frame.arguments_count), None => (self.inline_args_base, self.inline_args_count), }; - let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = &*self.realm.object_prototype as *const JsObject 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 +312,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.session.builtin_world().throw_type_error.get(); + let thrower_ptr = self.realm.session.builtin_world().throw_type_error.get(); if thrower_ptr.is_null() { // 绑定层尚未写入共享对象(正常路径不出现):退化为数据属性保调用可用。 if let Err(msg) = @@ -367,7 +367,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.session.builtin_world().array_proto.as_ptr(); + let array_proto = self.realm.session.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 +397,7 @@ impl Vm { }; let n = count as usize; let rest_len = n.saturating_sub(fixed_count); - let proto_ptr = self.session.builtin_world().array_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.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 +417,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.session.builtin_world().array_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.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 c5e28636..90c88f21 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.session.builtin_world().string_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.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.session.builtin_world().number_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.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.session.builtin_world().boolean_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.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.session.builtin_world().bigint_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.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.session.builtin_world().symbol_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.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.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = self.realm.session.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 935ed13d..9c486dcd 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.generator_proto.as_ptr() as *mut JsObject); + let gen_proto_val = JsValue::from_js_object(self.realm.generator_proto.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,12 @@ 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.session.builtin_world().fn_proto_val(); - let world = vm.session.builtin_world(); + let fn_proto_val = vm.realm.session.builtin_world().fn_proto_val(); + let world = vm.realm.session.builtin_world(); // %GeneratorPrototype%:proto = %IteratorPrototype%(生成器是迭代器,继承 // @@iterator 与 Iterator helper 方法),方法 next/return/throw。 let iterator_proto_val = - JsValue::from_js_object(vm.session.builtin_world().iterator_proto.as_ptr() as *mut JsObject); + JsValue::from_js_object(vm.realm.session.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 +836,7 @@ pub(crate) fn init_generator_intrinsics(vm: &mut Vm) { world, gen_label, ); - Vm::swap_intrinsic_proto(&mut vm.generator_proto, *gen_proto); + Vm::swap_intrinsic_proto(&mut vm.realm.generator_proto, *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 +888,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.generator_proto.as_ptr() as *mut JsObject)); + let ppos2 = gf_proto.push_prop(JsValue::from_js_object(vm.realm.generator_proto.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 +902,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.generator_function_proto, *gf_proto); + Vm::swap_intrinsic_proto(&mut vm.realm.generator_function_proto, *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.generator_function_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(vm.realm.generator_function_proto.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 +932,7 @@ pub(crate) fn init_generator_intrinsics(vm: &mut Vm) { // 绑定 global:GeneratorFunction 槽已存在则原位更新(full_reset 未重建 global // 时旧槽指向已弃 ctor),不存在则开新槽。 - let global_ptr = vm.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = vm.realm.session.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/lib.rs b/crates/oxide_vm/src/lib.rs index 2eb7165d..cf2daacc 100644 --- a/crates/oxide_vm/src/lib.rs +++ b/crates/oxide_vm/src/lib.rs @@ -23,6 +23,8 @@ pub mod native; mod native_box_dispatch; /// Promise 运行时(状态盒 + 微任务队列 + 构造器/方法实现)。 pub mod promise; +/// 每 VM 的 realm 组合:内核会话、session GC 簿记与 10 个内建原型槽。 +mod realm; /// session 级 mark-sweep GC:标记-清扫 session arena 对象与 session 字符串。 pub mod session_gc; mod suspended; diff --git a/crates/oxide_vm/src/promise/aggregate.rs b/crates/oxide_vm/src/promise/aggregate.rs index 52db5d70..917a1272 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.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(self.realm.session.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.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(self.realm.session.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.aggregate_error_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(self.realm.aggregate_error_proto.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.session.builtin_world().array_proto.as_ptr() as *mut JsObject); + let array_proto = JsValue::from_js_object(self.realm.session.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.session.builtin_world().array_proto.as_ptr() as *mut JsObject); + let array_proto = JsValue::from_js_object(self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.session.builtin_world().error_proto.as_ptr() as *mut JsObject); + JsValue::from_js_object(self.realm.session.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.session.builtin_world().error_constructor.as_ptr() as *mut JsObject); + JsValue::from_js_object(self.realm.session.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.aggregate_error_proto, *proto); - Self::swap_intrinsic_proto(&mut self.aggregate_error_constructor, *ctor); + Self::swap_intrinsic_proto(&mut self.realm.aggregate_error_proto, *proto); + Self::swap_intrinsic_proto(&mut self.realm.aggregate_error_constructor, *ctor); // SAFETY: aggregate_error_proto 为堆址固定的 P(Arc 透明包装),本行前刚经 swap_intrinsic_proto 落地;与 ctor_mut 分属不同对象,无别名。 - let proto_mut = unsafe { &mut *self.aggregate_error_proto.as_mut_ptr() }; + let proto_mut = unsafe { &mut *self.realm.aggregate_error_proto.as_mut_ptr() }; proto_mut - .set_prop_at(0u32, JsValue::from_js_object(self.aggregate_error_constructor.as_ptr() as *mut JsObject)); + .set_prop_at(0u32, JsValue::from_js_object(self.realm.aggregate_error_constructor.as_ptr() as *mut JsObject)); // SAFETY: aggregate_error_constructor 同为堆址固定的 P(Arc 透明包装);此处写 prototype 槽位(下标 2),与 proto_mut 分属不同对象,无别名。 - let ctor_mut = unsafe { &mut *self.aggregate_error_constructor.as_mut_ptr() }; - ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.aggregate_error_proto.as_ptr() as *mut JsObject)); + 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)); // 绑定 global(槽已存在则更新)。 - let global_ptr = self.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = self.realm.session.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.aggregate_error_constructor.as_ptr() as *mut JsObject); + let ctor_val = JsValue::from_js_object(self.realm.aggregate_error_constructor.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.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let object_proto = JsValue::from_js_object(vm.realm.session.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.session.builtin_world().array_proto.as_ptr() as *mut JsObject); + let array_proto = JsValue::from_js_object(vm.realm.session.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.aggregate_error_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(vm.realm.aggregate_error_proto.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 a082796b..f8e4e740 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.promise_proto.as_ptr() as *mut JsObject); + let proto_val = JsValue::from_js_object(self.realm.promise_proto.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.session.builtin_world().data_view_constructor.as_ptr() as *mut JsObject, + self.realm.session.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.session.builtin_world().data_view_proto.as_ptr() as *mut JsObject); + JsValue::from_js_object(self.realm.session.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.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let object_proto = JsValue::from_js_object(self.realm.session.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,10 @@ 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.session.builtin_world().fn_proto_val(); - let world = self.session.builtin_world(); + let fn_proto_val = self.realm.session.builtin_world().fn_proto_val(); + let world = self.realm.session.builtin_world(); let object_proto_val = - JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + JsValue::from_js_object(self.realm.session.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)); @@ -327,23 +327,23 @@ impl Vm { ); // 固定地址后互相接线:proto.constructor ↔ ctor.prototype。 - Self::swap_intrinsic_proto(&mut self.promise_proto, *proto); - Self::swap_intrinsic_proto(&mut self.promise_constructor, *ctor); + Self::swap_intrinsic_proto(&mut self.realm.promise_proto, *proto); + Self::swap_intrinsic_proto(&mut self.realm.promise_constructor, *ctor); // SAFETY: `promise_proto` 为 `P`(Arc 透明包装),堆址固定且本行前刚经 // swap_intrinsic_proto 落地;与下一处 ctor_mut 指向不同对象,无别名。 - let proto_mut = unsafe { &mut *self.promise_proto.as_mut_ptr() }; - proto_mut.set_prop_at(0u32, JsValue::from_js_object(self.promise_constructor.as_ptr() as *mut JsObject)); + 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)); // SAFETY: `promise_constructor` 同为堆址固定的存活 `P`(Arc 透明包装); // 此处写 prototype 槽位(下标 2),与 proto_mut 分属不同对象,无别名。 - let ctor_mut = unsafe { &mut *self.promise_constructor.as_mut_ptr() }; + let ctor_mut = unsafe { &mut *self.realm.promise_constructor.as_mut_ptr() }; // prototype 槽位在 length/name 之后(下标 2)。 - ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.promise_proto.as_ptr() as *mut JsObject)); + ctor_mut.set_prop_at(2u32, JsValue::from_js_object(self.realm.promise_proto.as_ptr() as *mut JsObject)); // Promise[Symbol.species] 访问器:getter 返回 receiver,派生类沿静态原型链 // 解析 @@species 得自身构造器。ctor 经本函数每次调用全新构造,同键槽不累积。 crate::bindings::bind_accessor_getter_key( &self.kernel_core, - &self.session, + &self.realm.session, ctor_mut, oxide_types::private_key::make_well_known_symbol_key(oxide_types::private_key::WELL_KNOWN_SYMBOL_SPECIES), "get [Symbol.species]", @@ -351,12 +351,12 @@ impl Vm { ); // 绑定 global:槽已存在则更新(full_reset 未重建 global 时旧槽指向已弃 ctor)。 - let global_ptr = self.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = self.realm.session.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.promise_constructor.as_ptr() as *mut JsObject); + let ctor_val = JsValue::from_js_object(self.realm.promise_constructor.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 +392,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.promise_proto.as_ptr() as *mut JsObject); + let promise_proto_val = JsValue::from_js_object(vm.realm.promise_proto.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 93626264..00d4fb9e 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.promise_constructor.as_ptr() as *mut JsObject)); + return Ok(JsValue::from_js_object(self.realm.promise_constructor.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.promise_constructor.as_ptr() as *mut JsObject)); + .unwrap_or_else(|| JsValue::from_js_object(vm.realm.promise_constructor.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 8c63b20f..b09bc3ee 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.promise_constructor.as_ptr() as *mut JsObject); + let intrinsic = JsValue::from_js_object(self.realm.promise_constructor.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.session.builtin_world().fn_proto_val(); + let fn_proto = self.realm.session.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.promise_constructor.as_ptr() as *mut JsObject); + let intrinsic = JsValue::from_js_object(self.realm.promise_constructor.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 new file mode 100644 index 00000000..94a7a7c0 --- /dev/null +++ b/crates/oxide_vm/src/realm.rs @@ -0,0 +1,44 @@ +//! 每 VM 的 realm 组合:内核会话(builtin world 与 global 对象)、 +//! session GC 簿记与 10 个内建原型槽。 +//! +//! `Vm` 以单个 `realm: Realm` 字段持有本组合。`kernel_core` +//! 留在 `Vm` 上不进 Realm(`active_vms` 边界守卫计数是 per-VM 语义, +//! `KernelCore` 是跨 realm 共享结构,多 realm 共享同一 kernel)。 + +use oxide_kernel::kernel::KernelSession; +use oxide_types::mem::P; +use oxide_types::object::JsObject; + +use crate::vm_state::GcState; + +/// 每 VM 的 realm 组合:内核会话(builtin world 与 global 对象)、 +/// session GC 簿记与 10 个内建原型槽。 +/// +/// 字段顺序按原 `Vm` 声明顺序(session 先、10 个 P 字段次之、gc 最后), +/// 保 Drop 相对顺序。 +pub(crate) struct Realm { + pub(crate) session: KernelSession, + /// `%Object.prototype%`:session 的 Object 原型(global 的 `[[Prototype]]` 挂它)。 + pub(crate) object_prototype: P, + /// `%GeneratorPrototype%`:生成器实例的原型(next/return/throw 方法挂此)。 + pub(crate) generator_proto: P, + /// `%GeneratorFunction.prototype%`:生成器函数对象的原型(`constructor` 指向 + /// `%GeneratorFunction%`,使 `g.constructor.name` 解析为 "GeneratorFunction")。 + pub(crate) generator_function_proto: P, + /// `%Promise%` 构造器(resolve/reject 静态方法挂此,global 的 Promise 槽指向它)。 + pub(crate) promise_constructor: P, + /// `%Promise.prototype%`:Promise 实例的原型(then/catch/finally 方法挂此)。 + pub(crate) promise_proto: P, + /// `%AggregateError%` 构造器(Promise.any 拒绝时构造 AggregateError 用)。 + pub(crate) aggregate_error_constructor: P, + /// `%AggregateError.prototype%`(proto = %Error.prototype%)。 + pub(crate) aggregate_error_proto: P, + /// `%AsyncFunction.prototype%`:异步函数对象的原型(`constructor` 指向 `%AsyncFunction%`)。 + pub(crate) async_function_proto: P, + /// `%AsyncGeneratorPrototype%`:异步生成器实例的原型(next/return/throw/@@asyncIterator)。 + pub(crate) async_generator_proto: P, + /// `%AsyncGeneratorFunction.prototype%`:异步生成器函数对象的原型。 + pub(crate) async_generator_function_proto: P, + /// session 堆与 GC 簿记(对象/字符串/BigInt/cell 四表 + 水位与账目)。 + pub(crate) gc: GcState, +} diff --git a/crates/oxide_vm/src/session_gc.rs b/crates/oxide_vm/src/session_gc.rs index 394a750c..2d4cd46a 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.gc_state.session_object_ptrs.len()); + vm_debug!("[GC] clear_all_marks: {} session objects", vm.realm.gc.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.gc_state.session_object_ptrs { + for &ptr in &vm.realm.gc.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.gc_state.session_object_ptrs); + let old_ptrs = std::mem::take(&mut vm.realm.gc.session_object_ptrs); let mut survivors = Vec::with_capacity(old_ptrs.len()); // 弱键定夺先于死对象释放:键对象(含死键)此刻全部仍分配, @@ -689,12 +689,12 @@ impl SessionGc { } } let live = survivors.len() as u64; - vm.gc_state.session_object_ptrs = survivors; + vm.realm.gc.session_object_ptrs = survivors; // 对象口径重算:存活对象头 + 堆数据求和(串/BigInt/cell 分量不在此口径, // 由各清扫路径按存活补回,与完整收集的最终账目一致)。 - vm.gc_state.session_bytes_allocated = vm - .gc_state + vm.realm.gc.session_bytes_allocated = vm + .realm.gc .session_object_ptrs .iter() .filter(|&&ptr| !ptr.is_null()) @@ -737,8 +737,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.gc_state.session_string_ptrs.len()); - let old = std::mem::take(&mut vm.gc_state.session_string_ptrs); + 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); let mut freed = 0u64; let mut live_bytes = 0usize; let mut live = Vec::with_capacity(old.len()); @@ -756,8 +756,8 @@ impl SessionGc { freed += unsafe { Self::drop_dead_session_string(ptr) }; } } - vm.gc_state.session_string_ptrs = live; - vm.gc_state.session_bytes_allocated = vm.gc_state.session_bytes_allocated.saturating_add(live_bytes); + vm.realm.gc.session_string_ptrs = live; + vm.realm.gc.session_bytes_allocated = vm.realm.gc.session_bytes_allocated.saturating_add(live_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 +769,7 @@ impl SessionGc { /// forwarding 表与根指针重写。在字符串清扫之后运行。 /// 返回释放的字节数。 pub(crate) fn sweep_session_bigints(&mut self, vm: &mut Vm) -> u64 { - let old = vm.gc_state.session_bigint_ptrs.borrow_mut().drain(..).collect::>(); + let old = vm.realm.gc.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 +788,7 @@ impl SessionGc { } } } - *vm.gc_state.session_bigint_ptrs.borrow_mut() = live; + *vm.realm.gc.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 +804,7 @@ impl SessionGc { /// 统计、不重算存活账目。 /// 返回释放的字节数。 pub(crate) fn sweep_session_cells(&mut self, vm: &mut Vm) -> u64 { - let old = vm.gc_state.session_cell_ptrs.borrow_mut().drain(..).collect::>(); + let old = vm.realm.gc.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 +822,7 @@ impl SessionGc { } } } - *vm.gc_state.session_cell_ptrs.borrow_mut() = live; + *vm.realm.gc.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 +831,10 @@ impl SessionGc { /// 完整收集的触发判断:对象、字符串或 BigInt 表非空,且字节账目已达阈值。 pub(crate) fn should_collect(&self, vm: &Vm) -> bool { - (!vm.gc_state.session_object_ptrs.is_empty() - || !vm.gc_state.session_string_ptrs.is_empty() - || !vm.gc_state.session_bigint_ptrs.borrow().is_empty()) - && vm.gc_state.session_bytes_allocated >= vm.kernel_core().config().session_gc_threshold + (!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 } /// 执行期字符串回收的触发判断:账目须超过水位(上次收集后的存活字节 + @@ -842,10 +842,10 @@ impl SessionGc { /// 保证死对象超阈值即被 reset 回收;执行期走增量水位,活串超阈值时不每指令 /// 重复触发无死串可回收的白跑。 pub(crate) fn should_collect_strings(&self, vm: &Vm) -> bool { - (!vm.gc_state.session_object_ptrs.is_empty() - || !vm.gc_state.session_string_ptrs.is_empty() - || !vm.gc_state.session_bigint_ptrs.borrow().is_empty()) - && vm.gc_state.session_bytes_allocated >= vm.gc_state.string_gc_watermark + (!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 } /// debug 兜底:mark 之后断言所有存活对象持有的字符串边都已登记进 @@ -853,7 +853,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.gc_state.session_object_ptrs { + for &ptr in &vm.realm.gc.session_object_ptrs { if ptr.is_null() { continue; } @@ -942,7 +942,7 @@ impl SessionGc { // 扣减字符串账目(保留对象账目),再补回存活串字节。 let object_bytes: usize = vm - .gc_state + .realm.gc .session_object_ptrs .iter() .filter(|&&ptr| !ptr.is_null()) @@ -951,7 +951,7 @@ impl SessionGc { size_of::() as u64 + Self::object_heap_data_bytes(obj) }) .sum::() as usize; - vm.gc_state.session_bytes_allocated = object_bytes; + vm.realm.gc.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); @@ -964,7 +964,7 @@ impl SessionGc { // 抬高下次触发水位:本次存活字节 + 阈值增量,避免活串超阈值时每指令重复 // 触发无死串可回收的完整 mark + 串清扫。 let threshold = vm.kernel_core().config().session_gc_threshold; - vm.gc_state.string_gc_watermark = vm.gc_state.session_bytes_allocated.saturating_add(threshold); + vm.realm.gc.string_gc_watermark = vm.realm.gc.session_bytes_allocated.saturating_add(threshold); let elapsed = start.elapsed(); self.total_collections += 1; @@ -1034,18 +1034,18 @@ impl SessionGc { // 串,串表整体计入,与 strings-only 口径一致;BigInt/cell 不在手工 // 账目内,由 `run_alloc_bytes` 公式按表长单列)。 let mut string_bytes: usize = 0; - for &ptr in &vm.gc_state.session_string_ptrs { + for &ptr in &vm.realm.gc.session_string_ptrs { if ptr.is_null() { continue; } // SAFETY: ptr 在字符串表登记,收尾前有效。 string_bytes += size_of::() + unsafe { (*ptr).payload_bytes() }; } - vm.gc_state.session_bytes_allocated = vm.gc_state.session_bytes_allocated.saturating_add(string_bytes); + vm.realm.gc.session_bytes_allocated = vm.realm.gc.session_bytes_allocated.saturating_add(string_bytes); // 抬高下次触发水位:当前分配包络 + 阈值增量——存活包络超阈值时不每指令 // 重复触发无死对象可回收的白跑。 - vm.gc_state.gc_watermark = vm.run_alloc_bytes().saturating_add(vm.gc_state.gc_threshold_cached); + vm.realm.gc.gc_watermark = vm.run_alloc_bytes().saturating_add(vm.realm.gc.gc_threshold_cached); 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 46ddb341..d670740e 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.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let proto_ptr = vm.realm.session.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.session.global_object().as_ptr() as *mut JsObject)); + assert!(has_ptr(&roots, vm.realm.session.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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.mark(&vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.session_gc = gc; assert!(unsafe { (*root_session).is_gc_marked() }); assert!(unsafe { (*reachable_session).is_gc_marked() }); @@ -173,17 +173,17 @@ 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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.mark(&vm); let _ = gc.sweep(&mut vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.session_gc = gc; - assert_eq!(vm.gc_state.session_object_ptrs.len(), 2); + assert_eq!(vm.realm.gc.session_object_ptrs.len(), 2); // 原地 sweep 不搬移存活对象:根与子保持原址(克隆体即原对象),死对象出表。 - assert!(vm.gc_state.session_object_ptrs.contains(&root_session)); - assert!(!vm.gc_state.session_object_ptrs.contains(&dead_session)); + assert!(vm.realm.gc.session_object_ptrs.contains(&root_session)); + assert!(!vm.realm.gc.session_object_ptrs.contains(&dead_session)); assert!(!vm - .gc_state + .realm.gc .session_object_ptrs .iter() .any(|ptr| unsafe { (*(*ptr)).is_gc_marked() })); @@ -192,7 +192,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.session.builtin_world().array_proto.as_ptr() as *mut JsObject; + let array_proto = vm.realm.session.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 +212,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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.mark(&vm); let _ = gc.sweep(&mut vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.session_gc = gc; // 原地 sweep 不搬移存活对象:数组保持原址、根指针不变,死元素对象 // 原地释放并出表,存活集合只剩数组与元素 0 的对象。 @@ -227,7 +227,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.gc_state.session_object_ptrs.len(), 2); + assert_eq!(vm.realm.gc.session_object_ptrs.len(), 2); } fn vm_with_low_threshold() -> Vm { @@ -247,7 +247,7 @@ fn native_ok(result: NativeResult) -> JsValue { /// 分配一个原型指向 Map.prototype 的占位对象并写入寄存器,作为构造器调用的 `this`。 fn map_this(vm: &mut Vm, reg: u8) -> JsValue { - let proto = vm.session.builtin_world().map_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.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 +259,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.session.builtin_world().data_view_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.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 +271,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.session.builtin_world().set_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.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 +284,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.session.builtin_world().disposable_stack_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.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 +297,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.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.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 +312,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.gc_state.session_object_ptrs.is_empty()); - let tracked_before = vm.gc_state.session_object_ptrs.len(); + assert!(!vm.realm.gc.session_object_ptrs.is_empty()); + let tracked_before = vm.realm.gc.session_object_ptrs.len(); vm.regs[0] = JsValue::undefined(); vm.regs[1] = JsValue::undefined(); vm.reset(); - assert!(vm.gc_state.session_object_ptrs.len() <= tracked_before); - assert_eq!(vm.gc_state.session_object_ptrs.len(), 0); - assert_eq!(vm.gc_state.session_bytes_allocated, 0); + 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); } #[test] @@ -330,7 +330,7 @@ fn gc_stats_summary_includes_collection() { plain_object(&mut vm); vm.regs[0] = JsValue::undefined(); vm.maybe_collect_session_gc(); - let summary = vm.gc_state.session_gc.stats_summary(); + let summary = vm.realm.gc.session_gc.stats_summary(); assert!(summary.contains("[GC] collection")); } @@ -343,7 +343,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.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = vm.realm.session.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 +351,7 @@ fn full_collect_keeps_global_root_edges_in_place() { vm.maybe_collect_session_gc(); - let global = vm.session.global_object(); + let global = vm.realm.session.global_object(); let pos = vm .kernel_core .shape_forge() @@ -401,15 +401,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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.collect(&mut vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.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.gc_state.session_object_ptrs.len(), 3); + assert_eq!(vm.realm.gc.session_object_ptrs.len(), 3); } #[test] @@ -425,15 +425,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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.collect(&mut vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.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.gc_state.session_object_ptrs.len(), 2); + assert_eq!(vm.realm.gc.session_object_ptrs.len(), 2); } /// 造带条目盒的 WeakMap 形 session 对象(构造体落地前的单测代用形态)。 @@ -470,9 +470,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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.collect(&mut vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.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 +503,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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.collect(&mut vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.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 +535,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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.collect(&mut vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.session_gc = gc; let live_wm = unsafe { &*vm.regs[0].as_js_object_ptr() }; // 原地 sweep 不搬移对象:键与值边原地保留,按原键读回同一对象。 @@ -547,7 +547,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.gc_state.session_object_ptrs.len(), 3); + assert_eq!(vm.realm.gc.session_object_ptrs.len(), 3); } /// in-run 原地 sweep·死键面:无强根的 session 键在原地 sweep 按 mark 位判死, @@ -560,7 +560,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.session.builtin_world().object_proto.as_ptr() as *mut JsObject); + let p_key = JsValue::from_js_object(vm.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject); unsafe { oxide_builtins::weak_map::weak_map_insert( &mut *wm, @@ -574,7 +574,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.gc_state.session_object_ptrs.contains(&key), "死键对象须出表"); + assert!(!vm.realm.gc.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 +645,8 @@ fn session_gc_traces_map_string_and_bigint_value() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.gc_state.session_string_ptrs.contains(&str_ptr)); - assert!(vm.gc_state.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.session_string_ptrs.contains(&str_ptr)); + assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); } /// 盒持唯一引用的 session 串与 BigInt 经 Set native 边进入存活集: @@ -672,8 +672,8 @@ fn session_gc_traces_set_string_and_bigint_value() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.gc_state.session_string_ptrs.contains(&str_ptr)); - assert!(vm.gc_state.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.session_string_ptrs.contains(&str_ptr)); + assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); } /// 盒持唯一引用的 session 串与 BigInt 经资源栈条目边进入存活集: @@ -702,8 +702,8 @@ fn session_gc_traces_dispose_stack_string_and_bigint_value() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.gc_state.session_string_ptrs.contains(&str_ptr)); - assert!(vm.gc_state.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.session_string_ptrs.contains(&str_ptr)); + assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); } /// 挂起帧内期望值 BigInt 查找:挂起点唯一持有期望值(帧内死槽残留的 @@ -740,8 +740,8 @@ fn session_gc_traces_promise_string_and_bigint_result() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.gc_state.session_string_ptrs.contains(&str_ptr)); - assert!(vm.gc_state.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.session_string_ptrs.contains(&str_ptr)); + assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); } /// 挂起生成器帧寄存器持唯一引用 session BigInt:yield 挂起时 b 存活 @@ -771,7 +771,7 @@ fn session_gc_traces_suspended_generator_bigint_value() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.gc_state.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); } /// 挂起异步函数帧持唯一引用 session BigInt:body await 永不结算的 promise, @@ -815,7 +815,7 @@ fn session_gc_traces_suspended_async_function_bigint_value() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.gc_state.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); } /// 挂起异步生成器帧持唯一引用 session BigInt:首次 next() 挂起于 yield, @@ -844,7 +844,7 @@ fn session_gc_traces_suspended_async_generator_bigint_value() { collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.gc_state.session_bigint_ptrs.borrow().contains(&bi_ptr)); + assert!(vm.realm.gc.session_bigint_ptrs.borrow().contains(&bi_ptr)); } #[test] @@ -883,9 +883,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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.collect(&mut vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.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 +936,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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.collect(&mut vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.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.gc_state.session_object_ptrs.len(), 2); + assert_eq!(vm.realm.gc.session_object_ptrs.len(), 2); vm.regs[0] = live_view; vm.regs[1] = JsValue::int(0); @@ -953,9 +953,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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.collect(vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.session_gc = gc; } #[test] @@ -963,12 +963,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.gc_state.session_string_ptrs.contains(&dead_ptr)); + assert!(vm.realm.gc.session_string_ptrs.contains(&dead_ptr)); // 没有根引用 `dead`(它只作为值包装存在于 Rust 栈上)。 collect(&mut vm); - assert!(!vm.gc_state.session_string_ptrs.contains(&dead_ptr)); + assert!(!vm.realm.gc.session_string_ptrs.contains(&dead_ptr)); } #[test] @@ -980,7 +980,7 @@ fn session_string_survives_when_in_register() { collect(&mut vm); - assert!(vm.gc_state.session_string_ptrs.contains(&live_ptr)); + assert!(vm.realm.gc.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 +1003,7 @@ fn session_string_survives_via_object_property() { collect(&mut vm); - assert!(vm.gc_state.session_string_ptrs.contains(&s_ptr)); + assert!(vm.realm.gc.session_string_ptrs.contains(&s_ptr)); assert_eq!(unsafe { (*s_ptr).as_str() }, "prop-string"); } @@ -1013,13 +1013,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.gc_state.session_string_ptrs.contains(&perm_ptr)); + assert!(!vm.realm.gc.session_string_ptrs.contains(&perm_ptr)); vm.regs[0] = perm; collect(&mut vm); // 绝不被 session 清扫释放(仍可读,仍不在 session 集合中)。 - assert!(!vm.gc_state.session_string_ptrs.contains(&perm_ptr)); + assert!(!vm.realm.gc.session_string_ptrs.contains(&perm_ptr)); assert_eq!(unsafe { (*perm_ptr).as_str() }, "perm"); } @@ -1030,11 +1030,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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); let before = gc.total_bytes_freed; gc.collect(&mut vm); let after = gc.total_bytes_freed; - vm.gc_state.session_gc = gc; + vm.realm.gc.session_gc = gc; assert!(after >= before + expected); } @@ -1055,7 +1055,7 @@ fn compile(source: &str) -> oxide_bytecode::module::CompiledModule { } fn global_prop_opt(vm: &Vm, name: &str) -> Option { - let global = vm.session.global_object(); + let global = vm.realm.session.global_object(); let si = vm.kernel_core().perm_interner().intern(name).0; vm.resolve_property(global, si) } @@ -1071,7 +1071,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.gc_state.session_cell_ptrs.borrow().is_empty(), "run1 应分配 upvalue cell"); + assert!(!vm.realm.gc.session_cell_ptrs.borrow().is_empty(), "run1 应分配 upvalue cell"); // reset 触发对象 sweep:存活对象原地保留,死对象原地释放。 vm.reset(); @@ -1126,10 +1126,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.gc_state.session_cell_ptrs.borrow().is_empty()); + assert!(!vm.realm.gc.session_cell_ptrs.borrow().is_empty()); vm.full_reset(); - assert!(vm.gc_state.session_cell_ptrs.borrow().is_empty(), "full_reset 应释放全部 cell"); + assert!(vm.realm.gc.session_cell_ptrs.borrow().is_empty(), "full_reset 应释放全部 cell"); let result = vm .run(&Arc::new(compile( @@ -1186,13 +1186,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.gc_state.session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应登记在表"); + assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应登记在表"); vm.regs.fill(JsValue::undefined()); collect(&mut vm); // 只断言表成员——sweep 已释放的指针解引用即 UB。 - assert!(vm.gc_state.session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应跨完整收集存活"); + assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "活 cell 应跨完整收集存活"); } /// 活跃帧 cell_stack 根:内层函数 upvalue cell 在活跃帧 cell_stack 上(低阈值使 @@ -1245,14 +1245,14 @@ fn suspended_state_box_cell_root_survives_collect() { .copied() .find(|p| !p.is_null()) .expect("挂起帧应持有 x cell"); - assert!(vm.gc_state.session_cell_ptrs.borrow().contains(&cell_ptr), "cell 应登记在表"); + assert!(vm.realm.gc.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.gc_state.session_cell_ptrs.borrow().contains(&cell_ptr), "挂起帧 cell 应跨完整收集存活"); + assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "挂起帧 cell 应跨完整收集存活"); // 恢复后读值:cell 未被误释放。 let read = vm.run(&Arc::new(compile("globalThis.g.next().value"))).expect("read"); @@ -1312,7 +1312,7 @@ fn module_ns_cell_root_survives_collect() { .into_iter() .next() .expect("依赖 ns 应持有 x 的 cell"); - assert!(vm.gc_state.session_cell_ptrs.borrow().contains(&cell_ptr), "cell 应登记在表"); + assert!(vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "cell 应登记在表"); vm.regs.fill(JsValue::undefined()); vm.regs[0] = ns_val; @@ -1320,7 +1320,7 @@ fn module_ns_cell_root_survives_collect() { // 只断言表成员——sweep 已释放的指针解引用即 UB。 assert!( - vm.gc_state.session_cell_ptrs.borrow().contains(&cell_ptr), + vm.realm.gc.session_cell_ptrs.borrow().contains(&cell_ptr), "模块 ns cell 应跨完整收集存活" ); @@ -1346,16 +1346,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.gc_state.session_gc.total_collections, 0); - assert!(vm.gc_state.session_string_ptrs.contains(&seed_ptr)); - assert!(vm.gc_state.session_string_ptrs.contains(&returned.as_string_ptr_mut())); + 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())); // 显式触发后:无根引用的 seed 被回收,入根的 returned 存活(安全点语义)。 vm.regs[0] = returned; vm.maybe_collect_session_strings(); - assert!(vm.gc_state.session_gc.total_collections >= 1); - assert!(!vm.gc_state.session_string_ptrs.contains(&seed_ptr)); - assert!(vm.gc_state.session_string_ptrs.contains(&returned.as_string_ptr_mut())); + 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())); } #[test] @@ -1388,10 +1388,10 @@ fn strings_only_collection_preserves_all_root_kinds() { vm.maybe_collect_session_strings(); - assert!(vm.gc_state.session_string_ptrs.contains(®_str.as_string_ptr_mut())); - assert!(vm.gc_state.session_string_ptrs.contains(&prop_str.as_string_ptr_mut())); - assert!(vm.gc_state.session_string_ptrs.contains(&epoch_str.as_string_ptr_mut())); - assert!(!vm.gc_state.session_string_ptrs.contains(&dead_ptr)); + 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_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 +1404,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.gc_state.session_object_ptrs.clone(); + let before: Vec<_> = vm.realm.gc.session_object_ptrs.clone(); let s = vm.new_string_owned("x".repeat(64)); vm.regs[1] = s; @@ -1412,7 +1412,7 @@ fn strings_only_collection_does_not_move_objects() { vm.maybe_collect_session_strings(); // 对象指针逐一相同:不搬移、不重写根。 - assert_eq!(vm.gc_state.session_object_ptrs, before); + assert_eq!(vm.realm.gc.session_object_ptrs, before); assert_eq!(vm.regs[0].as_js_object_ptr(), obj_session); assert_eq!(vm.session_object_count(), 1); } @@ -1435,10 +1435,10 @@ fn multiple_strings_only_cycles_keep_objects_alive() { } // 分配不自动触发,每轮显式触发一次回收。 vm.maybe_collect_session_strings(); - assert!(vm.gc_state.session_object_ptrs.contains(&obj_session)); + assert!(vm.realm.gc.session_object_ptrs.contains(&obj_session)); assert_eq!(unsafe { (*obj_session).get_prop_at(0) }, live); } - assert!(vm.gc_state.session_string_ptrs.contains(&live.as_string_ptr_mut())); + assert!(vm.realm.gc.session_string_ptrs.contains(&live.as_string_ptr_mut())); } #[test] @@ -1458,15 +1458,15 @@ fn strings_only_then_full_collect_keeps_object_strings_live() { // 第一轮 strings-only:对象被 mark 置位。收尾清残留,否则该位残留至完整 // 收集会使 mark DFS 在对象处短路、漏标其属性中的串;随后校验串跨完整收集存活。 vm.maybe_collect_session_strings(); - assert!(vm.gc_state.session_string_ptrs.contains(&s_ptr)); + assert!(vm.realm.gc.session_string_ptrs.contains(&s_ptr)); // 完整收集(reset 的 maybe_collect 路径):对象搬移、字符串地址稳定。 - let mut gc = std::mem::take(&mut vm.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.collect(&mut vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.session_gc = gc; // 存活对象经重写仍可达其串:串未被误释放,内容可读。 - assert!(vm.gc_state.session_string_ptrs.contains(&s_ptr)); + assert!(vm.realm.gc.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 +1487,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.gc_state.session_bytes_allocated, expected); - assert!(!vm.gc_state.session_string_ptrs.contains(&dead.as_string_ptr_mut())); + assert_eq!(vm.realm.gc.session_bytes_allocated, expected); + assert!(!vm.realm.gc.session_string_ptrs.contains(&dead.as_string_ptr_mut())); } #[test] @@ -1500,7 +1500,7 @@ fn run_alloc_formula_lists_bigint_and_cell_via_tables() { // 下不成立,钉向量面互不相交。 let arr = vm.alloc_object(JsObject::new_array( oxide_kernel::shape_forge::EMPTY_SHAPE_ID, - JsValue::from_js_object(vm.session.builtin_world().array_proto.as_ptr() as *mut JsObject), + JsValue::from_js_object(vm.realm.session.builtin_world().array_proto.as_ptr() as *mut JsObject), 2, )); vm.regs[3] = JsValue::from_js_object(arr); @@ -1509,30 +1509,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.gc_state.alloc_cell(JsValue::int(9), true); + vm.realm.gc.alloc_cell(JsValue::int(9), true); // 公式 = 手工账目 + BigInt 表长 × 尺寸 + cell 表长 × 尺寸,三分量两两不相交。 assert_eq!( vm.run_alloc_bytes(), - vm.gc_state.session_bytes_allocated - + vm.gc_state.session_bigint_ptrs.borrow().len() * size_of::() - + vm.gc_state.session_cell_ptrs.borrow().len() * size_of::() + 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::() ); // 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.gc_state.session_bytes_allocated, + vm.realm.gc.session_bytes_allocated, 2 * size_of::() + arr_heap_bytes as usize + string_bytes ); // 完整收集后:死 cell 出表、存活 BigInt 留表,账目保持对象 + 串口径。 vm.collect_session_gc(); - assert_eq!(vm.gc_state.session_cell_ptrs.borrow().len(), 0); - assert_eq!(vm.gc_state.session_bigint_ptrs.borrow().len(), 1); + 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.gc_state.session_bytes_allocated, + vm.realm.gc.session_bytes_allocated, 2 * size_of::() + arr_heap_bytes as usize + string_bytes ); // 深采样全量重算与轻层公式零增长下逐位相等:存活对象带堆区(元素向量), @@ -1546,8 +1546,8 @@ fn runtime_collection_below_threshold_is_noop() { let s = vm.new_string_owned("small".repeat(4)); vm.regs[0] = s; - assert_eq!(vm.gc_state.session_gc.total_collections, 0); - assert!(vm.gc_state.session_string_ptrs.contains(&s.as_string_ptr_mut())); + assert_eq!(vm.realm.gc.session_gc.total_collections, 0); + assert!(vm.realm.gc.session_string_ptrs.contains(&s.as_string_ptr_mut())); } // ── rope(Cons)GC 传播闭包 ── @@ -1570,9 +1570,9 @@ fn rope_survives_collection_with_children_propagated() { // 父(寄存器根)+ 子节点(经传播闭包)全部存活,内容可读。 let parent_ptr = parent.as_string_ptr_mut(); - assert!(vm.gc_state.session_string_ptrs.contains(&parent_ptr)); - assert!(vm.gc_state.session_string_ptrs.contains(&l_ptr)); - assert!(vm.gc_state.session_string_ptrs.contains(&r_ptr)); + 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_eq!(unsafe { (*parent_ptr).as_lossy_str() }, "left-partright-part"); } @@ -1581,14 +1581,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.gc_state.session_string_ptrs.contains(&parent_ptr)); + assert!(vm.realm.gc.session_string_ptrs.contains(&parent_ptr)); // 父与子均无根引用 → 整树回收。 collect(&mut vm); - assert!(!vm.gc_state.session_string_ptrs.contains(&parent_ptr)); - assert!(!vm.gc_state.session_string_ptrs.contains(&l_ptr)); - assert!(!vm.gc_state.session_string_ptrs.contains(&r_ptr)); + 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)); } #[test] @@ -1605,8 +1605,8 @@ fn rope_product_freed_with_parent() { // 父死 → 连带释放产物(不 double-free、不泄漏;产物从不进 session 表)。 let flat_mut = flat_ptr as *mut JsString; - assert!(!vm.gc_state.session_string_ptrs.contains(&parent_ptr)); - assert!(!vm.gc_state.session_string_ptrs.contains(&flat_mut)); + assert!(!vm.realm.gc.session_string_ptrs.contains(&parent_ptr)); + assert!(!vm.realm.gc.session_string_ptrs.contains(&flat_mut)); } #[test] @@ -1623,7 +1623,7 @@ fn rope_deep_chain_mark_iterative() { collect(&mut vm); - assert!(vm.gc_state.session_string_ptrs.contains(&chain_ptr)); + assert!(vm.realm.gc.session_string_ptrs.contains(&chain_ptr)); assert_eq!(unsafe { (*chain_ptr).as_lossy_str() }, format!("root{}", "x".repeat(1024))); } @@ -1640,8 +1640,8 @@ fn rope_perm_child_untouched_by_sweep() { collect(&mut vm); // perm 子节点永不释放(不在 session 表);session 子节点随父存活。 - assert!(!vm.gc_state.session_string_ptrs.contains(&perm_ptr)); - assert!(vm.gc_state.session_string_ptrs.contains(&session_ptr)); + assert!(!vm.realm.gc.session_string_ptrs.contains(&perm_ptr)); + assert!(vm.realm.gc.session_string_ptrs.contains(&session_ptr)); assert_eq!(unsafe { (*parent.as_string_ptr_mut()).as_lossy_str() }, "perm-leafsession-leaf"); } @@ -1655,7 +1655,7 @@ fn full_reset_frees_rope_and_product() { // full_reset 清空全部 session 字符串(连带产物):无泄漏、无 double-free。 vm.full_reset(); - assert!(vm.gc_state.session_string_ptrs.is_empty()); + assert!(vm.realm.gc.session_string_ptrs.is_empty()); } // ── 执行期两档收集 ────────────────────────────────────────────────────── @@ -1708,7 +1708,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.gc_state.session_object_ptrs.contains(&dead), "死 session 对象应被原地释放并出表"); + assert!(!vm.realm.gc.session_object_ptrs.contains(&dead), "死 session 对象应被原地释放并出表"); // 挂起形:生成器在 for-in 内 yield 后 run 结束,迭代器经状态盒持有 // (vm.iters 已空)——键经状态盒边入根收集标活,收集照常执行。 @@ -1725,7 +1725,7 @@ 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 - .gc_state + .realm.gc .session_object_ptrs .iter() .any(|&p| !p.is_null() && unsafe { (*p).type_tag == JsObject::OBJ_TYPE_GENERATOR }); @@ -1900,11 +1900,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.gc_state.session_gc); + let mut gc = std::mem::take(&mut vm.realm.gc.session_gc); gc.mark(&vm); - vm.gc_state.session_gc = gc; + vm.realm.gc.session_gc = gc; - let counts = &vm.gc_state.session_gc.root_counts; + let counts = &vm.realm.gc.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 e77a78ca..07db89df 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.session().global_object().as_ptr() as *mut JsObject); + let global = JsValue::from_js_object(vm.realm.session.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.gc_state.pending_forced_collect = true; + vm.realm.gc.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 6aa8c2c8..7cc5f463 100644 --- a/crates/oxide_vm/src/vm/call.rs +++ b/crates/oxide_vm/src/vm/call.rs @@ -47,9 +47,9 @@ impl Vm { obj.set_session_epoch(true); obj.set_heap_alloc(true); let ptr = Box::into_raw(Box::new(obj)); - self.gc_state.session_object_ptrs.push(ptr); + self.realm.gc.session_object_ptrs.push(ptr); // 直 session 分配计入堆账目(与 promote 同式:对象头 + 对象堆数据)。 - self.gc_state.session_bytes_allocated += std::mem::size_of::() + self.realm.gc.session_bytes_allocated += std::mem::size_of::() + crate::session_gc::SessionGc::object_heap_data_bytes(unsafe { &*ptr }) as usize; ptr } @@ -296,7 +296,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.session.global_object().as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.session.global_object().as_ptr() as *mut JsObject) } else if sub_is_strict { this_value } else { @@ -402,7 +402,7 @@ mod tests { } fn native_function(vm: &mut Vm, f: crate::native::NativeFn) -> JsValue { - let proto = vm.session.builtin_world().function_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.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 c94689a5..5f834858 100644 --- a/crates/oxide_vm/src/vm/dispatch_loop.rs +++ b/crates/oxide_vm/src/vm/dispatch_loop.rs @@ -128,29 +128,29 @@ impl Vm { // 宿主 `$262.gc()` 置位的强制收集与压力模式收集在此边界消费: // 完整收集唯一安全点(无在途 dispatch 局部值),与正常触发路径 // 同一入口。压力模式开启后每个顶层边界做一次完整收集。 - if self.gc_state.pending_forced_collect || self.gc_state.gc_pressure_mode { - self.gc_state.pending_forced_collect = false; + if self.realm.gc.pending_forced_collect || self.realm.gc.gc_pressure_mode { + self.realm.gc.pending_forced_collect = false; self.collect_session_gc(); } // 峰值高水位:同一边界采样 session 堆账目上界 - let bytes = self.gc_state.session_bytes_allocated; - if bytes > self.gc_state.session_bytes_peak { - self.gc_state.session_bytes_peak = bytes; + let bytes = self.realm.gc.session_bytes_allocated; + if bytes > self.realm.gc.session_bytes_peak { + self.realm.gc.session_bytes_peak = bytes; } // 单 run 分配包络高水位:O(1) 读(手工账目 + BigInt/cell 表长)。 let alloc = self.run_alloc_bytes(); - if alloc > self.gc_state.run_alloc_peak { - self.gc_state.run_alloc_peak = alloc; + if alloc > self.realm.gc.run_alloc_peak { + self.realm.gc.run_alloc_peak = alloc; } // 压力模式开启时跳过两档水位收集:完整收集已涵盖其全部工作, // 同边界双触发只会多一轮空转 mark。 - if bytes >= self.gc_state.string_gc_watermark && !self.gc_state.gc_pressure_mode { + if bytes >= self.realm.gc.string_gc_watermark && !self.realm.gc.gc_pressure_mode { self.maybe_collect_session_strings(); } // 执行期原地 sweep 收集:O(1) 包络超触发水位 → 收集(无门控: // 迭代器键引用经根收集标活,原地释放不搬移存活对象; // 边界契约见 maybe_collect_in_run)。 - if alloc >= self.gc_state.gc_watermark && !self.gc_state.gc_pressure_mode { + if alloc >= self.realm.gc.gc_watermark && !self.realm.gc.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 ab9ce994..73e59d2e 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.session.global_object().as_ptr() as *mut JsObject)); + f(RootGroup::Global, JsValue::from_js_object(self.realm.session.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.gc_state.session_gc); + let mut session_gc = std::mem::take(&mut self.realm.gc.session_gc); session_gc.maybe_collect(self); - self.gc_state.session_gc = session_gc; + self.realm.gc.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.gc_state.session_gc); + let mut session_gc = std::mem::take(&mut self.realm.gc.session_gc); session_gc.maybe_collect_strings_only(self); - self.gc_state.session_gc = session_gc; + self.realm.gc.session_gc = session_gc; } /// 执行期原地 sweep 收集的 dispatch 安全点入口:仅在循环顶 @@ -187,35 +187,35 @@ impl Vm { /// - 无对象可回收时仍跑一轮(mark + 原地清扫),水位同点抬高, /// 触发间距由包络增量控制。 pub(crate) fn maybe_collect_in_run(&mut self) { - let mut session_gc = std::mem::take(&mut self.gc_state.session_gc); + let mut session_gc = std::mem::take(&mut self.realm.gc.session_gc); session_gc.collect_in_run(self); - self.gc_state.session_gc = session_gc; + self.realm.gc.session_gc = session_gc; } /// 只读访问 session GC 的统计(回收次数、存活/死亡对象数、释放字节等)。 pub fn session_gc_stats(&self) -> &SessionGc { - &self.gc_state.session_gc + &self.realm.gc.session_gc } /// 当前 session arena 中存活(已晋升)的对象数量。 pub fn session_object_count(&self) -> usize { - self.gc_state.session_object_ptrs.len() + self.realm.gc.session_object_ptrs.len() } /// session 当前分配的字节数(对象 + 存活字符串)。 pub fn session_bytes_allocated(&self) -> usize { - self.gc_state.session_bytes_allocated + self.realm.gc.session_bytes_allocated } /// 执行期 session 堆账目的峰值高水位(顶层指令边界采样,全量重置清零)。 pub fn session_bytes_peak(&self) -> usize { - self.gc_state.session_bytes_peak + self.realm.gc.session_bytes_peak } /// 本 run 累计分配字节的高水位:`run_alloc_bytes` 的顶层指令边界 /// 采样上界,run 边界(reset/full_reset)重起算。留存内存观测锚。 pub fn run_alloc_peak(&self) -> usize { - self.gc_state.run_alloc_peak + self.realm.gc.run_alloc_peak } /// 本 run 累计分配字节:session 手工堆账目(session 对象及其属性向量 + @@ -228,9 +228,9 @@ impl Vm { /// 属性区(元素/属性向量扩容)增长对其不可见——上限判定须配合 /// [`Self::run_alloc_bytes_full`] 的深采样层。 pub(crate) fn run_alloc_bytes(&self) -> usize { - self.gc_state.session_bytes_allocated - + self.gc_state.session_bigint_ptrs.borrow().len() * std::mem::size_of::() - + self.gc_state.session_cell_ptrs.borrow().len() * std::mem::size_of::() + 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::() } /// 本 run 累计分配字节(O(1) 读)。 @@ -248,7 +248,7 @@ impl Vm { /// 与 [`Self::run_alloc_bytes`] 的轻层公式逐位相等。 pub(crate) fn run_alloc_bytes_full(&self) -> u64 { let mut bytes = 0u64; - for &ptr in self.gc_state.session_object_ptrs.iter() { + for &ptr in self.realm.gc.session_object_ptrs.iter() { if ptr.is_null() { continue; } @@ -256,15 +256,15 @@ impl Vm { let obj = unsafe { &*ptr }; bytes += std::mem::size_of::() as u64 + SessionGc::object_heap_data_bytes(obj); } - for &ptr in &self.gc_state.session_string_ptrs { + for &ptr in &self.realm.gc.session_string_ptrs { if ptr.is_null() { continue; } // SAFETY: ptr 在字符串表登记,收尾前有效。 bytes += (std::mem::size_of::() + unsafe { (*ptr).payload_bytes() }) as u64; } - bytes += (self.gc_state.session_bigint_ptrs.borrow().len() * std::mem::size_of::()) as u64; - bytes += (self.gc_state.session_cell_ptrs.borrow().len() * std::mem::size_of::()) 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 } @@ -278,9 +278,9 @@ impl Vm { /// - 须在执行外的安全点调用(无在途 builtin 局部裸指针、dispatch 未重入); /// 执行期触发仍走水位路径,本入口供事后观测(如基准测 workload 后留存堆)。 pub fn collect_session_gc(&mut self) { - let mut session_gc = std::mem::take(&mut self.gc_state.session_gc); + let mut session_gc = std::mem::take(&mut self.realm.gc.session_gc); session_gc.collect(self); - self.gc_state.session_gc = session_gc; + self.realm.gc.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 51fbecee..c9ac10a4 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -17,11 +17,11 @@ use smallvec::SmallVec; /// 初始化一个 session 的内置对象(global 槽位、各构造器与原型、IC 预热)。 pub use crate::bindings::init_kernel_builtins; use crate::native::NativeFn; +use crate::realm::Realm; use crate::vm_debug; -use crate::vm_state::{GcState, IterState, ProfilingState, SampleState, SymbolState}; +use crate::vm_state::{IterState, ProfilingState, SampleState, SymbolState}; use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::error::JsErrorKind; -use oxide_types::mem::P; use oxide_types::object::{Cell, JsObject, JsString, NativeFnPtr}; use oxide_types::private_key::INT_KEY_COUNT; use oxide_types::value::JsValue; @@ -175,7 +175,9 @@ pub struct Vm { pub(crate) active_immutables: *const [JsValue], pub(crate) frames: SmallVec<[CallFrame; 16]>, pub(crate) kernel_core: Arc, - pub(crate) session: KernelSession, + /// 每 VM 的 realm 组合:内核会话(builtin world 与 global 对象)、 + /// session GC 簿记与 10 个内建原型槽(见 `realm` 模块)。 + pub(crate) realm: Realm, /// `"length"` 属性键的 intern id 缓存:进程内稳定(PermInterner append-only、 /// KernelCore 不重建),属性 get/set 热路径免每次 intern(hash64 + DashMap + /// RwLock 读锁)。 @@ -190,26 +192,6 @@ pub struct Vm { /// f64→string 十六槽 last-value 缓存的值(session 串,经 `for_each_value` /// 登记为 GC 根;`full_reset` 在 session 串释放前清空)。 pub(crate) number_to_string_cache_vals: [JsValue; 16], - pub object_prototype: P, - /// `%GeneratorPrototype%`:生成器实例的原型(next/return/throw 方法挂此)。 - pub generator_proto: P, - /// `%GeneratorFunction.prototype%`:生成器函数对象的原型(`constructor` 指向 - /// `%GeneratorFunction%`,使 `g.constructor.name` 解析为 "GeneratorFunction")。 - pub generator_function_proto: P, - /// `%Promise%` 构造器(resolve/reject 静态方法挂此,global 的 Promise 槽指向它)。 - pub promise_constructor: P, - /// `%Promise.prototype%`:Promise 实例的原型(then/catch/finally 方法挂此)。 - pub promise_proto: P, - /// `%AggregateError%` 构造器(Promise.any 拒绝时构造 AggregateError 用)。 - pub aggregate_error_constructor: P, - /// `%AggregateError.prototype%`(proto = %Error.prototype%)。 - pub aggregate_error_proto: P, - /// `%AsyncFunction.prototype%`:异步函数对象的原型(`constructor` 指向 `%AsyncFunction%`)。 - pub async_function_proto: P, - /// `%AsyncGeneratorPrototype%`:异步生成器实例的原型(next/return/throw/@@asyncIterator)。 - pub async_generator_proto: P, - /// `%AsyncGeneratorFunction.prototype%`:异步生成器函数对象的原型。 - pub async_generator_function_proto: P, /// 微任务队列(Promise reactions / thenable 委托),`run()` 末尾 FIFO drain。 pub(crate) job_queue: VecDeque, /// Atomics.waitAsync waiter 表:键 = (缓冲对象指针, 元素字节偏移),值 = @@ -357,8 +339,6 @@ pub struct Vm { /// 异步生成器 body `dispatch()` 的 AWAIT 让出信号:置 true 表示挂起在 await, /// 恢复方(异步生成器内嵌 dispatch 循环)据此快照挂起状态。 pub(crate) async_gen_suspended: bool, - /// 分组保存 session arena / GC 簿记状态。 - pub(crate) gc_state: GcState, /// 分组保存 `Symbol` intern 状态。 pub(crate) symbols: SymbolState, /// 分组保存活跃的 for-in / for-of 迭代器状态。 @@ -440,12 +420,12 @@ impl Vm { /// 只读访问当前 session(builtin world 与 global object)。 pub fn session(&self) -> &KernelSession { - &self.session + &self.realm.session } /// 判定裸指针是否指向当前 session 的 `%Object.prototype%`。 pub(crate) fn is_object_prototype(&self, ptr: *const JsObject) -> bool { - let proto_ptr = self.session.builtin_world().object_proto.as_ptr(); + let proto_ptr = self.realm.session.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 c9f0ff3a..28cb3315 100644 --- a/crates/oxide_vm/src/vm/vmhost.rs +++ b/crates/oxide_vm/src/vm/vmhost.rs @@ -46,7 +46,7 @@ impl oxide_runtime_api::VmHost for Vm { self.kernel_core() } fn session(&self) -> &KernelSession { - self.session() + &self.realm.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.generator_function_proto.as_ptr()) - || std::ptr::eq(p, self.async_function_proto.as_ptr()) - || std::ptr::eq(p, self.async_generator_function_proto.as_ptr()) + 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()) } 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 88fb782d..4a8d3442 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.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = self.realm.session.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.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = self.realm.session.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 e43cad26..1a22187b 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.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = &*self.realm.object_prototype as *const JsObject 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.object_prototype as *const JsObject as *mut JsObject; + let proto_ptr = &*self.realm.object_prototype as *const JsObject 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.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.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.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.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.session.builtin_world().array_proto.as_ptr() as *mut JsObject; + let proto_ptr = self.realm.session.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 954a55ef..7804194b 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.session.global_object().as_ptr()) { + if !std::ptr::eq(obj as *const JsObject, self.realm.session.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.session.global_object().as_ptr()) { + if !std::ptr::eq(obj as *const JsObject, self.realm.session.global_object().as_ptr()) { return; } if depth != 0 || shape_id != obj.shape_id() { @@ -305,7 +305,7 @@ impl Vm { if map.is_empty() { return; } - let global = self.session.global_object(); + let global = self.realm.session.global_object(); for (name, reg) in map { if (*reg as usize) < pack_end { continue; @@ -1543,7 +1543,7 @@ mod tests { } fn native_function(vm: &mut Vm, f: crate::native::NativeFn) -> JsValue { - let proto = vm.session.builtin_world().function_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.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 +1552,7 @@ mod tests { } fn plain_object(vm: &mut Vm) -> JsValue { - let proto = vm.session.builtin_world().object_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.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 +1681,7 @@ mod tests { // ===== 大索引(越稠密上限)写降级命名属性的行为钉 ===== fn new_array_val(vm: &mut Vm, n: u32) -> JsValue { - let proto = vm.session.builtin_world().array_proto.as_ptr() as *mut JsObject; + let proto = vm.realm.session.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 f06de05f..1085a6ed 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.session.global_object().as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.session.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.gc_state.session_object_ptrs { + for &ptr in &self.realm.gc.session_object_ptrs { if ptr.is_null() { continue; } @@ -692,7 +692,7 @@ impl Vm { // 顶层 this:脚本为全局对象(ECMA-262 全局执行上下文); // ES module 顶层环境 GetThisBinding 返回 undefined。记录到 // top_level_this:rerun 清空寄存器文件后按此恢复顶层 this。 - let global = self.session.global_object(); + let global = self.realm.session.global_object(); let this_val = if module.is_es_module { JsValue::undefined() } else { diff --git a/crates/oxide_vm/src/vm_state.rs b/crates/oxide_vm/src/vm_state.rs index ead649e7..1277d4bd 100644 --- a/crates/oxide_vm/src/vm_state.rs +++ b/crates/oxide_vm/src/vm_state.rs @@ -1,6 +1,6 @@ //! `Vm` 的状态子结构分组。 //! -//! `Vm` 以字段持有这些子结构(`gc_state`、`symbols`、`iters`、`profiling`), +//! `Vm` 以字段持有这些子结构(`realm` 内的 `gc`、`symbols`、`iters`、`profiling`), //! 使各子系统维护者只需改动一个结构而非庞大的 `Vm` 主结构。约定: //! Symbol/Iter/Profiling 为完整子模块(逻辑自包含);`GcState` 仅做字段分类—— //! GC 的 mark/sweep/alloc_object 需要扫描跨全部子结构的根(regs、frames、 diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index 87fcc1d4..43fdb303 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -6,6 +6,7 @@ use std::sync::Arc; use oxide_bytecode::module::Constant; use crate::bindings; +use crate::realm::Realm; use crate::vm::{TableGen, Vm}; use crate::vm_info; use crate::vm_state::{GcState, IterState, ProfilingState, SampleState, SymbolState}; @@ -63,21 +64,38 @@ impl Vm { active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0), frames: smallvec::SmallVec::new(), kernel_core: core, - session, + 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 { + 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_bytes_allocated: 0, + session_bytes_peak: 0, + run_alloc_peak: 0, + string_gc_watermark: gc_threshold, + gc_threshold_cached: gc_threshold, + 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], number_to_string_cache_vals: [JsValue::undefined(); 16], - 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())), job_queue: VecDeque::new(), atomics_waiters: std::collections::HashMap::new(), math_rng_state: 0, @@ -134,21 +152,6 @@ impl Vm { async_gen_context: None, async_gen_dispatch: false, async_gen_suspended: false, - gc_state: 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_bytes_allocated: 0, - session_bytes_peak: 0, - run_alloc_peak: 0, - string_gc_watermark: gc_threshold, - gc_threshold_cached: gc_threshold, - gc_watermark: gc_threshold, - pending_forced_collect: false, - gc_pressure_mode: gc_pressure_mode_from_env(), - }, symbols: SymbolState { symbol_counter: 0, symbol_descriptions: Vec::new(), @@ -183,7 +186,7 @@ impl Vm { vm.init_async_intrinsics(); vm.init_async_generator_intrinsics(); // Promise 全局绑定发生在快照采集之后,重录快照避免首次 full_reset 误判脏。 - vm.session.record_snapshot(); + vm.realm.session.record_snapshot(); // 边界守卫计数:VM 完整构造后登记,与 `Drop for Vm` 的注销恰好配对。 vm.kernel_core.note_vm_started(); vm_info!("Vm created"); @@ -208,21 +211,38 @@ impl Vm { active_immutables: std::ptr::slice_from_raw_parts(std::ptr::null(), 0), frames: smallvec::SmallVec::new(), kernel_core: core, - session, + 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 { + 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_bytes_allocated: 0, + session_bytes_peak: 0, + run_alloc_peak: 0, + string_gc_watermark: gc_threshold, + gc_threshold_cached: gc_threshold, + 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], number_to_string_cache_vals: [JsValue::undefined(); 16], - 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())), job_queue: VecDeque::new(), atomics_waiters: std::collections::HashMap::new(), math_rng_state: 0, @@ -279,21 +299,6 @@ impl Vm { async_gen_context: None, async_gen_dispatch: false, async_gen_suspended: false, - gc_state: 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_bytes_allocated: 0, - session_bytes_peak: 0, - run_alloc_peak: 0, - string_gc_watermark: gc_threshold, - gc_threshold_cached: gc_threshold, - gc_watermark: gc_threshold, - pending_forced_collect: false, - gc_pressure_mode: gc_pressure_mode_from_env(), - }, symbols: SymbolState { symbol_counter: 0, symbol_descriptions: Vec::new(), @@ -328,7 +333,7 @@ impl Vm { vm.init_async_intrinsics(); vm.init_async_generator_intrinsics(); // Promise 全局绑定发生在快照采集之后,重录快照避免首次 full_reset 误判脏。 - vm.session.record_snapshot(); + vm.realm.session.record_snapshot(); // 边界守卫计数:VM 完整构造后登记,与 `Drop for Vm` 的注销恰好配对。 vm.kernel_core.note_vm_started(); vm_info!("Vm created (pool)"); @@ -365,26 +370,26 @@ impl Vm { // 防御兜底:属性值写原语已推进 generation,常规覆盖写由快照对比发现; // 若未来出现绕过属性写原语的裸属性区改写,session 对象会被 session GC 回收, // 保留 global 将持悬垂指针,故带 session 对象时强制 bump 保证 global 重建。 - if !self.gc_state.session_object_ptrs.is_empty() { - let global_ptr = self.session.global_object().as_ptr() as *mut JsObject; + if !self.realm.gc.session_object_ptrs.is_empty() { + let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject; unsafe { &mut *global_ptr }.bump_generation(); } - let dirty = self.session.selective_reset(&self.kernel_core); + let dirty = self.realm.session.selective_reset(&self.kernel_core); if dirty.any_builtin_dirty() { - bindings::rebind_dirty_builtins(&self.kernel_core, &mut self.session, Some(&dirty)); + bindings::rebind_dirty_builtins(&self.kernel_core, &mut self.realm.session, Some(&dirty)); } if dirty.global { - let global_ptr = self.session.global_object().as_ptr() as *mut JsObject; + let global_ptr = self.realm.session.global_object().as_ptr() as *mut JsObject; let global = unsafe { &mut *global_ptr }; - bindings::bind_global_builtin_slots(&self.kernel_core, &self.session, global); + bindings::bind_global_builtin_slots(&self.kernel_core, &self.realm.session, global); } - self.object_prototype = P::clone(&self.session.builtin_world().object_proto); + self.realm.object_prototype = P::clone(&self.realm.session.builtin_world().object_proto); self.init_generator_intrinsics(); self.init_promise_intrinsics(); self.init_async_intrinsics(); self.init_async_generator_intrinsics(); // 快照须在 Promise 全局绑定之后采集:绑定会修改 global 世代。 - self.session.record_snapshot(); + self.realm.session.record_snapshot(); self.clear_full_reset_state(); vm_info!("full_reset completed"); } @@ -392,14 +397,14 @@ impl Vm { /// benchmark 专用重置路径:总是丢弃并重建整个 session 与内置对象。 #[doc(hidden)] pub fn full_reset_legacy_for_bench(&mut self) { - self.session = KernelSession::new(&self.kernel_core); - bindings::init_kernel_builtins(&self.kernel_core, &mut self.session); - self.object_prototype = P::clone(&self.session.builtin_world().object_proto); + 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.init_generator_intrinsics(); self.init_promise_intrinsics(); self.init_async_intrinsics(); self.init_async_generator_intrinsics(); - self.session.record_snapshot(); + self.realm.session.record_snapshot(); self.clear_full_reset_state(); } @@ -431,14 +436,14 @@ 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.gc_state.session_bytes_allocated = 0; - self.gc_state.session_bytes_peak = 0; - self.gc_state.run_alloc_peak = 0; - self.gc_state.string_gc_watermark = self.kernel_core.config().session_gc_threshold; + 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; // 执行期收集水位同点复位(同式:阈值增量起算):旧 run 的存活包络 // 不延续到新 run 的触发判定。 - self.gc_state.gc_watermark = self.gc_state.gc_threshold_cached; - self.gc_state.session_gc = crate::session_gc::SessionGc::new(); + self.realm.gc.gc_watermark = self.realm.gc.gc_threshold_cached; + self.realm.gc.session_gc = crate::session_gc::SessionGc::new(); self.symbols.reset(); self.root_reg_limit = 0; self.active_reg_limit = 0; @@ -459,7 +464,7 @@ impl Vm { self.iters.reset(); // 对象逐条独占释放:本体 + 堆数据 + upvalue 列表,顺序由 // drop_dead_session_object 收口。 - for ptr in self.gc_state.session_object_ptrs.drain(..) { + for ptr in self.realm.gc.session_object_ptrs.drain(..) { if ptr.is_null() { continue; } @@ -469,7 +474,7 @@ impl Vm { } self.free_session_string_heap_data(); self.free_session_bigint_heap_data(); - self.gc_state.free_cells(); + self.realm.gc.free_cells(); } /// 释放 VM 内建原型 P 对象(生成器/Promise/异步族与 Object 原型)的堆外属性区。 @@ -481,16 +486,16 @@ impl Vm { /// world 引用同一对象,其属性区归 session 收尾(`teardown_builtins`)释放。 pub(crate) fn teardown_intrinsic_protos(&mut 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, + &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; @@ -565,7 +570,7 @@ impl Vm { self.sampling.clear_records(); // GC 统计同属执行期状态:池化 Vm 跨 run 复用,不清零则 per-run // 指标(gc_trigger_count)跨文件累积,报告失真。 - let gc = &mut self.gc_state.session_gc; + let gc = &mut self.realm.gc.session_gc; gc.total_collections = 0; gc.total_bytes_freed = 0; gc.total_objects_scanned = 0; @@ -590,9 +595,9 @@ impl Vm { // 指针作废,下次 run 重装。 self.active_immutables = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); // 单 run 分配包络按 run 边界重起算(与 run_alloc_bytes 起算口径同源)。 - self.gc_state.run_alloc_peak = 0; + self.realm.gc.run_alloc_peak = 0; // 执行期收集水位与包络同起算:旧 run 的存活包络不延续到新 run 的触发判定。 - self.gc_state.gc_watermark = self.gc_state.gc_threshold_cached; + self.realm.gc.gc_watermark = self.realm.gc.gc_threshold_cached; self.root_reg_limit = 0; self.active_reg_limit = 0; } @@ -664,8 +669,8 @@ impl Vm { /// 水位触发(此时 builtin 局部值已落地为执行根,分配点触发会误释放 /// 仅存于局部/构造中的活串)。 fn register_session_string(&mut self, ptr: *mut JsString, bytes: usize) -> JsValue { - self.gc_state.session_string_ptrs.push(ptr); - self.gc_state.session_bytes_allocated += std::mem::size_of::() + bytes; + self.realm.gc.session_string_ptrs.push(ptr); + self.realm.gc.session_bytes_allocated += std::mem::size_of::() + bytes; JsValue::string(ptr) } @@ -737,7 +742,7 @@ impl Vm { /// (`full_reset` / `clear_full_reset_state`)时调用,此时没有存活的 session 对象 /// 会引用它们。较轻量的 `reset()` 刻意保留它们,与 session 对象跨 eval 存活一致。 fn free_session_string_heap_data(&mut self) { - for ptr in self.gc_state.session_string_ptrs.drain(..) { + for ptr in self.realm.gc.session_string_ptrs.drain(..) { // SAFETY: 每个指针来自 new_string/new_cons_string 的 Box::into_raw, // 且只在这里(或 sweep)恰好释放一次;内部连带释放 rope 扁平化产物。 unsafe { @@ -767,13 +772,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.async_generator_function_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.async_generator_function_proto.as_ptr() as *mut JsObject) } else if is_generator { - JsValue::from_js_object(self.generator_function_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.generator_function_proto.as_ptr() as *mut JsObject) } else if is_async { - JsValue::from_js_object(self.async_function_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.async_function_proto.as_ptr() as *mut JsObject) } else { - JsValue::from_js_object(self.session.builtin_world().function_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.session.builtin_world().function_proto.as_ptr() as *mut JsObject) }; let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto_val); obj.set_function(true); @@ -798,11 +803,11 @@ impl Vm { // 原型对象自身的 [[Prototype]]:生成器为 %GeneratorPrototype%,普通函数为 Object.prototype。 // 纯异步函数(非生成器)无 `prototype` 属性:规范不为其建 prototype 子对象。 let proto_of_proto = if is_async_generator { - JsValue::from_js_object(self.async_generator_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.async_generator_proto.as_ptr() as *mut JsObject) } else if is_generator { - JsValue::from_js_object(self.generator_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.generator_proto.as_ptr() as *mut JsObject) } else { - JsValue::from_js_object(self.session.builtin_world().object_proto.as_ptr() as *mut JsObject) + JsValue::from_js_object(self.realm.session.builtin_world().object_proto.as_ptr() as *mut JsObject) }; // prototype 子对象与函数本体同走 session 分配:`f.prototype === // globalThis.f.prototype` 要求两侧同一对象,逃逸写不会克隆出第二份。 @@ -864,11 +869,11 @@ impl Vm { /// 分配一个 BigInt 值:把 `i128` 堆分配为 box 并返回携带指针的 `JsValue`。 /// - /// box 指针登记进 `gc_state.session_bigint_ptrs`,在 `full_reset` 统一释放。 + /// box 指针登记进 `realm.gc.session_bigint_ptrs`,在 `full_reset` 统一释放。 /// `&self` 使 `convert_immutables`(常量池 → JsValue)也能分配。 pub fn new_bigint(&self, v: num_bigint::BigInt) -> JsValue { let ptr = Box::into_raw(Box::new(v)); - self.gc_state.session_bigint_ptrs.borrow_mut().push(ptr); + self.realm.gc.session_bigint_ptrs.borrow_mut().push(ptr); JsValue::bigint(ptr) } @@ -881,7 +886,7 @@ impl Vm { /// 释放全部 session 堆 BigInt box。仅在完全隔离重置(`full_reset`)时调用, /// 此时没有存活的 session 对象/寄存器会引用它们。 fn free_session_bigint_heap_data(&mut self) { - for ptr in self.gc_state.session_bigint_ptrs.borrow_mut().drain(..) { + for ptr in self.realm.gc.session_bigint_ptrs.borrow_mut().drain(..) { // SAFETY: 每个指针来自 new_bigint 的 Box::into_raw(Box::new(BigInt)), // 且只在这里恰好释放一次。 unsafe { diff --git a/crates/oxide_vm/src/vm_support/tests.rs b/crates/oxide_vm/src/vm_support/tests.rs index 3bff8e32..dd61313d 100644 --- a/crates/oxide_vm/src/vm_support/tests.rs +++ b/crates/oxide_vm/src/vm_support/tests.rs @@ -6,7 +6,7 @@ fn global_prop(vm: &Vm, name: &str) -> JsValue { } fn global_prop_opt(vm: &Vm, name: &str) -> Option { - let global = vm.session.global_object(); + let global = vm.realm.session.global_object(); let si = vm.kernel_core.perm_interner().intern(name).0; vm.kernel_core .shape_forge() @@ -68,17 +68,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.session.global_object.as_ptr(); + let old_global = vm.realm.session.global_object.as_ptr(); vm.full_reset(); // global 必须重建:旧 global 与其 session 子引用随 session 释放,Array 恢复内置构造器。 - assert!(!std::ptr::eq(old_global, vm.session.global_object.as_ptr())); + assert!(!std::ptr::eq(old_global, vm.realm.session.global_object.as_ptr())); assert!(std::ptr::eq( global_prop(&vm, "Array").as_js_object_ptr(), - vm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject + vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject )); - assert!(!vm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// P 原型上方法槽的 wrapper 对象原始指针(rebuild 跨轮复用验证用)。 @@ -98,7 +98,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.session.global_object.as_ptr() as *mut JsObject; + let g = vm.realm.session.global_object.as_ptr() as *mut JsObject; // SAFETY: global 是本 session 对象,安全点内无并发读者。 unsafe { (*g).prop_vec_len() } } @@ -110,9 +110,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.session.builtin_world().array_proto.as_ptr(), "push"); + let push_before = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push"); let push_shape_before = unsafe { &*push_before }.shape_id(); - let registry_before = vm.session.builtin_world().leaked_object_count(); + let registry_before = vm.realm.session.builtin_world().leaked_object_count(); let slots_before = global_slot_count(&vm); for i in 0..3u32 { @@ -126,10 +126,10 @@ fn full_reset_rebuild_reuses_method_wrappers_across_rounds() { vm.full_reset(); } - let push_after = method_wrapper_ptr(&vm, vm.session.builtin_world().array_proto.as_ptr(), "push"); + let push_after = method_wrapper_ptr(&vm, vm.realm.session.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.session.builtin_world().leaked_object_count(); + let registry_after = vm.realm.session.builtin_world().leaked_object_count(); assert!( registry_after <= registry_before, "释放表计数跨轮不应增长: {registry_before} -> {registry_after}" @@ -161,7 +161,7 @@ fn full_reset_rebuild_keeps_global_slot_count_flat() { // Error 槽应指向本轮重建的构造器(非滞留旧指针)。 assert!(std::ptr::eq( global_prop(&vm, "Error").as_js_object_ptr(), - vm.session.builtin_world().error_constructor.as_ptr() as *mut JsObject + vm.realm.session.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 +279,74 @@ 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.session.builtin_world); - let global_ptr = vm.session.global_object.as_ptr(); - let object_proto_ptr = vm.session.builtin_world().object_proto.as_ptr(); + 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(); vm.full_reset(); - assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.session.builtin_world))); - assert!(std::ptr::eq(global_ptr, vm.session.global_object.as_ptr())); - assert!(std::ptr::eq(object_proto_ptr, vm.session.builtin_world().object_proto.as_ptr())); - assert!(!vm.session.is_dirty_since_snapshot()); + 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()); } #[test] fn full_reset_global_dirty_rebuilds_global_and_restores_slots() { let mut vm = Vm::new(); - let world_ptr = Arc::as_ptr(&vm.session.builtin_world); - let global_ptr = vm.session.global_object.as_ptr(); - let global = unsafe { &mut *(vm.session.global_object.as_ptr() as *mut JsObject) }; + 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) }; bindings::bind_global_value(&vm.kernel_core, global, "userGlobal", JsValue::int(99)); - unsafe { &mut *(vm.session.global_object.as_ptr() as *mut JsObject) }.bump_generation(); + unsafe { &mut *(vm.realm.session.global_object.as_ptr() as *mut JsObject) }.bump_generation(); vm.full_reset(); - assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.session.builtin_world))); - assert!(!std::ptr::eq(global_ptr, vm.session.global_object.as_ptr())); + 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!(global_prop_opt(&vm, "userGlobal").is_none()); assert!(std::ptr::eq( global_prop(&vm, "Array").as_js_object_ptr(), - vm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject + vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject )); assert!(std::ptr::eq( global_prop(&vm, "globalThis").as_js_object_ptr(), - vm.session.global_object.as_ptr() as *mut JsObject + vm.realm.session.global_object.as_ptr() as *mut JsObject )); - assert!(!vm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } #[test] fn full_reset_dirty_builtin_rebinds_global_slot() { let mut vm = Vm::new(); - let old_object_proto = vm.session.builtin_world().object_proto.as_ptr(); - let old_array_proto = vm.session.builtin_world().array_proto.as_ptr(); + 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(); unsafe { &mut *(old_array_proto as *mut JsObject) }.bump_generation(); vm.full_reset(); - assert!(std::ptr::eq(old_object_proto, vm.session.builtin_world().object_proto.as_ptr())); - assert!(!std::ptr::eq(old_array_proto, vm.session.builtin_world().array_proto.as_ptr())); + 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( global_prop(&vm, "Array").as_js_object_ptr(), - vm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject + vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject )); let constructor_si = vm.kernel_core.perm_interner().intern("constructor").0; - let array_proto = &*vm.session.builtin_world().array_proto; + let array_proto = &*vm.realm.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.session.builtin_world().array_constructor.as_ptr() as *mut JsObject + vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject )); - assert!(!vm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } #[test] fn full_reset_dirty_function_keeps_call_working() { let mut vm = Vm::new(); - let function_proto = vm.session.builtin_world().function_proto.as_ptr(); + let function_proto = vm.realm.session.builtin_world().function_proto.as_ptr(); unsafe { &mut *(function_proto as *mut JsObject) }.bump_generation(); vm.full_reset(); @@ -366,7 +366,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.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// Function 家族脏重建:未重建家族的方法 wrapper 是跨重置存活对象,其 proto @@ -374,9 +374,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.session.builtin_world().function_proto.as_ptr() as *mut JsObject; + let old_fn_proto = vm.realm.session.builtin_world().function_proto.as_ptr() as *mut JsObject; // 保留方法 wrapper:array 家族不重建,wrapper 对象与其 proto 槽跨重置存活。 - let array_proto = vm.session.builtin_world().array_proto.as_ptr() as *mut JsObject; + let array_proto = vm.realm.session.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 +388,12 @@ fn full_reset_dirty_function_repoints_retained_wrapper_proto() { vm.full_reset(); - let new_fn_proto = vm.session.builtin_world().function_proto.as_ptr() as *mut JsObject; + let new_fn_proto = vm.realm.session.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.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// 新键写脏 object/array/string/function 四家族(S2 脏源形态):full_reset 后 @@ -406,7 +406,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.session.is_dirty_since_snapshot()); + assert!(vm.realm.session.is_dirty_since_snapshot()); vm.full_reset(); @@ -420,7 +420,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.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// 未捕获异常侧通道是执行期状态:原生错误后可能残留原值,reset/full_reset @@ -592,32 +592,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.session.builtin_world().object_proto.as_ptr(); + let old_object_proto = vm.realm.session.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.session.builtin_world().object_proto.as_ptr())); + assert!(!std::ptr::eq(old_object_proto, vm.realm.session.builtin_world().object_proto.as_ptr())); assert!(std::ptr::eq( - vm.object_prototype.as_ptr(), - vm.session.builtin_world().object_proto.as_ptr() + vm.realm.object_prototype.as_ptr(), + vm.realm.session.builtin_world().object_proto.as_ptr() )); - assert!(!vm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } #[test] fn full_reset_object_dirty_rebinds_iterator_family() { let mut vm = Vm::new(); - let old_object_proto = vm.session.builtin_world().object_proto.as_ptr(); - let old_iterator_proto = vm.session.builtin_world().iterator_proto.as_ptr(); + 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(); // 用户修改 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.session.builtin_world().object_proto.as_ptr())); - assert!(!std::ptr::eq(old_iterator_proto, vm.session.builtin_world().iterator_proto.as_ptr())); + 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())); // global 保留(dirty.global=false):其 Iterator 函数对象的 prototype // 属性须对齐到重建后的 %IteratorPrototype%。 let iter_val = global_prop(&vm, "Iterator"); @@ -630,10 +630,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.session.builtin_world().iterator_proto.as_ptr() as *mut JsObject + vm.realm.session.builtin_world().iterator_proto.as_ptr() as *mut JsObject )); // full_reset 后 session 干净;此后 run_source 执行才重新累积世代变化。 - assert!(!vm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.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 +662,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.session.builtin_world().array_iterator_proto.as_ptr() as *mut JsObject; + let arr_iter_proto = vm.realm.session.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.session.global_object.as_ptr() as *mut JsObject) }.bump_generation(); + unsafe { &mut *(vm.realm.session.global_object.as_ptr() as *mut JsObject) }.bump_generation(); vm.full_reset(); // builtin 未脏 → 迭代器原型保留原对象且属性槽不膨胀(重复 full_reset 不再追加)。 - let arr_iter_proto_after = vm.session.builtin_world().array_iterator_proto.as_ptr() as *mut JsObject; + let arr_iter_proto_after = vm.realm.session.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.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.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 +862,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.session.builtin_world().array_proto.as_ptr(); + let old_array_proto = vm.realm.session.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.session.is_dirty_since_snapshot(), "值覆盖应推进世代被脏检测捕获"); + assert!(vm.realm.session.is_dirty_since_snapshot(), "值覆盖应推进世代被脏检测捕获"); vm.full_reset(); - assert!(!std::ptr::eq(old_array_proto, vm.session.builtin_world().array_proto.as_ptr())); + assert!(!std::ptr::eq(old_array_proto, vm.realm.session.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.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// 对象值形态的既有槽覆盖同样须被发现:epoch 对象写入 builtin P 对象不被晋升, @@ -883,17 +883,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.session.builtin_world().array_proto.as_ptr(); + let old_array_proto = vm.realm.session.builtin_world().array_proto.as_ptr(); let _ = run_source(&mut vm, "Array.prototype.push = {}; 0"); - assert!(vm.session.is_dirty_since_snapshot(), "对象值覆盖应被脏检测捕获"); + assert!(vm.realm.session.is_dirty_since_snapshot(), "对象值覆盖应被脏检测捕获"); vm.full_reset(); - assert!(!std::ptr::eq(old_array_proto, vm.session.builtin_world().array_proto.as_ptr())); + assert!(!std::ptr::eq(old_array_proto, vm.realm.session.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.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// 零 session 对象的全局内置值覆盖:`globalThis.Array = 9` 不触发 promote, @@ -905,17 +905,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.gc_state.session_object_ptrs.is_empty(), "原始值覆盖不产生 session 对象"); - assert!(vm.session.is_dirty_since_snapshot(), "全局值覆盖应推进世代"); + assert!(vm.realm.gc.session_object_ptrs.is_empty(), "原始值覆盖不产生 session 对象"); + assert!(vm.realm.session.is_dirty_since_snapshot(), "全局值覆盖应推进世代"); vm.full_reset(); assert!(std::ptr::eq( global_prop(&vm, "Array").as_js_object_ptr(), - vm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject + vm.realm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject )); assert_eq!(run_source(&mut vm, "[1].push(2)"), JsValue::int(2)); - assert!(!vm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// IC 二次写(miss 落既有槽 + 命中直写)与复合赋值(set_member_prop 既有槽) @@ -927,19 +927,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.session.is_dirty_since_snapshot(), "IC 二次写应被脏检测捕获"); + assert!(vm.realm.session.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.session.is_dirty_since_snapshot(), "复合赋值应被脏检测捕获"); + assert!(vm.realm.session.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.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// wrapper 本体新增 own 属性:写入的是方法 wrapper 自身而非所属 P 对象, @@ -949,20 +949,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.session.builtin_world().array_proto.as_ptr(), "push"); + let push_before = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push"); let _ = run_source(&mut vm, "Array.prototype.push.custom = 1; 0"); - assert!(vm.session.is_dirty_since_snapshot(), "wrapper 本体写应被脏检测捕获"); + assert!(vm.realm.session.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.session.builtin_world().array_proto.as_ptr(), "push"); + let push_after = method_wrapper_ptr(&vm, vm.realm.session.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.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// wrapper 本体写 epoch 对象值:epoch 对象不晋升、不登记,若 wrapper 跨轮 @@ -973,13 +973,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.session.is_dirty_since_snapshot(), "wrapper 对象值写应被脏检测捕获"); + assert!(vm.realm.session.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.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// wrapper 既有 own 槽的值覆盖:首次写已建槽后重录快照,第二次写走 @@ -989,16 +989,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.session.record_snapshot(); + vm.realm.session.record_snapshot(); let _ = run_source(&mut vm, "Array.prototype.push.custom = 2; 0"); - assert!(vm.session.is_dirty_since_snapshot(), "wrapper 既有槽覆盖应被脏检测捕获"); + assert!(vm.realm.session.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.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// 不可复用宿主对象(`Reflect`)的属性写:登记面无复用键,脏时靠 global @@ -1009,13 +1009,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.session.is_dirty_since_snapshot(), "keyless 宿主对象写应被脏检测捕获"); + assert!(vm.realm.session.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.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// global 函数 wrapper(`parseInt`)的属性写:global 函数 wrapper 无 P 家族 @@ -1026,13 +1026,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.session.is_dirty_since_snapshot(), "global 函数 wrapper 写应被脏检测捕获"); + assert!(vm.realm.session.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.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// wrapper 写重建后基线已刷新:后续仅 P 对象脏写的轮次复用新 wrapper、 @@ -1044,8 +1044,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.session.builtin_world().array_proto.as_ptr(), "push"); - let registry_after = vm.session.builtin_world().leaked_object_count(); + 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(); for i in 0..3u32 { let source = format!("Array.prototype['w{i}'] = 1; Object.prototype['w{i}'] = 2; 0"); @@ -1053,15 +1053,15 @@ fn wrapper_write_rebuild_then_p_writes_reuse_fresh_wrapper() { vm.full_reset(); } - let push_final = method_wrapper_ptr(&vm, vm.session.builtin_world().array_proto.as_ptr(), "push"); + let push_final = method_wrapper_ptr(&vm, vm.realm.session.builtin_world().array_proto.as_ptr(), "push"); assert!(std::ptr::eq(push_after, push_final), "P 写轮应复用 wrapper,不应每轮失效重建"); assert_eq!( - vm.session.builtin_world().leaked_object_count(), + vm.realm.session.builtin_world().leaked_object_count(), registry_after, "P 写轮释放登记表不应增长" ); assert_eq!(run_source(&mut vm, "[1, 2].push(3)"), JsValue::int(3)); - assert!(!vm.session.is_dirty_since_snapshot()); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// 假阳性护栏:只写用户对象的良性运行不得误判 wrapper 脏——wrapper 世代 @@ -1070,16 +1070,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.session.builtin_world); + let world_ptr = Arc::as_ptr(&vm.realm.session.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.session.is_dirty_since_snapshot(), "只写用户对象不应误判 wrapper 脏"); + assert!(!vm.realm.session.is_dirty_since_snapshot(), "只写用户对象不应误判 wrapper 脏"); vm.full_reset(); - assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.session.builtin_world)), "无污染不应重建 world"); - assert!(!vm.session.is_dirty_since_snapshot()); + assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.builtin_world)), "无污染不应重建 world"); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } /// 假阳性护栏:只写用户对象的良性运行不得误判 builtin 脏。 @@ -1091,16 +1091,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.session.builtin_world); + let world_ptr = Arc::as_ptr(&vm.realm.session.builtin_world); run_source(&mut vm, "({ x: 1 }).x + [1, 2].length"); assert_eq!(vm.session_object_count(), 2, "良性表达式应产生两枚临时对象(对象字面量 + 数组字面量)"); - assert!(!vm.session.is_dirty_since_snapshot(), "良性运行不应误判 builtin 脏"); + assert!(!vm.realm.session.is_dirty_since_snapshot(), "良性运行不应误判 builtin 脏"); vm.full_reset(); - assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.session.builtin_world)), "builtin world 不应重建"); - assert!(!vm.session.is_dirty_since_snapshot()); + assert!(std::ptr::eq(world_ptr, Arc::as_ptr(&vm.realm.session.builtin_world)), "builtin world 不应重建"); + assert!(!vm.realm.session.is_dirty_since_snapshot()); } #[test]