feat: SharedArrayBuffer 原型对象与构造器 meta 正式绑定

This commit is contained in:
2026-09-23 15:49:11 +00:00
parent dc09ccc1da
commit f2ac04ea13
6 changed files with 342 additions and 20 deletions
@@ -0,0 +1,57 @@
use std::sync::Arc;
use crate::bind_constructor;
use crate::bindings::{bind_accessor_getter, configure_native_constructor};
use oxide_kernel::kernel::{KernelCore, KernelSession};
use oxide_types::object::JsObject;
/// 把 SharedArrayBuffer 构造器与原型访问器绑定到 global。
pub fn bind_shared_array_buffer(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
let ctor_ptr = session.builtin_world().shared_array_buffer_constructor.as_ptr() as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr };
let proto_ptr = session.builtin_world().shared_array_buffer_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr };
configure_native_constructor(
ctor,
oxide_builtins::array_buffer::shared_array_buffer_constructor::<crate::vm::Vm> as *const (),
1,
);
// SAB 原型当前无静态方法(grow/growable 臂与 slice 归后续面);
// byteLength/maxByteLength 两枚访问器(set 恒 undefined)读载荷字节数。
bind_accessor_getter(
core,
session,
proto,
"byteLength",
oxide_builtins::array_buffer::shared_array_buffer_byte_length::<crate::vm::Vm> as *const (),
);
bind_accessor_getter(
core,
session,
proto,
"maxByteLength",
oxide_builtins::array_buffer::shared_array_buffer_max_byte_length::<crate::vm::Vm> as *const (),
);
// growable 定长臂:恒 false,品牌校验抛 TypeError(proto 自身访问即抛)。
bind_accessor_getter(
core,
session,
proto,
"growable",
oxide_builtins::array_buffer::shared_array_buffer_growable::<crate::vm::Vm> as *const (),
);
bind_constructor!(
core,
global,
"SharedArrayBuffer",
ctor_ptr,
oxide_builtins::array_buffer::shared_array_buffer_constructor::<crate::vm::Vm>,
1,
hash: true
);
}
+2 -7
View File
@@ -8,7 +8,7 @@ use oxide_types::value::JsValue;
use crate::bindings::{bind_global_value, configure_native_constructor};
const STUBS: [(&str, *const (), u8); 7] = [
const STUBS: [(&str, *const (), u8); 6] = [
("Proxy", oxide_builtins::stubs::proxy_stub::<crate::vm::Vm> as *const (), 2),
("WeakMap", oxide_builtins::stubs::weakmap_stub::<crate::vm::Vm> as *const (), 0),
("WeakSet", oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (), 0),
@@ -18,15 +18,10 @@ const STUBS: [(&str, *const (), u8); 7] = [
oxide_builtins::stubs::finalization_registry_stub::<crate::vm::Vm> as *const (),
1,
),
(
"SharedArrayBuffer",
oxide_builtins::array_buffer::shared_array_buffer_constructor::<crate::vm::Vm> as *const (),
1,
),
("Atomics", oxide_builtins::stubs::atomics_stub::<crate::vm::Vm> as *const (), 0),
];
/// 把未实现内置(Proxy/WeakMap/WeakSet/WeakRef/FinalizationRegistry/SharedArrayBuffer/Atomics)
/// 把未实现内置(Proxy/WeakMap/WeakSet/WeakRef/FinalizationRegistry/Atomics)
/// 的 stub 构造器绑定到 global,并登记到 `stub_objects` 供快照跟踪。
pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &mut JsObject) {
let builtin_world = Arc::get_mut(&mut session.builtin_world)
+16 -6
View File
@@ -44,6 +44,8 @@ pub mod bind_reflect;
pub mod bind_regexp;
/// Set 构造器与原型的 native 方法绑定。
pub mod bind_set;
/// SharedArrayBuffer 构造器与原型的 native 方法绑定。
pub mod bind_shared_array_buffer;
/// String 构造器与原型的 native 方法绑定。
pub mod bind_string;
/// 未实现内置(Proxy/BigInt/WeakMap 等)的 stub 构造器绑定。
@@ -632,9 +634,10 @@ fn install_to_string_tags(core: &Arc<KernelCore>, session: &KernelSession) {
let sh = core.shape_forge().as_ref();
let tag_id = oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG;
let tag_key = oxide_types::private_key::make_well_known_symbol_key(tag_id);
let cases: [(*mut JsObject, &str); 12] = [
let cases: [(*mut JsObject, &str); 13] = [
(world.map_proto.as_ptr() as *mut JsObject, "Map"),
(world.array_buffer_proto.as_ptr() as *mut JsObject, "ArrayBuffer"),
(world.shared_array_buffer_proto.as_ptr() as *mut JsObject, "SharedArrayBuffer"),
(world.set_proto.as_ptr() as *mut JsObject, "Set"),
(world.data_view_proto.as_ptr() as *mut JsObject, "DataView"),
(world.array_iterator_proto.as_ptr() as *mut JsObject, "Array Iterator"),
@@ -884,11 +887,6 @@ fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &m
oxide_builtins::stubs::finalization_registry_stub::<crate::vm::Vm> as *const (),
1,
),
(
"SharedArrayBuffer",
oxide_builtins::array_buffer::shared_array_buffer_constructor::<crate::vm::Vm> as *const (),
1,
),
("Atomics", oxide_builtins::stubs::atomics_stub::<crate::vm::Vm> as *const (), 0),
]
.into_iter()
@@ -923,6 +921,11 @@ pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession
oxide_builtins::array_buffer::array_buffer_constructor::<crate::vm::Vm> as *const (),
1,
);
configure_existing_ctor(
&world.shared_array_buffer_constructor,
oxide_builtins::array_buffer::shared_array_buffer_constructor::<crate::vm::Vm> as *const (),
1,
);
configure_existing_ctor(
&world.data_view_constructor,
oxide_builtins::data_view::data_view_constructor::<crate::vm::Vm> as *const (),
@@ -976,6 +979,10 @@ pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession
"ArrayBuffer",
JsValue::from_js_object(world.array_buffer_constructor.as_ptr() as *mut JsObject),
),
(
"SharedArrayBuffer",
JsValue::from_js_object(world.shared_array_buffer_constructor.as_ptr() as *mut JsObject),
),
("DataView", JsValue::from_js_object(world.data_view_constructor.as_ptr() as *mut JsObject)),
("Error", JsValue::from_js_object(world.error_constructor.as_ptr() as *mut JsObject)),
("String", JsValue::from_js_object(world.string_constructor.as_ptr() as *mut JsObject)),
@@ -1160,6 +1167,9 @@ pub fn rebind_dirty_builtins(core: &Arc<KernelCore>, session: &mut KernelSession
if dirty.map_or(true, |d| d.array_buffer) {
bind_array_buffer::bind_array_buffer(core, session, global);
}
if dirty.map_or(true, |d| d.shared_array_buffer) {
bind_shared_array_buffer::bind_shared_array_buffer(core, session, global);
}
if dirty.map_or(true, |d| d.data_view) {
bind_data_view::bind_data_view(core, session, global);
}
@@ -1158,3 +1158,167 @@ fn sab_constructor_and_brand_rejection() {
.unwrap();
assert!(result.as_bool());
}
/// 构造器 meta 面钉:length 值 1 描述符 {f,f,t},prototype {f,f,f} 值恒等,
/// prototype.constructor 互指 {1,0,1},proto 链挂 Object.prototype。
#[test]
fn sab_ctor_meta_descriptors() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"(function () { \
var l = Object.getOwnPropertyDescriptor(SharedArrayBuffer, 'length'); \
var p = Object.getOwnPropertyDescriptor(SharedArrayBuffer, 'prototype'); \
var c = Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'constructor'); \
return l.value === 1 && l.writable === false && l.enumerable === false && l.configurable === true \
&& p.value === SharedArrayBuffer.prototype \
&& p.writable === false && p.enumerable === false && p.configurable === false \
&& SharedArrayBuffer.prototype.constructor === SharedArrayBuffer \
&& c.writable === true && c.enumerable === false && c.configurable === true \
&& Object.getPrototypeOf(SharedArrayBuffer.prototype) === Object.prototype; })()",
)
.unwrap();
assert!(result.as_bool());
}
/// 原型 @@toStringTag 钉:值 "SharedArrayBuffer"、{f,f,t},Object.prototype
/// toString 经标签链读出 "[object SharedArrayBuffer]"。
#[test]
fn sab_proto_to_string_tag() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"(function () { \
var d = Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, Symbol.toStringTag); \
return d.value === 'SharedArrayBuffer' \
&& d.writable === false && d.enumerable === false && d.configurable === true \
&& Object.prototype.toString.call(new SharedArrayBuffer(0)) === '[object SharedArrayBuffer]'; })()",
)
.unwrap();
assert!(result.as_bool());
}
/// 实例 proto 挂回钉:getPrototypeOf === proto,无 own byteLength 数据属性,
/// 构造长度经原型访问器读回,访问器描述符 {get,set 缺,e:false,c:true}。
#[test]
fn sab_instance_proto_and_readback() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"(function () { \
var sab = new SharedArrayBuffer(42); \
var own = Object.getOwnPropertyDescriptor(sab, 'byteLength'); \
var d = Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'byteLength'); \
return Object.getPrototypeOf(sab) === SharedArrayBuffer.prototype \
&& SharedArrayBuffer.prototype.constructor === SharedArrayBuffer \
&& own === undefined \
&& sab.byteLength === 42 \
&& typeof d.get === 'function' && d.set === undefined \
&& d.enumerable === false && d.configurable === true; })()",
)
.unwrap();
assert!(result.as_bool());
}
/// receiver 品牌校验钉:get.call(undefined/{} / ArrayBuffer 实例) 均 TypeError。
#[test]
fn sab_bytelength_this_checks() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"(function () { \
var d = Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'byteLength'); \
var undef_threw = false, obj_threw = false, ab_threw = false; \
try { d.get.call(undefined); } catch (e) { undef_threw = e instanceof TypeError; } \
try { d.get.call({}); } catch (e) { obj_threw = e instanceof TypeError; } \
try { d.get.call(new ArrayBuffer(4)); } catch (e) { ab_threw = e instanceof TypeError; } \
return undef_threw && obj_threw && ab_threw; })()",
)
.unwrap();
assert!(result.as_bool());
}
/// maxByteLength 非 growable 臂钉:值同字节数,描述符 {get,set 缺,e:false,c:true},
/// getter name/length 元数据。
#[test]
fn sab_max_bytelength_descriptor_and_values() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"(function () { \
var sab8 = new SharedArrayBuffer(8); \
var sab0 = new SharedArrayBuffer(0); \
var d = Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'maxByteLength'); \
return sab8.maxByteLength === 8 && sab0.maxByteLength === 0 \
&& typeof d.get === 'function' && d.set === undefined \
&& d.enumerable === false && d.configurable === true \
&& d.get.name === 'get maxByteLength' && d.get.length === 0; })()",
)
.unwrap();
assert!(result.as_bool());
}
/// GetPrototypeFromConstructor 钉:newTarget.prototype 为对象时实例挂其原型;
/// prototype 属性非对象时回落 %SharedArrayBuffer.prototype%;getter 抛错原值传播。
#[test]
fn sab_new_target_proto_and_fallback() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"(function () { \
var c = Reflect.construct(SharedArrayBuffer, [8], Object); \
if (Object.getPrototypeOf(c) !== Object.prototype) { return false; } \
var D = function () {}; \
D.prototype = null; \
var d = Reflect.construct(SharedArrayBuffer, [4], D); \
if (Object.getPrototypeOf(d) !== SharedArrayBuffer.prototype) { return false; } \
var E = function () {}; \
Object.defineProperty(E, 'prototype', { get: function () { throw new RangeError('x'); } }); \
var threw = false; \
try { Reflect.construct(SharedArrayBuffer, [1], E); } catch (e) { threw = e instanceof RangeError; } \
return threw; })()",
)
.unwrap();
assert!(result.as_bool());
}
/// full_reset(dirty 重建)重绑钉:重建后构造、实例读回、proto 互指、
/// toStringTag 标签全部存活。
#[test]
fn sab_full_reset_rebinds() {
let mut vm = Vm::new();
eval(&mut vm, "new SharedArrayBuffer(4).byteLength; 0").expect("run1");
vm.full_reset();
let result = eval(
&mut vm,
"(function () { \
var sab = new SharedArrayBuffer(4); \
return (sab instanceof SharedArrayBuffer) + '|' \
+ (sab.byteLength === 4) + '|' \
+ (SharedArrayBuffer.prototype.constructor === SharedArrayBuffer) + '|' \
+ (sab[Symbol.toStringTag] === 'SharedArrayBuffer'); })()",
)
.unwrap();
let text = vm.lookup_str(result).unwrap_or_default().to_string();
assert_eq!(text, "true|true|true|true");
}
/// STUBS 表收缩钉:剩余 6 枚 stub 全局仍为 function 且 Atomics 调用仍抛
/// TypeError(占位行为未随 SAB 移出而漂移)。
#[test]
fn sab_stubs_shrunk_six_remain() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"(function () { \
var names = ['Proxy', 'WeakMap', 'WeakSet', 'WeakRef', 'FinalizationRegistry', 'Atomics']; \
for (var i = 0; i < names.length; i++) { \
if (typeof globalThis[names[i]] !== 'function') { return false; } \
} \
var threw = false; \
try { Atomics.add(new SharedArrayBuffer(4), 0, 1); } catch (e) { threw = e instanceof TypeError; } \
return threw; })()",
)
.unwrap();
assert!(result.as_bool());
}