diff --git a/Cargo.lock b/Cargo.lock index 5d38d5b3..01e1eb23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.8.86" +version = "0.8.87" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.8.86" +version = "0.8.87" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.8.86" +version = "0.8.87" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.8.86" +version = "0.8.87" dependencies = [ "ansi_term", "bumpalo", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.8.86" +version = "0.8.87" dependencies = [ "lru", "oxide_bytecode", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.8.86" +version = "0.8.87" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.8.86" +version = "0.8.87" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.8.86" +version = "0.8.87" dependencies = [ "num-bigint", "oxc_allocator", @@ -1284,7 +1284,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.8.86" +version = "0.8.87" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.8.86" +version = "0.8.87" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1305,7 +1305,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.8.86" +version = "0.8.87" dependencies = [ "bumpalo", "dashmap", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.8.86" +version = "0.8.87" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1331,7 +1331,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.8.86" +version = "0.8.87" dependencies = [ "tracing", "tracing-appender", @@ -1340,7 +1340,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.8.86" +version = "0.8.87" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1355,7 +1355,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.8.86" +version = "0.8.87" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1371,7 +1371,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.8.86" +version = "0.8.87" dependencies = [ "num-bigint", "num-traits", @@ -1384,7 +1384,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.8.86" +version = "0.8.87" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1402,7 +1402,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.8.86" +version = "0.8.87" dependencies = [ "bumpalo", "dashmap", @@ -1415,7 +1415,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.8.86" +version = "0.8.87" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index cb8fabb0..783e5215 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.8.86" +version = "0.8.87" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_kernel/src/builtin/construct.rs b/crates/oxide_kernel/src/builtin/construct.rs index 7d81a5e9..c4f9d050 100644 --- a/crates/oxide_kernel/src/builtin/construct.rs +++ b/crates/oxide_kernel/src/builtin/construct.rs @@ -508,6 +508,12 @@ impl BuiltinWorld { let message_port_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let message_channel_constructor = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); + // BroadcastChannel 原型与构造器的内核接线占位(空对象):真实原型/ + // 构造器本体与原型链连线由绑定层后续任务填充,本处只预留槽位使快照/ + // 脏检查/收尾枚举覆盖到这两枚 P 字段。 + let broadcast_channel_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); + let broadcast_channel_constructor = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); + let (date_proto, date_constructor) = make_named_pair(string_forge, shape_forge, labels, "Date"); let (set_proto, set_constructor) = make_named_pair(string_forge, shape_forge, labels, "Set"); let (map_proto, map_constructor) = make_named_pair(string_forge, shape_forge, labels, "Map"); @@ -657,6 +663,8 @@ impl BuiltinWorld { console_object, message_port_proto, message_channel_constructor, + broadcast_channel_proto, + broadcast_channel_constructor, leaked_objects: std::cell::RefCell::new(Vec::new()), }; wire_builtin_world_links(&world); diff --git a/crates/oxide_kernel/src/builtin/rebuild.rs b/crates/oxide_kernel/src/builtin/rebuild.rs index 059d8e98..d34d3988 100644 --- a/crates/oxide_kernel/src/builtin/rebuild.rs +++ b/crates/oxide_kernel/src/builtin/rebuild.rs @@ -407,6 +407,20 @@ impl BuiltinWorld { (current.message_port_proto.clone(), current.message_channel_constructor.clone()) }; + // BroadcastChannel 原型与构造器:脏则换新空对象占位(与全量构造同形), + // 未脏沿用旧指针。 + let (broadcast_channel_proto, broadcast_channel_constructor) = if dirty.broadcast_channel { + ( + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + ) + } else { + ( + current.broadcast_channel_proto.clone(), + current.broadcast_channel_constructor.clone(), + ) + }; + // 迭代器原型与资源栈原型依赖 Object.prototype(链到其上):object 家族重建时 // 一并重建,否则旧原型链指向已释放的 object_proto。 let ( @@ -559,6 +573,8 @@ impl BuiltinWorld { console_object, message_port_proto, message_channel_constructor, + broadcast_channel_proto, + broadcast_channel_constructor, leaked_objects: std::cell::RefCell::new(Vec::new()), }; wire_builtin_world_links(&world); diff --git a/crates/oxide_kernel/src/builtin/world.rs b/crates/oxide_kernel/src/builtin/world.rs index a0e85ec1..cd825d6b 100644 --- a/crates/oxide_kernel/src/builtin/world.rs +++ b/crates/oxide_kernel/src/builtin/world.rs @@ -144,6 +144,12 @@ pub struct BuiltinWorld { /// `MessageChannel` 构造器槽位:纯内核接线占位(空对象),真实构造器 /// 本体由绑定层后续任务填充。 pub message_channel_constructor: P, + /// `BroadcastChannel.prototype` 槽位:纯内核接线占位(空对象),真实 + /// 原型本体与构造器对由绑定层后续任务填充;脏家族位为 `broadcast_channel`。 + pub broadcast_channel_proto: P, + /// `BroadcastChannel` 构造器槽位:纯内核接线占位(空对象),真实构造器 + /// 本体由绑定层后续任务填充。 + pub broadcast_channel_constructor: P, /// 释放登记表(`Box::into_raw` 对象的清单,session 收尾统一释放,非内存泄漏): /// 绑定层经 `Box::into_raw` 持有的函数/宿主对象(方法 wrapper、访问器、 /// 错误构造器、Reflect/Iterator、内建原型构造器、`$262` 宿主等)。 @@ -374,7 +380,7 @@ impl BuiltinWorld { /// session 收尾(`teardown_heap_data`)与选择性重建收尾(`retire_replaced`) /// 的 P 字段枚举唯一入口:`BuiltinWorld` 新增 P 字段须在此同步补一行,否则 /// 收尾时该字段属性区无法释放、重建原型槽改写/释放漏掉该字段。 - pub(crate) fn all_p_fields(&self) -> [&P; 97] { + pub(crate) fn all_p_fields(&self) -> [&P; 99] { [ &self.object_proto, &self.array_proto, @@ -473,6 +479,8 @@ impl BuiltinWorld { &self.console_object, &self.message_port_proto, &self.message_channel_constructor, + &self.broadcast_channel_proto, + &self.broadcast_channel_constructor, ] } @@ -608,6 +616,8 @@ impl BuiltinWorld { BuiltinId::AsyncIteratorProto => &self.async_iterator_proto, BuiltinId::MessagePortProto => &self.message_port_proto, BuiltinId::MessageChannelCtor => &self.message_channel_constructor, + BuiltinId::BroadcastChannelProto => &self.broadcast_channel_proto, + BuiltinId::BroadcastChannelCtor => &self.broadcast_channel_constructor, } } } diff --git a/crates/oxide_kernel/src/kernel/builtin_id.rs b/crates/oxide_kernel/src/kernel/builtin_id.rs index 8998f705..3b32bfd8 100644 --- a/crates/oxide_kernel/src/kernel/builtin_id.rs +++ b/crates/oxide_kernel/src/kernel/builtin_id.rs @@ -1,4 +1,4 @@ -//! 内置对象 id 枚举(`BuiltinId` 87 变体 + `ALL` 顺序钉表)、世代快照 +//! 内置对象 id 枚举(`BuiltinId` 89 变体 + `ALL` 顺序钉表)、世代快照 //! (`BuiltinSnapshot`)与按家族划分的脏标记位集(`BuiltinDirtySet`); //! `NUM_BUILTINS` 文档承载"新增 BuiltinWorld 字段须同步"四处约束注记。 @@ -11,7 +11,7 @@ use crate::builtin::BuiltinWorld; /// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及 /// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的 /// builtin 家族。 -pub const NUM_BUILTINS: usize = 87; +pub const NUM_BUILTINS: usize = 89; /// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。 /// @@ -107,6 +107,8 @@ pub enum BuiltinId { AsyncIteratorProto = 84, MessagePortProto = 85, MessageChannelCtor = 86, + BroadcastChannelProto = 87, + BroadcastChannelCtor = 88, } impl BuiltinId { @@ -201,6 +203,8 @@ impl BuiltinId { BuiltinId::AsyncIteratorProto, BuiltinId::MessagePortProto, BuiltinId::MessageChannelCtor, + BuiltinId::BroadcastChannelProto, + BuiltinId::BroadcastChannelCtor, ]; } @@ -277,6 +281,7 @@ pub struct BuiltinDirtySet { pub global: bool, pub console: bool, pub message_channel: bool, + pub broadcast_channel: bool, } impl BuiltinDirtySet { @@ -311,6 +316,7 @@ impl BuiltinDirtySet { global: true, console: true, message_channel: true, + broadcast_channel: true, } } @@ -339,6 +345,7 @@ impl BuiltinDirtySet { || self.stubs || self.console || self.message_channel + || self.broadcast_channel } /// 是否存在任何污染(builtin world 或 global object)。 @@ -351,13 +358,13 @@ impl BuiltinDirtySet { mod tests { use super::*; - /// 槽对齐面:ALL 表与判别值 0..86 严格同序,快照数组随 NUM_BUILTINS + /// 槽对齐面:ALL 表与判别值 0..88 严格同序,快照数组随 NUM_BUILTINS /// 自动扩维、逐槽对齐。 #[test] fn builtin_snapshot_all_slots_aligned() { - assert_eq!(NUM_BUILTINS, 87); + assert_eq!(NUM_BUILTINS, 89); assert_eq!(BuiltinId::ALL.len(), NUM_BUILTINS); - for i in 0..86usize { + for i in 0..88usize { assert_eq!(BuiltinId::ALL[i] as usize, i); } assert_eq!(BuiltinId::ALL[81], BuiltinId::SharedArrayBufferProto); @@ -366,6 +373,8 @@ mod tests { assert_eq!(BuiltinId::ALL[84], BuiltinId::AsyncIteratorProto); assert_eq!(BuiltinId::ALL[85], BuiltinId::MessagePortProto); assert_eq!(BuiltinId::ALL[86], BuiltinId::MessageChannelCtor); + assert_eq!(BuiltinId::ALL[87], BuiltinId::BroadcastChannelProto); + assert_eq!(BuiltinId::ALL[88], BuiltinId::BroadcastChannelCtor); // 快照经 session 全量构造路径采集,generations 数组维度 = 槽数。 use crate::kernel::{KernelConfig, KernelCore, KernelSession}; diff --git a/crates/oxide_kernel/src/kernel/session.rs b/crates/oxide_kernel/src/kernel/session.rs index b9995234..c3dcd86f 100644 --- a/crates/oxide_kernel/src/kernel/session.rs +++ b/crates/oxide_kernel/src/kernel/session.rs @@ -198,6 +198,9 @@ impl KernelSession { console: gen(BuiltinId::Console) != snap(BuiltinId::Console), message_channel: gen(BuiltinId::MessagePortProto) != snap(BuiltinId::MessagePortProto) || gen(BuiltinId::MessageChannelCtor) != snap(BuiltinId::MessageChannelCtor), + broadcast_channel: gen(BuiltinId::BroadcastChannelProto) + != snap(BuiltinId::BroadcastChannelProto) + || gen(BuiltinId::BroadcastChannelCtor) != snap(BuiltinId::BroadcastChannelCtor), }; // wrapper 本体被写不落在任何家族位上(写的是 wrapper 自身而非所属 P // 对象):收缩为全脏,强制重建全部家族与 global,使被写的可复用 diff --git a/crates/oxide_runtime_api/src/host.rs b/crates/oxide_runtime_api/src/host.rs index de31794f..e01b29a8 100644 --- a/crates/oxide_runtime_api/src/host.rs +++ b/crates/oxide_runtime_api/src/host.rs @@ -102,6 +102,8 @@ pub enum ProtoKind { BigUint64ArrayConstructor, // 消息通道原型 MessagePortProto, + // 广播通道原型 + BroadcastChannelProto, } /// builtins 依赖的 `Vm` 能力集合。 diff --git a/crates/oxide_vm/src/vm/vmhost.rs b/crates/oxide_vm/src/vm/vmhost.rs index 0892b2f6..531b6531 100644 --- a/crates/oxide_vm/src/vm/vmhost.rs +++ b/crates/oxide_vm/src/vm/vmhost.rs @@ -143,6 +143,7 @@ impl oxide_runtime_api::VmHost for Vm { ProtoKind::BigInt64ArrayConstructor => &world.bigint64array_constructor, ProtoKind::BigUint64ArrayConstructor => &world.biguint64array_constructor, ProtoKind::MessagePortProto => &world.message_port_proto, + ProtoKind::BroadcastChannelProto => &world.broadcast_channel_proto, }; P::as_ptr(p) as *mut JsObject } diff --git a/crates/oxide_vm/src/vm_state.rs b/crates/oxide_vm/src/vm_state.rs index 3f669e8b..28c1fd2a 100644 --- a/crates/oxide_vm/src/vm_state.rs +++ b/crates/oxide_vm/src/vm_state.rs @@ -61,6 +61,11 @@ pub(crate) struct GcState { /// 开启后每个顶层指令边界做一次完整收集,与强制收集共用同一安全点入口, /// 同时跳过两个水位触发的档(完整收集已涵盖其全部工作)。 pub(crate) gc_pressure_mode: bool, + /// BroadcastChannel per-realm 弱引用注册表:通道名 → 通道对象裸指针列表 + /// (弱引用,不保活)。构造器分配后登记、close 注销、GC sweep 按 mark 位 + /// 剪枝。当前仅声明,不填不读(登记与剪枝由后续任务接线)。 + #[allow(dead_code)] + pub(crate) broadcast_channels: HashMap>, } impl GcState { diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index 64cbe1de..a1004e9b 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -101,6 +101,7 @@ impl Vm { gc_watermark: gc_threshold, pending_forced_collect: false, gc_pressure_mode: gc_pressure_mode_from_env(), + broadcast_channels: std::collections::HashMap::new(), }), symbols: RefCell::new(SymbolState { symbol_descriptions: Vec::new(), @@ -257,6 +258,7 @@ impl Vm { gc_watermark: gc_threshold, pending_forced_collect: false, gc_pressure_mode: gc_pressure_mode_from_env(), + broadcast_channels: std::collections::HashMap::new(), }), symbols: RefCell::new(SymbolState { symbol_descriptions: Vec::new(),