feat: 新增 Event 基类载荷盒、GC 接线、构造器与原型绑定

This commit is contained in:
2026-10-10 08:24:13 +00:00
parent 9ecd573526
commit 8a8c3e9b9c
8 changed files with 642 additions and 46 deletions
+156 -19
View File
@@ -1,28 +1,165 @@
//! Event 体系绑定:本文件是内核接线占位(零行为变化)。九枚 P 字段
//! (Event / MessageEvent / ErrorEvent / CustomEvent 四枚原型与构造器对加
//! EventTarget 原型)当前是空对象,本函数为它们的绑定入口预留,真实构造器 /
//! 原型方法 / 原型链连线由后续任务就地填充(不新建对象——P 字段本身就是槽位,
//! 快照 / 脏检查 / 收尾枚举已覆盖)。
//! Event 基类绑定:就地填充 `event_constructor` / `event_proto` 两枚 P 字段
//! 占位(构造器本体、原型方法、原型 getter 与 Event 常量),并绑全局 `Event`。
//!
//! 九枚 P 字段中其余七枚(MessageEvent / ErrorEvent / CustomEvent 四对加
//! EventTarget 原型)由后续子任务就地填充,本函数不读不写。
use std::sync::Arc;
use crate::bind_constructor;
use crate::bindings::{apply_binding_table, bind_accessor_getter, configure_native_constructor};
use oxide_kernel::kernel::{KernelCore, KernelSession};
use oxide_types::object::JsObject;
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::value::JsValue;
/// Event 体系绑定入口(当前为空占位)。
/// 把 Event 构造器与原型方法绑定到 global。
///
/// `event_constructor` / `event_proto` 两枚 P 字段是空对象占位,本函数就地
/// 填充(不新建对象——P 字段本身就是槽位,快照/脏检查/收尾枚举已覆盖)。
///
/// # 幂等
/// 本函数从三条路径到达:`init_kernel_builtins`(占位是空对象)、
/// `rebind_dirty_builtins` 的 `event` 分支(占位是重建后的空对象)、
/// global-dirty 路径(占位可能已填满)。本函数当前不写任何槽位,空操作天然
/// 幂等;后续任务就地填充时,各槽写须沿用 `lookup_position` 前置守卫
/// (有槽即跳过),保持与 `bind_broadcast_channel` 同型的幂等契约。
/// `rebind_dirty_builtins` 的 `event` 分支(占位是重建后的空对象)、global-dirty
/// 路径(占位可能已填满)。全部槽写经 `lookup_position` 前置守卫,有槽即跳过;
/// `bind_constructor!` 的全局槽臂自带既有槽原位更新,天然幂等。
///
/// # 边界
/// 九枚 P 字段是空对象占位,本函数不读不写,行为与未调用一致;
/// EventTarget 原型不占 ProtoKind(builtins 不读它),绑定层后续直接访问
/// `world.event_target_proto` 槽。
///
/// `core` / `session` / `global` / `realm_id` 当前未使用,保留签名与其他
/// 绑定函数一致,供后续任务就地填充时直接取用。
pub fn bind_event(_core: &Arc<KernelCore>, _session: &KernelSession, _global: &mut JsObject, _realm_id: u32) {}
/// `realm_id` 未使用(Event 无 well-known 符号键),保留签名与其他绑定函数一致。
pub fn bind_event(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject, _realm_id: u32) {
let world = session.builtin_world();
let ctor_ptr = world.event_constructor.as_ptr() as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr };
let proto_ptr = world.event_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr };
let sf = core.perm_interner().as_ref();
let sh = core.shape_forge().as_ref();
// ---- 填构造器占位 ----
ctor.set_function(true);
// prototype 槽 → 原型对象(描述符 {f,f,f})。
let si_prototype = sf.intern("prototype").0;
if sh.lookup_position(ctor.shape_id(), si_prototype).is_none() {
let shape = sh.make_shape(ctor.shape_id(), si_prototype);
ctor.set_shape_id(shape);
let pos = ctor.push_prop(JsValue::from_js_object(proto_ptr));
ctor.set_data_meta(pos, PropAttributes::new(false, false, false));
ctor.bump_generation();
}
// name 槽 → "Event"(描述符 {f,f,t})。
let si_name = sf.intern("name").0;
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("Event").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]] → Function.prototype(幂等,裸写槽)。
let fn_proto_val = JsValue::from_js_object(world.function_proto.as_ptr() as *mut JsObject);
let _ = ctor.set_proto(fn_proto_val);
configure_native_constructor(ctor, oxide_builtins::event::event_constructor::<crate::vm::Vm> as *const (), 2);
bind_constructor!(
core,
global,
"Event",
ctor_ptr,
oxide_builtins::event::event_constructor::<crate::vm::Vm>,
2,
hash: true
);
// Event 常量:CAPTURING_PHASE / AT_TARGET / BUBBLING_PHASE(绑构造器,
// 描述符 {t,f,t})。
let constants: [(&str, i32); 3] = [("CAPTURING_PHASE", 1), ("AT_TARGET", 2), ("BUBBLING_PHASE", 3)];
for (name, value) in constants {
let si = sf.intern(name).0;
if sh.lookup_position(ctor.shape_id(), si).is_none() {
let shape = sh.make_shape(ctor.shape_id(), si);
ctor.set_shape_id(shape);
let pos = ctor.push_prop(JsValue::int(value));
ctor.set_data_meta(pos, PropAttributes::new(true, false, true));
ctor.bump_generation();
}
}
// ---- 填原型占位 ----
// 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();
}
// [[Prototype]] → Object.prototype(幂等,裸写槽)。
let object_proto_val = JsValue::from_js_object(world.object_proto.as_ptr() as *mut JsObject);
let _ = proto.set_proto(object_proto_val);
// 原型方法:preventDefault / stopPropagation / stopImmediatePropagation /
// composedPath(每方法先 lookup_position 守卫)。
let si_prevent_default = sf.intern("preventDefault").0;
if sh.lookup_position(proto.shape_id(), si_prevent_default).is_none() {
apply_binding_table(
world,
proto,
core,
&[
(
"preventDefault",
oxide_builtins::event::event_prevent_default::<crate::vm::Vm> as *const (),
0,
),
(
"stopPropagation",
oxide_builtins::event::event_stop_propagation::<crate::vm::Vm> as *const (),
0,
),
(
"stopImmediatePropagation",
oxide_builtins::event::event_stop_immediate_propagation::<crate::vm::Vm> as *const (),
0,
),
(
"composedPath",
oxide_builtins::event::event_composed_path::<crate::vm::Vm> as *const (),
0,
),
],
);
}
// 原型 getter:type / target / currentTarget / eventPhase / isTrusted /
// bubbles / cancelable / defaultPrevented / composed(每 getter 先
// lookup_position 守卫)。
let getter_fns: [(&str, *const ()); 9] = [
("type", oxide_builtins::event::event_type_getter::<crate::vm::Vm> as *const ()),
("target", oxide_builtins::event::event_target_getter::<crate::vm::Vm> as *const ()),
(
"currentTarget",
oxide_builtins::event::event_current_target_getter::<crate::vm::Vm> as *const (),
),
("eventPhase", oxide_builtins::event::event_phase_getter::<crate::vm::Vm> as *const ()),
("isTrusted", oxide_builtins::event::event_is_trusted_getter::<crate::vm::Vm> as *const ()),
("bubbles", oxide_builtins::event::event_bubbles_getter::<crate::vm::Vm> as *const ()),
("cancelable", oxide_builtins::event::event_cancelable_getter::<crate::vm::Vm> as *const ()),
(
"defaultPrevented",
oxide_builtins::event::event_default_prevented_getter::<crate::vm::Vm> as *const (),
),
("composed", oxide_builtins::event::event_composed_getter::<crate::vm::Vm> as *const ()),
];
for (name, fn_ptr) in getter_fns {
let si = sf.intern(name).0;
if sh.lookup_position(proto.shape_id(), si).is_none() {
bind_accessor_getter(core, session, proto, name, fn_ptr);
}
}
}
+35 -6
View File
@@ -1,7 +1,7 @@
//! native 载荷家族的单点分类与每家族边函数引用(家族表)。
//!
//! 分类是复合谓词:header 位(map/set/module_ns)优先于 `type_tag`,
//! 两维互斥、一个对象至多归入一个家族。tag 维显式枚举全部 0..=34,
//! 两维互斥、一个对象至多归入一个家族。tag 维显式枚举全部 0..=38,
//! 未登记 tag panic。
//!
//! 关键约定:
@@ -10,13 +10,13 @@
//! - 注册面集中在 `ops_for` 的家族表,mark/size/drop/clone/rewrite 各链
//! 由同一张表驱动,新增家族只在此登记。
use oxide_builtins::{data_view, disposable_stack, map, message_channel, module, set, typed_array, weak_map};
use oxide_builtins::{data_view, disposable_stack, event, map, message_channel, module, set, typed_array, weak_map};
use oxide_types::object::{Cell, JsObject, JsString};
use oxide_types::value::JsValue;
/// native 载荷家族。
///
/// 15 个 native 家族 + 空家族;空家族是无 native 盒类型的显式归类结果。
/// 21 个 native 家族 + 空家族;空家族是无 native 盒类型的显式归类结果。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum NativeBoxFamily {
/// 无 native 载荷盒。
@@ -53,6 +53,14 @@ pub(crate) enum NativeBoxFamily {
MessagePort,
/// BroadcastChannel 通道对象(tag 34,mpsc 发送/接收对与通道名,无对象边)。
BroadcastChannel,
/// Event 事件基类对象(tag 35,type 串与九项状态位载荷盒)。
Event,
/// MessageEvent 事件派生类对象(tag 36,data 与 ports 边)。
MessageEvent,
/// ErrorEvent 事件派生类对象(tag 37,message 与 error 边)。
ErrorEvent,
/// CustomEvent 事件派生类对象(tag 38,detail 边)。
CustomEvent,
/// mapped arguments 对象(tag 20,同步状态盒存 `native_data`,无引用边)。
Arguments,
}
@@ -87,7 +95,7 @@ pub(crate) fn classify(obj: &JsObject) -> NativeBoxFamily {
/// 按类型 tag 分类 native 载荷家族。
///
/// # 边界与前提
/// - 0..=34 全 tag 显式枚举;未登记 tag(35 及以上)panic。
/// - 0..=38 全 tag 显式枚举;未登记 tag(39 及以上)panic。
pub(crate) fn classify_by_tag(tag: u8) -> NativeBoxFamily {
match tag {
JsObject::OBJ_TYPE_ARRAY_BUFFER => NativeBoxFamily::ArrayBuffer,
@@ -126,6 +134,10 @@ pub(crate) fn classify_by_tag(tag: u8) -> NativeBoxFamily {
JsObject::OBJ_TYPE_RAW_JSON => NativeBoxFamily::None,
JsObject::OBJ_TYPE_MESSAGE_PORT => NativeBoxFamily::MessagePort,
JsObject::OBJ_TYPE_BROADCAST_CHANNEL => NativeBoxFamily::BroadcastChannel,
JsObject::OBJ_TYPE_EVENT => NativeBoxFamily::Event,
JsObject::OBJ_TYPE_MESSAGE_EVENT => NativeBoxFamily::MessageEvent,
JsObject::OBJ_TYPE_ERROR_EVENT => NativeBoxFamily::ErrorEvent,
JsObject::OBJ_TYPE_CUSTOM_EVENT => NativeBoxFamily::CustomEvent,
_ => panic!("unregistered type tag: {tag}"),
}
}
@@ -223,6 +235,19 @@ pub(crate) fn ops_for(family: NativeBoxFamily) -> NativeBoxOps {
string_edges: None,
cell_edges: None,
},
// Event 基类载荷盒:type 串边与 target / current_target 对象边。
NativeBoxFamily::Event => NativeBoxOps {
object_edges: Some(event::event_native_edges),
string_edges: None,
cell_edges: None,
},
// 事件派生类(MessageEvent / ErrorEvent / CustomEvent)载荷盒由后续子
// 任务填充;本臂当前无引用边,仅 size/drop 链消费。
NativeBoxFamily::MessageEvent | NativeBoxFamily::ErrorEvent | NativeBoxFamily::CustomEvent => NativeBoxOps {
object_edges: None,
string_edges: None,
cell_edges: None,
},
// mapped arguments 同步状态盒:无引用边(位图与帧身份均为原始值),
// 三边函数全空,仅 size/drop 链消费。
NativeBoxFamily::Arguments => NativeBoxOps {
@@ -261,6 +286,10 @@ mod tests {
JsObject::OBJ_TYPE_REGEXP | JsObject::OBJ_TYPE_REGEX_STUB => NativeBoxFamily::RegExp,
JsObject::OBJ_TYPE_MESSAGE_PORT => NativeBoxFamily::MessagePort,
JsObject::OBJ_TYPE_BROADCAST_CHANNEL => NativeBoxFamily::BroadcastChannel,
JsObject::OBJ_TYPE_EVENT => NativeBoxFamily::Event,
JsObject::OBJ_TYPE_MESSAGE_EVENT => NativeBoxFamily::MessageEvent,
JsObject::OBJ_TYPE_ERROR_EVENT => NativeBoxFamily::ErrorEvent,
JsObject::OBJ_TYPE_CUSTOM_EVENT => NativeBoxFamily::CustomEvent,
JsObject::OBJ_TYPE_ARGUMENTS => NativeBoxFamily::Arguments,
_ => NativeBoxFamily::None,
}
@@ -268,7 +297,7 @@ mod tests {
#[test]
fn tag_dimension_every_tag_hits_exactly_one_family() {
for tag in 0..=34u8 {
for tag in 0..=38u8 {
let obj = obj_with_tag(tag);
let family = classify(&obj);
assert_eq!(family, expected_family_for_tag(tag), "tag {tag} 家族不一致");
@@ -302,7 +331,7 @@ mod tests {
#[test]
#[should_panic(expected = "unregistered type tag")]
fn unregistered_tag_panics() {
let obj = obj_with_tag(35);
let obj = obj_with_tag(39);
classify(&obj);
}
}
+11 -1
View File
@@ -11,7 +11,7 @@ use crate::native_box_dispatch;
use crate::vm::RootGroup;
use crate::vm::Vm;
use oxide_builtins::{
array_buffer, broadcast_channel, data_view, disposable_stack, map, message_channel, module, regexp, set,
array_buffer, broadcast_channel, data_view, disposable_stack, event, map, message_channel, module, regexp, set,
typed_array, weak_map,
};
@@ -132,6 +132,7 @@ impl SessionGc {
bytes += weak_map::weak_map_native_size(obj);
bytes += message_channel::message_port_native_size(obj);
bytes += broadcast_channel::broadcast_channel_native_size(obj);
bytes += event::event_native_size(obj);
bytes += crate::arguments_gc::arguments_native_size(obj);
bytes
@@ -375,6 +376,14 @@ 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) {
if value.is_string() {
Self::mark_string_live(live, value.as_string_ptr_mut());
}
}
}
}
/// 从 VM roots 标记存活 session 对象、字符串与 BigInt,供 `sweep` 判定。
@@ -574,6 +583,7 @@ impl SessionGc {
freed_bytes += weak_map::drop_weak_map_native(obj);
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 += crate::arguments_gc::drop_arguments_native(obj);
freed_bytes