mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: SharedArrayBuffer 最小构造器与载荷生命周期
This commit is contained in:
@@ -159,6 +159,88 @@ pub fn drop_array_buffer_native(obj: &mut JsObject) -> u64 {
|
||||
bytes
|
||||
}
|
||||
|
||||
/// 分配定长 SharedArrayBuffer 对象(零填充字节缓冲,`max_byte_length` 存 0)。
|
||||
pub(crate) fn new_shared_array_buffer<H: VmHost>(vm: &mut H, data: Vec<u8>) -> *mut JsObject {
|
||||
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null());
|
||||
obj.type_tag = JsObject::OBJ_TYPE_SHARED_ARRAY_BUFFER;
|
||||
let payload = ArrayBufferPayload {
|
||||
data: Some(data),
|
||||
max_byte_length: 0,
|
||||
immutable: false,
|
||||
};
|
||||
let payload_ptr = Box::into_raw(Box::new(payload));
|
||||
// SAFETY: SharedArrayBuffer 对象不可调用,native_fn 槽复用为不透明载荷盒
|
||||
// 指针,与 ArrayBuffer 存储模式一致。
|
||||
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(payload_ptr as *const ()) }));
|
||||
vm.alloc_object(obj)
|
||||
}
|
||||
|
||||
pub(crate) fn shared_array_buffer_payload_ptr(obj: &JsObject) -> Option<*mut ArrayBufferPayload> {
|
||||
if !obj.is_shared_array_buffer_obj() {
|
||||
return None;
|
||||
}
|
||||
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut ArrayBufferPayload)
|
||||
}
|
||||
|
||||
/// SharedArrayBuffer 载荷盒字节数(`native_fn` 槽);非 SAB 或已释放 → 0。
|
||||
pub fn shared_array_buffer_native_size(obj: &JsObject) -> u64 {
|
||||
let payload_ptr = match shared_array_buffer_payload_ptr(obj) {
|
||||
Some(p) => p,
|
||||
None => return 0,
|
||||
};
|
||||
if payload_ptr.is_null() {
|
||||
return 0;
|
||||
}
|
||||
// SAFETY: payload_ptr 非空且指向存活载荷盒。
|
||||
let payload = unsafe { &*payload_ptr };
|
||||
(std::mem::size_of::<ArrayBufferPayload>() + payload.data.as_ref().map_or(0, |d| d.capacity())) as u64
|
||||
}
|
||||
|
||||
/// 释放 SharedArrayBuffer 载荷盒(native_fn 槽),返回字节数;槽置空后重复调用零释放。
|
||||
pub fn drop_shared_array_buffer_native(obj: &mut JsObject) -> u64 {
|
||||
let bytes = shared_array_buffer_native_size(obj);
|
||||
if bytes == 0 {
|
||||
return 0;
|
||||
}
|
||||
let Some(payload_ptr) = shared_array_buffer_payload_ptr(obj) else {
|
||||
return 0;
|
||||
};
|
||||
// SAFETY: payload_ptr 非空(size 已验证),Box::from_raw 恰好释放一次。
|
||||
let payload = unsafe { Box::from_raw(payload_ptr) };
|
||||
drop(payload);
|
||||
obj.set_native_fn(None);
|
||||
bytes
|
||||
}
|
||||
|
||||
/// 克隆 SharedArrayBuffer 载荷(字节缓冲)到新对象(跨 epoch 克隆流程用)。
|
||||
pub fn clone_shared_array_buffer_native(old_obj: &JsObject, new_obj: &mut JsObject) {
|
||||
let Some(ptr) = shared_array_buffer_payload_ptr(old_obj) else {
|
||||
return;
|
||||
};
|
||||
if ptr.is_null() {
|
||||
return;
|
||||
}
|
||||
// SAFETY: ptr 非空且指向存活载荷盒(克隆/晋升臂由 GC 持有源对象)。
|
||||
let cloned = unsafe { &*ptr }.clone();
|
||||
let cloned_ptr = Box::into_raw(Box::new(cloned));
|
||||
new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned_ptr as *const ()) }));
|
||||
}
|
||||
|
||||
/// `SharedArrayBuffer(length)`:构造语义仅 `new`;length 缺省 0,经 ToIndex
|
||||
/// 传播式(强转副作用原值上抛),超引擎上界 → RangeError。最小实现:字节
|
||||
/// 缓冲零填充定长,无 resizable 选项面与 `[[ArrayBufferMaxByteLength]]`。
|
||||
pub fn shared_array_buffer_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if !vm.constructing_native() {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer must be called with new"));
|
||||
}
|
||||
let length = if args.len() > 1 { native_try!(to_index(vm, vm.reg(args[1]))) } else { 0 };
|
||||
if length > MAX_ARRAY_BUFFER_LENGTH {
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "invalid SharedArrayBuffer length"));
|
||||
}
|
||||
let obj_ptr = new_shared_array_buffer(vm, vec![0; length]);
|
||||
NativeResult::Ok(JsValue::from_js_object(obj_ptr))
|
||||
}
|
||||
|
||||
/// 读入口:校验 receiver 为 ArrayBuffer 并取载荷指针。detach(载荷 `data` 为
|
||||
/// `None`)不在此分叉,由消费方现读 `data` 时按既有错误形态处理。
|
||||
pub(crate) fn array_buffer_payload<H: VmHost>(
|
||||
|
||||
@@ -25,10 +25,6 @@ pub fn weakref_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||
pub fn finalization_registry_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||
stub_error(vm, "FinalizationRegistry")
|
||||
}
|
||||
/// `SharedArrayBuffer` 全局占位:始终抛 TypeError。按设计延后实现。
|
||||
pub fn shared_array_buffer_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||
stub_error(vm, "SharedArrayBuffer")
|
||||
}
|
||||
/// `Atomics` 全局占位:始终抛 TypeError。按设计延后实现。
|
||||
pub fn atomics_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||
stub_error(vm, "Atomics")
|
||||
|
||||
@@ -208,6 +208,11 @@ impl JsObject {
|
||||
/// 与宽松相等按 undefined 处理,Type 与其余强制转换按普通对象处理;无额外
|
||||
/// 内部载荷。
|
||||
pub const OBJ_TYPE_HTML_DDA: u8 = 29;
|
||||
/// SharedArrayBuffer 对象:与 ArrayBuffer 同构的字节载荷盒(`native_fn`
|
||||
/// 槽存 `ArrayBufferPayload`),但无 `[[ArrayBufferMaxByteLength]]`/定长
|
||||
/// 语义面;接收者品牌守卫按本 tag 拒绝,ArrayBuffer 方法族对其抛
|
||||
/// TypeError。释放与深拷贝守卫同 ArrayBuffer。
|
||||
pub const OBJ_TYPE_SHARED_ARRAY_BUFFER: u8 = 30;
|
||||
/// `is_session_epoch` 字段中的 session 标记位。
|
||||
pub const SESSION_EPOCH_BIT: u8 = 0x01;
|
||||
/// `is_session_epoch` 字段中的 GC 标记位。
|
||||
@@ -268,6 +273,11 @@ impl JsObject {
|
||||
pub fn is_array_buffer_obj(&self) -> bool {
|
||||
self.type_tag == Self::OBJ_TYPE_ARRAY_BUFFER
|
||||
}
|
||||
/// 是否 SharedArrayBuffer 对象。
|
||||
#[inline]
|
||||
pub fn is_shared_array_buffer_obj(&self) -> bool {
|
||||
self.type_tag == Self::OBJ_TYPE_SHARED_ARRAY_BUFFER
|
||||
}
|
||||
/// 是否 DataView 对象。
|
||||
#[inline]
|
||||
pub fn is_data_view_obj(&self) -> bool {
|
||||
|
||||
@@ -20,7 +20,7 @@ const STUBS: [(&str, *const (), u8); 7] = [
|
||||
),
|
||||
(
|
||||
"SharedArrayBuffer",
|
||||
oxide_builtins::stubs::shared_array_buffer_stub::<crate::vm::Vm> as *const (),
|
||||
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),
|
||||
|
||||
@@ -882,7 +882,7 @@ fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
||||
),
|
||||
(
|
||||
"SharedArrayBuffer",
|
||||
oxide_builtins::stubs::shared_array_buffer_stub::<crate::vm::Vm> as *const (),
|
||||
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),
|
||||
|
||||
@@ -67,6 +67,9 @@ impl Vm {
|
||||
} else if src_ref.is_array_buffer_obj() {
|
||||
// 字节缓冲深拷贝到新对象:源 Vec 随 epoch 释放,互不共享。
|
||||
array_buffer::clone_array_buffer_native(src_ref, dst_ref);
|
||||
} else if src_ref.is_shared_array_buffer_obj() {
|
||||
// 字节缓冲深拷贝到新对象:源 Vec 随 epoch 释放,互不共享。
|
||||
array_buffer::clone_shared_array_buffer_native(src_ref, dst_ref);
|
||||
} else if src_ref.is_generator_obj() {
|
||||
// 生成器状态盒深拷贝到新对象:源盒随 epoch 释放,互不共享。
|
||||
crate::generator::clone_generator_native_with_rewrite(src_ref, dst_ref, |value| {
|
||||
|
||||
@@ -116,6 +116,7 @@ impl SessionGc {
|
||||
bytes += module::module_ns_native_size(obj);
|
||||
bytes += disposable_stack::disposable_stack_native_size(obj);
|
||||
bytes += array_buffer::array_buffer_native_size(obj);
|
||||
bytes += array_buffer::shared_array_buffer_native_size(obj);
|
||||
bytes += regexp::regexp_native_size(obj);
|
||||
bytes += typed_array::typed_array_native_size(obj);
|
||||
bytes += data_view::data_view_native_size(obj);
|
||||
@@ -537,6 +538,7 @@ impl SessionGc {
|
||||
freed_bytes += module::drop_module_ns_native(obj);
|
||||
freed_bytes += disposable_stack::drop_dispose_native(obj);
|
||||
freed_bytes += array_buffer::drop_array_buffer_native(obj);
|
||||
freed_bytes += array_buffer::drop_shared_array_buffer_native(obj);
|
||||
freed_bytes += regexp::drop_regexp_native(obj);
|
||||
freed_bytes += typed_array::drop_typed_array_native(obj);
|
||||
freed_bytes += data_view::drop_data_view_native(obj);
|
||||
@@ -625,6 +627,8 @@ impl SessionGc {
|
||||
disposable_stack::clone_dispose_native_with_rewrite(old_ref, new_ref, |value| value);
|
||||
} else if old_ref.is_array_buffer_obj() {
|
||||
array_buffer::clone_array_buffer_native(old_ref, new_ref);
|
||||
} else if old_ref.is_shared_array_buffer_obj() {
|
||||
array_buffer::clone_shared_array_buffer_native(old_ref, new_ref);
|
||||
} else if old_ref.is_typed_array_obj() {
|
||||
typed_array::clone_typed_array_native_with_rewrite(old_ref, new_ref, |value| value);
|
||||
} else if old_ref.is_data_view_obj() {
|
||||
|
||||
@@ -1134,7 +1134,26 @@ fn ab_sti_brand_family() {
|
||||
var t2 = false, t3 = false; \
|
||||
var nab = new ArrayBuffer(8); \
|
||||
try { new nab.sliceToImmutable(); t3 = true; } catch (e) { t2 = e instanceof TypeError; t3 = false; } \
|
||||
return ok && calls.length === 0 && t2 && calls2.length === 0 && !t3; })()",
|
||||
return ok && calls.length === 0 && t2 && calls2.length === 0 && !t3; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// SharedArrayBuffer 最小构造钉:`new SharedArrayBuffer(n)` 建 n 字节缓冲,
|
||||
/// 非 ArrayBuffer 品牌(ArrayBuffer 方法族对其先读参后抛 TypeError)。
|
||||
#[test]
|
||||
fn sab_constructor_and_brand_rejection() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"(function () { var sab = new SharedArrayBuffer(4); \
|
||||
var calls = []; \
|
||||
var start = { valueOf: function () { calls.push('s'); return 0; } }; \
|
||||
var t = false; \
|
||||
try { ArrayBuffer.prototype.sliceToImmutable.call(sab, start); t = true; } \
|
||||
catch (e) { t = e instanceof TypeError; } \
|
||||
return t && calls.length === 0; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
|
||||
Reference in New Issue
Block a user