Files
OxideJS-New/crates/oxide_vm/src/bindings/bind_worker.rs
T
2026-10-12 17:25:42 +00:00

260 lines
12 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Worker 构造器绑定:Box 分配构造器(不占 `BuiltinWorld` P 字段、不新增
//! `BuiltinId`、不占 `BuiltinDirtySet` 脏位)加 `track_leaked_object` 加填原型
//! 占位加全局槽,全槽 `lookup_position` 幂等守卫。
//!
//! 与 `bind_iterator_global` 同型:构造器经 `Box::into_raw` 登记 world 释放
//! 登记表(session 收尾统一释放),全局 `Worker` 槽经 `bind_constructor!` 原位
//! 安装(既有槽更新槽值、无槽新开)。
use crate::bind_constructor;
use crate::bindings::{
apply_binding_table, bind_accessor_getset, bind_accessor_getter, configure_native_constructor, BindEnv,
};
use oxide_kernel::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::string_forge::PermInterner;
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)与
/// `importScripts` 绑 global(worker realm 经 `self` === global 可达,
/// 主 realm 无 `self` 时为无害占位)。
///
/// `realm_id` 未使用(Worker 无 well-known 符号键),保留签名与其他绑定函数一致。
pub fn bind_worker(env: BindEnv<'_>, global: &mut JsObject) {
let world = env.world();
let sf = env.string_forge();
let sh = env.shape_forge();
// ---- 构造器本体(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(),
_ => {
// Worker.prototype 的 [[Prototype]] 指向 EventTarget.prototype
// (事件三方法经原型链可达;EventTarget 无构造器,仅暴露原型)。
let et_proto = world.event_target_proto.as_ptr() as *mut JsObject;
let proto = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(et_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(
env,
proto,
&[
("postMessage", crate::worker::bindings::worker_post_message as *const (), 1),
("terminate", crate::worker::bindings::worker_terminate as *const (), 0),
],
);
}
// onmessage / onerror / onmessageerror 访问器属性(getter 读注册表属性条目、
// setter 写)。幂等守卫。
for name in ["onmessage", "onerror", "onmessageerror"] {
let key = sf.intern(name).0;
if sh.lookup_position(proto.shape_id(), key).is_none() {
bind_accessor_getset(
env,
proto,
key,
&format!("get {name}"),
&format!("set {name}"),
crate::worker::bindings::worker_event_handler_getter as *const (),
crate::worker::bindings::worker_event_handler_setter as *const (),
);
}
}
}
// 全局 `Worker` 槽(bind_constructor! 自带既有槽原位更新、无槽新开)。
bind_constructor!(
env,
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(
env,
global,
&[
("postMessage", crate::worker::bindings::self_post_message as *const (), 1),
("close", crate::worker::bindings::self_close as *const (), 0),
],
);
}
// importScripts(worker 全局,逐 URL 同步读文件、编译、执行;主 realm 为无害占位)。
// 幂等守卫。
let si_import_scripts = sf.intern("importScripts").0;
if sh.lookup_position(global.shape_id(), si_import_scripts).is_none() {
apply_binding_table(env, global, &[("importScripts", crate::worker::bindings::import_scripts as *const (), 1)]);
}
// EventTarget 三方法直绑全局(worker realm 经 self === global 可达;全局原型链
// 是 Object.prototype,首版不改全局原型,三方法直绑全局等价可见)。幂等守卫。
let si_self_add = sf.intern("addEventListener").0;
if sh.lookup_position(global.shape_id(), si_self_add).is_none() {
apply_binding_table(
env,
global,
&[
(
"addEventListener",
oxide_builtins::event_target::event_target_add_event_listener::<crate::vm::Vm> as *const (),
3,
),
(
"removeEventListener",
oxide_builtins::event_target::event_target_remove_event_listener::<crate::vm::Vm> as *const (),
3,
),
(
"dispatchEvent",
oxide_builtins::event_target::event_target_dispatch_event::<crate::vm::Vm> as *const (),
1,
),
],
);
}
// self 全局属性:值为 global 对象本身(worker realm 经 self === global 可达,
// self.onmessage / self.postMessage 由此成立)。幂等守卫。
let si_self = sf.intern("self").0;
if sh.lookup_position(global.shape_id(), si_self).is_none() {
let global_ptr = global as *mut JsObject;
let shape = sh.make_shape(global.shape_id(), si_self);
global.set_shape_id(shape);
global.ensure_hash_props().push(JsValue::from_js_object(global_ptr));
let pos = global.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
global.set_data_meta(pos, PropAttributes::new(true, false, true));
global.bump_generation();
}
// onmessage / onmessageerror / onerror 直绑全局(访问器,worker realm 经
// self === global 可达,self.onmessage = fn 由此成立;主 realm 为无害占位)。
// 幂等守卫。
for name in ["onmessage", "onmessageerror", "onerror"] {
let key = sf.intern(name).0;
if sh.lookup_position(global.shape_id(), key).is_none() {
bind_accessor_getset(
env,
global,
key,
&format!("get {name}"),
&format!("set {name}"),
crate::worker::bindings::worker_event_handler_getter as *const (),
crate::worker::bindings::worker_event_handler_setter as *const (),
);
}
}
// name / location:worker realm 绑 getter(读 thread-local 真值,规范中
// WorkerGlobalScope.name 为只读访问器);主 realm 绑空串数据属性(与浏览器
// window.name 同一形状的无害占位,不挡全局 var name 赋值)。幂等守卫。
let worker_realm = crate::worker::bindings::worker_thread();
let si_name_global = sf.intern("name").0;
if sh.lookup_position(global.shape_id(), si_name_global).is_none() {
if worker_realm {
bind_accessor_getter(env, global, "name", crate::worker::bindings::self_name as *const ());
} else {
bind_global_empty_string_property(global, sf, sh, si_name_global);
}
}
let si_location = sf.intern("location").0;
if sh.lookup_position(global.shape_id(), si_location).is_none() {
if worker_realm {
bind_accessor_getter(env, global, "location", crate::worker::bindings::self_location as *const ());
} else {
bind_global_empty_string_property(global, sf, sh, si_location);
}
}
}
/// 在 global 上绑一个空串数据属性(主 realm 的 name / location 占位)。
fn bind_global_empty_string_property(global: &mut JsObject, sf: &PermInterner, sh: &ShapeForge, si: u32) {
let shape = sh.make_shape(global.shape_id(), si);
global.set_shape_id(shape);
global
.ensure_hash_props()
.push(JsValue::perm_string(sf.string_ptr(sf.intern("").0)));
let pos = global.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
global.set_data_meta(pos, PropAttributes::new(true, false, true));
global.bump_generation();
}