mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: CustomEvent 载荷盒加 GC 加构造器加原型
This commit is contained in:
Generated
+19
-19
@@ -1160,7 +1160,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_builtins"
|
name = "oxide_builtins"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"fast-float",
|
"fast-float",
|
||||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_bytecode"
|
name = "oxide_bytecode"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cfg"
|
name = "oxide_cfg"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_emit",
|
"oxide_emit",
|
||||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cli"
|
name = "oxide_cli"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
@@ -1227,7 +1227,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_code_cache"
|
name = "oxide_code_cache"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lru",
|
"lru",
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
@@ -1236,7 +1236,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_compiler"
|
name = "oxide_compiler"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1256,7 +1256,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_dce"
|
name = "oxide_dce"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1270,7 +1270,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_emit"
|
name = "oxide_emit"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
@@ -1285,7 +1285,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_engine"
|
name = "oxide_engine"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_compiler",
|
"oxide_compiler",
|
||||||
@@ -1297,7 +1297,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_ir"
|
name = "oxide_ir"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1306,7 +1306,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_kernel"
|
name = "oxide_kernel"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -1320,7 +1320,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_liveness"
|
name = "oxide_liveness"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1332,7 +1332,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_log"
|
name = "oxide_log"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
@@ -1341,7 +1341,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_parser"
|
name = "oxide_parser"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
"oxc_ast",
|
"oxc_ast",
|
||||||
@@ -1356,7 +1356,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_regalloc"
|
name = "oxide_regalloc"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1372,7 +1372,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_runtime_api"
|
name = "oxide_runtime_api"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -1385,7 +1385,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_test262"
|
name = "oxide_test262"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_compiler",
|
"oxide_compiler",
|
||||||
@@ -1403,7 +1403,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_types"
|
name = "oxide_types"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -1416,7 +1416,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_vm"
|
name = "oxide_vm"
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.14.13"
|
version = "0.14.14"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
//! Event 基类与 MessageEvent / ErrorEvent 派生类行为面:`Event` 构造器
|
//! Event 基类与 MessageEvent / ErrorEvent / CustomEvent 派生类行为面:
|
||||||
//! (`new Event(type, init)`)、原型 getter(type / target / currentTarget /
|
//! `Event` 构造器(`new Event(type, init)`)、原型 getter(type / target /
|
||||||
//! eventPhase / isTrusted / bubbles / cancelable / defaultPrevented /
|
//! currentTarget / eventPhase / isTrusted / bubbles / cancelable /
|
||||||
//! composed)与原型方法(preventDefault / stopPropagation /
|
//! defaultPrevented / composed)与原型方法(preventDefault / stopPropagation /
|
||||||
//! stopImmediatePropagation / composedPath);`MessageEvent` 构造器
|
//! stopImmediatePropagation / composedPath);`MessageEvent` 构造器
|
||||||
//! (`new MessageEvent(type, init)`)与原型 getter(data / origin / lastEpoch /
|
//! (`new MessageEvent(type, init)`)与原型 getter(data / origin / lastEpoch /
|
||||||
//! source / ports);`ErrorEvent` 构造器(`new ErrorEvent(type, init)`)与
|
//! source / ports);`ErrorEvent` 构造器(`new ErrorEvent(type, init)`)与
|
||||||
//! 原型 getter(message / filename / lineno / colno / error)。
|
//! 原型 getter(message / filename / lineno / colno / error);`CustomEvent`
|
||||||
|
//! 构造器(`new CustomEvent(type, init)`)与原型 getter(detail)。
|
||||||
//!
|
//!
|
||||||
//! 事件载荷盒(type 串与九项状态位,MessageEvent 加 data / origin /
|
//! 事件载荷盒(type 串与九项状态位,MessageEvent 加 data / origin /
|
||||||
//! lastEpoch / source / ports 五字段,ErrorEvent 加 message / filename /
|
//! lastEpoch / source / ports 五字段,ErrorEvent 加 message / filename /
|
||||||
//! lineno / colno / error 五字段)存于对象 `native_fn` 槽,向 GC 家族表
|
//! lineno / colno / error 五字段,CustomEvent 加 detail 一字段)存于对象
|
||||||
//! 暴露 edges/size/drop 三自由函数。
|
//! `native_fn` 槽,向 GC 家族表暴露 edges/size/drop 三自由函数。
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
@@ -73,6 +74,18 @@ struct ErrorEventInner {
|
|||||||
error: JsValue,
|
error: JsValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// CustomEvent 载荷盒:事件基类字段(`EventInner` 嵌入,位于偏移 0)加详情
|
||||||
|
/// 字段(detail)。存于对象 `native_fn` 槽(`Box::into_raw`,realm 局部
|
||||||
|
/// 单线程)。
|
||||||
|
///
|
||||||
|
/// `#[repr(C)]` 固定 `base` 位于偏移 0:派生类盒指针转基类指针读首字段依赖该
|
||||||
|
/// 偏移稳定(与 `EventInner` 同型约束)。
|
||||||
|
#[repr(C)]
|
||||||
|
struct CustomEventInner {
|
||||||
|
base: EventInner,
|
||||||
|
detail: JsValue,
|
||||||
|
}
|
||||||
|
|
||||||
/// 是否事件系对象(Event / MessageEvent / ErrorEvent / CustomEvent 四标签)。
|
/// 是否事件系对象(Event / MessageEvent / ErrorEvent / CustomEvent 四标签)。
|
||||||
pub fn is_event_family_obj(obj: &JsObject) -> bool {
|
pub fn is_event_family_obj(obj: &JsObject) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
@@ -1233,3 +1246,198 @@ pub fn error_event_error_getter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRes
|
|||||||
};
|
};
|
||||||
NativeResult::Ok(inner.error)
|
NativeResult::Ok(inner.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// —— CustomEvent 派生类 ——
|
||||||
|
|
||||||
|
/// 取 CustomEvent 载荷盒指针:仅 CustomEvent 标签对象返回 `Some`,其余标签
|
||||||
|
/// 返回 `None`。
|
||||||
|
fn custom_event_payload_ptr(obj: &JsObject) -> Option<*mut CustomEventInner> {
|
||||||
|
if obj.type_tag != JsObject::OBJ_TYPE_CUSTOM_EVENT {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut CustomEventInner)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 收集 CustomEvent 的引用边(基类 type / target / current_target 边加
|
||||||
|
/// detail 边)。
|
||||||
|
///
|
||||||
|
/// `target` / `current_target` 空窗口(构造期回填之前)为 null,边函数自守,
|
||||||
|
/// 语义自含。
|
||||||
|
pub fn custom_event_native_edges(obj: &JsObject) -> Vec<JsValue> {
|
||||||
|
let Some(ptr) = custom_event_payload_ptr(obj) else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
if ptr.is_null() {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
// SAFETY: ptr 指向有效载荷盒(槽位仅构造期写入、释放时置空),只读字段。
|
||||||
|
let inner = unsafe { &*ptr };
|
||||||
|
let mut edges = vec![inner.base.r#type, inner.base.target, inner.base.current_target];
|
||||||
|
edges.push(inner.detail);
|
||||||
|
edges
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 只读核算 CustomEvent 载荷盒字节(不释放)。
|
||||||
|
pub fn custom_event_native_size(obj: &JsObject) -> u64 {
|
||||||
|
let Some(ptr) = custom_event_payload_ptr(obj) else {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
if ptr.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
mem::size_of::<CustomEventInner>() as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 释放 CustomEvent 载荷盒(`Box<CustomEventInner>`),返回释放字节数。
|
||||||
|
///
|
||||||
|
/// 盒 drop 时基类字段与 detail 的 JsValue 边随盒释放(GC 已判定死对象);
|
||||||
|
/// 槽置空保证重复调用零释放(幂等)。
|
||||||
|
pub fn drop_custom_event_native(obj: &mut JsObject) -> u64 {
|
||||||
|
let bytes = custom_event_native_size(obj);
|
||||||
|
if bytes == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let Some(ptr) = custom_event_payload_ptr(obj) else {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
// SAFETY: ptr 非空(custom_event_native_size 已验证),Box::from_raw 恰好
|
||||||
|
// 释放一次。
|
||||||
|
unsafe {
|
||||||
|
drop(Box::from_raw(ptr));
|
||||||
|
}
|
||||||
|
obj.set_native_fn(None);
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CustomEvent 构造器:`new CustomEvent(type, init)`。
|
||||||
|
///
|
||||||
|
/// # 步骤
|
||||||
|
/// 1. 非构造形态(`!constructing_native`)抛 TypeError(HTML 规范只有
|
||||||
|
/// [[Construct]],无 [[Call]])
|
||||||
|
/// 2. type 经完整 ToString 转串;空串抛 SyntaxError
|
||||||
|
/// 3. init 字典读 detail(缺省 null)
|
||||||
|
/// 4. 建载荷盒入 receiver 的 `native_fn` 槽,置 type_tag
|
||||||
|
/// 5. 返回 receiver
|
||||||
|
///
|
||||||
|
/// # 边界与前提
|
||||||
|
/// - receiver(`args[0]`)是 VM 按构造器 prototype 新分配的对象,proto 已
|
||||||
|
/// 指向 %CustomEvent.prototype%;
|
||||||
|
/// - init 缺省(undefined)或非对象时 detail 取缺省。
|
||||||
|
///
|
||||||
|
/// # 副作用
|
||||||
|
/// - receiver 的 `native_fn` 槽写入载荷盒、`type_tag` 置 CustomEvent 标签。
|
||||||
|
pub fn custom_event_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
if !vm.constructing_native() {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(vm, "CustomEvent constructor requires new"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let this_val = vm.reg(args[0]);
|
||||||
|
if !this_val.is_object() {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(vm, "CustomEvent constructor requires new"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// type 经完整 ToString 转串(对象经 ToPrimitive,Symbol 抛 TypeError)。
|
||||||
|
let type_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||||
|
let type_str = match oxide_runtime_api::to_string_full(type_val, vm) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => {
|
||||||
|
if let Some(exc) = vm.take_uncaught_value() {
|
||||||
|
return NativeResult::Err(exc);
|
||||||
|
}
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a string"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if type_str.is_empty() {
|
||||||
|
return NativeResult::Err(crate::error::create_syntax_error(vm, "The provided type is empty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// init 字典读 detail(缺省或非对象时取缺省)。
|
||||||
|
let mut detail = JsValue::null();
|
||||||
|
if args.len() > 2 {
|
||||||
|
let init_val = vm.reg(args[2]);
|
||||||
|
if init_val.is_object() {
|
||||||
|
let init_ptr = init_val.as_js_object_ptr();
|
||||||
|
// SAFETY: init_val 已确认是对象值,借用即时消费。
|
||||||
|
let init_obj = unsafe { &*init_ptr };
|
||||||
|
let si = vm.perm_intern("detail");
|
||||||
|
match vm.ordinary_get(init_obj, si, init_val) {
|
||||||
|
Ok(v) => {
|
||||||
|
if !v.is_undefined() {
|
||||||
|
detail = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
if let Some(exc) = vm.take_uncaught_value() {
|
||||||
|
return NativeResult::Err(exc);
|
||||||
|
}
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(
|
||||||
|
vm,
|
||||||
|
"CustomEvent init property getter failed",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 建载荷盒入 receiver 的 native_fn 槽,置 CustomEvent 标签。
|
||||||
|
let type_js = vm.new_string(&type_str);
|
||||||
|
let inner = Box::into_raw(Box::new(CustomEventInner {
|
||||||
|
base: EventInner {
|
||||||
|
r#type: type_js,
|
||||||
|
target: JsValue::null(),
|
||||||
|
current_target: JsValue::null(),
|
||||||
|
event_phase: 0,
|
||||||
|
is_trusted: false,
|
||||||
|
bubbles: false,
|
||||||
|
cancelable: false,
|
||||||
|
default_prevented: false,
|
||||||
|
propagation_stopped: false,
|
||||||
|
immediate_stopped: false,
|
||||||
|
composed: false,
|
||||||
|
},
|
||||||
|
detail,
|
||||||
|
}));
|
||||||
|
// SAFETY: receiver 不可调用,native_fn 存不透明 `Box<CustomEventInner>`
|
||||||
|
// 指针,与 ArrayBuffer / RegExp 的类型化存储模式一致。
|
||||||
|
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
|
||||||
|
obj.type_tag = JsObject::OBJ_TYPE_CUSTOM_EVENT;
|
||||||
|
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(inner as *const ()) }));
|
||||||
|
NativeResult::Ok(this_val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解析 this 为 CustomEvent 载荷盒共享引用:非 CustomEvent 对象或无载荷盒
|
||||||
|
/// 返回 None。
|
||||||
|
///
|
||||||
|
/// # 注意事项
|
||||||
|
/// 返回引用按 `'a` 声称为存活,实际存活至载荷盒被 GC 释放;调用方须在无 GC
|
||||||
|
/// 窗口的单 native 函数内消费,不得跨用户调用窗口持有。
|
||||||
|
fn custom_event_inner_of<'a>(this_val: JsValue) -> Option<&'a CustomEventInner> {
|
||||||
|
if !this_val.is_object() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let obj_ptr = this_val.as_js_object_ptr();
|
||||||
|
if obj_ptr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// SAFETY: this_val 已确认是对象值,obj_ptr 指向合法 JsObject。
|
||||||
|
let obj = unsafe { &*obj_ptr };
|
||||||
|
let box_ptr = custom_event_payload_ptr(obj)?;
|
||||||
|
if box_ptr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// SAFETY: box_ptr 指向有效载荷盒(构造期写入、释放时置空);'a 存活由
|
||||||
|
// 调用方「无 GC 窗口内消费」约定保证。
|
||||||
|
Some(unsafe { &*box_ptr })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `CustomEvent.prototype.detail`:返回事件详情(缺省为 null)。
|
||||||
|
pub fn custom_event_detail_getter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||||
|
let Some(inner) = custom_event_inner_of(this_val) else {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(
|
||||||
|
vm,
|
||||||
|
"CustomEvent.prototype.detail getter called on non-CustomEvent object",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
NativeResult::Ok(inner.detail)
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
//! 就地填充 `message_event_constructor` / `message_event_proto` 两枚 P 字段
|
//! 就地填充 `message_event_constructor` / `message_event_proto` 两枚 P 字段
|
||||||
//! 占位(构造器本体与原型 getter),并绑全局 `MessageEvent`;就地填充
|
//! 占位(构造器本体与原型 getter),并绑全局 `MessageEvent`;就地填充
|
||||||
//! `error_event_constructor` / `error_event_proto` 两枚 P 字段占位(构造器
|
//! `error_event_constructor` / `error_event_proto` 两枚 P 字段占位(构造器
|
||||||
//! 本体与原型 getter),并绑全局 `ErrorEvent`。
|
//! 本体与原型 getter),并绑全局 `ErrorEvent`;就地填充
|
||||||
//!
|
//! `custom_event_constructor` / `custom_event_proto` 两枚 P 字段占位(构造器
|
||||||
//! 九枚 P 字段中其余两枚(CustomEvent 一对)由后续子任务就地填充,本函数
|
//! 本体与原型 getter),并绑全局 `CustomEvent`。
|
||||||
//! 不读不写。
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -16,12 +15,14 @@ use oxide_kernel::kernel::{KernelCore, KernelSession};
|
|||||||
use oxide_types::object::{JsObject, PropAttributes};
|
use oxide_types::object::{JsObject, PropAttributes};
|
||||||
use oxide_types::value::JsValue;
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
/// 把 Event / MessageEvent / ErrorEvent 构造器及原型方法绑定到 global。
|
/// 把 Event / MessageEvent / ErrorEvent / CustomEvent 构造器及原型方法绑定到
|
||||||
|
/// global。
|
||||||
///
|
///
|
||||||
/// `event_constructor` / `event_proto`、`message_event_constructor` /
|
/// `event_constructor` / `event_proto`、`message_event_constructor` /
|
||||||
/// `message_event_proto` 与 `error_event_constructor` / `error_event_proto`
|
/// `message_event_proto`、`error_event_constructor` / `error_event_proto` 与
|
||||||
/// 六枚 P 字段是空对象占位,本函数就地填充(不新建对象——P 字段本身就是
|
/// `custom_event_constructor` / `custom_event_proto` 八枚 P 字段是空对象占位,
|
||||||
/// 槽位,快照/脏检查/收尾枚举已覆盖)。
|
/// 本函数就地填充(不新建对象——P 字段本身就是槽位,快照/脏检查/收尾枚举已
|
||||||
|
/// 覆盖)。
|
||||||
///
|
///
|
||||||
/// # 幂等
|
/// # 幂等
|
||||||
/// 本函数从三条路径到达:`init_kernel_builtins`(占位是空对象)、
|
/// 本函数从三条路径到达:`init_kernel_builtins`(占位是空对象)、
|
||||||
@@ -314,6 +315,69 @@ pub fn bind_event(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- 填 CustomEvent 构造器与原型占位 ----
|
||||||
|
let ce_ctor_ptr = world.custom_event_constructor.as_ptr() as *mut JsObject;
|
||||||
|
let ce_ctor = unsafe { &mut *ce_ctor_ptr };
|
||||||
|
let ce_proto_ptr = world.custom_event_proto.as_ptr() as *mut JsObject;
|
||||||
|
let ce_proto = unsafe { &mut *ce_proto_ptr };
|
||||||
|
|
||||||
|
// 构造器:function 标志、prototype 槽、name 槽、[[Prototype]] →
|
||||||
|
// Function.prototype(幂等,裸写槽)。
|
||||||
|
ce_ctor.set_function(true);
|
||||||
|
if sh.lookup_position(ce_ctor.shape_id(), si_prototype).is_none() {
|
||||||
|
let shape = sh.make_shape(ce_ctor.shape_id(), si_prototype);
|
||||||
|
ce_ctor.set_shape_id(shape);
|
||||||
|
let pos = ce_ctor.push_prop(JsValue::from_js_object(ce_proto_ptr));
|
||||||
|
ce_ctor.set_data_meta(pos, PropAttributes::new(false, false, false));
|
||||||
|
ce_ctor.bump_generation();
|
||||||
|
}
|
||||||
|
if sh.lookup_position(ce_ctor.shape_id(), si_name).is_none() {
|
||||||
|
let shape = sh.make_shape(ce_ctor.shape_id(), si_name);
|
||||||
|
ce_ctor.set_shape_id(shape);
|
||||||
|
ce_ctor
|
||||||
|
.ensure_hash_props()
|
||||||
|
.push(JsValue::perm_string(sf.string_ptr(sf.intern("CustomEvent").0)));
|
||||||
|
let pos = ce_ctor.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
|
||||||
|
ce_ctor.set_data_meta(pos, PropAttributes::new(false, false, true));
|
||||||
|
ce_ctor.bump_generation();
|
||||||
|
}
|
||||||
|
let _ = ce_ctor.set_proto(fn_proto_val);
|
||||||
|
|
||||||
|
configure_native_constructor(
|
||||||
|
ce_ctor,
|
||||||
|
oxide_builtins::event::custom_event_constructor::<crate::vm::Vm> as *const (),
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
bind_constructor!(
|
||||||
|
core,
|
||||||
|
global,
|
||||||
|
"CustomEvent",
|
||||||
|
ce_ctor_ptr,
|
||||||
|
oxide_builtins::event::custom_event_constructor::<crate::vm::Vm>,
|
||||||
|
2,
|
||||||
|
hash: true
|
||||||
|
);
|
||||||
|
|
||||||
|
// 原型:constructor 槽、[[Prototype]] → Event.prototype、一个 getter
|
||||||
|
// (detail,先 lookup_position 守卫)。
|
||||||
|
if sh.lookup_position(ce_proto.shape_id(), si_constructor).is_none() {
|
||||||
|
let shape = sh.make_shape(ce_proto.shape_id(), si_constructor);
|
||||||
|
ce_proto.set_shape_id(shape);
|
||||||
|
let pos = ce_proto.push_prop(JsValue::from_js_object(ce_ctor_ptr));
|
||||||
|
ce_proto.set_data_meta(pos, PropAttributes::new(true, false, true));
|
||||||
|
ce_proto.bump_generation();
|
||||||
|
}
|
||||||
|
let _ = ce_proto.set_proto(event_proto_val);
|
||||||
|
|
||||||
|
let ce_getter_fns: [(&str, *const ()); 1] =
|
||||||
|
[("detail", oxide_builtins::event::custom_event_detail_getter::<crate::vm::Vm> as *const ())];
|
||||||
|
for (name, fn_ptr) in ce_getter_fns {
|
||||||
|
let si = sf.intern(name).0;
|
||||||
|
if sh.lookup_position(ce_proto.shape_id(), si).is_none() {
|
||||||
|
bind_accessor_getter(core, session, ce_proto, name, fn_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---- 填 EventTarget 原型占位(无构造器,仅暴露原型与三方法)----
|
// ---- 填 EventTarget 原型占位(无构造器,仅暴露原型与三方法)----
|
||||||
// 幂等:三方法经 lookup_position 守卫,[[Prototype]] 裸写槽幂等。
|
// 幂等:三方法经 lookup_position 守卫,[[Prototype]] 裸写槽幂等。
|
||||||
let et_proto_ptr = world.event_target_proto.as_ptr() as *mut JsObject;
|
let et_proto_ptr = world.event_target_proto.as_ptr() as *mut JsObject;
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ pub mod bind_date;
|
|||||||
pub mod bind_disposable_stack;
|
pub mod bind_disposable_stack;
|
||||||
/// Error 家族构造器与原型的 native 方法绑定(含各子类型构造器创建)。
|
/// Error 家族构造器与原型的 native 方法绑定(含各子类型构造器创建)。
|
||||||
pub mod bind_error;
|
pub mod bind_error;
|
||||||
/// Event 体系绑定入口(Event / MessageEvent / ErrorEvent 构造器与原型就地
|
/// Event 体系绑定入口(Event / MessageEvent / ErrorEvent / CustomEvent 构造器
|
||||||
/// 填充,CustomEvent 一对由后续任务填充)。
|
/// 与原型就地填充)。
|
||||||
pub mod bind_event;
|
pub mod bind_event;
|
||||||
/// Function 构造器与原型的 native 方法绑定。
|
/// Function 构造器与原型的 native 方法绑定。
|
||||||
pub mod bind_function;
|
pub mod bind_function;
|
||||||
|
|||||||
@@ -255,10 +255,10 @@ pub(crate) fn ops_for(family: NativeBoxFamily) -> NativeBoxOps {
|
|||||||
string_edges: None,
|
string_edges: None,
|
||||||
cell_edges: None,
|
cell_edges: None,
|
||||||
},
|
},
|
||||||
// CustomEvent 载荷盒由后续子任务填充;本臂当前无引用边,仅 size/drop
|
// CustomEvent 派生类载荷盒:基类 type / target / current_target 边加
|
||||||
// 链消费。
|
// detail 边。
|
||||||
NativeBoxFamily::CustomEvent => NativeBoxOps {
|
NativeBoxFamily::CustomEvent => NativeBoxOps {
|
||||||
object_edges: None,
|
object_edges: Some(event::custom_event_native_edges),
|
||||||
string_edges: None,
|
string_edges: None,
|
||||||
cell_edges: None,
|
cell_edges: None,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ impl SessionGc {
|
|||||||
bytes += event::event_native_size(obj);
|
bytes += event::event_native_size(obj);
|
||||||
bytes += event::message_event_native_size(obj);
|
bytes += event::message_event_native_size(obj);
|
||||||
bytes += event::error_event_native_size(obj);
|
bytes += event::error_event_native_size(obj);
|
||||||
|
bytes += event::custom_event_native_size(obj);
|
||||||
bytes += crate::arguments_gc::arguments_native_size(obj);
|
bytes += crate::arguments_gc::arguments_native_size(obj);
|
||||||
|
|
||||||
bytes
|
bytes
|
||||||
@@ -379,13 +380,13 @@ impl SessionGc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 事件系载荷盒(Event / MessageEvent / ErrorEvent / CustomEvent)字符串
|
// 事件系载荷盒(Event / MessageEvent / ErrorEvent / CustomEvent)字符串
|
||||||
// 边标记:按标签取对应边函数;CustomEvent 载荷盒由后续子任务填充,
|
// 边标记:按标签取对应边函数。
|
||||||
// 当前取空边。
|
|
||||||
if event::is_event_family_obj(obj) {
|
if event::is_event_family_obj(obj) {
|
||||||
let edges = match obj.type_tag {
|
let edges = match obj.type_tag {
|
||||||
JsObject::OBJ_TYPE_EVENT => event::event_native_edges(obj),
|
JsObject::OBJ_TYPE_EVENT => event::event_native_edges(obj),
|
||||||
JsObject::OBJ_TYPE_MESSAGE_EVENT => event::message_event_native_edges(obj),
|
JsObject::OBJ_TYPE_MESSAGE_EVENT => event::message_event_native_edges(obj),
|
||||||
JsObject::OBJ_TYPE_ERROR_EVENT => event::error_event_native_edges(obj),
|
JsObject::OBJ_TYPE_ERROR_EVENT => event::error_event_native_edges(obj),
|
||||||
|
JsObject::OBJ_TYPE_CUSTOM_EVENT => event::custom_event_native_edges(obj),
|
||||||
_ => Vec::new(),
|
_ => Vec::new(),
|
||||||
};
|
};
|
||||||
for value in edges {
|
for value in edges {
|
||||||
@@ -596,6 +597,7 @@ impl SessionGc {
|
|||||||
freed_bytes += event::drop_event_native(obj);
|
freed_bytes += event::drop_event_native(obj);
|
||||||
freed_bytes += event::drop_message_event_native(obj);
|
freed_bytes += event::drop_message_event_native(obj);
|
||||||
freed_bytes += event::drop_error_event_native(obj);
|
freed_bytes += event::drop_error_event_native(obj);
|
||||||
|
freed_bytes += event::drop_custom_event_native(obj);
|
||||||
freed_bytes += crate::arguments_gc::drop_arguments_native(obj);
|
freed_bytes += crate::arguments_gc::drop_arguments_native(obj);
|
||||||
|
|
||||||
freed_bytes
|
freed_bytes
|
||||||
|
|||||||
@@ -289,3 +289,48 @@ fn error_event_box_edge_survives_gc() {
|
|||||||
vm.collect_session_gc();
|
vm.collect_session_gc();
|
||||||
assert_eq!(vm_eval(&mut vm, "globalThis.e.error.tag;"), "1");
|
assert_eq!(vm_eval(&mut vm, "globalThis.e.error.tag;"), "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// CustomEvent 构造器:init 的 detail 经原型 getter 可读。
|
||||||
|
#[test]
|
||||||
|
fn custom_event_constructor_detail() {
|
||||||
|
assert_eq!(eval("new CustomEvent('x', { detail: { a: 1 } }).detail.a;"), "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CustomEvent 缺省字段:detail 为 null。
|
||||||
|
#[test]
|
||||||
|
fn custom_event_detail_defaults_null() {
|
||||||
|
assert_eq!(eval("new CustomEvent('x').detail === null;"), "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CustomEvent 原型链:instanceof CustomEvent 与 instanceof Event 均真,
|
||||||
|
/// 原型双亲为 Event.prototype。
|
||||||
|
#[test]
|
||||||
|
fn custom_event_proto_chain() {
|
||||||
|
assert_eq!(
|
||||||
|
eval(
|
||||||
|
"var e = new CustomEvent('x'); \
|
||||||
|
e instanceof CustomEvent && e instanceof Event && \
|
||||||
|
Object.getPrototypeOf(e) === CustomEvent.prototype && \
|
||||||
|
Object.getPrototypeOf(CustomEvent.prototype) === Event.prototype;"
|
||||||
|
),
|
||||||
|
"true"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CustomEvent 非构造形态抛 TypeError。
|
||||||
|
#[test]
|
||||||
|
fn custom_event_requires_new() {
|
||||||
|
assert_eq!(
|
||||||
|
eval("try { CustomEvent('x'); 'no-throw'; } catch (e) { e instanceof TypeError; }"),
|
||||||
|
"true"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CustomEvent 盒边 GC 保活:detail 字段引用对象经完整收集后仍存活。
|
||||||
|
#[test]
|
||||||
|
fn custom_event_box_edge_survives_gc() {
|
||||||
|
let mut vm = vm_with_script("globalThis.e = new CustomEvent('x', { detail: { a: 1 } });");
|
||||||
|
// 强制完整收集:detail 字段是盒内 GC 边,对象应保活。
|
||||||
|
vm.collect_session_gc();
|
||||||
|
assert_eq!(vm_eval(&mut vm, "globalThis.e.detail.a;"), "1");
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user