feat: MessageEvent 载荷盒加 GC 加构造器加原型加交付整合

This commit is contained in:
2026-10-10 14:17:25 +00:00
parent 24a8baaf62
commit 1633512d5f
9 changed files with 631 additions and 97 deletions
+83 -7
View File
@@ -1,8 +1,10 @@
//! Event 基类绑定:就地填充 `event_constructor` / `event_proto` 两枚 P 字段
//! 占位(构造器本体、原型方法、原型 getter 与 Event 常量),并绑全局 `Event`。
//! Event 体系绑定:就地填充 `event_constructor` / `event_proto` 两枚 P 字段
//! 占位(构造器本体、原型方法、原型 getter 与 Event 常量),并绑全局 `Event`;
//! 就地填充 `message_event_constructor` / `message_event_proto` 两枚 P 字段
//! 占位(构造器本体与原型 getter),并绑全局 `MessageEvent`。
//!
//! 九枚 P 字段中其余七枚(MessageEvent / ErrorEvent / CustomEvent 四对加
//! EventTarget 原型)由后续子任务就地填充,本函数不读不写。
//! 九枚 P 字段中其余四枚(ErrorEvent / CustomEvent 两对)由后续子任务就地
//! 填充,本函数不读不写。
use std::sync::Arc;
@@ -12,10 +14,11 @@ use oxide_kernel::kernel::{KernelCore, KernelSession};
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::value::JsValue;
/// 把 Event 构造器与原型方法绑定到 global。
/// 把 Event 与 MessageEvent 构造器及原型方法绑定到 global。
///
/// `event_constructor` / `event_proto` 两枚 P 字段是空对象占位,本函数就地
/// 填充(不新建对象——P 字段本身就是槽位,快照/脏检查/收尾枚举已覆盖)。
/// `event_constructor` / `event_proto` 与 `message_event_constructor` /
/// `message_event_proto` 四枚 P 字段是空对象占位,本函数就地填充(不新建
/// 对象——P 字段本身就是槽位,快照/脏检查/收尾枚举已覆盖)。
///
/// # 幂等
/// 本函数从三条路径到达:`init_kernel_builtins`(占位是空对象)、
@@ -163,6 +166,79 @@ pub fn bind_event(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
}
}
// ---- 填 MessageEvent 构造器与原型占位 ----
let me_ctor_ptr = world.message_event_constructor.as_ptr() as *mut JsObject;
let me_ctor = unsafe { &mut *me_ctor_ptr };
let me_proto_ptr = world.message_event_proto.as_ptr() as *mut JsObject;
let me_proto = unsafe { &mut *me_proto_ptr };
// 构造器:function 标志、prototype 槽、name 槽、[[Prototype]] →
// Function.prototype(幂等,裸写槽)。
me_ctor.set_function(true);
if sh.lookup_position(me_ctor.shape_id(), si_prototype).is_none() {
let shape = sh.make_shape(me_ctor.shape_id(), si_prototype);
me_ctor.set_shape_id(shape);
let pos = me_ctor.push_prop(JsValue::from_js_object(me_proto_ptr));
me_ctor.set_data_meta(pos, PropAttributes::new(false, false, false));
me_ctor.bump_generation();
}
if sh.lookup_position(me_ctor.shape_id(), si_name).is_none() {
let shape = sh.make_shape(me_ctor.shape_id(), si_name);
me_ctor.set_shape_id(shape);
me_ctor
.ensure_hash_props()
.push(JsValue::perm_string(sf.string_ptr(sf.intern("MessageEvent").0)));
let pos = me_ctor.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
me_ctor.set_data_meta(pos, PropAttributes::new(false, false, true));
me_ctor.bump_generation();
}
let _ = me_ctor.set_proto(fn_proto_val);
configure_native_constructor(
me_ctor,
oxide_builtins::event::message_event_constructor::<crate::vm::Vm> as *const (),
2,
);
bind_constructor!(
core,
global,
"MessageEvent",
me_ctor_ptr,
oxide_builtins::event::message_event_constructor::<crate::vm::Vm>,
2,
hash: true
);
// 原型:constructor 槽、[[Prototype]] → Event.prototype、五个 getter
// (data / origin / lastEpoch / source / ports,每 getter 先 lookup_position
// 守卫)。
if sh.lookup_position(me_proto.shape_id(), si_constructor).is_none() {
let shape = sh.make_shape(me_proto.shape_id(), si_constructor);
me_proto.set_shape_id(shape);
let pos = me_proto.push_prop(JsValue::from_js_object(me_ctor_ptr));
me_proto.set_data_meta(pos, PropAttributes::new(true, false, true));
me_proto.bump_generation();
}
let event_proto_val = JsValue::from_js_object(world.event_proto.as_ptr() as *mut JsObject);
let _ = me_proto.set_proto(event_proto_val);
let me_getter_fns: [(&str, *const ()); 5] = [
("data", oxide_builtins::event::message_event_data_getter::<crate::vm::Vm> as *const ()),
("origin", oxide_builtins::event::message_event_origin_getter::<crate::vm::Vm> as *const ()),
(
"lastEpoch",
oxide_builtins::event::message_event_last_epoch_getter::<crate::vm::Vm> as *const (),
),
("source", oxide_builtins::event::message_event_source_getter::<crate::vm::Vm> as *const ()),
("ports", oxide_builtins::event::message_event_ports_getter::<crate::vm::Vm> as *const ()),
];
for (name, fn_ptr) in me_getter_fns {
let si = sf.intern(name).0;
if sh.lookup_position(me_proto.shape_id(), si).is_none() {
bind_accessor_getter(core, session, me_proto, name, fn_ptr);
}
}
// ---- 填 EventTarget 原型占位(无构造器,仅暴露原型与三方法)----
// 幂等:三方法经 lookup_position 守卫,[[Prototype]] 裸写槽幂等。
let et_proto_ptr = world.event_target_proto.as_ptr() as *mut JsObject;
+10 -3
View File
@@ -241,9 +241,16 @@ pub(crate) fn ops_for(family: NativeBoxFamily) -> NativeBoxOps {
string_edges: None,
cell_edges: None,
},
// 事件派生类(MessageEvent / ErrorEvent / CustomEvent)载荷盒由后续子
// 任务填充;本臂当前无引用边,仅 size/drop 链消费。
NativeBoxFamily::MessageEvent | NativeBoxFamily::ErrorEvent | NativeBoxFamily::CustomEvent => NativeBoxOps {
// MessageEvent 派生类载荷盒:基类 type / target / current_target 边加
// data / source / ports 边。
NativeBoxFamily::MessageEvent => NativeBoxOps {
object_edges: Some(event::message_event_native_edges),
string_edges: None,
cell_edges: None,
},
// ErrorEvent / CustomEvent 载荷盒由后续子任务填充;本两臂当前无引用
// 边,仅 size/drop 链消费。
NativeBoxFamily::ErrorEvent | NativeBoxFamily::CustomEvent => NativeBoxOps {
object_edges: None,
string_edges: None,
cell_edges: None,
+12 -3
View File
@@ -133,6 +133,7 @@ impl SessionGc {
bytes += message_channel::message_port_native_size(obj);
bytes += broadcast_channel::broadcast_channel_native_size(obj);
bytes += event::event_native_size(obj);
bytes += event::message_event_native_size(obj);
bytes += crate::arguments_gc::arguments_native_size(obj);
bytes
@@ -376,9 +377,16 @@ impl SessionGc {
Self::mark_string_live(live, obj.get_regexp_flags().as_string_ptr_mut());
}
}
// Event 载荷盒 type 字段持有字符串边。
if obj.is_event_obj() {
for value in event::event_native_edges(obj) {
// 事件系载荷盒(Event / MessageEvent / ErrorEvent / CustomEvent)字符串
// 边标记:按标签取对应边函数;ErrorEvent / CustomEvent 载荷盒由后续子
// 任务填充,当前取空边。
if event::is_event_family_obj(obj) {
let edges = match obj.type_tag {
JsObject::OBJ_TYPE_EVENT => event::event_native_edges(obj),
JsObject::OBJ_TYPE_MESSAGE_EVENT => event::message_event_native_edges(obj),
_ => Vec::new(),
};
for value in edges {
if value.is_string() {
Self::mark_string_live(live, value.as_string_ptr_mut());
}
@@ -584,6 +592,7 @@ impl SessionGc {
freed_bytes += message_channel::drop_message_port_native(obj);
freed_bytes += broadcast_channel::drop_broadcast_channel_native(obj);
freed_bytes += event::drop_event_native(obj);
freed_bytes += event::drop_message_event_native(obj);
freed_bytes += crate::arguments_gc::drop_arguments_native(obj);
freed_bytes
+47 -20
View File
@@ -1,6 +1,6 @@
//! Worker API 的 concrete `&mut Vm` native 函数:构造器、`postMessage`/`terminate`
//! 原型方法、`self` 面(`self.postMessage`/`self.close`/`self.name`/`self.location`)
//! 与 `MessageEvent` 构造辅助。
//! 与 `MessageEvent` / 错误事件构造辅助。
//!
//! 关键约定:
//! - 函数是 concrete `&mut Vm` 形态(`NativeFn`),不挂 `VmHost` trait(trait 是单
@@ -287,30 +287,59 @@ pub(crate) fn self_location(vm: &mut Vm, _args: &[u8]) -> NativeResult {
NativeResult::Ok(vm.new_string(""))
}
/// 事件构造辅助:建真实 Event 盒(`type` 字段)加 `data` / `message` 普通属性。
/// 消息事件构造辅助:建真实 MessageEvent 盒(`data` + `type`,`ports` 空数组)。
///
/// 消息事件与错误事件同型,共用本辅助:消息事件 `message` 为 undefined,
/// 错误事件 `data` 为 undefined、`message` 为错误串。
/// 消息路径(`deliver_worker_messages` / `deliver_self_message`)经本辅助建
/// 真实 MessageEvent,`e instanceof MessageEvent` 与 `e instanceof Event` 均
/// 成立,`e.data` 经原型 getter 读盒字段。
///
/// # 步骤
/// 1. 经 `create_event_box` 建真实 Event 盒(`type` 取 `type_val` 的字符串形态,
/// `[[Prototype]]` → `Event.prototype`),使 `dispatchEvent` 品牌守卫通过。
/// 2. define `data` 属性(rehydrate 后的消息值,可枚举;GC 边)。
/// 3. `message` 非 undefined 时 define `message` 属性(错误消息串,可枚举)。
/// 4. 返回对象。
/// 1. 读 `data`(`args[1]`)与 `type`(`args[2]`)。
/// 2. 委托 `create_message_event_box` 建真实 MessageEvent 盒(`origin` 空串、
/// `lastEpoch` 0、`source` null、`ports` 空数组,`[[Prototype]]` →
/// `MessageEvent.prototype`),使 `dispatchEvent` 品牌守卫通过。
/// 3. 返回对象。
///
/// # 边界与前提
/// - `data` 是 rehydrate 后的消息值(GC 边,经属性区入 mark)。
/// - `type` 是事件类型串(`"message"`/`"error"`/`"messageerror"`)。
/// - `message` 是错误消息串(仅错误事件定义,消息事件为 undefined)。
/// - Event 盒无 `data` 字段(8.4 的 MessageEvent 才补),`data` 作盒对象普通属性。
/// - `data` 是 rehydrate 后的消息值(GC 边,经盒边入 mark)。
/// - `type` 是事件类型串(`"message"`)。
///
/// # 副作用
/// - 新建一个 Event 盒对象(经 `alloc_object` 入对象表)。
/// - 新建一个 MessageEvent 盒对象(经 `alloc_object` 入对象表)。
pub(crate) fn message_event_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let data_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let type_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
let message_val = if args.len() > 3 { vm.reg(args[3]) } else { JsValue::undefined() };
// 建真实 MessageEvent 盒(type 取 type_val 的字符串形态)。
let type_text = vm.lookup_str(type_val).unwrap_or_default();
let ptr = oxide_builtins::event::create_message_event_box(vm, data_val, &type_text);
NativeResult::Ok(JsValue::from_js_object(ptr))
}
/// 错误事件构造辅助(临时形态):建真实 Event 盒(`type` 字段)加 `data` /
/// `message` 普通属性。
///
/// 错误路径(`deliver_error_event`)经本辅助建错误事件:`data` 为 undefined、
/// `message` 为错误串,供 onerror 处理器读取。
///
/// # 步骤
/// 1. 读 `type`(`args[1]`)与 `message`(`args[2]`)。
/// 2. 经 `create_event_box` 建真实 Event 盒(`type` 取 `type_val` 的字符串
/// 形态,`[[Prototype]]` → `Event.prototype`),使 `dispatchEvent` 品牌守卫
/// 通过。
/// 3. define `data` 属性(undefined,可枚举)与 `message` 属性(错误消息串,
/// 可枚举)。
/// 4. 返回对象。
///
/// # 边界与前提
/// - `type` 是事件类型串(`"error"`/`"messageerror"`)。
/// - `message` 是错误消息串。
///
/// # 副作用
/// - 新建一个 Event 盒对象(经 `alloc_object` 入对象表)。
pub(crate) fn build_error_event(vm: &mut Vm, args: &[u8]) -> NativeResult {
let type_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let message_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
// 建真实 Event 盒(type 取 type_val 的字符串形态)。
let type_text = vm.lookup_str(type_val).unwrap_or_default();
@@ -319,11 +348,9 @@ pub(crate) fn message_event_constructor(vm: &mut Vm, args: &[u8]) -> NativeResul
let event_obj = unsafe { &mut *ptr };
let si_data = vm.perm_intern("data");
let _ = vm.define_data_property(event_obj, si_data, data_val, PropAttributes::DEFAULT_DATA);
if !message_val.is_undefined() {
let si_message = vm.perm_intern("message");
let _ = vm.define_data_property(event_obj, si_message, message_val, PropAttributes::DEFAULT_DATA);
}
let _ = vm.define_data_property(event_obj, si_data, JsValue::undefined(), PropAttributes::DEFAULT_DATA);
let si_message = vm.perm_intern("message");
let _ = vm.define_data_property(event_obj, si_message, message_val, PropAttributes::DEFAULT_DATA);
NativeResult::Ok(JsValue::from_js_object(ptr))
}
+39 -19
View File
@@ -318,8 +318,8 @@ impl Vm {
/// 2. 对每个 worker `poll_worker_messages` 排空通道(数据面 rehydrate 进主 realm)。
/// 3. 反查 Worker 对象(GC 根)。
/// 4. 错误面逐条交付 `onerror`;消息交付失败面逐条交付 `onmessageerror`。
/// 5. 数据面读 `onmessage` 属性,可调用时逐条建 MessageEvent 经
/// `execute_task` + `call_function_sync` 触发,后 `drain_microtasks`。
/// 5. 数据面逐条建真实 MessageEvent 经 `dispatchEvent` 交付(注册表查
/// 监听器),后 `drain_microtasks`。
///
/// # 返回值
/// 本轮是否交付了任何面(false 时调用方 1ms 轮询,避免忙等)。
@@ -368,7 +368,7 @@ impl Vm {
if self.worker_object(id).is_none() {
break;
}
let event = self.build_message_event(data, "message", None);
let event = self.build_message_event(data, "message");
self.dispatch_event_on(worker_obj, event);
self.drain_microtasks();
}
@@ -407,35 +407,55 @@ impl Vm {
/// # 副作用
/// - 触发监听器回调与微任务 drain。
fn deliver_error_event(&mut self, worker_obj: JsValue, event_type: &str, message: &str) {
let event = self.build_message_event(JsValue::undefined(), event_type, Some(message));
let event = self.build_error_event(event_type, message);
self.dispatch_event_on(worker_obj, event);
self.drain_microtasks();
}
/// 建事件对象(`data` + `type` + `message` 属性),委托 `message_event_constructor`
/// 构造辅助。消息事件 `message` 为 `None`(undefined),错误事件 `data` 为
/// undefined、`message` 为错误串。
/// 消息路径建事件对象:建真实 MessageEvent(`data` + `type`,`ports` 空
/// 数组),委托 `message_event_constructor` 构造辅助。
///
/// # 步骤
/// 1. `type` 与 `message` 串物化为 session 串(`message` 为 `None` 时 undefined)。
/// 2. `data` / `type` / `message` 写入寄存器 1 / 2 / 3,委托
/// `message_event_constructor`(按寄存器下标读取)建对象。
/// 1. `type` 串物化为 session 串。
/// 2. `data` / `type` 写入寄存器 1 / 2,委托 `message_event_constructor`
/// (按寄存器下标读取)建对象。
///
/// # 返回值
/// 新建的事件对象值(构造失败时返回错误对象值)。
///
/// # 副作用
/// - 新建一个事件对象(经 `alloc_object` 入对象表)。
fn build_message_event(&mut self, data: JsValue, type_str: &str, message: Option<&str>) -> JsValue {
/// - 新建一个 MessageEvent 盒对象(经 `alloc_object` 入对象表)。
fn build_message_event(&mut self, data: JsValue, type_str: &str) -> JsValue {
let type_val = self.new_string(type_str);
let message_val = match message {
Some(m) => self.new_string(m),
None => JsValue::undefined(),
};
self.set_reg(1, data);
self.set_reg(2, type_val);
self.set_reg(3, message_val);
match bindings::message_event_constructor(self, &[0, 1, 2, 3]) {
match bindings::message_event_constructor(self, &[0, 1, 2]) {
NativeResult::Ok(v) => v,
NativeResult::Err(e) => e,
// 构造辅助只建对象,不产生尾调用(防御臂,不可达)。
NativeResult::TailCall { .. } => JsValue::undefined(),
}
}
/// 错误路径建事件对象(临时形态):建真实 Event 盒(`type` 字段)加
/// `data` / `message` 普通属性,委托 `build_error_event` 构造辅助。
///
/// # 步骤
/// 1. `type` 与 `message` 串物化为 session 串。
/// 2. `type` / `message` 写入寄存器 1 / 2,委托 `build_error_event`
/// (按寄存器下标读取)建对象。
///
/// # 返回值
/// 新建的事件对象值(构造失败时返回错误对象值)。
///
/// # 副作用
/// - 新建一个 Event 盒对象(经 `alloc_object` 入对象表)。
fn build_error_event(&mut self, type_str: &str, message: &str) -> JsValue {
let type_val = self.new_string(type_str);
let message_val = self.new_string(message);
self.set_reg(1, type_val);
self.set_reg(2, message_val);
match bindings::build_error_event(self, &[0, 1, 2]) {
NativeResult::Ok(v) => v,
NativeResult::Err(e) => e,
// 构造辅助只建对象,不产生尾调用(防御臂,不可达)。
@@ -478,7 +498,7 @@ impl Vm {
/// - 触发监听器回调与微任务 drain。
pub(crate) fn deliver_self_message(&mut self, value: MessageValue) {
let data = rehydrate_message(self, &value);
let event = self.build_message_event(data, "message", None);
let event = self.build_message_event(data, "message");
// 取 worker global 对象指针(Ref 守卫在语句块内消费,不跨 &mut 借用长存)。
let global_ptr = {
let session = self.realm.session.borrow();