mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 11:11:11 +08:00
feat: ErrorEvent 载荷盒加 GC 加构造器加原型加 onerror 整合
This commit is contained in:
@@ -316,41 +316,32 @@ pub(crate) fn message_event_constructor(vm: &mut Vm, args: &[u8]) -> NativeResul
|
||||
NativeResult::Ok(JsValue::from_js_object(ptr))
|
||||
}
|
||||
|
||||
/// 错误事件构造辅助(临时形态):建真实 Event 盒(`type` 字段)加 `data` /
|
||||
/// `message` 普通属性。
|
||||
/// 错误事件构造辅助:建真实 ErrorEvent 盒(`message` 为错误串、`filename`
|
||||
/// 空串、`lineno` 与 `colno` 为 0、`error` 为 null)。
|
||||
///
|
||||
/// 错误路径(`deliver_error_event`)经本辅助建错误事件:`data` 为 undefined、
|
||||
/// `message` 为错误串,供 onerror 处理器读取。
|
||||
/// 错误路径(`deliver_error_event`)经本辅助建真实 ErrorEvent,
|
||||
/// `e instanceof ErrorEvent` 与 `e instanceof Event` 均成立,`e.message`
|
||||
/// 经原型 getter 读盒字段。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 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. 返回对象。
|
||||
/// 2. 委托 `create_error_event_box` 建真实 ErrorEvent 盒(`[[Prototype]]` →
|
||||
/// `ErrorEvent.prototype`),使 `dispatchEvent` 品牌守卫通过。
|
||||
/// 3. 返回对象。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `type` 是事件类型串(`"error"`/`"messageerror"`)。
|
||||
/// - `message` 是错误消息串。
|
||||
/// - `message` 是错误消息串(GC 边,经盒边入 mark)。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 新建一个 Event 盒对象(经 `alloc_object` 入对象表)。
|
||||
/// - 新建一个 ErrorEvent 盒对象(经 `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 的字符串形态)。
|
||||
// 建真实 ErrorEvent 盒(type 与 message 取实参的字符串形态)。
|
||||
let type_text = vm.lookup_str(type_val).unwrap_or_default();
|
||||
let ptr = oxide_builtins::event::create_event_box(vm, &type_text);
|
||||
// SAFETY: ptr 是 create_event_box 刚 alloc_object 的对象,存活且本段无别名。
|
||||
let event_obj = unsafe { &mut *ptr };
|
||||
|
||||
let si_data = vm.perm_intern("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);
|
||||
|
||||
let message_text = vm.lookup_str(message_val).unwrap_or_default();
|
||||
let ptr = oxide_builtins::event::create_error_event_box(vm, &message_text, &type_text);
|
||||
NativeResult::Ok(JsValue::from_js_object(ptr))
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ impl Vm {
|
||||
/// 交付错误事件:建事件、经 `dispatchEvent` 交付(注册表查监听器)。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 建错误事件(`type` = `event_type`、`message` = `message`、`data` = undefined)。
|
||||
/// 1. 建错误事件(`type` = `event_type`、`message` = `message`)。
|
||||
/// 2. 经 `dispatch_event_on` 交付到 `worker_obj`,后 `drain_microtasks`。
|
||||
///
|
||||
/// # 边界与前提
|
||||
@@ -437,8 +437,9 @@ impl Vm {
|
||||
}
|
||||
}
|
||||
|
||||
/// 错误路径建事件对象(临时形态):建真实 Event 盒(`type` 字段)加
|
||||
/// `data` / `message` 普通属性,委托 `build_error_event` 构造辅助。
|
||||
/// 错误路径建事件对象:建真实 ErrorEvent(`message` 为错误串、`filename`
|
||||
/// 空串、`lineno` 与 `colno` 为 0、`error` 为 null),委托
|
||||
/// `build_error_event` 构造辅助。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. `type` 与 `message` 串物化为 session 串。
|
||||
@@ -449,7 +450,7 @@ impl Vm {
|
||||
/// 新建的事件对象值(构造失败时返回错误对象值)。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 新建一个 Event 盒对象(经 `alloc_object` 入对象表)。
|
||||
/// - 新建一个 ErrorEvent 盒对象(经 `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);
|
||||
@@ -829,6 +830,49 @@ mod tests {
|
||||
let _ = std::fs::remove_file(&worker_path);
|
||||
}
|
||||
|
||||
/// 端到端:worker 脚本编译失败,主线程 onerror 收到真实 ErrorEvent
|
||||
/// (e instanceof ErrorEvent 与 e instanceof Event 均真)。
|
||||
#[test]
|
||||
fn worker_e2e_onerror_is_error_event() {
|
||||
let mut vm = vm_with_compiler();
|
||||
|
||||
// worker 脚本:语法错误(编译失败)。
|
||||
let worker_path = write_worker_script("function { 语法错误");
|
||||
// 主脚本:建 Worker、设 addEventListener('error'),记录 instanceof 结果。
|
||||
let main_script = format!(
|
||||
"var w = new Worker('{}'); w.addEventListener('error', function(e) {{ globalThis.isEE = e instanceof ErrorEvent; globalThis.isE = e instanceof Event; }});",
|
||||
worker_path.display()
|
||||
);
|
||||
vm.run(&Arc::new(compile_script(&main_script))).expect("主脚本应运行");
|
||||
|
||||
// 事件循环轮询至 globalThis.isEE 被设置(带截止,防 flaky)。
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
loop {
|
||||
vm.deliver_worker_messages();
|
||||
vm.cleanup_disconnected_workers();
|
||||
if global_value_opt(&vm, "isEE").is_some() {
|
||||
break;
|
||||
}
|
||||
if Instant::now() >= deadline {
|
||||
panic!("5 秒内 onerror 未被调用");
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
assert_eq!(
|
||||
global_value_opt(&vm, "isEE"),
|
||||
Some(JsValue::bool(true)),
|
||||
"事件应 instanceof ErrorEvent"
|
||||
);
|
||||
assert_eq!(
|
||||
global_value_opt(&vm, "isE"),
|
||||
Some(JsValue::bool(true)),
|
||||
"事件应 instanceof Event"
|
||||
);
|
||||
|
||||
vm.shutdown_workers();
|
||||
let _ = std::fs::remove_file(&worker_path);
|
||||
}
|
||||
|
||||
/// onmessageerror 交付辅助:无自然触发,直接单测交付辅助(handler 被调用、
|
||||
/// 事件 type 为 "messageerror")。不钉触发可达性(当前架构不可达)。
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user