mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 11:11:11 +08:00
feat: SharedArrayBuffer 最小构造器与载荷生命周期
This commit is contained in:
@@ -159,6 +159,88 @@ pub fn drop_array_buffer_native(obj: &mut JsObject) -> u64 {
|
|||||||
bytes
|
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` 为
|
/// 读入口:校验 receiver 为 ArrayBuffer 并取载荷指针。detach(载荷 `data` 为
|
||||||
/// `None`)不在此分叉,由消费方现读 `data` 时按既有错误形态处理。
|
/// `None`)不在此分叉,由消费方现读 `data` 时按既有错误形态处理。
|
||||||
pub(crate) fn array_buffer_payload<H: VmHost>(
|
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 {
|
pub fn finalization_registry_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||||
stub_error(vm, "FinalizationRegistry")
|
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。按设计延后实现。
|
/// `Atomics` 全局占位:始终抛 TypeError。按设计延后实现。
|
||||||
pub fn atomics_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
pub fn atomics_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||||
stub_error(vm, "Atomics")
|
stub_error(vm, "Atomics")
|
||||||
|
|||||||
@@ -208,6 +208,11 @@ impl JsObject {
|
|||||||
/// 与宽松相等按 undefined 处理,Type 与其余强制转换按普通对象处理;无额外
|
/// 与宽松相等按 undefined 处理,Type 与其余强制转换按普通对象处理;无额外
|
||||||
/// 内部载荷。
|
/// 内部载荷。
|
||||||
pub const OBJ_TYPE_HTML_DDA: u8 = 29;
|
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 标记位。
|
/// `is_session_epoch` 字段中的 session 标记位。
|
||||||
pub const SESSION_EPOCH_BIT: u8 = 0x01;
|
pub const SESSION_EPOCH_BIT: u8 = 0x01;
|
||||||
/// `is_session_epoch` 字段中的 GC 标记位。
|
/// `is_session_epoch` 字段中的 GC 标记位。
|
||||||
@@ -268,6 +273,11 @@ impl JsObject {
|
|||||||
pub fn is_array_buffer_obj(&self) -> bool {
|
pub fn is_array_buffer_obj(&self) -> bool {
|
||||||
self.type_tag == Self::OBJ_TYPE_ARRAY_BUFFER
|
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 对象。
|
/// 是否 DataView 对象。
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_data_view_obj(&self) -> bool {
|
pub fn is_data_view_obj(&self) -> bool {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const STUBS: [(&str, *const (), u8); 7] = [
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
"SharedArrayBuffer",
|
"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,
|
1,
|
||||||
),
|
),
|
||||||
("Atomics", oxide_builtins::stubs::atomics_stub::<crate::vm::Vm> as *const (), 0),
|
("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",
|
"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,
|
1,
|
||||||
),
|
),
|
||||||
("Atomics", oxide_builtins::stubs::atomics_stub::<crate::vm::Vm> as *const (), 0),
|
("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() {
|
} else if src_ref.is_array_buffer_obj() {
|
||||||
// 字节缓冲深拷贝到新对象:源 Vec 随 epoch 释放,互不共享。
|
// 字节缓冲深拷贝到新对象:源 Vec 随 epoch 释放,互不共享。
|
||||||
array_buffer::clone_array_buffer_native(src_ref, dst_ref);
|
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() {
|
} else if src_ref.is_generator_obj() {
|
||||||
// 生成器状态盒深拷贝到新对象:源盒随 epoch 释放,互不共享。
|
// 生成器状态盒深拷贝到新对象:源盒随 epoch 释放,互不共享。
|
||||||
crate::generator::clone_generator_native_with_rewrite(src_ref, dst_ref, |value| {
|
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 += module::module_ns_native_size(obj);
|
||||||
bytes += disposable_stack::disposable_stack_native_size(obj);
|
bytes += disposable_stack::disposable_stack_native_size(obj);
|
||||||
bytes += array_buffer::array_buffer_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 += regexp::regexp_native_size(obj);
|
||||||
bytes += typed_array::typed_array_native_size(obj);
|
bytes += typed_array::typed_array_native_size(obj);
|
||||||
bytes += data_view::data_view_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 += module::drop_module_ns_native(obj);
|
||||||
freed_bytes += disposable_stack::drop_dispose_native(obj);
|
freed_bytes += disposable_stack::drop_dispose_native(obj);
|
||||||
freed_bytes += array_buffer::drop_array_buffer_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 += regexp::drop_regexp_native(obj);
|
||||||
freed_bytes += typed_array::drop_typed_array_native(obj);
|
freed_bytes += typed_array::drop_typed_array_native(obj);
|
||||||
freed_bytes += data_view::drop_data_view_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);
|
disposable_stack::clone_dispose_native_with_rewrite(old_ref, new_ref, |value| value);
|
||||||
} else if old_ref.is_array_buffer_obj() {
|
} else if old_ref.is_array_buffer_obj() {
|
||||||
array_buffer::clone_array_buffer_native(old_ref, new_ref);
|
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() {
|
} else if old_ref.is_typed_array_obj() {
|
||||||
typed_array::clone_typed_array_native_with_rewrite(old_ref, new_ref, |value| value);
|
typed_array::clone_typed_array_native_with_rewrite(old_ref, new_ref, |value| value);
|
||||||
} else if old_ref.is_data_view_obj() {
|
} else if old_ref.is_data_view_obj() {
|
||||||
|
|||||||
@@ -1134,7 +1134,26 @@ fn ab_sti_brand_family() {
|
|||||||
var t2 = false, t3 = false; \
|
var t2 = false, t3 = false; \
|
||||||
var nab = new ArrayBuffer(8); \
|
var nab = new ArrayBuffer(8); \
|
||||||
try { new nab.sliceToImmutable(); t3 = true; } catch (e) { t2 = e instanceof TypeError; t3 = false; } \
|
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();
|
.unwrap();
|
||||||
assert!(result.as_bool());
|
assert!(result.as_bool());
|
||||||
|
|||||||
Reference in New Issue
Block a user