diff --git a/Cargo.lock b/Cargo.lock index ba5b75ee..37eadc29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.21.2" +version = "0.21.3" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.21.2" +version = "0.21.3" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.21.2" +version = "0.21.3" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.21.2" +version = "0.21.3" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.21.2" +version = "0.21.3" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.21.2" +version = "0.21.3" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1258,7 +1258,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.21.2" +version = "0.21.3" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1272,7 +1272,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.21.2" +version = "0.21.3" dependencies = [ "num-bigint", "oxc_allocator", @@ -1288,7 +1288,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.21.2" +version = "0.21.3" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1300,7 +1300,7 @@ dependencies = [ [[package]] name = "oxide_fold" -version = "0.21.2" +version = "0.21.3" dependencies = [ "num-bigint", "oxide_bytecode", @@ -1312,7 +1312,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.21.2" +version = "0.21.3" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.21.2" +version = "0.21.3" dependencies = [ "bumpalo", "dashmap", @@ -1335,7 +1335,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.21.2" +version = "0.21.3" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1347,7 +1347,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.21.2" +version = "0.21.3" dependencies = [ "tracing", "tracing-appender", @@ -1356,11 +1356,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.21.2" +version = "0.21.3" [[package]] name = "oxide_parser" -version = "0.21.2" +version = "0.21.3" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1375,7 +1375,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.21.2" +version = "0.21.3" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1391,7 +1391,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.21.2" +version = "0.21.3" dependencies = [ "num-bigint", "num-traits", @@ -1404,7 +1404,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.21.2" +version = "0.21.3" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1424,7 +1424,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.21.2" +version = "0.21.3" dependencies = [ "bumpalo", "dashmap", @@ -1437,7 +1437,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.21.2" +version = "0.21.3" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index a260fa25..dfc15e09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ members = [ ] [workspace.package] -version = "0.21.2" +version = "0.21.3" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_vm/src/bindings/bind_shadow_realm.rs b/crates/oxide_vm/src/bindings/bind_shadow_realm.rs new file mode 100644 index 00000000..6b13cfd8 --- /dev/null +++ b/crates/oxide_vm/src/bindings/bind_shadow_realm.rs @@ -0,0 +1,167 @@ +//! ShadowRealm 构造器绑定:Box 分配构造器(不占 `BuiltinWorld` P 字段、不新增 +//! `BuiltinId`、不占 `BuiltinDirtySet` 脏位)加 prototype 与全局槽,全槽 +//! `lookup_position` 幂等守卫。 +//! +//! 与 `bind_worker` 同型:构造器经 `Box::into_raw` 登记 world 释放 +//! 登记表(session 收尾统一释放),全局 `ShadowRealm` 槽经 `bind_constructor!` +//! 原位安装(既有槽更新槽值、无槽新开)。 + +use crate::bind_constructor; +use crate::bindings::{ + apply_binding_table, bind_well_known_data_property, configure_native_constructor, BindEnv, +}; +use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_types::object::{JsObject, PropAttributes}; +use oxide_types::value::JsValue; + +/// 把 ShadowRealm 构造器(Box 分配)与 prototype 方法绑定到 global。 +/// +/// # 幂等 +/// 本函数从三条路径到达:`init_kernel_builtins`(初始)、`rebind_dirty_builtins` +/// 的无条件分支(构造器不在脏体系内)、global-dirty 路径 +/// (`bind_global_builtin_slots`)。构造器本体经 `lookup_position` 守卫复用(有 +/// 槽即不新建);原型槽位经 `lookup_position` 守卫(有槽即跳过); +/// `bind_constructor!` 的全局槽臂自带既有槽原位更新。 +/// +/// # 副作用 +/// - 构造器与原型对象经 `Box::into_raw` 登记 world 释放登记表(session 收尾统一 +/// 释放);全局 `ShadowRealm` 槽经 `bind_constructor!` 原位安装。 +/// - prototype 上装 `constructor` 指回构造器、`@@toStringTag` 数据属性 +/// "ShadowRealm" 与 `evaluate` / `importValue` 方法(not supported 桩)。 +/// +/// `realm_id` 经 `@@toStringTag` 符号键编码使用(well-known 符号键按 +/// (realm 编号,局部下标)编码)。 +pub fn bind_shadow_realm(env: BindEnv<'_>, global: &mut JsObject) { + let world = env.world(); + let sf = env.string_forge(); + let sh = env.shape_forge(); + + // ---- 构造器本体(Box 分配,不占 P 字段)---- + // 幂等:全局 `ShadowRealm` 槽已有构造器时复用(不新建)。 + let si_shadow_realm = sf.intern("ShadowRealm").0; + let ctor_ptr: *mut JsObject = match sh.lookup_position(global.shape_id(), si_shadow_realm) { + Some(pos) if global.get_prop_at(pos).is_object() => global.get_prop_at(pos).as_js_object_ptr(), + _ => { + let function_proto = world.function_proto.as_ptr() as *mut JsObject; + let mut ctor = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto)); + ctor.set_function(true); + configure_native_constructor( + &mut ctor, + crate::shadow_realm::shadow_realm_constructor as *const (), + 0, + ); + let ptr = Box::into_raw(Box::new(ctor)); + world.track_leaked_object(ptr); + ptr + } + }; + + // 构造器 name 槽(描述符 { f,f,t }),幂等守卫。 + let si_name = sf.intern("name").0; + { + let ctor = unsafe { &mut *ctor_ptr }; + if sh.lookup_position(ctor.shape_id(), si_name).is_none() { + let shape = sh.make_shape(ctor.shape_id(), si_name); + ctor.set_shape_id(shape); + ctor.ensure_hash_props() + .push(JsValue::perm_string(sf.string_ptr(sf.intern("ShadowRealm").0))); + let pos = ctor.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1); + ctor.set_data_meta(pos, PropAttributes::new(false, false, true)); + ctor.bump_generation(); + } + } + + // ---- 原型占位 ---- + // 幂等:构造器 prototype 槽已有原型时复用(不新建)。 + let si_prototype = sf.intern("prototype").0; + let proto_ptr: *mut JsObject = { + let ctor = unsafe { &*ctor_ptr }; + match sh.lookup_position(ctor.shape_id(), si_prototype) { + Some(pos) if ctor.get_prop_at(pos).is_object() => ctor.get_prop_at(pos).as_js_object_ptr(), + _ => { + // ShadowRealm.prototype 的 [[Prototype]] 指向 Object.prototype。 + let object_proto = world.object_proto.as_ptr() as *mut JsObject; + let proto = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)); + let ptr = Box::into_raw(Box::new(proto)); + world.track_leaked_object(ptr); + // 回填构造器 prototype 槽(描述符 { f,f,f })。 + let ctor = unsafe { &mut *ctor_ptr }; + let shape = sh.make_shape(ctor.shape_id(), si_prototype); + ctor.set_shape_id(shape); + ctor.ensure_hash_props().push(JsValue::from_js_object(ptr)); + let pos = ctor.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1); + ctor.set_data_meta(pos, PropAttributes::new(false, false, false)); + ctor.bump_generation(); + ptr + } + } + }; + + { + let proto = unsafe { &mut *proto_ptr }; + + // constructor 槽 → 构造器对象(描述符 { t,f,t }),幂等守卫。 + let si_constructor = sf.intern("constructor").0; + if sh.lookup_position(proto.shape_id(), si_constructor).is_none() { + let shape = sh.make_shape(proto.shape_id(), si_constructor); + proto.set_shape_id(shape); + let pos = proto.push_prop(JsValue::from_js_object(ctor_ptr)); + proto.set_data_meta(pos, PropAttributes::new(true, false, true)); + proto.bump_generation(); + } + + // @@toStringTag 数据属性 "ShadowRealm"(描述符 { f,f,t }),幂等守卫。 + let tag_key = oxide_types::private_key::encode_symbol_key( + env.realm_id(), + oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG, + ); + if sh.lookup_position(proto.shape_id(), tag_key).is_none() { + let tag_val = JsValue::perm_string(sf.string_ptr(sf.intern("ShadowRealm").0)); + bind_well_known_data_property( + env.core, + proto, + oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG, + tag_val, + PropAttributes::new(false, false, true), + env.realm_id(), + ); + } + + // evaluate / importValue 方法(not supported 桩,幂等守卫)。 + let si_evaluate = sf.intern("evaluate").0; + if sh.lookup_position(proto.shape_id(), si_evaluate).is_none() { + apply_binding_table( + env, + proto, + &[( + "evaluate", + crate::shadow_realm::evaluate_stub as *const (), + 1, + )], + ); + } + let si_import_value = sf.intern("importValue").0; + if sh.lookup_position(proto.shape_id(), si_import_value).is_none() { + apply_binding_table( + env, + proto, + &[( + "importValue", + crate::shadow_realm::import_value_stub as *const (), + 2, + )], + ); + } + } + + // 全局 `ShadowRealm` 槽(bind_constructor! 自带既有槽原位更新、无槽新开)。 + bind_constructor!( + env, + global, + "ShadowRealm", + ctor_ptr, + crate::shadow_realm::shadow_realm_constructor, + 0, + hash: true + ); +} diff --git a/crates/oxide_vm/src/bindings/mod.rs b/crates/oxide_vm/src/bindings/mod.rs index 0417c856..c5f55fe9 100644 --- a/crates/oxide_vm/src/bindings/mod.rs +++ b/crates/oxide_vm/src/bindings/mod.rs @@ -53,6 +53,8 @@ pub mod bind_object; pub mod bind_reflect; /// RegExp 构造器与原型的 native 方法绑定。 pub mod bind_regexp; +/// ShadowRealm 构造器绑定(Box 分配,不占 BuiltinWorld P 字段)。 +pub mod bind_shadow_realm; /// Set 构造器与原型的 native 方法绑定。 pub mod bind_set; /// SharedArrayBuffer 构造器与原型的 native 方法绑定。 @@ -1326,6 +1328,7 @@ pub fn bind_global_builtin_slots( bind_reflect_global(env, global); bind_iterator_global(env, global); bind_worker::bind_worker(env, global); + bind_shadow_realm::bind_shadow_realm(env, global); bind_disposable_stack::bind_disposable_stack(env, global); bind_async_disposable_stack::bind_async_disposable_stack(env, global); bind_stub_globals(env, global); @@ -1461,6 +1464,8 @@ pub fn rebind_dirty_builtins( // worker 构造器是 Box 分配对象(不在 BuiltinWorld 快照内),不占脏位: // 无条件重绑(构造器经 lookup_position 守卫复用,幂等)。 bind_worker::bind_worker(env, global); + // ShadowRealm 构造器同为 Box 分配对象(不占脏位),无条件重绑,幂等。 + bind_shadow_realm::bind_shadow_realm(env, global); // tag 目标横跨多个脏分组(Map/Set/DataView/Math/JSON/迭代器原型), // 各组各自重绑后统一补装;安装点自带幂等检查。 install_to_string_tags(env); diff --git a/crates/oxide_vm/src/lib.rs b/crates/oxide_vm/src/lib.rs index 5b837b29..758a1cd4 100644 --- a/crates/oxide_vm/src/lib.rs +++ b/crates/oxide_vm/src/lib.rs @@ -33,6 +33,8 @@ mod proxy; mod realm; /// session 级 mark-sweep GC:标记-清扫 session arena 对象与 session 字符串。 pub mod session_gc; +/// ShadowRealm native 实现(构造器建影子 realm 与实例对象、evaluate / importValue 桩)。 +mod shadow_realm; mod suspended; /// test262 宿主对象 `$262` 的最小实现(非标准全局,供 test262 套件使用)。 pub mod test262_host; diff --git a/crates/oxide_vm/src/shadow_realm.rs b/crates/oxide_vm/src/shadow_realm.rs new file mode 100644 index 00000000..eba7b74c --- /dev/null +++ b/crates/oxide_vm/src/shadow_realm.rs @@ -0,0 +1,197 @@ +//! ShadowRealm native 实现:构造器(建影子 realm 与实例对象)与 `evaluate` / +//! `importValue` 的 not supported 桩。 +//! +//! 关键约定: +//! - 函数是 concrete `&mut Vm` 形态(同 `worker_constructor`),不挂 `VmHost` +//! trait:构造器要新建 Vm(影子 realm),`VmHost` 接口面表达不了。 +//! - 实例对象是 PLAIN 对象(`[[Prototype]]` → ShadowRealm.prototype),主 Vm +//! 侧表 `shadow_realm_objects` 是 brand 校验依据(this 指针在侧表),不占 +//! OBJ_TYPE 槽位。 +//! - `evaluate` / `importValue` 现为 not supported 桩(抛含 `not supported` +//! 子串的 TypeError),真实现是后续任务。 + +use oxide_builtins::error; +use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_runtime_api::{NativeResult, ProtoKind, VmHost}; +use oxide_types::object::JsObject; +use oxide_types::value::JsValue; + +use crate::vm::Vm; + +/// `new ShadowRealm()`:建影子 realm(独立 Vm)与实例对象。 +/// +/// # 步骤 +/// 1. 构造形态校验(规范无 `[[Call]]`:非构造调用抛 TypeError)。 +/// 2. 建影子 Vm:复用主 Vm 的 `KernelCore`(共享 intern/shape/code 缓存)并 +/// 注入同一动态编译服务。 +/// 3. 建实例对象:PLAIN 对象,`[[Prototype]]` → ShadowRealm.prototype。 +/// 4. 登记主 Vm 侧表:键 = 影子 realm 编号,值 = (实例对象,影子 Vm)。 +/// 5. 返回实例对象。 +/// +/// # 边界与前提 +/// - 影子 Vm 存主 Vm 侧表、不跨线程:`full_reset` 与 `Drop` 统一清理。 +/// - 实例对象是 GC 根(侧表登记);影子 Vm 堆独立,主 Vm 的 GC 不触达。 +/// +/// # 副作用 +/// - 新建一个影子 Vm(新 session 加全量 builtin world);登记一条侧表条目 +/// (GC 根)。 +pub(crate) fn shadow_realm_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult { + // 构造形态校验(规范只有 [[Construct]])。 + if !vm.constructing_native { + return NativeResult::Err(error::create_type_error( + vm, + "ShadowRealm constructor requires new", + )); + } + + // 建影子 Vm:共享主 Vm 的 KernelCore 与动态编译服务。 + let mut shadow_vm = Vm::with_kernel_core(vm.kernel_core.clone()); + shadow_vm.set_compiler_service(vm.compiler.clone()); + + // 建实例对象:PLAIN 对象,[[Prototype]] → ShadowRealm.prototype。 + let proto_val = shadow_realm_prototype_value(vm); + let obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto_val); + let ptr = vm.alloc_object(obj); + + // 登记主 Vm 侧表(键 = 影子 realm 编号,值 = 实例对象加影子 Vm)。 + let realm_id = shadow_vm.realm_id() as u64; + vm.shadow_realm_objects + .insert(realm_id, (JsValue::from_js_object(ptr), shadow_vm)); + + NativeResult::Ok(JsValue::from_js_object(ptr)) +} + +/// `ShadowRealm.prototype.evaluate` 桩:按规范前置步骤校验后抛 not supported。 +/// +/// # 步骤 +/// 1. brand 校验(`this` 须是 ShadowRealm 实例)。 +/// 2. 实参 `source` 非字符串抛 TypeError(规范只接受 String,不做 ToString)。 +/// 3. 编译 `source`(编译期 SyntaxError 原样透传;语法判定与 realm 无关, +/// 经调用方编译服务完成)。 +/// 4. 抛 not supported。 +/// +/// # 边界 +/// - 桩消息含 `not supported` 子串(test262 判定器据此判能力缺失)。 +pub(crate) fn evaluate_stub(vm: &mut Vm, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + if let Err(e) = shadow_realm_brand_check(vm, this_val) { + return NativeResult::Err(e); + } + + // 实参 source(args[1]):非字符串抛 TypeError。 + let source_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let source = match vm.lookup_str(source_val) { + Some(s) => s, + None => { + return NativeResult::Err(error::create_type_error( + vm, + "ShadowRealm.prototype.evaluate source must be a string", + )) + } + }; + + // 编译期语法错误原样透传(SyntaxError)。 + if let Err(msg) = vm.compiler.compile_script(&source) { + return NativeResult::Err(error::create_syntax_error(vm, &msg)); + } + + NativeResult::Err(error::create_type_error( + vm, + "ShadowRealm.prototype.evaluate is not supported", + )) +} + +/// `ShadowRealm.prototype.importValue` 桩:按规范前置步骤校验后抛 not supported。 +/// +/// # 步骤 +/// 1. brand 校验(`this` 须是 ShadowRealm 实例)。 +/// 2. `ToString(specifier)`(abrupt completion 原值传播)。 +/// 3. 实参 `exportName` 非字符串抛 TypeError(规范只接受 String,不做 ToString)。 +/// 4. 抛 not supported。 +/// +/// # 边界 +/// - 桩消息含 `not supported` 子串(test262 判定器据此判能力缺失)。 +pub(crate) fn import_value_stub(vm: &mut Vm, args: &[u8]) -> NativeResult { + let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); + if let Err(e) = shadow_realm_brand_check(vm, this_val) { + return NativeResult::Err(e); + } + + // ToString(specifier):abrupt completion 传播原异常(native 重入点原值已物化 + // 入异常通道)。 + let specifier_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + if let Err(e) = oxide_runtime_api::to_string_full(specifier_val, vm) { + let exc = vm + .exception_value + .take() + .unwrap_or_else(|| error::create_from_text(vm, &e)); + return NativeResult::Err(exc); + } + + // 实参 exportName(args[2]):非字符串抛 TypeError。 + let export_name_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + if !export_name_val.is_string() { + return NativeResult::Err(error::create_type_error( + vm, + "ShadowRealm.prototype.importValue exportName must be a string", + )); + } + + NativeResult::Err(error::create_type_error( + vm, + "ShadowRealm.prototype.importValue is not supported", + )) +} + +/// brand 校验:`this` 须是对象且指针在主 Vm 侧表内(实例对象注册表)。 +fn shadow_realm_brand_check(vm: &mut Vm, this_val: JsValue) -> Result<(), JsValue> { + if !this_val.is_object() { + return Err(error::create_type_error( + vm, + "ShadowRealm method called on non-object", + )); + } + let this_ptr = this_val.as_js_object_ptr(); + let in_table = vm + .shadow_realm_objects + .values() + .any(|(obj, _)| obj.as_js_object_ptr() == this_ptr); + if !in_table { + return Err(error::create_type_error(vm, "not a ShadowRealm instance")); + } + Ok(()) +} + +/// 读全局 `ShadowRealm` 构造器的 `prototype` 属性(ShadowRealm.prototype)。 +/// +/// # 边界 +/// - 全局 `ShadowRealm` 槽缺失或构造器无 `prototype` 属性时回退到 +/// `%Object.prototype%`(防御性兜底,正常路径 `bind_shadow_realm` 已安装)。 +fn shadow_realm_prototype_value(vm: &mut Vm) -> JsValue { + let object_proto = JsValue::from_js_object(vm.builtin_proto(ProtoKind::ObjectProto)); + let global = vm.global_object(); + let global_ptr = global.as_ptr() as *mut JsObject; + // SAFETY: global_ptr 是当前 session 的 global 对象,存活。 + let global_obj = unsafe { &*global_ptr }; + let si_shadow_realm = vm.perm_intern("ShadowRealm"); + let Some(store) = vm.get_own_property_slot(global_obj, si_shadow_realm) else { + return object_proto; + }; + let ctor_val = global_obj.get_prop_at(store); + if !ctor_val.is_object() { + return object_proto; + } + let ctor_ptr = ctor_val.as_js_object_ptr(); + // SAFETY: ctor_ptr 是 ShadowRealm 构造器对象,存活。 + let ctor_obj = unsafe { &*ctor_ptr }; + let si_prototype = vm.perm_intern("prototype"); + let Some(proto_store) = vm.get_own_property_slot(ctor_obj, si_prototype) else { + return object_proto; + }; + let proto_val = ctor_obj.get_prop_at(proto_store); + if proto_val.is_object() { + proto_val + } else { + object_proto + } +} diff --git a/crates/oxide_vm/src/vm/gc_hooks.rs b/crates/oxide_vm/src/vm/gc_hooks.rs index 17192283..b64bd6c8 100644 --- a/crates/oxide_vm/src/vm/gc_hooks.rs +++ b/crates/oxide_vm/src/vm/gc_hooks.rs @@ -8,7 +8,7 @@ use super::frames::Completion; use super::Vm; use crate::session_gc::SessionGc; -/// GC 根组来源:`for_each_value` 枚举的 29 个根组,变体顺序与根清单 +/// GC 根组来源:`for_each_value` 枚举的 30 个根组,变体顺序与根清单 /// 的枚举顺序一致。 /// /// 变体下标是逐组计数数组(`SessionGc::root_counts`)的下标;`COUNT` 与 @@ -41,6 +41,7 @@ pub(crate) enum RootGroup { ForInIters, Global, WorkerObjects, + ShadowRealmObjects, DisposeStack, EventTargets, ModuleEvalStates, @@ -48,7 +49,7 @@ pub(crate) enum RootGroup { impl RootGroup { /// 根组总数(与变体数同源,绑定逐组计数数组的长度)。 - pub const COUNT: usize = 29; + pub const COUNT: usize = 30; } impl Vm { @@ -56,7 +57,7 @@ impl Vm { /// 覆盖执行核心的全部 JsValue 持有点:regs/帧/各栈段/cell/在途异常与完成/ /// 挂起信号/迭代器/微任务/global/Worker 对象注册表。 /// - /// 闭包接收根组来源(`RootGroup`)与根值:组是枚举顺序中的 29 根组之一, + /// 闭包接收根组来源(`RootGroup`)与根值:组是枚举顺序中的 30 根组之一, /// 调用方按组计数(如 `SessionGc` 的逐组计数)。 pub(crate) fn for_each_value(&self, mut f: impl FnMut(RootGroup, JsValue)) { for value in &self.regs { @@ -186,6 +187,11 @@ impl Vm { for &worker_obj in self.worker_objects.values() { f(RootGroup::WorkerObjects, worker_obj); } + // ShadowRealm 实例注册表:实例对象是 GC 根(注册表保活至 full_reset + // 清表),漏根 → sweep 释放 → brand 校验解引用悬垂。 + for (instance_obj, _) in self.shadow_realm_objects.values() { + f(RootGroup::ShadowRealmObjects, *instance_obj); + } // EventTarget 监听器注册表:监听器回调是 GC 根(存活目标的回调随目标存活, // 漏根 → 回调被 sweep 释放 → 派发解引用悬垂)。弱键剪枝见 sweep;此处 // 无条件标活全部回调(死目标的回调多活一轮收集,非泄漏,sweep 已剪其条目)。 diff --git a/crates/oxide_vm/src/vm/mod.rs b/crates/oxide_vm/src/vm/mod.rs index f154d056..a747a2d3 100644 --- a/crates/oxide_vm/src/vm/mod.rs +++ b/crates/oxide_vm/src/vm/mod.rs @@ -477,6 +477,10 @@ pub struct Vm { /// 是 GC 根(`for_each_value` 遍历),注册表保活至 `worker_terminate` / /// `full_reset` 清表,无悬垂风险。 pub(crate) worker_objects: std::collections::HashMap, + /// ShadowRealm 实例注册表:键 = 影子 realm 编号,值 = (实例对象,影子 + /// Vm)。实例对象是 GC 根(`for_each_value` 遍历);影子 Vm 不跨线程, + /// 随主 Vm 整体迁移,在 `full_reset` / `Drop` 清表时统一释放。 + pub(crate) shadow_realm_objects: std::collections::HashMap, } impl Drop for Vm { @@ -534,6 +538,10 @@ impl Drop for Vm { // 条目自持模块 Arc 与常量 BigInt 存储(Vec),存储随 Vm 整体迁移 // (同不变量 1 条款:Vm 独占分配、地址稳定、跨线程移动不悬垂);键是 // Arc 指针身份,条目自持 Arc 保证键指针在条目存活期内不被分配器复用。 +// +// 8. shadow_realm_objects 字段(HashMap):实例对象指针 +// 归不变量 1 覆盖;影子 Vm 各持独立 session 堆、不跨线程共享(同不变量 1 +// 与 3 条款),随主 Vm 整体迁移、地址稳定不悬垂。 unsafe impl Send for Vm {} impl Vm { diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index a32f312a..1a5482c0 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -257,6 +257,7 @@ impl Vm { worker_registry: std::collections::HashMap::new(), worker_next_id: 0, worker_objects: std::collections::HashMap::new(), + shadow_realm_objects: std::collections::HashMap::new(), }; vm.init_generator_intrinsics(); vm.init_promise_intrinsics(); @@ -426,6 +427,7 @@ impl Vm { worker_registry: std::collections::HashMap::new(), worker_next_id: 0, worker_objects: std::collections::HashMap::new(), + shadow_realm_objects: std::collections::HashMap::new(), }; vm.init_generator_intrinsics(); vm.init_promise_intrinsics(); @@ -562,6 +564,9 @@ impl Vm { // Worker 对象注册表持 session 对象指针:teardown 释放全部 session 对象 // 前须清表,否则残留悬垂指针。 self.worker_objects.clear(); + // ShadowRealm 实例注册表同持 session 对象指针(实例对象):teardown + // 释放全部 session 对象前须清表,影子 Vm 随条目 drop 统一释放。 + self.shadow_realm_objects.clear(); self.realm.teardown_session_heap_data(); self.realm.gc.borrow_mut().session_bytes_allocated = 0; self.realm.gc.borrow_mut().session_bytes_peak = 0;