mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 11:11:11 +08:00
1444 lines
57 KiB
Rust
1444 lines
57 KiB
Rust
//! Event 基类与 MessageEvent / ErrorEvent / CustomEvent 派生类行为面:
|
||
//! `Event` 构造器(`new Event(type, init)`)、原型 getter(type / target /
|
||
//! currentTarget / eventPhase / isTrusted / bubbles / cancelable /
|
||
//! defaultPrevented / composed)与原型方法(preventDefault / stopPropagation /
|
||
//! stopImmediatePropagation / composedPath);`MessageEvent` 构造器
|
||
//! (`new MessageEvent(type, init)`)与原型 getter(data / origin / lastEpoch /
|
||
//! source / ports);`ErrorEvent` 构造器(`new ErrorEvent(type, init)`)与
|
||
//! 原型 getter(message / filename / lineno / colno / error);`CustomEvent`
|
||
//! 构造器(`new CustomEvent(type, init)`)与原型 getter(detail)。
|
||
//!
|
||
//! 事件载荷盒(type 串与九项状态位,MessageEvent 加 data / origin /
|
||
//! lastEpoch / source / ports 五字段,ErrorEvent 加 message / filename /
|
||
//! lineno / colno / error 五字段,CustomEvent 加 detail 一字段)存于对象
|
||
//! `native_fn` 槽,向 GC 家族表暴露 edges/size/drop 三自由函数。
|
||
|
||
use std::mem;
|
||
|
||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||
use oxide_types::object::{JsObject, NativeFnPtr};
|
||
use oxide_types::value::JsValue;
|
||
|
||
use oxide_runtime_api::{NativeResult, ProtoKind, VmHost};
|
||
|
||
/// Event 载荷盒:存于对象 `native_fn` 槽(`Box::into_raw`,realm 局部单线程)。
|
||
/// `r#type` 是事件类型串(GC 字符串边),`target` / `current_target` 是目标
|
||
/// 对象(GC 对象边,初始 null),其余为简单状态位。
|
||
///
|
||
/// `#[repr(C)]` 固定字段按声明序、首字段偏移 0:派生类盒以本结构为首字段嵌入,
|
||
/// 派发路径把盒指针转基类指针读首字段,依赖该偏移稳定。
|
||
#[repr(C)]
|
||
struct EventInner {
|
||
r#type: JsValue,
|
||
target: JsValue,
|
||
current_target: JsValue,
|
||
event_phase: u32,
|
||
is_trusted: bool,
|
||
bubbles: bool,
|
||
cancelable: bool,
|
||
default_prevented: bool,
|
||
propagation_stopped: bool,
|
||
immediate_stopped: bool,
|
||
composed: bool,
|
||
}
|
||
|
||
/// MessageEvent 载荷盒:事件基类字段(`EventInner` 嵌入,位于偏移 0)加消息
|
||
/// 五字段(data / origin / lastEpoch / source / ports)。存于对象 `native_fn`
|
||
/// 槽(`Box::into_raw`,realm 局部单线程)。
|
||
///
|
||
/// `#[repr(C)]` 固定 `base` 位于偏移 0:派生类盒指针转基类指针读首字段依赖该
|
||
/// 偏移稳定(与 `EventInner` 同型约束)。
|
||
#[repr(C)]
|
||
struct MessageEventInner {
|
||
base: EventInner,
|
||
data: JsValue,
|
||
origin: JsValue,
|
||
last_epoch: u32,
|
||
source: JsValue,
|
||
ports: Vec<JsValue>,
|
||
}
|
||
|
||
/// ErrorEvent 载荷盒:事件基类字段(`EventInner` 嵌入,位于偏移 0)加错误
|
||
/// 五字段(message / filename / lineno / colno / error)。存于对象
|
||
/// `native_fn` 槽(`Box::into_raw`,realm 局部单线程)。
|
||
///
|
||
/// `#[repr(C)]` 固定 `base` 位于偏移 0:派生类盒指针转基类指针读首字段依赖该
|
||
/// 偏移稳定(与 `EventInner` 同型约束)。
|
||
#[repr(C)]
|
||
struct ErrorEventInner {
|
||
base: EventInner,
|
||
message: JsValue,
|
||
filename: JsValue,
|
||
lineno: u32,
|
||
colno: u32,
|
||
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 四标签)。
|
||
pub fn is_event_family_obj(obj: &JsObject) -> bool {
|
||
matches!(
|
||
obj.type_tag,
|
||
JsObject::OBJ_TYPE_EVENT
|
||
| JsObject::OBJ_TYPE_MESSAGE_EVENT
|
||
| JsObject::OBJ_TYPE_ERROR_EVENT
|
||
| JsObject::OBJ_TYPE_CUSTOM_EVENT
|
||
)
|
||
}
|
||
|
||
/// 取载荷盒指针:仅 Event 标签对象返回 `Some`,其余标签返回 `None`。
|
||
fn event_payload_ptr(obj: &JsObject) -> Option<*mut EventInner> {
|
||
if !obj.is_event_obj() {
|
||
return None;
|
||
}
|
||
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut EventInner)
|
||
}
|
||
|
||
/// 取事件系基类字段指针:仅事件系标签对象返回 `Some`,其余标签返回 `None`。
|
||
///
|
||
/// 派生类对象的 `native_fn` 槽存 `Box<派生类Inner>`,其首字段是 `EventInner`
|
||
/// (基类字段位于偏移 0),指针可安全转基类字段指针。
|
||
fn event_family_base_ptr(obj: &JsObject) -> Option<*mut EventInner> {
|
||
if !is_event_family_obj(obj) {
|
||
return None;
|
||
}
|
||
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut EventInner)
|
||
}
|
||
|
||
/// 收集 Event 的引用边(type 串边与 target / current_target 对象边)。
|
||
///
|
||
/// `target` / `current_target` 空窗口(构造期回填之前)为 null,边函数自守,
|
||
/// 语义自含。
|
||
pub fn event_native_edges(obj: &JsObject) -> Vec<JsValue> {
|
||
let Some(ptr) = event_payload_ptr(obj) else {
|
||
return Vec::new();
|
||
};
|
||
if ptr.is_null() {
|
||
return Vec::new();
|
||
}
|
||
// SAFETY: ptr 指向有效载荷盒(槽位仅构造期写入、释放时置空),只读字段。
|
||
let inner = unsafe { &*ptr };
|
||
vec![inner.r#type, inner.target, inner.current_target]
|
||
}
|
||
|
||
/// 只读核算 Event 载荷盒字节(不释放)。
|
||
pub fn event_native_size(obj: &JsObject) -> u64 {
|
||
let Some(ptr) = event_payload_ptr(obj) else {
|
||
return 0;
|
||
};
|
||
if ptr.is_null() {
|
||
return 0;
|
||
}
|
||
mem::size_of::<EventInner>() as u64
|
||
}
|
||
|
||
/// 释放 Event 载荷盒(`Box<EventInner>`),返回释放字节数。
|
||
///
|
||
/// 盒 drop 时 type / target / current_target 的 JsValue 边随盒释放(GC 已
|
||
/// 判定死对象);槽置空保证重复调用零释放(幂等)。
|
||
pub fn drop_event_native(obj: &mut JsObject) -> u64 {
|
||
let bytes = event_native_size(obj);
|
||
if bytes == 0 {
|
||
return 0;
|
||
}
|
||
let Some(ptr) = event_payload_ptr(obj) else {
|
||
return 0;
|
||
};
|
||
// SAFETY: ptr 非空(event_native_size 已验证),Box::from_raw 恰好释放一次。
|
||
unsafe {
|
||
drop(Box::from_raw(ptr));
|
||
}
|
||
obj.set_native_fn(None);
|
||
bytes
|
||
}
|
||
|
||
/// 读事件类型串(GC 字符串边)。非事件系对象或无载荷盒返回 None。
|
||
///
|
||
/// 供派发路径(`dispatchEvent`)取事件类型串、intern 后查监听器。
|
||
pub fn event_type_value(obj: &JsObject) -> Option<JsValue> {
|
||
let ptr = event_family_base_ptr(obj)?;
|
||
if ptr.is_null() {
|
||
return None;
|
||
}
|
||
// SAFETY: ptr 指向有效载荷盒(构造期写入、释放时置空),只读字段。
|
||
Some(unsafe { &*ptr }.r#type)
|
||
}
|
||
|
||
/// 设派发目标与阶段:`target` / `current_target` 置 `target`、`event_phase` 置
|
||
/// `phase`。非事件系对象或无载荷盒时 no-op。
|
||
///
|
||
/// 供派发路径(`dispatchEvent`)在调用监听器前设目标与 AT_TARGET 阶段。
|
||
pub fn event_set_dispatch(obj: &mut JsObject, target: JsValue, phase: u32) {
|
||
let Some(ptr) = event_family_base_ptr(obj) else {
|
||
return;
|
||
};
|
||
if ptr.is_null() {
|
||
return;
|
||
}
|
||
// SAFETY: ptr 指向有效载荷盒,写入派发字段(无 GC 窗口)。
|
||
let inner = unsafe { &mut *ptr };
|
||
inner.target = target;
|
||
inner.current_target = target;
|
||
inner.event_phase = phase;
|
||
}
|
||
|
||
/// 读 `defaultPrevented` 标志。非事件系对象或无载荷盒返回 None。
|
||
pub fn event_default_prevented(obj: &JsObject) -> Option<bool> {
|
||
let ptr = event_family_base_ptr(obj)?;
|
||
if ptr.is_null() {
|
||
return None;
|
||
}
|
||
// SAFETY: ptr 指向有效载荷盒,只读字段。
|
||
Some(unsafe { &*ptr }.default_prevented)
|
||
}
|
||
|
||
/// 读 `stopImmediatePropagation` 标志。非事件系对象或无载荷盒返回 None。
|
||
pub fn event_immediate_stopped(obj: &JsObject) -> Option<bool> {
|
||
let ptr = event_family_base_ptr(obj)?;
|
||
if ptr.is_null() {
|
||
return None;
|
||
}
|
||
// SAFETY: ptr 指向有效载荷盒,只读字段。
|
||
Some(unsafe { &*ptr }.immediate_stopped)
|
||
}
|
||
|
||
/// 解析 this 为事件系基类字段共享引用:非事件系对象或无载荷盒返回 None。
|
||
///
|
||
/// # 注意事项
|
||
/// 返回引用按 `'a` 声称为存活,实际存活至载荷盒被 GC 释放;调用方须在无 GC
|
||
/// 窗口的单 native 函数内消费,不得跨用户调用窗口持有。
|
||
fn event_inner_of<'a>(this_val: JsValue) -> Option<&'a EventInner> {
|
||
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 = event_family_base_ptr(obj)?;
|
||
if box_ptr.is_null() {
|
||
return None;
|
||
}
|
||
// SAFETY: box_ptr 指向有效载荷盒(构造期写入、释放时置空);'a 存活由
|
||
// 调用方「无 GC 窗口内消费」约定保证。
|
||
Some(unsafe { &*box_ptr })
|
||
}
|
||
|
||
/// 解析 this 为事件系基类字段可变引用:非事件系对象或无载荷盒返回 None。
|
||
///
|
||
/// # 注意事项
|
||
/// 返回引用按 `'a` 声称为存活,实际存活至载荷盒被 GC 释放;调用方须在无 GC
|
||
/// 窗口的单 native 函数内消费,不得跨用户调用窗口持有。
|
||
fn event_inner_of_mut<'a>(this_val: JsValue) -> Option<&'a mut EventInner> {
|
||
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 { &mut *obj_ptr };
|
||
let box_ptr = event_family_base_ptr(obj)?;
|
||
if box_ptr.is_null() {
|
||
return None;
|
||
}
|
||
// SAFETY: box_ptr 指向有效载荷盒(构造期写入、释放时置空);'a 存活由
|
||
// 调用方「无 GC 窗口内消费」约定保证。
|
||
Some(unsafe { &mut *box_ptr })
|
||
}
|
||
|
||
/// Event 构造器:`new Event(type, init)`。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 非构造形态(`!constructing_native`)抛 TypeError(HTML 规范只有
|
||
/// [[Construct]],无 [[Call]])
|
||
/// 2. type 经完整 ToString 转串;空串抛 SyntaxError
|
||
/// 3. init 字典读 bubbles / cancelable / composed(缺省 false)
|
||
/// 4. 建载荷盒入 receiver 的 `native_fn` 槽,置 type_tag
|
||
/// 5. 返回 receiver
|
||
///
|
||
/// # 边界与前提
|
||
/// - receiver(`args[0]`)是 VM 按构造器 prototype 新分配的对象,proto 已
|
||
/// 指向 %Event.prototype%;
|
||
/// - init 缺省(undefined)或非对象时对应标志取 false。
|
||
///
|
||
/// # 副作用
|
||
/// - receiver 的 `native_fn` 槽写入载荷盒、`type_tag` 置 Event 标签。
|
||
pub fn event_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
if !vm.constructing_native() {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "Event constructor requires new"));
|
||
}
|
||
|
||
let this_val = vm.reg(args[0]);
|
||
if !this_val.is_object() {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "Event 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 字典读 bubbles / cancelable / composed(缺省或非对象时取 false)。
|
||
let mut bubbles = false;
|
||
let mut cancelable = false;
|
||
let mut composed = false;
|
||
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 };
|
||
for (name, slot) in
|
||
[("bubbles", &mut bubbles), ("cancelable", &mut cancelable), ("composed", &mut composed)]
|
||
{
|
||
let si = vm.perm_intern(name);
|
||
match vm.ordinary_get(init_obj, si, init_val) {
|
||
Ok(v) => *slot = oxide_runtime_api::to_boolean(v),
|
||
Err(_) => {
|
||
if let Some(exc) = vm.take_uncaught_value() {
|
||
return NativeResult::Err(exc);
|
||
}
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event init property getter failed",
|
||
));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 建载荷盒入 receiver 的 native_fn 槽,置 Event 标签。
|
||
let type_js = vm.new_string(&type_str);
|
||
let inner = Box::into_raw(Box::new(EventInner {
|
||
r#type: type_js,
|
||
target: JsValue::null(),
|
||
current_target: JsValue::null(),
|
||
event_phase: 0,
|
||
is_trusted: false,
|
||
bubbles,
|
||
cancelable,
|
||
default_prevented: false,
|
||
propagation_stopped: false,
|
||
immediate_stopped: false,
|
||
composed,
|
||
}));
|
||
// SAFETY: receiver 不可调用,native_fn 存不透明 `Box<EventInner>` 指针,
|
||
// 与 ArrayBuffer / RegExp 的类型化存储模式一致。
|
||
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
|
||
obj.type_tag = JsObject::OBJ_TYPE_EVENT;
|
||
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(inner as *const ()) }));
|
||
NativeResult::Ok(this_val)
|
||
}
|
||
|
||
/// 建一个 Event 载荷盒对象(type 为 `type_str`),返回对象指针。
|
||
///
|
||
/// 供交付路径(消息事件 / 错误事件)建真实 Event 盒,使 `dispatchEvent` 品牌守卫
|
||
/// 通过。盒的 `target` / `currentTarget` 初始 null、`eventPhase` 初始 0(派发时设);
|
||
/// `[[Prototype]]` 指向 `Event.prototype`(`e instanceof Event` 成立)。
|
||
///
|
||
/// # 副作用
|
||
/// - 新建一个 Event 盒对象(经 `alloc_object` 入对象表)。
|
||
pub fn create_event_box<H: VmHost>(vm: &mut H, type_str: &str) -> *mut JsObject {
|
||
let type_js = vm.new_string(type_str);
|
||
let inner = Box::into_raw(Box::new(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,
|
||
}));
|
||
let event_proto = JsValue::from_js_object(vm.builtin_proto(ProtoKind::EventProto));
|
||
let obj = JsObject::new_empty(EMPTY_SHAPE_ID, event_proto);
|
||
let ptr = vm.alloc_object(obj);
|
||
// SAFETY: ptr 是本函数刚 alloc_object 的对象,存活且本段无别名;native_fn 存
|
||
// 不透明 `Box<EventInner>` 指针,与 ArrayBuffer / RegExp 的类型化存储模式一致。
|
||
let event_obj = unsafe { &mut *ptr };
|
||
event_obj.type_tag = JsObject::OBJ_TYPE_EVENT;
|
||
event_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(inner as *const ()) }));
|
||
ptr
|
||
}
|
||
|
||
/// `Event.prototype.type`:返回事件类型串。
|
||
pub fn event_type_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) = event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.type getter called on non-Event object",
|
||
));
|
||
};
|
||
NativeResult::Ok(inner.r#type)
|
||
}
|
||
|
||
/// `Event.prototype.target`:返回事件目标对象(未派发时 null)。
|
||
pub fn event_target_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) = event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.target getter called on non-Event object",
|
||
));
|
||
};
|
||
NativeResult::Ok(inner.target)
|
||
}
|
||
|
||
/// `Event.prototype.currentTarget`:返回当前派发目标(未派发时 null)。
|
||
pub fn event_current_target_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) = event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.currentTarget getter called on non-Event object",
|
||
));
|
||
};
|
||
NativeResult::Ok(inner.current_target)
|
||
}
|
||
|
||
/// `Event.prototype.eventPhase`:返回派发阶段(0 未派发 / 1 捕获 / 2 目标 / 3 冒泡)。
|
||
pub fn event_phase_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) = event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.eventPhase getter called on non-Event object",
|
||
));
|
||
};
|
||
NativeResult::Ok(JsValue::int(inner.event_phase as i32))
|
||
}
|
||
|
||
/// `Event.prototype.isTrusted`:返回是否由用户操作产生(引擎恒 false)。
|
||
pub fn event_is_trusted_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) = event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.isTrusted getter called on non-Event object",
|
||
));
|
||
};
|
||
NativeResult::Ok(JsValue::bool(inner.is_trusted))
|
||
}
|
||
|
||
/// `Event.prototype.bubbles`:返回是否冒泡。
|
||
pub fn event_bubbles_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) = event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.bubbles getter called on non-Event object",
|
||
));
|
||
};
|
||
NativeResult::Ok(JsValue::bool(inner.bubbles))
|
||
}
|
||
|
||
/// `Event.prototype.cancelable`:返回是否可取消。
|
||
pub fn event_cancelable_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) = event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.cancelable getter called on non-Event object",
|
||
));
|
||
};
|
||
NativeResult::Ok(JsValue::bool(inner.cancelable))
|
||
}
|
||
|
||
/// `Event.prototype.defaultPrevented`:返回默认行为是否已被阻止。
|
||
pub fn event_default_prevented_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) = event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.defaultPrevented getter called on non-Event object",
|
||
));
|
||
};
|
||
NativeResult::Ok(JsValue::bool(inner.default_prevented))
|
||
}
|
||
|
||
/// `Event.prototype.composed`:返回是否跨 Shadow DOM 边界。
|
||
pub fn event_composed_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) = event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.composed getter called on non-Event object",
|
||
));
|
||
};
|
||
NativeResult::Ok(JsValue::bool(inner.composed))
|
||
}
|
||
|
||
/// `Event.prototype.preventDefault`:cancelable 时置 defaultPrevented。
|
||
pub fn event_prevent_default<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||
let Some(inner) = event_inner_of_mut(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.preventDefault called on non-Event object",
|
||
));
|
||
};
|
||
if inner.cancelable {
|
||
inner.default_prevented = true;
|
||
}
|
||
NativeResult::Ok(JsValue::undefined())
|
||
}
|
||
|
||
/// `Event.prototype.stopPropagation`:置传播停止标志。
|
||
pub fn event_stop_propagation<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||
let Some(inner) = event_inner_of_mut(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.stopPropagation called on non-Event object",
|
||
));
|
||
};
|
||
inner.propagation_stopped = true;
|
||
NativeResult::Ok(JsValue::undefined())
|
||
}
|
||
|
||
/// `Event.prototype.stopImmediatePropagation`:置传播停止与立即停止双标志。
|
||
pub fn event_stop_immediate_propagation<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||
let Some(inner) = event_inner_of_mut(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.stopImmediatePropagation called on non-Event object",
|
||
));
|
||
};
|
||
inner.propagation_stopped = true;
|
||
inner.immediate_stopped = true;
|
||
NativeResult::Ok(JsValue::undefined())
|
||
}
|
||
|
||
/// `Event.prototype.composedPath`:返回事件传播路径(首版仅含 this 自身)。
|
||
pub fn event_composed_path<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||
let Some(_) = event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"Event.prototype.composedPath called on non-Event object",
|
||
));
|
||
};
|
||
// 首版:路径仅含 this 自身(完整传播路径由事件派发任务补全)。
|
||
let array_proto = JsValue::from_js_object(vm.builtin_proto(ProtoKind::ArrayProto));
|
||
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, 1));
|
||
// SAFETY: arr 是本函数刚分配的对象,存活且本段无别名。
|
||
unsafe {
|
||
(*arr).set_prop_at(0, this_val);
|
||
}
|
||
NativeResult::Ok(JsValue::from_js_object(arr))
|
||
}
|
||
|
||
// —— MessageEvent 派生类 ——
|
||
|
||
/// 取 MessageEvent 载荷盒指针:仅 MessageEvent 标签对象返回 `Some`,其余标签
|
||
/// 返回 `None`。
|
||
fn message_event_payload_ptr(obj: &JsObject) -> Option<*mut MessageEventInner> {
|
||
if obj.type_tag != JsObject::OBJ_TYPE_MESSAGE_EVENT {
|
||
return None;
|
||
}
|
||
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut MessageEventInner)
|
||
}
|
||
|
||
/// 收集 MessageEvent 的引用边(基类 type / target / current_target 边加
|
||
/// data / source / ports 边)。
|
||
///
|
||
/// `target` / `current_target` 空窗口(构造期回填之前)为 null,边函数自守,
|
||
/// 语义自含。
|
||
pub fn message_event_native_edges(obj: &JsObject) -> Vec<JsValue> {
|
||
let Some(ptr) = message_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.data);
|
||
edges.push(inner.source);
|
||
edges.extend(inner.ports.iter().copied());
|
||
edges
|
||
}
|
||
|
||
/// 只读核算 MessageEvent 载荷盒字节(不释放)。
|
||
pub fn message_event_native_size(obj: &JsObject) -> u64 {
|
||
let Some(ptr) = message_event_payload_ptr(obj) else {
|
||
return 0;
|
||
};
|
||
if ptr.is_null() {
|
||
return 0;
|
||
}
|
||
mem::size_of::<MessageEventInner>() as u64
|
||
}
|
||
|
||
/// 释放 MessageEvent 载荷盒(`Box<MessageEventInner>`),返回释放字节数。
|
||
///
|
||
/// 盒 drop 时基类字段与 data / origin / source / ports 的 JsValue 边随盒释放
|
||
/// (GC 已判定死对象);槽置空保证重复调用零释放(幂等)。
|
||
pub fn drop_message_event_native(obj: &mut JsObject) -> u64 {
|
||
let bytes = message_event_native_size(obj);
|
||
if bytes == 0 {
|
||
return 0;
|
||
}
|
||
let Some(ptr) = message_event_payload_ptr(obj) else {
|
||
return 0;
|
||
};
|
||
// SAFETY: ptr 非空(message_event_native_size 已验证),Box::from_raw 恰好
|
||
// 释放一次。
|
||
unsafe {
|
||
drop(Box::from_raw(ptr));
|
||
}
|
||
obj.set_native_fn(None);
|
||
bytes
|
||
}
|
||
|
||
/// 建一个 MessageEvent 载荷盒对象(`type` 串与 `data` 值),返回对象指针。
|
||
///
|
||
/// 供交付路径(消息事件)建真实 MessageEvent 盒,使 `dispatchEvent` 品牌守卫
|
||
/// 通过。盒的 `origin` 为空串、`lastEpoch` 为 0、`source` 为 null、`ports`
|
||
/// 为空数组;`[[Prototype]]` 指向 `MessageEvent.prototype`
|
||
/// (`e instanceof MessageEvent` 与 `e instanceof Event` 均成立)。
|
||
///
|
||
/// # 副作用
|
||
/// - 新建一个 MessageEvent 盒对象(经 `alloc_object` 入对象表)。
|
||
pub fn create_message_event_box<H: VmHost>(vm: &mut H, data: JsValue, type_str: &str) -> *mut JsObject {
|
||
let type_js = vm.new_string(type_str);
|
||
let origin_js = vm.new_string("");
|
||
let inner = Box::into_raw(Box::new(MessageEventInner {
|
||
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,
|
||
},
|
||
data,
|
||
origin: origin_js,
|
||
last_epoch: 0,
|
||
source: JsValue::null(),
|
||
ports: Vec::new(),
|
||
}));
|
||
let me_proto = JsValue::from_js_object(vm.builtin_proto(ProtoKind::MessageEventProto));
|
||
let obj = JsObject::new_empty(EMPTY_SHAPE_ID, me_proto);
|
||
let ptr = vm.alloc_object(obj);
|
||
// SAFETY: ptr 是本函数刚 alloc_object 的对象,存活且本段无别名;native_fn 存
|
||
// 不透明 `Box<MessageEventInner>` 指针,与 ArrayBuffer / RegExp 的类型化
|
||
// 存储模式一致。
|
||
let me_obj = unsafe { &mut *ptr };
|
||
me_obj.type_tag = JsObject::OBJ_TYPE_MESSAGE_EVENT;
|
||
me_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(inner as *const ()) }));
|
||
ptr
|
||
}
|
||
|
||
/// MessageEvent 构造器:`new MessageEvent(type, init)`。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 非构造形态(`!constructing_native`)抛 TypeError(HTML 规范只有
|
||
/// [[Construct]],无 [[Call]])
|
||
/// 2. type 经完整 ToString 转串;空串抛 SyntaxError
|
||
/// 3. init 字典读 data / origin / lastEpoch / source / ports(缺省:data 为
|
||
/// undefined、origin 为空串、lastEpoch 为 0、source 为 null、ports 为空数组)
|
||
/// 4. 建载荷盒入 receiver 的 `native_fn` 槽,置 type_tag
|
||
/// 5. 返回 receiver
|
||
///
|
||
/// # 边界与前提
|
||
/// - receiver(`args[0]`)是 VM 按构造器 prototype 新分配的对象,proto 已
|
||
/// 指向 %MessageEvent.prototype%;
|
||
/// - init 缺省(undefined)或非对象时对应字段取缺省。
|
||
///
|
||
/// # 副作用
|
||
/// - receiver 的 `native_fn` 槽写入载荷盒、`type_tag` 置 MessageEvent 标签。
|
||
pub fn message_event_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
if !vm.constructing_native() {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "MessageEvent constructor requires new"));
|
||
}
|
||
|
||
let this_val = vm.reg(args[0]);
|
||
if !this_val.is_object() {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "MessageEvent 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 字典读 data / origin / lastEpoch / source / ports(缺省或非对象
|
||
// 时取缺省)。
|
||
let mut data = JsValue::undefined();
|
||
let mut origin = JsValue::undefined();
|
||
let mut last_epoch = 0u32;
|
||
let mut source = JsValue::null();
|
||
let mut ports: Vec<JsValue> = Vec::new();
|
||
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 };
|
||
|
||
// data / origin / source 直读属性(getter 副作用与异常原值传播)。
|
||
for (name, slot) in [("data", &mut data), ("origin", &mut origin), ("source", &mut source)] {
|
||
let si = vm.perm_intern(name);
|
||
match vm.ordinary_get(init_obj, si, init_val) {
|
||
Ok(v) => *slot = v,
|
||
Err(_) => {
|
||
if let Some(exc) = vm.take_uncaught_value() {
|
||
return NativeResult::Err(exc);
|
||
}
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"MessageEvent init property getter failed",
|
||
));
|
||
}
|
||
}
|
||
}
|
||
|
||
// lastEpoch 读数值(非有限非负数归 0)。
|
||
let si_epoch = vm.perm_intern("lastEpoch");
|
||
match vm.ordinary_get(init_obj, si_epoch, init_val) {
|
||
Ok(v) => {
|
||
let n = oxide_runtime_api::to_number(v);
|
||
last_epoch = if n.is_finite() && n >= 0.0 { n as u32 } else { 0 };
|
||
}
|
||
Err(_) => {
|
||
if let Some(exc) = vm.take_uncaught_value() {
|
||
return NativeResult::Err(exc);
|
||
}
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"MessageEvent init property getter failed",
|
||
));
|
||
}
|
||
}
|
||
|
||
// ports 读数组属性并复制元素(非数组取空数组)。
|
||
let si_ports = vm.perm_intern("ports");
|
||
match vm.ordinary_get(init_obj, si_ports, init_val) {
|
||
Ok(v) => {
|
||
if v.is_object() {
|
||
let ports_ptr = v.as_js_object_ptr();
|
||
// SAFETY: v 已确认是对象值,借用即时消费。
|
||
let ports_obj = unsafe { &*ports_ptr };
|
||
if ports_obj.is_array() {
|
||
if let Some(elements) = ports_obj.array_elements_vec() {
|
||
ports = elements.to_vec();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Err(_) => {
|
||
if let Some(exc) = vm.take_uncaught_value() {
|
||
return NativeResult::Err(exc);
|
||
}
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"MessageEvent init property getter failed",
|
||
));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// origin 缺省为空串。
|
||
if origin.is_undefined() {
|
||
origin = vm.new_string("");
|
||
}
|
||
|
||
// 建载荷盒入 receiver 的 native_fn 槽,置 MessageEvent 标签。
|
||
let type_js = vm.new_string(&type_str);
|
||
let inner = Box::into_raw(Box::new(MessageEventInner {
|
||
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,
|
||
},
|
||
data,
|
||
origin,
|
||
last_epoch,
|
||
source,
|
||
ports,
|
||
}));
|
||
// SAFETY: receiver 不可调用,native_fn 存不透明 `Box<MessageEventInner>`
|
||
// 指针,与 ArrayBuffer / RegExp 的类型化存储模式一致。
|
||
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
|
||
obj.type_tag = JsObject::OBJ_TYPE_MESSAGE_EVENT;
|
||
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(inner as *const ()) }));
|
||
NativeResult::Ok(this_val)
|
||
}
|
||
|
||
/// 解析 this 为 MessageEvent 载荷盒共享引用:非 MessageEvent 对象或无载荷盒
|
||
/// 返回 None。
|
||
///
|
||
/// # 注意事项
|
||
/// 返回引用按 `'a` 声称为存活,实际存活至载荷盒被 GC 释放;调用方须在无 GC
|
||
/// 窗口的单 native 函数内消费,不得跨用户调用窗口持有。
|
||
fn message_event_inner_of<'a>(this_val: JsValue) -> Option<&'a MessageEventInner> {
|
||
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 = message_event_payload_ptr(obj)?;
|
||
if box_ptr.is_null() {
|
||
return None;
|
||
}
|
||
// SAFETY: box_ptr 指向有效载荷盒(构造期写入、释放时置空);'a 存活由
|
||
// 调用方「无 GC 窗口内消费」约定保证。
|
||
Some(unsafe { &*box_ptr })
|
||
}
|
||
|
||
/// `MessageEvent.prototype.data`:返回消息数据。
|
||
pub fn message_event_data_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) = message_event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"MessageEvent.prototype.data getter called on non-MessageEvent object",
|
||
));
|
||
};
|
||
NativeResult::Ok(inner.data)
|
||
}
|
||
|
||
/// `MessageEvent.prototype.origin`:返回消息来源源(缺省为空串)。
|
||
pub fn message_event_origin_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) = message_event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"MessageEvent.prototype.origin getter called on non-MessageEvent object",
|
||
));
|
||
};
|
||
NativeResult::Ok(inner.origin)
|
||
}
|
||
|
||
/// `MessageEvent.prototype.lastEpoch`:返回最后纪元(引擎恒 0)。
|
||
pub fn message_event_last_epoch_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) = message_event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"MessageEvent.prototype.lastEpoch getter called on non-MessageEvent object",
|
||
));
|
||
};
|
||
NativeResult::Ok(JsValue::int(inner.last_epoch as i32))
|
||
}
|
||
|
||
/// `MessageEvent.prototype.source`:返回消息来源对象(首版恒 null)。
|
||
pub fn message_event_source_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) = message_event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"MessageEvent.prototype.source getter called on non-MessageEvent object",
|
||
));
|
||
};
|
||
NativeResult::Ok(inner.source)
|
||
}
|
||
|
||
/// `MessageEvent.prototype.ports`:返回消息端口数组(首版恒空数组)。
|
||
pub fn message_event_ports_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) = message_event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"MessageEvent.prototype.ports getter called on non-MessageEvent object",
|
||
));
|
||
};
|
||
let array_proto = JsValue::from_js_object(vm.builtin_proto(ProtoKind::ArrayProto));
|
||
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, array_proto, inner.ports.len()));
|
||
// SAFETY: arr 是本函数刚分配的对象,存活且本段无别名。
|
||
unsafe {
|
||
for (i, value) in inner.ports.iter().enumerate() {
|
||
(*arr).set_prop_at(i, *value);
|
||
}
|
||
}
|
||
NativeResult::Ok(JsValue::from_js_object(arr))
|
||
}
|
||
|
||
// —— ErrorEvent 派生类 ——
|
||
|
||
/// 取 ErrorEvent 载荷盒指针:仅 ErrorEvent 标签对象返回 `Some`,其余标签
|
||
/// 返回 `None`。
|
||
fn error_event_payload_ptr(obj: &JsObject) -> Option<*mut ErrorEventInner> {
|
||
if obj.type_tag != JsObject::OBJ_TYPE_ERROR_EVENT {
|
||
return None;
|
||
}
|
||
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut ErrorEventInner)
|
||
}
|
||
|
||
/// 收集 ErrorEvent 的引用边(基类 type / target / current_target 边加
|
||
/// message / filename / error 边)。
|
||
///
|
||
/// `target` / `current_target` 空窗口(构造期回填之前)为 null,边函数自守,
|
||
/// 语义自含。
|
||
pub fn error_event_native_edges(obj: &JsObject) -> Vec<JsValue> {
|
||
let Some(ptr) = error_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.message);
|
||
edges.push(inner.filename);
|
||
edges.push(inner.error);
|
||
edges
|
||
}
|
||
|
||
/// 只读核算 ErrorEvent 载荷盒字节(不释放)。
|
||
pub fn error_event_native_size(obj: &JsObject) -> u64 {
|
||
let Some(ptr) = error_event_payload_ptr(obj) else {
|
||
return 0;
|
||
};
|
||
if ptr.is_null() {
|
||
return 0;
|
||
}
|
||
mem::size_of::<ErrorEventInner>() as u64
|
||
}
|
||
|
||
/// 释放 ErrorEvent 载荷盒(`Box<ErrorEventInner>`),返回释放字节数。
|
||
///
|
||
/// 盒 drop 时基类字段与 message / filename / error 的 JsValue 边随盒释放
|
||
/// (GC 已判定死对象);槽置空保证重复调用零释放(幂等)。
|
||
pub fn drop_error_event_native(obj: &mut JsObject) -> u64 {
|
||
let bytes = error_event_native_size(obj);
|
||
if bytes == 0 {
|
||
return 0;
|
||
}
|
||
let Some(ptr) = error_event_payload_ptr(obj) else {
|
||
return 0;
|
||
};
|
||
// SAFETY: ptr 非空(error_event_native_size 已验证),Box::from_raw 恰好
|
||
// 释放一次。
|
||
unsafe {
|
||
drop(Box::from_raw(ptr));
|
||
}
|
||
obj.set_native_fn(None);
|
||
bytes
|
||
}
|
||
|
||
/// 建一个 ErrorEvent 载荷盒对象(`type` 串与 `message` 串),返回对象指针。
|
||
///
|
||
/// 供交付路径(错误事件)建真实 ErrorEvent 盒,使 `dispatchEvent` 品牌守卫
|
||
/// 通过。盒的 `filename` 为空串、`lineno` 与 `colno` 为 0、`error` 为 null;
|
||
/// `[[Prototype]]` 指向 `ErrorEvent.prototype`(`e instanceof ErrorEvent` 与
|
||
/// `e instanceof Event` 均成立)。
|
||
///
|
||
/// # 副作用
|
||
/// - 新建一个 ErrorEvent 盒对象(经 `alloc_object` 入对象表)。
|
||
pub fn create_error_event_box<H: VmHost>(vm: &mut H, message: &str, type_str: &str) -> *mut JsObject {
|
||
let type_js = vm.new_string(type_str);
|
||
let message_js = vm.new_string(message);
|
||
let filename_js = vm.new_string("");
|
||
let inner = Box::into_raw(Box::new(ErrorEventInner {
|
||
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,
|
||
},
|
||
message: message_js,
|
||
filename: filename_js,
|
||
lineno: 0,
|
||
colno: 0,
|
||
error: JsValue::null(),
|
||
}));
|
||
let ee_proto = JsValue::from_js_object(vm.builtin_proto(ProtoKind::ErrorEventProto));
|
||
let obj = JsObject::new_empty(EMPTY_SHAPE_ID, ee_proto);
|
||
let ptr = vm.alloc_object(obj);
|
||
// SAFETY: ptr 是本函数刚 alloc_object 的对象,存活且本段无别名;native_fn 存
|
||
// 不透明 `Box<ErrorEventInner>` 指针,与 ArrayBuffer / RegExp 的类型化
|
||
// 存储模式一致。
|
||
let ee_obj = unsafe { &mut *ptr };
|
||
ee_obj.type_tag = JsObject::OBJ_TYPE_ERROR_EVENT;
|
||
ee_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(inner as *const ()) }));
|
||
ptr
|
||
}
|
||
|
||
/// ErrorEvent 构造器:`new ErrorEvent(type, init)`。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 非构造形态(`!constructing_native`)抛 TypeError(HTML 规范只有
|
||
/// [[Construct]],无 [[Call]])
|
||
/// 2. type 经完整 ToString 转串;空串抛 SyntaxError
|
||
/// 3. init 字典读 message / filename / lineno / colno / error(缺省:message
|
||
/// 为空串、filename 为空串、lineno 为 0、colno 为 0、error 为 null)
|
||
/// 4. 建载荷盒入 receiver 的 `native_fn` 槽,置 type_tag
|
||
/// 5. 返回 receiver
|
||
///
|
||
/// # 边界与前提
|
||
/// - receiver(`args[0]`)是 VM 按构造器 prototype 新分配的对象,proto 已
|
||
/// 指向 %ErrorEvent.prototype%;
|
||
/// - init 缺省(undefined)或非对象时对应字段取缺省。
|
||
///
|
||
/// # 副作用
|
||
/// - receiver 的 `native_fn` 槽写入载荷盒、`type_tag` 置 ErrorEvent 标签。
|
||
pub fn error_event_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
if !vm.constructing_native() {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "ErrorEvent constructor requires new"));
|
||
}
|
||
|
||
let this_val = vm.reg(args[0]);
|
||
if !this_val.is_object() {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "ErrorEvent 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 字典读 message / filename / lineno / colno / error(缺省或非对象
|
||
// 时取缺省)。
|
||
let mut message = JsValue::undefined();
|
||
let mut filename = JsValue::undefined();
|
||
let mut lineno = 0u32;
|
||
let mut colno = 0u32;
|
||
let mut error = 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 };
|
||
|
||
// message / filename / error 直读属性(getter 副作用与异常原值传播)。
|
||
for (name, slot) in [("message", &mut message), ("filename", &mut filename), ("error", &mut error)] {
|
||
let si = vm.perm_intern(name);
|
||
match vm.ordinary_get(init_obj, si, init_val) {
|
||
Ok(v) => *slot = v,
|
||
Err(_) => {
|
||
if let Some(exc) = vm.take_uncaught_value() {
|
||
return NativeResult::Err(exc);
|
||
}
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"ErrorEvent init property getter failed",
|
||
));
|
||
}
|
||
}
|
||
}
|
||
|
||
// lineno / colno 读数值(非有限非负数归 0)。
|
||
for (name, slot) in [("lineno", &mut lineno), ("colno", &mut colno)] {
|
||
let si = vm.perm_intern(name);
|
||
match vm.ordinary_get(init_obj, si, init_val) {
|
||
Ok(v) => {
|
||
let n = oxide_runtime_api::to_number(v);
|
||
*slot = if n.is_finite() && n >= 0.0 { n as u32 } else { 0 };
|
||
}
|
||
Err(_) => {
|
||
if let Some(exc) = vm.take_uncaught_value() {
|
||
return NativeResult::Err(exc);
|
||
}
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"ErrorEvent init property getter failed",
|
||
));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// message / filename 缺省为空串、error 缺省为 null(init 缺属性时
|
||
// ordinary_get 返回 undefined,须归回缺省)。
|
||
if message.is_undefined() {
|
||
message = vm.new_string("");
|
||
}
|
||
if filename.is_undefined() {
|
||
filename = vm.new_string("");
|
||
}
|
||
if error.is_undefined() {
|
||
error = JsValue::null();
|
||
}
|
||
|
||
// 建载荷盒入 receiver 的 native_fn 槽,置 ErrorEvent 标签。
|
||
let type_js = vm.new_string(&type_str);
|
||
let inner = Box::into_raw(Box::new(ErrorEventInner {
|
||
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,
|
||
},
|
||
message,
|
||
filename,
|
||
lineno,
|
||
colno,
|
||
error,
|
||
}));
|
||
// SAFETY: receiver 不可调用,native_fn 存不透明 `Box<ErrorEventInner>`
|
||
// 指针,与 ArrayBuffer / RegExp 的类型化存储模式一致。
|
||
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
|
||
obj.type_tag = JsObject::OBJ_TYPE_ERROR_EVENT;
|
||
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(inner as *const ()) }));
|
||
NativeResult::Ok(this_val)
|
||
}
|
||
|
||
/// 解析 this 为 ErrorEvent 载荷盒共享引用:非 ErrorEvent 对象或无载荷盒
|
||
/// 返回 None。
|
||
///
|
||
/// # 注意事项
|
||
/// 返回引用按 `'a` 声称为存活,实际存活至载荷盒被 GC 释放;调用方须在无 GC
|
||
/// 窗口的单 native 函数内消费,不得跨用户调用窗口持有。
|
||
fn error_event_inner_of<'a>(this_val: JsValue) -> Option<&'a ErrorEventInner> {
|
||
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 = error_event_payload_ptr(obj)?;
|
||
if box_ptr.is_null() {
|
||
return None;
|
||
}
|
||
// SAFETY: box_ptr 指向有效载荷盒(构造期写入、释放时置空);'a 存活由
|
||
// 调用方「无 GC 窗口内消费」约定保证。
|
||
Some(unsafe { &*box_ptr })
|
||
}
|
||
|
||
/// `ErrorEvent.prototype.message`:返回错误消息串(缺省为空串)。
|
||
pub fn error_event_message_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) = error_event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"ErrorEvent.prototype.message getter called on non-ErrorEvent object",
|
||
));
|
||
};
|
||
NativeResult::Ok(inner.message)
|
||
}
|
||
|
||
/// `ErrorEvent.prototype.filename`:返回错误发生文件名(缺省为空串)。
|
||
pub fn error_event_filename_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) = error_event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"ErrorEvent.prototype.filename getter called on non-ErrorEvent object",
|
||
));
|
||
};
|
||
NativeResult::Ok(inner.filename)
|
||
}
|
||
|
||
/// `ErrorEvent.prototype.lineno`:返回错误发生行号(缺省为 0)。
|
||
pub fn error_event_lineno_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) = error_event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"ErrorEvent.prototype.lineno getter called on non-ErrorEvent object",
|
||
));
|
||
};
|
||
NativeResult::Ok(JsValue::int(inner.lineno as i32))
|
||
}
|
||
|
||
/// `ErrorEvent.prototype.colno`:返回错误发生列号(缺省为 0)。
|
||
pub fn error_event_colno_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) = error_event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"ErrorEvent.prototype.colno getter called on non-ErrorEvent object",
|
||
));
|
||
};
|
||
NativeResult::Ok(JsValue::int(inner.colno as i32))
|
||
}
|
||
|
||
/// `ErrorEvent.prototype.error`:返回错误对象(缺省为 null)。
|
||
pub fn error_event_error_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) = error_event_inner_of(this_val) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"ErrorEvent.prototype.error getter called on non-ErrorEvent object",
|
||
));
|
||
};
|
||
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)
|
||
}
|