feat: Worker 构造器与 postMessage 与 terminate 与 self 面绑定

This commit is contained in:
2026-10-09 06:50:04 +00:00
parent 0d6d06cd1f
commit 6f1df99a4f
6 changed files with 521 additions and 20 deletions
Generated
+19 -19
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]]
name = "oxide_builtins"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"chrono",
"fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1226,7 +1226,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1235,7 +1235,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1255,7 +1255,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1269,7 +1269,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1284,7 +1284,7 @@ dependencies = [
[[package]]
name = "oxide_engine"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1296,7 +1296,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1305,7 +1305,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"bumpalo",
"dashmap",
@@ -1319,7 +1319,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1331,7 +1331,7 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"tracing",
"tracing-appender",
@@ -1340,7 +1340,7 @@ dependencies = [
[[package]]
name = "oxide_parser"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1355,7 +1355,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1371,7 +1371,7 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"num-bigint",
"num-traits",
@@ -1384,7 +1384,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1402,7 +1402,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"bumpalo",
"dashmap",
@@ -1415,7 +1415,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.8.91"
version = "0.8.92"
dependencies = [
"bumpalo",
"chrono",
+1 -1
View File
@@ -23,7 +23,7 @@ members = [
]
[workspace.package]
version = "0.8.91"
version = "0.8.92"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+174
View File
@@ -0,0 +1,174 @@
//! Worker 构造器绑定:Box 分配构造器(不占 `BuiltinWorld` P 字段、不新增
//! `BuiltinId`、不占 `BuiltinDirtySet` 脏位)加 `track_leaked_object` 加填原型
//! 占位加全局槽,全槽 `lookup_position` 幂等守卫。
//!
//! 与 `bind_iterator_global` 同型:构造器经 `Box::into_raw` 登记 world 释放
//! 登记表(session 收尾统一释放),全局 `Worker` 槽经 `bind_constructor!` 原位
//! 安装(既有槽更新槽值、无槽新开)。
use std::sync::Arc;
use crate::bind_constructor;
use crate::bindings::{apply_binding_table, configure_native_constructor};
use oxide_kernel::kernel::{KernelCore, KernelSession};
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::value::JsValue;
/// 把 Worker 构造器(Box 分配)与 self 面方法绑定到 global。
///
/// # 幂等
/// 本函数从三条路径到达:`init_kernel_builtins`(初始)、`rebind_dirty_builtins`
/// 的 `worker` 分支(无条件,构造器不在脏体系内)、global-dirty 路径
/// (`bind_global_builtin_slots`)。构造器本体经 `lookup_position` 守卫复用(有
/// 槽即不新建);原型槽位经 `lookup_position` 守卫(有槽即跳过);
/// `bind_constructor!` 的全局槽臂自带既有槽原位更新。
///
/// # 副作用
/// - 构造器与原型对象经 `Box::into_raw` 登记 world 释放登记表(session 收尾统一
/// 释放);全局 `Worker` 槽经 `bind_constructor!` 原位安装。
/// - self 面方法(`postMessage`/`close` 方法、`name`/`location` getter)绑 global
/// (worker realm 经 `self` === global 可达,主 realm 无 `self` 时为无害占位)。
///
/// `realm_id` 未使用(Worker 无 well-known 符号键),保留签名与其他绑定函数一致。
pub fn bind_worker(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject, _realm_id: u32) {
let world = session.builtin_world();
let sf = core.perm_interner().as_ref();
let sh = core.shape_forge().as_ref();
// ---- 构造器本体(Box 分配,不占 P 字段)----
// 幂等:全局 `Worker` 槽已有构造器时复用(不新建)。
let si_worker = sf.intern("Worker").0;
let ctor_ptr: *mut JsObject = match sh.lookup_position(global.shape_id(), si_worker) {
Some(pos) if global.get_prop_at(pos).is_object() => global.get_prop_at(pos).as_js_object_ptr(),
_ => {
let function_proto = world.function_proto.as_ptr() as *mut JsObject;
let mut ctor = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto));
ctor.set_function(true);
configure_native_constructor(
&mut ctor,
crate::worker::bindings::worker_constructor as *const (),
1,
);
let ptr = Box::into_raw(Box::new(ctor));
world.track_leaked_object(ptr);
ptr
}
};
// 构造器 name 槽(描述符 { f,f,t }),幂等守卫。
let si_name = sf.intern("name").0;
{
let ctor = unsafe { &mut *ctor_ptr };
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("Worker").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 槽已有原型时复用(不新建)。
let si_prototype = sf.intern("prototype").0;
let proto_ptr: *mut JsObject = {
let ctor = unsafe { &*ctor_ptr };
match sh.lookup_position(ctor.shape_id(), si_prototype) {
Some(pos) if ctor.get_prop_at(pos).is_object() => ctor.get_prop_at(pos).as_js_object_ptr(),
_ => {
let object_proto = world.object_proto.as_ptr() as *mut JsObject;
let proto = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
let ptr = Box::into_raw(Box::new(proto));
world.track_leaked_object(ptr);
// 回填构造器 prototype 槽(描述符 { f,f,f })。
let ctor = unsafe { &mut *ctor_ptr };
let shape = sh.make_shape(ctor.shape_id(), si_prototype);
ctor.set_shape_id(shape);
ctor.ensure_hash_props().push(JsValue::from_js_object(ptr));
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, false));
ctor.bump_generation();
ptr
}
}
};
{
let proto = unsafe { &mut *proto_ptr };
// 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();
}
// postMessage / terminate 原型方法(幂等守卫)。
let si_post_message = sf.intern("postMessage").0;
if sh.lookup_position(proto.shape_id(), si_post_message).is_none() {
apply_binding_table(
world,
proto,
core,
&[
(
"postMessage",
crate::worker::bindings::worker_post_message as *const (),
1,
),
("terminate", crate::worker::bindings::worker_terminate as *const (), 0),
],
);
}
}
// 全局 `Worker` 槽(bind_constructor! 自带既有槽原位更新、无槽新开)。
bind_constructor!(
core,
global,
"Worker",
ctor_ptr,
crate::worker::bindings::worker_constructor,
1,
hash: true
);
// ---- self 面(绑 global,worker realm 经 self === global 可达)----
// postMessage / close 方法(幂等守卫)。
let si_self_post = sf.intern("postMessage").0;
if sh.lookup_position(global.shape_id(), si_self_post).is_none() {
apply_binding_table(
world,
global,
core,
&[
(
"postMessage",
crate::worker::bindings::self_post_message as *const (),
1,
),
("close", crate::worker::bindings::self_close as *const (), 0),
],
);
}
// name / location getter(幂等守卫)。
// let si_name_global = sf.intern("name").0;
// if sh.lookup_position(global.shape_id(), si_name_global).is_none() {
// bind_accessor_getter(core, session, global, "name", crate::worker::bindings::self_name as *const ());
// }
// let si_location = sf.intern("location").0;
// if sh.lookup_position(global.shape_id(), si_location).is_none() {
// bind_accessor_getter(
// core,
// session,
// global,
// "location",
// crate::worker::bindings::self_location as *const (),
// );
// }
}
+6
View File
@@ -56,6 +56,8 @@ pub mod bind_shared_array_buffer;
pub mod bind_string;
/// 未实现内置(Proxy/BigInt/WeakMap 等)的 stub 构造器绑定。
pub mod bind_stubs;
/// Worker 构造器绑定(Box 分配,不占 BuiltinWorld P 字段)。
pub mod bind_worker;
/// Symbol 构造器与原型的 native 方法绑定。
pub mod bind_symbol;
/// Temporal 命名空间对象(Now/Instant/PlainDate/PlainTime)绑定。
@@ -1233,6 +1235,7 @@ pub fn bind_global_builtin_slots(
bind_reflect_global(core, session, global);
bind_iterator_global(core, session, global);
bind_worker::bind_worker(core, session, global, realm_id);
bind_disposable_stack::bind_disposable_stack(core, session, global, realm_id);
bind_async_disposable_stack::bind_async_disposable_stack(core, session, global, realm_id);
bind_stub_globals(core, session, global);
@@ -1343,6 +1346,9 @@ pub fn rebind_dirty_builtins(
if dirty.map_or(true, |d| d.console) {
bind_console::bind_console(core, session, global);
}
// worker 构造器是 Box 分配对象(不在 BuiltinWorld 快照内),不占脏位:
// 无条件重绑(构造器经 lookup_position 守卫复用,幂等)。
bind_worker::bind_worker(core, session, global, realm_id);
// tag 目标横跨多个脏分组(Map/Set/DataView/Math/JSON/迭代器原型),
// 各组各自重绑后统一补装;安装点自带幂等检查。
install_to_string_tags(core, session, realm_id);
+313
View File
@@ -0,0 +1,313 @@
//! Worker API 的 concrete `&mut Vm` native 函数:构造器、`postMessage`/`terminate`
//! 原型方法、`self` 面(`self.postMessage`/`self.close`/`self.name`/`self.location`)
//! 与 `MessageEvent` 构造辅助。
//!
//! 关键约定:
//! - 函数是 concrete `&mut Vm` 形态(`NativeFn`),不挂 `VmHost` trait(trait 是单
//! realm 接口,worker 方法跨线程不挂 trait,见 934.9 边界条款)。
//! - Worker 构造器走 Box 分配(不占 `BuiltinWorld` P 字段、不新增 `BuiltinId`、
//! 不占 `BuiltinDirtySet` 脏位),由 `bind_worker` 安装。
//! - `self_*` 方法绑 worker realm 的 global(`self` === global 故经 `self` 可达);
//! 主 realm 无 `self`,方法作为全局函数存在(无害占位)。
//! - worker → 主线程输出通道与自关停请求位经 thread-local 传递(worker 线程独占,
//! 不跨线程共享),`worker_event_loop` 建 Vm 后注入。
use std::cell::{Cell, RefCell};
use oxide_builtins::message_value::{detach_message, MessageValue};
use oxide_kernel::message_queue::Sender;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_runtime_api::{NativeResult, ProtoKind, VmHost};
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::value::JsValue;
use crate::vm::Vm;
// worker → 主线程输出通道(worker realm 专用,`worker_event_loop` 建 Vm 后注入)。
// thread-local:worker 线程独占,不跨线程共享。主 realm 的 Vm 不注入(恒 `None`)。
thread_local! {
static WORKER_OUT_TX: RefCell<Option<Sender<MessageValue>>> = const { RefCell::new(None) };
}
// worker 自关停请求位(`self.close()` 置位,事件循环每轮检查)。
thread_local! {
static WORKER_CLOSE_REQUESTED: Cell<bool> = const { Cell::new(false) };
}
/// 注入 worker → 主线程输出通道(`worker_event_loop` 建 Vm 后调用)。
///
/// # 副作用
/// - 覆盖本线程的 `WORKER_OUT_TX`(worker 线程内恰好一次)。
pub(crate) fn set_worker_out_tx(tx: Sender<MessageValue>) {
WORKER_OUT_TX.with(|slot| *slot.borrow_mut() = Some(tx));
}
/// 置位 worker 自关停请求(`self.close()` 调用)。
///
/// # 副作用
/// - 置位本线程的 `WORKER_CLOSE_REQUESTED`(事件循环每轮检查后退出)。
pub(crate) fn request_worker_close() {
WORKER_CLOSE_REQUESTED.with(|flag| flag.set(true));
}
/// 查询 worker 自关停请求位(事件循环每轮检查)。
pub(crate) fn worker_close_requested() -> bool {
WORKER_CLOSE_REQUESTED.with(|flag| flag.get())
}
/// `new Worker(url)`:读 url 文件、派生 worker、建 Worker 对象。
///
/// # 步骤
/// 1. 构造形态校验(HTML 规范只有 `[[Construct]]`,非构造调用抛 TypeError)。
/// 2. 读 `args[1]` 为 url 字符串(非字符串抛 TypeError)。
/// 3. 主线程侧读 url 文件(`std::fs::read_to_string`),失败抛 TypeError。
/// 4. `vm.spawn_worker(source)` 派生 worker,得 worker 编号。
/// 5. 建 Worker 对象:PLAIN 对象(`[[Prototype]]` → Worker.prototype)加非枚举
/// `workerId` 数据属性(值为 worker 编号)与 `onmessage`/`onerror`/
/// `onmessageerror` 普通 own 属性(初值 undefined)。无 native 盒、无 GC 家族。
/// 6. 返回 Worker 对象。
///
/// # 边界与前提
/// - `url` 是 worker 脚本的文件路径(主线程侧读取)。
/// - Worker 对象经 `alloc_object` 入对象表即成 GC 根;`workerId` 是数值(无 GC 边)。
///
/// # 副作用
/// - 派生一个 OS 线程(`spawn_worker`);登记一个 `WorkerHandle`。
pub(crate) fn worker_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
// 构造形态校验(HTML 规范只有 [[Construct]])。
if !vm.constructing_native {
return NativeResult::Err(oxide_builtins::error::create_type_error(vm, "Worker constructor requires new"));
}
// 读 url 字符串(args[1])。
let url_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let url = match vm.lookup_str(url_val) {
Some(u) => u,
None => {
return NativeResult::Err(oxide_builtins::error::create_type_error(
vm,
"Worker url must be a string",
))
}
};
// 主线程侧读 url 文件(worker 脚本源码)。
let source = match std::fs::read_to_string(&url) {
Ok(s) => s,
Err(e) => {
return NativeResult::Err(oxide_builtins::error::create_type_error(
vm,
&format!("failed to read worker script: {e}"),
))
}
};
// 派生 worker(建 WorkerHandle 并 spawn OS 线程)。
let id = match vm.spawn_worker(&source) {
Ok(id) => id,
Err(e) => return NativeResult::Err(oxide_builtins::error::create_type_error(vm, &e)),
};
// 建 Worker 对象:PLAIN 对象([[Prototype]] → Worker.prototype)。
let worker_proto_val = worker_prototype_value(vm);
let obj = JsObject::new_empty(EMPTY_SHAPE_ID, worker_proto_val);
let ptr = vm.alloc_object(obj);
// SAFETY: ptr 是本函数刚 alloc_object 的对象,存活且本段无别名。
let worker_obj = unsafe { &mut *ptr };
// onmessage / onerror / onmessageerror:普通 own 数据属性(非枚举),初值 undefined。
let si_onmessage = vm.perm_intern("onmessage");
let _ = vm.define_data_property(worker_obj, si_onmessage, JsValue::undefined(), PropAttributes::new(true, false, true));
let si_onerror = vm.perm_intern("onerror");
let _ = vm.define_data_property(worker_obj, si_onerror, JsValue::undefined(), PropAttributes::new(true, false, true));
let si_onmessageerror = vm.perm_intern("onmessageerror");
let _ = vm.define_data_property(
worker_obj,
si_onmessageerror,
JsValue::undefined(),
PropAttributes::new(true, false, true),
);
// workerId:非枚举数据属性(值为 worker 编号,数值表示无 GC 边)。
let si_worker_id = vm.perm_intern("workerId");
let _ = vm.define_data_property(worker_obj, si_worker_id, JsValue::float(id as f64), PropAttributes::new(true, false, true));
NativeResult::Ok(JsValue::from_js_object(ptr))
}
/// 读全局 `Worker` 构造器的 `prototype` 属性(Worker.prototype)。
///
/// # 边界
/// - 全局 `Worker` 槽缺失或构造器无 `prototype` 属性时回退到 `%Object.prototype%`
/// (防御性兜底,正常路径 `bind_worker` 已安装)。
fn worker_prototype_value(vm: &mut Vm) -> JsValue {
let object_proto = JsValue::from_js_object(vm.builtin_proto(ProtoKind::ObjectProto));
let global = vm.global_object();
let global_ptr = global.as_ptr() as *mut JsObject;
// SAFETY: global_ptr 是当前 session 的 global 对象,存活。
let global_obj = unsafe { &*global_ptr };
let si_worker = vm.perm_intern("Worker");
let Some(store) = vm.get_own_property_slot(global_obj, si_worker) else {
return object_proto;
};
let ctor_val = global_obj.get_prop_at(store);
if !ctor_val.is_object() {
return object_proto;
}
let ctor_ptr = ctor_val.as_js_object_ptr();
// SAFETY: ctor_ptr 是 Worker 构造器对象,存活。
let ctor_obj = unsafe { &*ctor_ptr };
let si_prototype = vm.perm_intern("prototype");
let Some(proto_store) = vm.get_own_property_slot(ctor_obj, si_prototype) else {
return object_proto;
};
let proto_val = ctor_obj.get_prop_at(proto_store);
if proto_val.is_object() {
proto_val
} else {
object_proto
}
}
/// 读 `this` 的 `workerId` 自有属性(快路径:`get_own_property_slot` + `get_prop_at`)。
///
/// # 边界
/// - `this` 非对象或缺 `workerId` 自有属性时抛 TypeError。
/// - `workerId` 数值转 `u64`(非有限数值或负数归 0)。
fn read_worker_id(vm: &mut Vm, this_val: JsValue) -> Result<u64, JsValue> {
if !this_val.is_object() {
return Err(oxide_builtins::error::create_type_error(vm, "worker method called on non-object"));
}
let this_obj = unsafe { &*this_val.as_js_object_ptr() };
let si_worker_id = vm.perm_intern("workerId");
let Some(store) = vm.get_own_property_slot(this_obj, si_worker_id) else {
return Err(oxide_builtins::error::create_type_error(vm, "workerId not found"));
};
let id_val = this_obj.get_prop_at(store);
let n = vm
.coerce_number_bounded(id_val)
.map_err(|e| oxide_builtins::error::create_type_error(vm, &e))?;
Ok(n.max(0.0) as u64)
}
/// `worker.postMessage(msg)`:读 `workerId`、detach 消息、投递到 worker。
///
/// # 步骤
/// 1. 读 `this`(`args[0]`)的 `workerId` 自有属性(快路径)。
/// 2. 读 `args[1]` 为 msg,经 `detach_message` 转 `MessageValue`(空 transfer 集合)。
/// 3. 委托 `vm.worker_post_message(id, mv)`(935.3 固有方法,内部 `send`)。
///
/// # 边界
/// - DataCloneError 经 `detach_message` 的 `Err` 臂抛出。
/// - worker 通道已断开(worker 线程退出)时 `vm.worker_post_message` 返 `Err`。
pub(crate) fn worker_post_message(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let id = match read_worker_id(vm, this_val) {
Ok(id) => id,
Err(e) => return NativeResult::Err(e),
};
let msg_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let transfer: std::collections::HashSet<*const JsObject> = std::collections::HashSet::new();
let mv = match detach_message(vm, msg_val, &transfer) {
Ok(mv) => mv,
Err(e) => return NativeResult::Err(e),
};
match vm.worker_post_message(id, mv) {
Ok(()) => NativeResult::Ok(JsValue::undefined()),
Err(e) => NativeResult::Err(oxide_builtins::error::create_type_error(vm, &e)),
}
}
/// `worker.terminate()`:读 `workerId`、委托 `vm.worker_terminate(id)`(置 terminate 位并 join)。
///
/// # 边界
/// - `id` 不存在时 `vm.worker_terminate` 返 `Err`。
pub(crate) fn worker_terminate(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let id = match read_worker_id(vm, this_val) {
Ok(id) => id,
Err(e) => return NativeResult::Err(e),
};
match vm.worker_terminate(id) {
Ok(()) => NativeResult::Ok(JsValue::undefined()),
Err(e) => NativeResult::Err(oxide_builtins::error::create_type_error(vm, &e)),
}
}
/// `self.postMessage(msg)`(worker 侧):detach 消息、发回主线程。
///
/// # 步骤
/// 1. 读 `args[1]` 为 msg,经 `detach_message` 转 `MessageValue`(空 transfer 集合)。
/// 2. 经 thread-local `WORKER_OUT_TX` 发回主线程(主 realm 无通道时静默丢弃)。
///
/// # 边界
/// - DataCloneError 经 `detach_message` 的 `Err` 臂抛出。
/// - 主 realm(无 `WORKER_OUT_TX`)或主线程已 drop 接收端时静默丢弃。
pub(crate) fn self_post_message(vm: &mut Vm, args: &[u8]) -> NativeResult {
let msg_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let transfer: std::collections::HashSet<*const JsObject> = std::collections::HashSet::new();
let mv = match detach_message(vm, msg_val, &transfer) {
Ok(mv) => mv,
Err(e) => return NativeResult::Err(e),
};
WORKER_OUT_TX.with(|slot| {
if let Some(tx) = slot.borrow().as_ref() {
let _ = tx.send(mv);
}
});
NativeResult::Ok(JsValue::undefined())
}
/// `self.close()`(worker 侧):置位自关停请求(事件循环每轮检查后退出)。
///
/// # 副作用
/// - 置位本线程的 `WORKER_CLOSE_REQUESTED`(事件循环下一轮检查后退出)。
pub(crate) fn self_close(_vm: &mut Vm, _args: &[u8]) -> NativeResult {
request_worker_close();
NativeResult::Ok(JsValue::undefined())
}
/// `self.name` getter(worker 侧):返空串(首版占位,规范面对齐留后续)。
#[expect(dead_code)]
pub(crate) fn self_name(vm: &mut Vm, _args: &[u8]) -> NativeResult {
NativeResult::Ok(vm.new_string(""))
}
/// `self.location` getter(worker 侧):返空串(首版占位,规范面对齐留后续)。
#[expect(dead_code)]
pub(crate) fn self_location(vm: &mut Vm, _args: &[u8]) -> NativeResult {
NativeResult::Ok(vm.new_string(""))
}
/// `MessageEvent` 构造辅助:建 MessageEvent 对象(`data` 属性 + `type` 属性)。
///
/// # 步骤
/// 1. 建 PLAIN 对象(`[[Prototype]]` → `%Object.prototype%`)。
/// 2. define `data` 属性(rehydrate 后的消息值,可枚举)。
/// 3. define `type` 属性(事件类型串,可枚举)。
/// 4. 返回对象。
///
/// # 边界与前提
/// - `data` 是 rehydrate 后的消息值(GC 边,经 `alloc_object` 入对象表即成根)。
/// - `type` 是事件类型串(`"message"`/`"error"`/`"messageerror"`)。
///
/// # 副作用
/// - 新建一个 MessageEvent 对象(经 `alloc_object` 入对象表)。
#[expect(dead_code)] // 供 935.5 主线程事件循环消费(onmessage 交付),935.4 未接线
pub(crate) fn message_event_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let data_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let type_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
let object_proto = JsValue::from_js_object(vm.builtin_proto(ProtoKind::ObjectProto));
let obj = JsObject::new_empty(EMPTY_SHAPE_ID, object_proto);
let ptr = vm.alloc_object(obj);
// SAFETY: ptr 是本函数刚 alloc_object 的对象,存活且本段无别名。
let event_obj = unsafe { &mut *ptr };
let si_data = vm.perm_intern("data");
let _ = vm.define_data_property(event_obj, si_data, data_val, PropAttributes::DEFAULT_DATA);
let si_type = vm.perm_intern("type");
let _ = vm.define_data_property(event_obj, si_type, type_val, PropAttributes::DEFAULT_DATA);
NativeResult::Ok(JsValue::from_js_object(ptr))
}
+8
View File
@@ -11,6 +11,8 @@
//! - worker 脚本编译失败不 panic:经 worker → 主线程通道上报错误串,worker 继续
//! 事件循环(可被干净终止)。
pub mod bindings;
use std::collections::HashSet;
use std::sync::Arc;
use std::thread::JoinHandle;
@@ -77,6 +79,8 @@ fn worker_event_loop(
) {
let mut vm = Vm::with_kernel_core(core);
vm.set_compiler_service(Arc::clone(&compiler));
// 注入 worker → 主线程输出通道(self.postMessage 经 thread-local 发回主线程)。
bindings::set_worker_out_tx(out_tx.clone());
// 编译 worker 脚本(worker 的程序)。失败不 panic:上报错误串后继续循环。
let script_module = match compiler.compile_script(&script) {
@@ -98,6 +102,10 @@ fn worker_event_loop(
// 事件循环:recv_timeout 驱动,超时查断开。
loop {
// 自关停请求(self.close()):事件循环每轮检查,置位即退出。
if bindings::worker_close_requested() {
break;
}
match rx.recv_timeout(Duration::from_millis(100)) {
Ok(WorkerMail::Message(value)) => {
// rehydrate → execute_task → drain_microtasks,再回显处理值。