mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 10:41:11 +08:00
260 lines
12 KiB
Rust
260 lines
12 KiB
Rust
//! 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();
|
||
}
|