diff --git a/Cargo.lock b/Cargo.lock index fd141caf..86a05aa1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.8.80" +version = "0.8.81" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.8.80" +version = "0.8.81" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.8.80" +version = "0.8.81" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.8.80" +version = "0.8.81" dependencies = [ "ansi_term", "bumpalo", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.8.80" +version = "0.8.81" dependencies = [ "lru", "oxide_bytecode", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.8.80" +version = "0.8.81" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.8.80" +version = "0.8.81" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.8.80" +version = "0.8.81" dependencies = [ "num-bigint", "oxc_allocator", @@ -1284,7 +1284,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.8.80" +version = "0.8.81" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.8.80" +version = "0.8.81" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1305,7 +1305,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.8.80" +version = "0.8.81" dependencies = [ "bumpalo", "dashmap", @@ -1319,7 +1319,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.8.80" +version = "0.8.81" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1331,7 +1331,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.8.80" +version = "0.8.81" dependencies = [ "tracing", "tracing-appender", @@ -1340,7 +1340,7 @@ dependencies = [ [[package]] name = "oxide_parser" -version = "0.8.80" +version = "0.8.81" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1355,7 +1355,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.8.80" +version = "0.8.81" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1371,11 +1371,12 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.8.80" +version = "0.8.81" dependencies = [ "num-bigint", "num-traits", "oxide_bytecode", + "oxide_kernel", "oxide_log", "oxide_types", "ryu", @@ -1383,7 +1384,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.8.80" +version = "0.8.81" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1401,7 +1402,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.8.80" +version = "0.8.81" dependencies = [ "bumpalo", "dashmap", @@ -1414,7 +1415,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.8.80" +version = "0.8.81" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 9703144a..8842ef90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ ] [workspace.package] -version = "0.8.80" +version = "0.8.81" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_builtins/src/array_buffer.rs b/crates/oxide_builtins/src/array_buffer.rs index be9b03b1..4cfbeac9 100644 --- a/crates/oxide_builtins/src/array_buffer.rs +++ b/crates/oxide_builtins/src/array_buffer.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_kernel::SharedBuffer; use oxide_types::object::{JsObject, NativeFnPtr}; use oxide_types::private_key::{encode_symbol_key, WELL_KNOWN_SYMBOL_SPECIES}; use oxide_types::value::JsValue; @@ -11,19 +12,82 @@ use crate::array::to_integer_or_infinity_bounded; pub(crate) const MAX_ARRAY_BUFFER_LENGTH: usize = 1 << 30; -/// ArrayBuffer 载荷:字节缓冲与状态位。`data` 为 `None` 即缓冲区已 detach; -/// `max_byte_length` 为存储态上限:0 即定长缓冲(resizable 判据),非 0 存 -/// 请求上限 + 1(上限 0 与定长必须可分);`immutable` 为字节缓冲写守卫标志。 -/// 载荷经 `Arc::into_raw` 存于对象 `native_fn` 槽(引用计数盒:SharedArrayBuffer -/// 克隆共享同一载荷,计数归零统一释放),GC 两自由函数(size/drop)对整结构体 -/// 操作。 +/// ArrayBuffer 载荷:字节缓冲与状态位。`detached` 为真即缓冲区已 detach +/// (detach 时字节区清空、标志置位,与空缓冲可分);`max_byte_length` 为 +/// 存储态上限:0 即定长缓冲(resizable 判据),非 0 存请求上限 + 1(上限 0 +/// 与定长必须可分);`immutable` 为字节缓冲写守卫标志。 +/// 载荷经 `Arc::into_raw` 存于对象 `native_fn` 槽(引用计数盒),GC 两自由 +/// 函数(size/drop)对整结构体操作。 #[derive(Clone)] pub(crate) struct ArrayBufferPayload { - pub(crate) data: Option>, + pub(crate) bytes: Vec, + pub(crate) detached: bool, pub(crate) max_byte_length: usize, pub(crate) immutable: bool, } +/// SharedArrayBuffer 载荷:共享字节缓冲(跨线程共享,预分配至真实上限,活长 +/// 原子推进)。载荷经 `Arc::into_raw` 存于对象 `native_fn` 槽(引用计数盒: +/// SAB 克隆共享同一载荷,计数归零统一释放);字节区账目在核的 +/// `shared_buffer_bytes`,session 账目只计结构体。 +#[derive(Clone)] +pub(crate) struct SharedArrayBufferPayload { + pub(crate) buffer: Arc, + pub(crate) growable: bool, +} + +/// 缓冲区载荷统一双认入口:ArrayBuffer 与 SharedArrayBuffer 载荷盒双标签 +/// 双认,消费方经本枚举统一分流。 +#[derive(Clone)] +pub(crate) enum BufferStore { + Ab(ArrayBufferPayload), + Sab(SharedArrayBufferPayload), +} + +impl BufferStore { + /// detached 探针:AB 臂读 detached 标志(区分 detached 与空缓冲); + /// SAB 臂无 detach 路径,恒 false。 + pub(crate) fn is_detached(&self) -> bool { + match self { + BufferStore::Ab(payload) => payload.detached, + BufferStore::Sab(_) => false, + } + } +} + +/// 缓冲区载荷可变借用(写点专用):AB 臂为对象字节区独占借用(detached +/// 不返回),SAB 臂为共享缓冲共享借用(`write_range` 取 `&self`)。与只读 +/// [`BufferStore`] 的克隆语义相区分:写点必须落回对象内载荷,克隆不可写回。 +pub(crate) enum BufferStoreMut<'a> { + Ab(&'a mut [u8]), + Sab(&'a SharedBuffer), +} + +/// 缓冲区载荷可变借用双认入口:AB 臂返回对象字节区独占借用(detached 或 +/// 槽位空 → `None`),SAB 臂返回共享缓冲共享借用;两标签之外(含槽位空) +/// 返回 `None`。 +pub(crate) fn buffer_store_mut_ptr<'a>(obj: &'a JsObject) -> Option> { + if obj.is_array_buffer_obj() { + let ptr = array_buffer_payload_ptr(obj)?; + if ptr.is_null() { + return None; + } + // SAFETY: ptr 非空且指向存活载荷盒,独占借用经返回移交调用方。 + let payload = unsafe { &mut *ptr }; + if payload.detached { + return None; + } + return Some(BufferStoreMut::Ab(&mut payload.bytes)); + } + let ptr = shared_array_buffer_payload_ptr(obj)?; + if ptr.is_null() { + return None; + } + // SAFETY: ptr 非空且指向存活载荷盒,共享借用经返回移交调用方。 + let payload = unsafe { &*ptr }; + Some(BufferStoreMut::Sab(&payload.buffer)) +} + macro_rules! native_try { ($expr:expr) => { match $expr { @@ -100,7 +164,8 @@ pub(crate) fn new_array_buffer( let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto); obj.type_tag = JsObject::OBJ_TYPE_ARRAY_BUFFER; let payload = ArrayBufferPayload { - data: Some(data), + bytes: data, + detached: false, max_byte_length, immutable: false, }; @@ -129,7 +194,7 @@ pub fn array_buffer_native_size(obj: &JsObject) -> u64 { } // SAFETY: payload_ptr 非空且指向存活载荷盒。 let payload = unsafe { &*payload_ptr }; - (std::mem::size_of::() + payload.data.as_ref().map_or(0, |d| d.capacity())) as u64 + (std::mem::size_of::() + payload.bytes.capacity()) as u64 } /// 释放 ArrayBuffer 载荷盒(native_fn 槽),返回字节数;槽置空后重复调用零释放。 @@ -155,16 +220,22 @@ pub(crate) fn default_shared_array_buffer_proto(vm: &H) -> JsValue { /// 分配携给定 proto 与载荷形态(`max_byte_length` 为存储态:0 定长,非 0 可 /// grow)的 SharedArrayBuffer 对象;byteLength 经原型访问器读(不写 own 数据 -/// 属性)。 +/// 属性)。字节区经 `SharedBuffer` 预分配(growable 预分至真实上限)并计入 +/// 核的 `shared_buffer_bytes` 账目。 pub(crate) fn new_shared_array_buffer( vm: &mut H, data: Vec, max_byte_length: usize, proto: JsValue, ) -> *mut JsObject { let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto); obj.type_tag = JsObject::OBJ_TYPE_SHARED_ARRAY_BUFFER; - let payload = ArrayBufferPayload { - data: Some(data), - max_byte_length, - immutable: false, + let core = vm.kernel_core().clone(); + let buffer = if max_byte_length == 0 { + SharedBuffer::new(core, data) + } else { + SharedBuffer::new_growable(core, data.len(), max_byte_length - 1) + }; + let payload = SharedArrayBufferPayload { + buffer: Arc::new(buffer), + growable: max_byte_length != 0, }; let payload_ptr = Arc::into_raw(Arc::new(payload)); // SAFETY: SharedArrayBuffer 对象不可调用,native_fn 槽复用为不透明载荷盒 @@ -173,33 +244,44 @@ pub(crate) fn new_shared_array_buffer( vm.alloc_object(obj) } -pub(crate) fn shared_array_buffer_payload_ptr(obj: &JsObject) -> Option<*mut ArrayBufferPayload> { +pub(crate) fn shared_array_buffer_payload_ptr(obj: &JsObject) -> Option<*mut SharedArrayBufferPayload> { if !obj.is_shared_array_buffer_obj() { return None; } - obj.native_fn().map(|ptr| ptr.as_ptr() as *mut ArrayBufferPayload) + obj.native_fn().map(|ptr| ptr.as_ptr() as *mut SharedArrayBufferPayload) } -/// 缓冲区载荷指针单收口:ArrayBuffer 与 SharedArrayBuffer 共用同一载荷 -/// 结构,双标签双认,返回统一形态的载荷盒指针;两标签之外(含槽位空) -/// 返回 `None`。 -pub(crate) fn buffer_payload_ptr(obj: &JsObject) -> Option<*mut ArrayBufferPayload> { +/// 缓冲区载荷统一双认入口:ArrayBuffer 与 SharedArrayBuffer 双标签双认, +/// 克隆出统一形态的载荷(AB 臂克隆字节区,SAB 臂仅增 Arc 计数);两标签 +/// 之外(含槽位空)返回 `None`。 +pub(crate) fn buffer_store_ptr(obj: &JsObject) -> Option { if obj.is_array_buffer_obj() { - return array_buffer_payload_ptr(obj); + let ptr = array_buffer_payload_ptr(obj)?; + if ptr.is_null() { + return None; + } + // SAFETY: ptr 非空且指向存活载荷盒,克隆只读标量与字节区。 + return Some(BufferStore::Ab(unsafe { (*ptr).clone() })); } - shared_array_buffer_payload_ptr(obj) + let ptr = shared_array_buffer_payload_ptr(obj)?; + if ptr.is_null() { + return None; + } + // SAFETY: ptr 非空且指向存活载荷盒,克隆只增 Arc 计数。 + Some(BufferStore::Sab(unsafe { (*ptr).clone() })) } /// 缓冲区载荷读入口(两标签双认):校验 receiver 为 ArrayBuffer 或 -/// SharedArrayBuffer 并取载荷指针。AB 臂经 `array_buffer_payload_ptr` 与 +/// SharedArrayBuffer 并取载荷。AB 臂经 `array_buffer_payload_ptr` 与 /// `array_buffer_payload` 路径同函数同序(逐位同形),SAB 臂经 /// `shared_array_buffer_payload_ptr`;非对象 / 双标签外 / 槽位空(防御背板) /// → TypeError。 /// /// # 边界与前提 -/// - SAB 无 detach 生产路径,载荷 `data` 恒在场;detach 分叉由消费方现读处理。 -/// - 调用方不得跨 JS 调用窗口持有返回指针(晋升可改写对象,须重取)。 -pub(crate) fn buffer_payload(vm: &mut H, this_val: JsValue) -> Result<*mut ArrayBufferPayload, JsValue> { +/// - SAB 无 detach 生产路径,载荷恒在场;detach 分叉由消费方经 +/// `is_detached()` 探针现读处理。 +/// - 调用方不得跨 JS 调用窗口持有返回载荷(晋升可改写对象,须重取)。 +pub(crate) fn buffer_store(vm: &mut H, this_val: JsValue) -> Result { if !this_val.is_object() { return Err(crate::error::create_type_error(vm, "ArrayBuffer method called on incompatible receiver")); } @@ -209,38 +291,34 @@ pub(crate) fn buffer_payload(vm: &mut H, this_val: JsValue) -> Result } // SAFETY: is_object 保证指针非空且对象本 session 存活。 let obj = unsafe { &*obj_ptr }; - let Some(payload_ptr) = buffer_payload_ptr(obj) else { + let Some(store) = buffer_store_ptr(obj) else { return Err(crate::error::create_type_error(vm, "ArrayBuffer method called on incompatible receiver")); }; - if payload_ptr.is_null() { - return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); - } - Ok(payload_ptr) + Ok(store) } -/// SharedArrayBuffer 载荷盒字节数(`native_fn` 槽);非 SAB 或已释放 → 0。 +/// SharedArrayBuffer 载荷盒字节数(`native_fn` 槽):只计结构体(字节区 +/// 账目在核的 `shared_buffer_bytes`);非 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::() + 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; }; + if payload_ptr.is_null() { + return 0; + } + std::mem::size_of::() as u64 +} + +/// 释放 SharedArrayBuffer 载荷盒(native_fn 槽),返回字节数;槽置空后重复 +/// 调用零释放。字节区随 `SharedBuffer` 引用计数归零经核账目减回。 +pub fn drop_shared_array_buffer_native(obj: &mut JsObject) -> u64 { + let Some(payload_ptr) = shared_array_buffer_payload_ptr(obj) else { + return 0; + }; + if payload_ptr.is_null() { + return 0; + } + let bytes = std::mem::size_of::() as u64; // SAFETY: payload_ptr 非空(size 已验证),Arc::from_raw 恰好减引用一次。 let payload = unsafe { Arc::from_raw(payload_ptr) }; drop(payload); @@ -331,10 +409,10 @@ pub fn shared_array_buffer_constructor(vm: &mut H, args: &[u8]) -> Na } /// 读入口:校验 receiver 为 SharedArrayBuffer 并取载荷指针。SAB 无 detach -/// 生产路径,载荷 `data` 恒在场(`map_or` 兜底与 AB 读入口同形)。 +/// 生产路径,载荷恒在场(`map_or` 兜底与 AB 读入口同形)。 pub(crate) fn shared_array_buffer_payload( vm: &mut H, this_val: JsValue, -) -> Result<*mut ArrayBufferPayload, JsValue> { +) -> Result<*mut SharedArrayBufferPayload, JsValue> { if !this_val.is_object() { return Err(crate::error::create_type_error( vm, @@ -355,45 +433,44 @@ pub(crate) fn shared_array_buffer_payload( let Some(payload_ptr) = obj.native_fn() else { return Err(crate::error::create_type_error(vm, "SharedArrayBuffer internal state invalid")); }; - let payload_ptr = payload_ptr.as_ptr() as *mut ArrayBufferPayload; + let payload_ptr = payload_ptr.as_ptr() as *mut SharedArrayBufferPayload; if payload_ptr.is_null() { return Err(crate::error::create_type_error(vm, "SharedArrayBuffer internal state invalid")); } Ok(payload_ptr) } -/// `SharedArrayBuffer.prototype.byteLength` getter:返回缓冲区字节数 -/// (定长臂读载荷 len;growable 臂归后续面)。 +/// `SharedArrayBuffer.prototype.byteLength` getter:返回缓冲区活字节数 +/// (定长与 growable 同读活长)。 pub fn shared_array_buffer_byte_length(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let payload_ptr = native_try!(shared_array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 shared_array_buffer_payload 校验为合法 SAB 载荷。 - let len = unsafe { &*payload_ptr }.data.as_ref().map_or(0, |d| d.len()); + let len = unsafe { &*payload_ptr }.buffer.len(); NativeResult::Ok(JsValue::int(len as i32)) } /// `SharedArrayBuffer.prototype.growable` getter:返回缓冲区是否 growable -/// (载荷存储态上限非 0:0 = 定长,非 0 = 请求上限 + 1)。 +/// (载荷 growable 位:构造期 maxByteLength 选项在场)。 /// 非 SAB receiver 经品牌校验抛 TypeError(RequireInternalSlot 同语义)。 pub fn shared_array_buffer_growable(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let payload_ptr = native_try!(shared_array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 shared_array_buffer_payload 校验为合法 SAB 载荷。 - NativeResult::Ok(JsValue::bool(unsafe { &*payload_ptr }.max_byte_length != 0)) + NativeResult::Ok(JsValue::bool(unsafe { &*payload_ptr }.growable)) } -/// `SharedArrayBuffer.prototype.maxByteLength` getter:定长(存储态上限 0) -/// 返回当前字节数;growable 返回真实上限(存储态 − 1 解码)。SAB 无 -/// detached 臂(载荷 `data` 恒在场)。 +/// `SharedArrayBuffer.prototype.maxByteLength` getter:定长返回当前字节数; +/// growable 返回真实上限(预分配真实上限)。SAB 无 detached 臂(载荷恒在场)。 pub fn shared_array_buffer_max_byte_length(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let payload_ptr = native_try!(shared_array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 shared_array_buffer_payload 校验为合法 SAB 载荷。 let payload = unsafe { &*payload_ptr }; - let value = if payload.max_byte_length == 0 { - payload.data.as_ref().map_or(0, |d| d.len()) + let value = if payload.growable { + payload.buffer.max_len() } else { - payload.max_byte_length - 1 + payload.buffer.len() }; NativeResult::Ok(JsValue::int(value as i32)) } @@ -403,15 +480,15 @@ pub fn shared_array_buffer_max_byte_length(vm: &mut H, args: &[u8]) - /// /// # 步骤 /// 1. this 品牌校验(非对象/非 SAB → TypeError)。 -/// 2. 非 growable(存储态上限 0)→ TypeError,先于强转。 +/// 2. 非 growable(载荷 growable 位未置)→ TypeError,先于强转。 /// 3. newByteLength = ToIntegerOrInfinity(newLength) 传播式(NaN → 0, /// ±Infinity 保留)。 /// 4. 重取 this 寄存器 + 载荷指针(强转 JS 调用窗可晋升——AB resize 同式; /// SAB 无 detach 面,仅晋升面)。 -/// 5. 重读存储态上限 0 → TypeError。 -/// 6. newByteLength < 0 或 > 真实上限(重读存储态 − 1)或 < 当前字节数 +/// 5. 重读 growable 位 0 → TypeError。 +/// 6. newByteLength < 0 或 > 真实上限(预分配真实上限)或 < 当前活长 /// (grow-only)→ RangeError。 -/// 7. `Vec::resize` 一次调用收口目标长度。 +/// 7. 活长原子推进(字节区已预分配,O(1))。 /// 8. 返回 undefined。 pub fn shared_array_buffer_grow(vm: &mut H, args: &[u8]) -> NativeResult { let this_reg = if args.is_empty() { 0 } else { args[0] }; @@ -419,7 +496,7 @@ pub fn shared_array_buffer_grow(vm: &mut H, args: &[u8]) -> NativeRes let payload_ptr = native_try!(shared_array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 shared_array_buffer_payload 校验为合法 SAB 载荷; // 状态位拷出后本次借用即结束,不跨 JS 调用点。 - if unsafe { &*payload_ptr }.max_byte_length == 0 { + if !unsafe { &*payload_ptr }.growable { return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer is not growable")); } let new_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; @@ -429,20 +506,17 @@ pub fn shared_array_buffer_grow(vm: &mut H, args: &[u8]) -> NativeRes let this_val = vm.reg(this_reg); let payload_ptr = native_try!(shared_array_buffer_payload(vm, this_val)); // SAFETY: 重取的 payload_ptr 指向存活载荷盒。 - let payload = unsafe { &mut *payload_ptr }; - if payload.max_byte_length == 0 { + let payload = unsafe { &*payload_ptr }; + if !payload.growable { return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer is not growable")); } - let Some(data) = payload.data.as_mut() else { - return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer internal state invalid")); - }; - // 存储态上限 +1 编码:真实上限 = max - 1(上限 0 的缓冲区只许 grow(0)); - // 当前长度之下拒缩。 - let real_max = payload.max_byte_length - 1; - if new_length < 0.0 || new_length > real_max as f64 || new_length < data.len() as f64 { + // 真实上限即预分配上限;当前活长之下拒缩。 + let real_max = payload.buffer.max_len(); + let cur_len = payload.buffer.len(); + if new_length < 0.0 || new_length > real_max as f64 || new_length < cur_len as f64 { return NativeResult::Err(crate::error::create_range_error(vm, "invalid SharedArrayBuffer length")); } - data.resize(new_length as usize, 0); + payload.buffer.grow(new_length as usize); NativeResult::Ok(JsValue::undefined()) } @@ -475,7 +549,7 @@ pub fn shared_array_buffer_slice(vm: &mut H, args: &[u8]) -> NativeRe let payload_ptr = native_try!(shared_array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 shared_array_buffer_payload 校验为合法 SAB 载荷; // 标量拷出后借用即结束,不跨 JS 调用窗口。 - let len = unsafe { &*payload_ptr }.data.as_ref().map_or(0, |d| d.len()); + let len = unsafe { &*payload_ptr }.buffer.len(); let start = if args.len() > 1 { Some(vm.reg(args[1])) } else { None }; let end = if args.len() > 2 { Some(vm.reg(args[2])) } else { None }; let (first, final_) = native_try!(ab_resolve_bounds(vm, len, start, end)); @@ -493,7 +567,7 @@ pub fn shared_array_buffer_slice(vm: &mut H, args: &[u8]) -> NativeRe let this_val = vm.reg(this_reg); let payload_ptr = native_try!(shared_array_buffer_payload(vm, this_val)); // SAFETY: 重取的 payload_ptr 指向存活载荷盒;字节借出止于拷贝语句。 - let data = unsafe { &*payload_ptr }.data.as_ref(); + let data = unsafe { &*payload_ptr }.buffer.as_slice(); // 结果三检:SAB 品牌槽 → SameValue → 活长。 if !new_val.is_object() { return NativeResult::Err(crate::error::create_type_error( @@ -521,23 +595,21 @@ pub fn shared_array_buffer_slice(vm: &mut H, args: &[u8]) -> NativeRe )); } // SAFETY: new_payload 经 shared_array_buffer_payload_ptr 校验为 SAB 载荷盒。 - if unsafe { &*new_payload }.data.as_ref().map_or(0, |d| d.len()) < new_len { + if unsafe { &*new_payload }.buffer.len() < new_len { return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer slice result is too small")); } - let Some(data) = data else { - return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer internal state invalid")); - }; // 直拷 newLen 字节(grow-only 恒在界,见 边界与前提)。 // SAFETY: new_payload 经 shared_array_buffer_payload_ptr 校验;SAB 无 - // detach 臂,data 必为 Some,可变借用止于本语句。 - if let Some(dest) = unsafe { (*new_payload).data.as_mut() } { - dest[..new_len].copy_from_slice(&data[first..first + new_len]); + // detach 臂,字节区恒在场,经 &self 裸写止于本语句。 + if unsafe { (*new_payload).buffer.write_range(0, &data[first..first + new_len]) }.is_err() { + // 防御背板:活长检已证在界,此臂不可达。 + return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer internal state invalid")); } NativeResult::Ok(new_val) } -/// 读入口:校验 receiver 为 ArrayBuffer 并取载荷指针。detach(载荷 `data` 为 -/// `None`)不在此分叉,由消费方现读 `data` 时按既有错误形态处理。 +/// 读入口:校验 receiver 为 ArrayBuffer 并取载荷指针。detach(detached 标志 +/// 置位)不在此分叉,由消费方现读标志时按既有错误形态处理。 pub(crate) fn array_buffer_payload( vm: &mut H, this_val: JsValue, ) -> Result<*mut ArrayBufferPayload, JsValue> { @@ -660,7 +732,8 @@ pub fn array_buffer_byte_length(vm: &mut H, args: &[u8]) -> NativeRes let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let payload_ptr = native_try!(array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。 - let len = unsafe { &*payload_ptr }.data.as_ref().map_or(0, |d| d.len()); + let payload = unsafe { &*payload_ptr }; + let len = if payload.detached { 0 } else { payload.bytes.len() }; NativeResult::Ok(JsValue::int(len as i32)) } @@ -681,11 +754,11 @@ pub fn array_buffer_max_byte_length(vm: &mut H, args: &[u8]) -> Nativ let payload_ptr = native_try!(array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。 let payload = unsafe { &*payload_ptr }; - if payload.data.is_none() { + if payload.detached { return NativeResult::Ok(JsValue::int(0)); } let value = if payload.max_byte_length == 0 { - payload.data.as_ref().map_or(0, |d| d.len()) + payload.bytes.len() } else { payload.max_byte_length - 1 }; @@ -700,13 +773,13 @@ pub fn array_buffer_immutable(vm: &mut H, args: &[u8]) -> NativeResul NativeResult::Ok(JsValue::bool(unsafe { &*payload_ptr }.immutable)) } -/// `ArrayBuffer.prototype.detached` getter:detached 判据即载荷 `data` 为 -/// `None`。 +/// `ArrayBuffer.prototype.detached` getter:detached 判据即载荷 detached +/// 标志(detach 时字节区清空、标志置位)。 pub fn array_buffer_detached(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let payload_ptr = native_try!(array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。 - NativeResult::Ok(JsValue::bool(unsafe { &*payload_ptr }.data.is_none())) + NativeResult::Ok(JsValue::bool(unsafe { &*payload_ptr }.detached)) } /// `ArrayBuffer.prototype.markImmutable()`:定长附着缓冲区置 immutable 标志 @@ -722,7 +795,7 @@ pub fn array_buffer_mark_immutable(vm: &mut H, args: &[u8]) -> Native let payload_ptr = native_try!(array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。 let payload = unsafe { &mut *payload_ptr }; - if payload.data.is_none() { + if payload.detached { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); } if payload.max_byte_length != 0 { @@ -732,12 +805,15 @@ pub fn array_buffer_mark_immutable(vm: &mut H, args: &[u8]) -> Native NativeResult::Ok(this_val) } -/// ArrayBuffer detach 生产路径:品牌守卫后载荷 `data → None`(字节缓冲随 -/// `Option` 置空释放,载荷盒本体存活至对象 drop)。返回 undefined。 +/// ArrayBuffer detach 生产路径:品牌守卫后载荷 detach(字节区清空、detached +/// 标志置位,载荷盒本体存活至对象 drop)。返回 undefined。 pub fn detach_array_buffer_native(vm: &mut H, this_val: JsValue) -> NativeResult { let payload_ptr = native_try!(array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。 - unsafe { (*payload_ptr).data = None }; + unsafe { + (*payload_ptr).bytes.clear(); + (*payload_ptr).detached = true; + }; NativeResult::Ok(JsValue::undefined()) } @@ -775,9 +851,9 @@ pub fn array_buffer_resize(vm: &mut H, args: &[u8]) -> NativeResult { // SAFETY: 重取的 payload_ptr 指向存活载荷盒。 let payload = unsafe { &mut *payload_ptr }; let max = payload.max_byte_length; - let Some(data) = payload.data.as_mut() else { + if payload.detached { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); - }; + } if max == 0 { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer is not resizable")); } @@ -786,7 +862,7 @@ pub fn array_buffer_resize(vm: &mut H, args: &[u8]) -> NativeResult { if new_length < 0.0 || new_length > real_max as f64 { return NativeResult::Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length")); } - data.resize(new_length as usize, 0); + payload.bytes.resize(new_length as usize, 0); NativeResult::Ok(JsValue::undefined()) } @@ -862,10 +938,11 @@ pub fn array_buffer_slice(vm: &mut H, args: &[u8]) -> NativeResult { let payload_ptr = native_try!(array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷; // 标量拷出后借用即结束,不跨 JS 调用窗口。 - let Some(data) = unsafe { &*payload_ptr }.data.as_ref() else { + let payload = unsafe { &*payload_ptr }; + if payload.detached { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); - }; - let len = data.len(); + } + let len = payload.bytes.len(); let start = if args.len() > 1 { Some(vm.reg(args[1])) } else { None }; let end = if args.len() > 2 { Some(vm.reg(args[2])) } else { None }; let (first, final_) = native_try!(ab_resolve_bounds(vm, len, start, end)); @@ -883,9 +960,11 @@ pub fn array_buffer_slice(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(this_reg); let payload_ptr = native_try!(array_buffer_payload(vm, this_val)); // SAFETY: 重取的 payload_ptr 指向存活载荷盒;标量拷出后借用即结束。 - let Some(data) = unsafe { &*payload_ptr }.data.as_ref() else { + let payload = unsafe { &*payload_ptr }; + if payload.detached { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); - }; + } + let data = &payload.bytes; // 结果五检:AB 载荷槽 → detached → immutable → SameValue → 长度。 let new_ptr = new_val.as_js_object_ptr(); if new_ptr.is_null() { @@ -909,7 +988,7 @@ pub fn array_buffer_slice(vm: &mut H, args: &[u8]) -> NativeResult { } // SAFETY: new_payload 经 array_buffer_payload_ptr 校验为 ArrayBuffer 载荷盒。 let new_state = unsafe { &*new_payload }; - if new_state.data.is_none() { + if new_state.detached { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer slice result is detached")); } if new_state.immutable { @@ -921,7 +1000,7 @@ pub fn array_buffer_slice(vm: &mut H, args: &[u8]) -> NativeResult { "ArrayBuffer slice result must not be the source buffer", )); } - if new_state.data.as_ref().map_or(0, |d| d.len()) < new_len { + if new_state.bytes.len() < new_len { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer slice result is too small")); } // 活长度夹拷贝写入结果缓冲前区(构造器已零填充):源中途收缩只拷现存 @@ -929,10 +1008,8 @@ pub fn array_buffer_slice(vm: &mut H, args: &[u8]) -> NativeResult { let current_len = data.len(); let count = if first < current_len { new_len.min(current_len - first) } else { 0 }; // SAFETY: new_payload 经 array_buffer_payload_ptr 校验;detached 臂已先行 - // 排除,data 必为 Some,可变借用止于本语句。 - if let Some(dest) = unsafe { (*new_payload).data.as_mut() } { - dest[..count].copy_from_slice(&data[first..first + count]); - } + // 排除,字节区恒在场,可变借用止于本语句。 + unsafe { (&mut (*new_payload).bytes)[..count].copy_from_slice(&data[first..first + count]) }; NativeResult::Ok(new_val) } @@ -959,10 +1036,11 @@ pub fn array_buffer_slice_to_immutable(vm: &mut H, args: &[u8]) -> Na let payload_ptr = native_try!(array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷; // 标量拷出后借用即结束,不跨 JS 调用窗口。 - let Some(data) = unsafe { &*payload_ptr }.data.as_ref() else { + let payload = unsafe { &*payload_ptr }; + if payload.detached { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); - }; - let len = data.len(); + } + let len = payload.bytes.len(); let start = if args.len() > 1 { Some(vm.reg(args[1])) } else { None }; let end = if args.len() > 2 { Some(vm.reg(args[2])) } else { None }; let (first, final_) = native_try!(ab_resolve_bounds(vm, len, start, end)); @@ -972,9 +1050,11 @@ pub fn array_buffer_slice_to_immutable(vm: &mut H, args: &[u8]) -> Na let this_val = vm.reg(this_reg); let payload_ptr = native_try!(array_buffer_payload(vm, this_val)); // SAFETY: 重取的 payload_ptr 指向存活载荷盒;标量拷出后借用即结束。 - let Some(data) = unsafe { &*payload_ptr }.data.as_ref() else { + let payload = unsafe { &*payload_ptr }; + if payload.detached { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); - }; + } + let data = &payload.bytes; if data.len() < final_ { return NativeResult::Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length")); } @@ -993,9 +1073,11 @@ pub fn array_buffer_slice_to_immutable(vm: &mut H, args: &[u8]) -> Na let this_val = vm.reg(this_reg); let payload_ptr = native_try!(array_buffer_payload(vm, this_val)); // SAFETY: 重取的 payload_ptr 指向存活载荷盒;字节借出止于本语句。 - let Some(data) = unsafe { &*payload_ptr }.data.as_ref() else { + let payload = unsafe { &*payload_ptr }; + if payload.detached { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); - }; + } + let data = &payload.bytes; if data.len() < final_ { return NativeResult::Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length")); } @@ -1036,8 +1118,8 @@ enum TransferKeep { /// 5. 物化拷贝(载荷指针新鲜,字节借出止于本步)。 /// 6. 新建缓冲(proto 取 %ArrayBuffer.prototype%,保持性按 keep),字节 /// 序列零填充至 newByteLength。 -/// 7. 再重取源载荷指针 detach(`data → None`):分配可已晋升源对象,新缓冲 -/// 持独立拷贝,源 detach 不影响返回值。 +/// 7. 再重取源载荷指针 detach(字节区清空、detached 置位):分配可已晋升 +/// 源对象,新缓冲持独立拷贝,源 detach 不影响返回值。 /// 8. 返回新缓冲。 fn array_buffer_copy_and_detach( vm: &mut H, this_reg: u8, new_length: Option, keep: TransferKeep, @@ -1046,7 +1128,8 @@ fn array_buffer_copy_and_detach( let payload_ptr = native_try!(array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷; // 只读当前字节长,借用即结束,不跨 JS 调用点。 - let cur_len = unsafe { &*payload_ptr }.data.as_ref().map_or(0, |d| d.len()); + let payload = unsafe { &*payload_ptr }; + let cur_len = if payload.detached { 0 } else { payload.bytes.len() }; let new_len = match new_length { // undefined 缺省臂(含显式 undefined)先于 ToIndex:显式 undefined 与 // 缺省同义,直接取源当前字节长。 @@ -1061,7 +1144,7 @@ fn array_buffer_copy_and_detach( // SAFETY: 重取的 payload_ptr 指向存活载荷盒;标量拷出后借用即结束。 let (cur_len, stored_max, immutable, detached) = unsafe { let p = &*payload_ptr; - (p.data.as_ref().map_or(0, |d| d.len()), p.max_byte_length, p.immutable, p.data.is_none()) + (if p.detached { 0 } else { p.bytes.len() }, p.max_byte_length, p.immutable, p.detached) }; if detached { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); @@ -1077,13 +1160,14 @@ fn array_buffer_copy_and_detach( if new_len > MAX_ARRAY_BUFFER_LENGTH { return NativeResult::Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length")); } - // SAFETY: 重取的 payload_ptr 指向存活载荷盒;detached 已排除,data 必为 - // Some。字节借出止于本语句。 + // SAFETY: 重取的 payload_ptr 指向存活载荷盒;detached 已排除,字节区恒 + // 在场。字节借出止于本语句。 let copy_len = new_len.min(cur_len); let mut new_data: Vec = unsafe { &*payload_ptr } - .data - .as_deref() - .map_or_else(Vec::new, |d| d[..copy_len].to_vec()); + .bytes + .as_slice() + .get(..copy_len) + .map_or_else(Vec::new, |d| d.to_vec()); new_data.resize(new_len, 0); let (dest_max, dest_immutable) = match keep { TransferKeep::Preserve => (stored_max, false), @@ -1103,7 +1187,10 @@ fn array_buffer_copy_and_detach( let this_val = vm.reg(this_reg); let payload_ptr = native_try!(array_buffer_payload(vm, this_val)); // SAFETY: 重取的 payload_ptr 指向存活载荷盒。 - unsafe { (*payload_ptr).data = None }; + unsafe { + (*payload_ptr).bytes.clear(); + (*payload_ptr).detached = true; + }; NativeResult::Ok(JsValue::from_js_object(dest_ptr)) } @@ -1172,7 +1259,8 @@ mod tests { #[test] fn detached_size_and_drop_idempotent() { let mut obj = ab_object_with_payload(ArrayBufferPayload { - data: None, + bytes: vec![], + detached: true, max_byte_length: 0, immutable: false, }); @@ -1186,7 +1274,8 @@ mod tests { #[test] fn attached_size_and_drop() { let mut obj = ab_object_with_payload(ArrayBufferPayload { - data: Some(vec![0u8; 8]), + bytes: vec![0u8; 8], + detached: false, max_byte_length: 0, immutable: false, }); @@ -1201,7 +1290,8 @@ mod tests { // 非 ArrayBuffer 类型标签(PLAIN)对象取载荷指针恒 None。 let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()); let payload_ptr = Arc::into_raw(Arc::new(ArrayBufferPayload { - data: Some(vec![0u8; 4]), + bytes: vec![0u8; 4], + detached: false, max_byte_length: 0, immutable: false, })); @@ -1282,7 +1372,7 @@ mod tests { let payload = unsafe { &*p }; assert_eq!(payload.max_byte_length, 9); assert!(!payload.immutable); - assert_eq!(payload.data.as_ref().expect("产物应附着").len(), 5); + assert_eq!(payload.bytes.len(), 5); } /// resize detach 守卫序钉:守卫在 newLength 求值之后——强转内 detach diff --git a/crates/oxide_builtins/src/atomics.rs b/crates/oxide_builtins/src/atomics.rs index 2ab3cc5c..0aff162a 100644 --- a/crates/oxide_builtins/src/atomics.rs +++ b/crates/oxide_builtins/src/atomics.rs @@ -8,10 +8,10 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::{JsObject, TypedArrayKind}; use oxide_types::value::JsValue; -use crate::array_buffer::{buffer_payload_ptr, ArrayBufferPayload}; +use crate::array_buffer::{buffer_store_mut_ptr, BufferStoreMut}; use crate::typed_array::{ get_typed_array_data, read_element, ta_element_value, ta_to_integer_or_infinity, ta_validate, write_element, - TypedArrayData, + write_element_shared, TypedArrayData, }; use oxide_runtime_api::{NativeResult, ProtoKind, VmHost}; @@ -78,9 +78,9 @@ fn is_bigint_kind(kind: TypedArrayKind) -> bool { /// /// # 副作用 /// 无(纯读;写守卫只读 immutable 位)。 -fn resolve_atomic_access( +fn resolve_atomic_access<'a, H: VmHost>( vm: &mut H, this_val: JsValue, writable: bool, index_val: JsValue, -) -> Result<(*mut ArrayBufferPayload, TypedArrayData, usize), JsValue> { +) -> Result<(BufferStoreMut<'a>, TypedArrayData, usize), JsValue> { let view = get_typed_array_data(vm, this_val)?; if !is_integer_kind(view.kind) { return Err(type_error(vm, "Atomics operations require an integer TypedArray type")); @@ -92,44 +92,55 @@ fn resolve_atomic_access( } let index = ta_to_integer_or_infinity(vm, index_val)?; - // 缓冲字节长:AB/SAB 双认(buffer_payload_ptr),载荷缺失视同越界。 + // 缓冲字节长:AB/SAB 双认,detached 视同 0(越界)。 let buffer_ptr = view.buffer.as_js_object_ptr(); if buffer_ptr.is_null() { return Err(type_error(vm, "TypedArray buffer internal state invalid")); } - // SAFETY: buffer 对象与视图同生命周期,此处只读载荷指针。 - let payload_ptr = match buffer_payload_ptr(unsafe { &*buffer_ptr }) { - Some(p) if !p.is_null() => p, - _ => return Err(range_error(vm, "invalid indexed access to Atomics")), + // SAFETY: buffer 对象与视图同生命周期,此处只读写载荷。 + let obj: &'a JsObject = unsafe { &*buffer_ptr }; + let buffer_len = match buffer_store_mut_ptr(obj) { + Some(BufferStoreMut::Ab(bytes)) => bytes.len(), + Some(BufferStoreMut::Sab(buffer)) => buffer.len(), + None => 0, }; - // SAFETY: payload_ptr 经 buffer_payload_ptr 校验为合法载荷盒,只读字节长。 - let buffer_len = unsafe { (*payload_ptr).data.as_ref().map_or(0, |d| d.len()) }; let offset = view.byte_offset as f64 + index * (view.kind.bytes_per_element() as f64); if offset < 0.0 || offset >= buffer_len as f64 { return Err(range_error(vm, "invalid indexed access to Atomics")); } - Ok((payload_ptr, view, offset as usize)) + // 界判通过,载荷必在场(detached 已越界排除)。 + let store = buffer_store_mut_ptr(obj) + .ok_or_else(|| range_error(vm, "invalid indexed access to Atomics"))?; + Ok((store, view, offset as usize)) } -/// 读指定偏移元素并转为 JS 值;detach(`data` 为 `None`)返回 `None`。 +/// 读指定偏移元素并转为 JS 值(detach 已由 resolve 的越界判收口,载荷恒在场)。 fn atomic_read( - vm: &mut H, payload_ptr: *mut ArrayBufferPayload, kind: TypedArrayKind, offset: usize, + vm: &mut H, store: &BufferStoreMut, kind: TypedArrayKind, offset: usize, ) -> Option { - // SAFETY: payload_ptr 经 resolve_atomic_access 校验为存活载荷盒,只读字节切片。 - let bytes = unsafe { &*payload_ptr }.data.as_deref()?; + // SAFETY: store 经 resolve_atomic_access 校验为存活载荷,只读字节切片。 + let bytes: &[u8] = match store { + BufferStoreMut::Ab(b) => b, + BufferStoreMut::Sab(buffer) => buffer.as_slice(), + }; Some(read_element(vm, kind, bytes, offset)) } -/// 把已转换值按位模式写入指定偏移;detach 静默 no-op(越界语义已由偏移判收口)。 +/// 把已转换值按位模式写入指定偏移(detach 已由 resolve 的越界判收口)。 fn atomic_write( - vm: &mut H, payload_ptr: *mut ArrayBufferPayload, kind: TypedArrayKind, offset: usize, value: JsValue, + vm: &mut H, store: &mut BufferStoreMut, kind: TypedArrayKind, offset: usize, value: JsValue, ) { - // SAFETY: payload_ptr 经 resolve_atomic_access 校验为存活载荷盒,只写字节切片。 - let Some(bytes) = unsafe { &mut *payload_ptr }.data.as_deref_mut() else { - return; - }; - write_element(vm, kind, bytes, offset, value); + // SAFETY: store 经 resolve_atomic_access 校验为存活载荷,只写字节切片。 + match store { + BufferStoreMut::Ab(bytes) => { + write_element(vm, kind, bytes, offset, value); + } + BufferStoreMut::Sab(buffer) => { + // SAB 臂无 detach 路径,写经共享缓冲(越界静默不写)。 + write_element_shared(vm, kind, buffer, offset, value); + } + } } /// 读-改-写运算结果:整数宽度按算术/位运算在 i32 域(语料值 < 2^31,f64 精确), @@ -184,9 +195,9 @@ fn arithmetic_write_value( /// /// # 副作用 /// 无(纯读)。 -fn resolve_atomic_wait_access( +fn resolve_atomic_wait_access<'a, H: VmHost>( vm: &mut H, this_val: JsValue, index_val: JsValue, require_sab: bool, -) -> Result<(*mut ArrayBufferPayload, TypedArrayData, usize, bool), JsValue> { +) -> Result<(BufferStoreMut<'a>, TypedArrayData, usize, bool), JsValue> { let view = get_typed_array_data(vm, this_val)?; if !matches!(view.kind, TypedArrayKind::Int32 | TypedArrayKind::BigInt64) { return Err(type_error(vm, "not an int32 or BigInt64 typed array")); @@ -197,18 +208,18 @@ fn resolve_atomic_wait_access( if buffer_ptr.is_null() { return Err(type_error(vm, "TypedArray buffer internal state invalid")); } - // SAFETY: buffer 对象与视图同生命周期,此处只读标签与载荷指针。 - let buffer = unsafe { &*buffer_ptr }; - let is_sab = buffer.is_shared_array_buffer_obj(); + // SAFETY: buffer 对象与视图同生命周期,此处只读写载荷。 + let obj: &'a JsObject = unsafe { &*buffer_ptr }; + let is_sab = obj.is_shared_array_buffer_obj(); if require_sab && !is_sab { return Err(type_error(vm, "Atomics.wait cannot be used on a non-shared buffer")); } - let payload_ptr = match buffer_payload_ptr(buffer) { - Some(p) if !p.is_null() => p, - _ => return Err(range_error(vm, "invalid indexed access to Atomics")), + // 缓冲字节长:AB/SAB 双认,detached 视同 0(越界)。 + let buffer_len = match buffer_store_mut_ptr(obj) { + Some(BufferStoreMut::Ab(bytes)) => bytes.len(), + Some(BufferStoreMut::Sab(buffer)) => buffer.len(), + None => 0, }; - // SAFETY: payload_ptr 经 buffer_payload_ptr 校验为合法载荷盒,只读字节长。 - let buffer_len = unsafe { (*payload_ptr).data.as_ref().map_or(0, |d| d.len()) }; let index = ta_to_integer_or_infinity(vm, index_val)?; let esize = view.kind.bytes_per_element() as f64; @@ -216,7 +227,10 @@ fn resolve_atomic_wait_access( if offset < 0.0 || offset + esize > buffer_len as f64 { return Err(range_error(vm, "invalid indexed access to Atomics")); } - Ok((payload_ptr, view, offset as usize, is_sab)) + // 界判通过,载荷必在场(detached 已越界排除)。 + let store = buffer_store_mut_ptr(obj) + .ok_or_else(|| range_error(vm, "invalid indexed access to Atomics"))?; + Ok((store, view, offset as usize, is_sab)) } /// `Atomics.wait(typedArray, index, value, timeout)` 单线程退化面:值/超时 @@ -227,11 +241,11 @@ pub fn atomics_wait(vm: &mut H, args: &[u8]) -> NativeResult { let index_val = arg_at(vm, args, 2); let value_val = arg_at(vm, args, 3); let timeout_val = arg_at(vm, args, 4); - let (payload_ptr, view, offset, _) = native_try!(resolve_atomic_wait_access(vm, ta_val, index_val, true)); + let (store, view, offset, _) = native_try!(resolve_atomic_wait_access(vm, ta_val, index_val, true)); let value = native_try!(ta_element_value(vm, view.kind, value_val)); // 超时 ToNumber 归一(NaN → +∞、负值 → 0):单线程下无观察差,只承载毒传播。 native_try!(oxide_runtime_api::to_number_full(timeout_val, vm).map_err(|e| crate::iterator::engine_error(vm, &e))); - let old = atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined()); + let old = atomic_read(vm, &store, view.kind, offset).unwrap_or(JsValue::undefined()); let equal = if is_bigint_kind(view.kind) { let old_bi = vm.bigint_value(old).clone(); let value_bi = vm.bigint_value(value).clone(); @@ -250,7 +264,7 @@ pub fn atomics_notify(vm: &mut H, args: &[u8]) -> NativeResult { let ta_val = arg_at(vm, args, 1); let index_val = arg_at(vm, args, 2); let count_val = arg_at(vm, args, 3); - let (_payload_ptr, view, offset, is_sab) = native_try!(resolve_atomic_wait_access(vm, ta_val, index_val, false)); + let (_store, view, offset, is_sab) = native_try!(resolve_atomic_wait_access(vm, ta_val, index_val, false)); // count 实参缺省 → +∞(全唤醒);显式传入才走强转(毒传播语料钉: // 非 SAB 亦先评估 count 再返 0)。 let count = if args.len() > 3 { @@ -270,8 +284,8 @@ pub fn atomics_notify(vm: &mut H, args: &[u8]) -> NativeResult { pub fn atomics_load(vm: &mut H, args: &[u8]) -> NativeResult { let ta_val = arg_at(vm, args, 1); let index_val = arg_at(vm, args, 2); - let (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, false, index_val)); - NativeResult::Ok(atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined())) + let (store, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, false, index_val)); + NativeResult::Ok(atomic_read(vm, &store, view.kind, offset).unwrap_or(JsValue::undefined())) } /// `Atomics.store(typedArray, index, value)`:写值后读回返回(宽度归一,-0 → +0)。 @@ -279,10 +293,10 @@ pub fn atomics_store(vm: &mut H, args: &[u8]) -> NativeResult { let ta_val = arg_at(vm, args, 1); let index_val = arg_at(vm, args, 2); let value_val = arg_at(vm, args, 3); - let (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); + let (mut store, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); let value = native_try!(ta_element_value(vm, view.kind, value_val)); - atomic_write(vm, payload_ptr, view.kind, offset, value); - NativeResult::Ok(atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(value)) + atomic_write(vm, &mut store, view.kind, offset, value); + NativeResult::Ok(atomic_read(vm, &store, view.kind, offset).unwrap_or(value)) } /// `Atomics.exchange(typedArray, index, value)`:读旧值、写新值、返回旧值。 @@ -290,10 +304,10 @@ pub fn atomics_exchange(vm: &mut H, args: &[u8]) -> NativeResult { let ta_val = arg_at(vm, args, 1); let index_val = arg_at(vm, args, 2); let value_val = arg_at(vm, args, 3); - let (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); - let old = atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined()); + let (mut store, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); + let old = atomic_read(vm, &store, view.kind, offset).unwrap_or(JsValue::undefined()); let value = native_try!(ta_element_value(vm, view.kind, value_val)); - atomic_write(vm, payload_ptr, view.kind, offset, value); + atomic_write(vm, &mut store, view.kind, offset, value); NativeResult::Ok(old) } @@ -327,11 +341,11 @@ fn atomic_arithmetic(vm: &mut H, args: &[u8], op: &str) -> NativeResu let ta_val = arg_at(vm, args, 1); let index_val = arg_at(vm, args, 2); let value_val = arg_at(vm, args, 3); - let (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); - let old = atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined()); + let (mut store, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); + let old = atomic_read(vm, &store, view.kind, offset).unwrap_or(JsValue::undefined()); let value = native_try!(ta_element_value(vm, view.kind, value_val)); let result = arithmetic_write_value(vm, view.kind, op, old, value); - atomic_write(vm, payload_ptr, view.kind, offset, result); + atomic_write(vm, &mut store, view.kind, offset, result); NativeResult::Ok(old) } @@ -342,8 +356,8 @@ pub fn atomics_compare_exchange(vm: &mut H, args: &[u8]) -> NativeRes let index_val = arg_at(vm, args, 2); let expected_val = arg_at(vm, args, 3); let replacement_val = arg_at(vm, args, 4); - let (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); - let old = atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined()); + let (mut store, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); + let old = atomic_read(vm, &store, view.kind, offset).unwrap_or(JsValue::undefined()); let expected = native_try!(ta_element_value(vm, view.kind, expected_val)); let matched = if is_bigint_kind(view.kind) { let old_bi = vm.bigint_value(old).clone(); @@ -354,7 +368,7 @@ pub fn atomics_compare_exchange(vm: &mut H, args: &[u8]) -> NativeRes }; if matched { let replacement = native_try!(ta_element_value(vm, view.kind, replacement_val)); - atomic_write(vm, payload_ptr, view.kind, offset, replacement); + atomic_write(vm, &mut store, view.kind, offset, replacement); } NativeResult::Ok(old) } @@ -390,9 +404,9 @@ pub fn atomics_wait_async(vm: &mut H, args: &[u8]) -> NativeResult { let index_val = arg_at(vm, args, 2); let value_val = arg_at(vm, args, 3); let timeout_val = arg_at(vm, args, 4); - let (payload_ptr, view, offset, _) = native_try!(resolve_atomic_wait_access(vm, ta_val, index_val, true)); + let (store, view, offset, _) = native_try!(resolve_atomic_wait_access(vm, ta_val, index_val, true)); let value = native_try!(ta_element_value(vm, view.kind, value_val)); - let old = atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined()); + let old = atomic_read(vm, &store, view.kind, offset).unwrap_or(JsValue::undefined()); let equal = if is_bigint_kind(view.kind) { let old_bi = vm.bigint_value(old).clone(); let value_bi = vm.bigint_value(value).clone(); diff --git a/crates/oxide_builtins/src/data_view.rs b/crates/oxide_builtins/src/data_view.rs index cf38f27f..af467da6 100644 --- a/crates/oxide_builtins/src/data_view.rs +++ b/crates/oxide_builtins/src/data_view.rs @@ -5,7 +5,9 @@ use oxide_types::value::JsValue; use num_bigint::BigInt; use num_traits::ToPrimitive; -use crate::array_buffer::{buffer_payload, ArrayBufferPayload}; +use crate::array_buffer::{ + buffer_store, buffer_store_mut_ptr, buffer_store_ptr, BufferStore, BufferStoreMut, +}; use oxide_runtime_api::{NativeResult, ProtoKind, VmHost}; @@ -111,17 +113,19 @@ fn get_data_view_data(vm: &mut H, this_val: JsValue) -> Result( - vm: &mut H, payload_ptr: *mut ArrayBufferPayload, view: DataViewData, + vm: &mut H, store: &BufferStore, view: DataViewData, ) -> Result { - // SAFETY: payload_ptr 由调用方读入口(buffer_payload)校验为合法缓冲区载荷。 - let Some(data) = unsafe { &*payload_ptr }.data.as_deref() else { + if store.is_detached() { return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); + } + let live = match store { + BufferStore::Ab(payload) => payload.bytes.len(), + BufferStore::Sab(payload) => payload.buffer.len(), }; - let live = data.len(); if view.length_is_auto { if view.byte_offset > live { return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); @@ -136,10 +140,12 @@ fn dv_view_bounds( /// set 面入口写守卫:immutable 缓冲 → TypeError(先于一切实参强转)。 fn dv_buffer_writable(vm: &mut H, view: DataViewData) -> Result<(), JsValue> { - let payload_ptr = buffer_payload(vm, view.buffer)?; - // SAFETY: payload_ptr 经 buffer_payload 校验为合法缓冲区载荷(AB/SAB 双认)。 - if unsafe { &*payload_ptr }.immutable { - return Err(crate::error::create_type_error(vm, "ArrayBuffer is immutable")); + let store = buffer_store(vm, view.buffer)?; + // SAFETY: store 经 buffer_store 校验为合法缓冲区载荷(AB/SAB 双认)。 + if let BufferStore::Ab(payload) = store { + if payload.immutable { + return Err(crate::error::create_type_error(vm, "ArrayBuffer is immutable")); + } } Ok(()) } @@ -149,32 +155,37 @@ fn dv_buffer_writable(vm: &mut H, view: DataViewData) -> Result<(), J /// 返回(寄存器重读值、live 长、存储上限)。 fn revalidate_buffer(vm: &mut H, buf_reg: u8) -> Result<(JsValue, usize, usize), JsValue> { let buffer = vm.reg(buf_reg); - let payload_ptr = buffer_payload(vm, buffer)?; - // SAFETY: payload_ptr 经 buffer_payload 校验为合法缓冲区载荷(AB/SAB 双认)。 - let payload = unsafe { &*payload_ptr }; - let Some(data) = payload.data.as_deref() else { + let store = buffer_store(vm, buffer)?; + // SAFETY: store 经 buffer_store 校验为合法缓冲区载荷(AB/SAB 双认)。 + if store.is_detached() { return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); + } + let (live, max_byte_length) = match store { + BufferStore::Ab(payload) => (payload.bytes.len(), payload.max_byte_length), + BufferStore::Sab(payload) => (payload.buffer.len(), usize::from(payload.growable)), }; - Ok((buffer, data.len(), payload.max_byte_length)) + Ok((buffer, live, max_byte_length)) } /// GetViewValue 后半:按已校验的视图与偏移读 N 字节。界判核先 /// (detach/OOB → TypeError,live 长口径),后视图相对越界 /// (→ RangeError,判活视图长)。 fn read_bytes_at(vm: &mut H, view: DataViewData, offset: usize) -> Result<[u8; N], JsValue> { - let payload_ptr = buffer_payload(vm, view.buffer)?; - // SAFETY: payload_ptr 经 buffer_payload 校验为合法缓冲区载荷(AB/SAB 双认)。 - let Some(buffer) = unsafe { &*payload_ptr }.data.as_deref() else { - return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); - }; - let view_len = dv_view_bounds(vm, payload_ptr, view)?; + let store = buffer_store(vm, view.buffer)?; + // SAFETY: store 经 buffer_store 校验为合法缓冲区载荷(AB/SAB 双认)。 + let view_len = dv_view_bounds(vm, &store, view)?; match offset.checked_add(N) { Some(end) if end <= view_len => {} _ => return Err(crate::error::create_range_error(vm, "DataView offset out of bounds")), } let abs = view.byte_offset + offset; let mut out = [0u8; N]; - out.copy_from_slice(&buffer[abs..abs + N]); + // 界判核已证载荷在场(detached 已拒),下方只读字节切片。 + let bytes: &[u8] = match &store { + BufferStore::Ab(payload) => &payload.bytes, + BufferStore::Sab(payload) => payload.buffer.as_slice(), + }; + out.copy_from_slice(&bytes[abs..abs + N]); Ok(out) } @@ -182,20 +193,35 @@ fn read_bytes_at(vm: &mut H, view: DataViewData, offs fn write_bytes( vm: &mut H, view: DataViewData, offset: usize, bytes: [u8; N], ) -> Result<(), JsValue> { - let payload_ptr = buffer_payload(vm, view.buffer)?; - // 界判核共享借用先于可变借用建立,避免活别名。 - let view_len = dv_view_bounds(vm, payload_ptr, view)?; - // SAFETY: payload_ptr 经 buffer_payload 校验为合法缓冲区载荷(AB/SAB 双认); - // 界判核已证 data 在场,下方守卫为结构性兜底。 - let Some(buffer) = unsafe { &mut *payload_ptr }.data.as_deref_mut() else { + let buffer_ptr = view.buffer.as_js_object_ptr(); + if buffer_ptr.is_null() { return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); - }; + } + // SAFETY: buffer 对象与视图同生命周期,此处只读写载荷。 + let obj = unsafe { &*buffer_ptr }; + // 界判核(只读克隆)先于可变借用建立,避免活别名。 + let store = buffer_store_ptr(obj) + .ok_or_else(|| crate::error::create_type_error(vm, "ArrayBuffer method called on incompatible receiver"))?; + let view_len = dv_view_bounds(vm, &store, view)?; match offset.checked_add(N) { Some(end) if end <= view_len => {} _ => return Err(crate::error::create_range_error(vm, "DataView offset out of bounds")), } let abs = view.byte_offset + offset; - buffer[abs..abs + N].copy_from_slice(&bytes); + // 界判核已拒 detached;此处取可变借用写回对象内载荷。 + let store = buffer_store_mut_ptr(obj) + .ok_or_else(|| crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"))?; + match store { + BufferStoreMut::Ab(b) => { + b[abs..abs + N].copy_from_slice(&bytes); + } + BufferStoreMut::Sab(buffer) => { + // 界判核已证 abs + N 在活长内,越界为结构性不可达(防御背板)。 + buffer + .write_range(abs, &bytes) + .map_err(|_| crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"))?; + } + } Ok(()) } @@ -258,7 +284,7 @@ pub fn data_view_constructor(vm: &mut H, args: &[u8]) -> NativeResult } let buffer = vm.reg(args[1]); // 首个载荷取用只做品牌校验,指针即弃,不持借用跨 JS 调用。 - native_try!(buffer_payload(vm, buffer)); + native_try!(buffer_store(vm, buffer)); let byte_offset = if args.len() > 2 { native_try!(to_index(vm, vm.reg(args[2]), "DataView byteOffset out of bounds")) @@ -731,8 +757,8 @@ pub fn data_view_buffer_getter(vm: &mut H, args: &[u8]) -> NativeResu pub fn data_view_byte_offset_getter(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_data_view_data(vm, this_val)); - let payload_ptr = native_try!(buffer_payload(vm, view.buffer)); - native_try!(dv_view_bounds(vm, payload_ptr, view)); + let store = native_try!(buffer_store(vm, view.buffer)); + native_try!(dv_view_bounds(vm, &store, view)); NativeResult::Ok(JsValue::float(view.byte_offset as f64)) } @@ -742,7 +768,7 @@ pub fn data_view_byte_offset_getter(vm: &mut H, args: &[u8]) -> Nativ pub fn data_view_byte_length_getter(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_data_view_data(vm, this_val)); - let payload_ptr = native_try!(buffer_payload(vm, view.buffer)); - let view_len = native_try!(dv_view_bounds(vm, payload_ptr, view)); + let store = native_try!(buffer_store(vm, view.buffer)); + let view_len = native_try!(dv_view_bounds(vm, &store, view)); NativeResult::Ok(JsValue::float(view_len as f64)) } diff --git a/crates/oxide_builtins/src/message_value.rs b/crates/oxide_builtins/src/message_value.rs index 6985f801..6bc3cd77 100644 --- a/crates/oxide_builtins/src/message_value.rs +++ b/crates/oxide_builtins/src/message_value.rs @@ -268,17 +268,22 @@ fn detach_array_buffer( // 转移:移动源载荷入消息,源转 detached 态。 // SAFETY: payload_ptr 经 array_buffer_payload_ptr 校验为存活载荷盒; // 移动为标量操作,不跨 JS 调用。 - let data = unsafe { (*payload_ptr).data.take() }; - let Some(data) = data else { + let src_detached = unsafe { (*payload_ptr).detached }; + if src_detached { return Err(data_clone_error(vm, "detached buffer in transfer list")); }; + let data = unsafe { std::mem::take(&mut (*payload_ptr).bytes) }; + unsafe { (*payload_ptr).detached = true }; return Ok(MessageValue::ArrayBuffer(Arc::new(data))); } // 未转移:克隆字节。 // SAFETY: payload_ptr 经 array_buffer_payload_ptr 校验为存活载荷盒;标量 // 拷出后借用即结束,不跨 JS 调用。 - let data = unsafe { (*payload_ptr).data.clone() }; - let Some(data) = data else { + let (data, detached) = unsafe { + let p = &*payload_ptr; + (p.bytes.clone(), p.detached) + }; + if detached { return Err(data_clone_error(vm, "detached ArrayBuffer is not cloneable")); }; Ok(MessageValue::ArrayBuffer(Arc::new(data))) @@ -802,7 +807,7 @@ mod tests { assert!(cl.is_array_buffer_obj()); let ptr = crate::array_buffer::array_buffer_payload_ptr(cl).unwrap(); // SAFETY: ptr 经 array_buffer_payload_ptr 校验为存活载荷盒。 - assert_eq!(unsafe { &*ptr }.data.as_ref(), Some(&vec![1, 2, 3, 4])); + assert_eq!(unsafe { &*ptr }.bytes.as_slice(), &[1, 2, 3, 4]); } #[test] @@ -816,13 +821,13 @@ mod tests { let src = unsafe { &*v.as_js_object_ptr() }; let src_ptr2 = crate::array_buffer::array_buffer_payload_ptr(src).unwrap(); // SAFETY: src_ptr2 经校验为存活载荷盒。 - assert!(unsafe { &*src_ptr2 }.data.is_none(), "transfer 后源应 detach"); + assert!(unsafe { &*src_ptr2 }.detached, "transfer 后源应 detach"); let mut target = Vm::new(); let r = rehydrate(&mut target, &mv); let cl = unsafe { &*r.as_js_object_ptr() }; let ptr = crate::array_buffer::array_buffer_payload_ptr(cl).unwrap(); // SAFETY: ptr 经校验为存活载荷盒。 - assert_eq!(unsafe { &*ptr }.data.as_ref(), Some(&vec![9, 8, 7])); + assert_eq!(unsafe { &*ptr }.bytes.as_slice(), &[9, 8, 7]); } #[test] diff --git a/crates/oxide_builtins/src/structured_clone.rs b/crates/oxide_builtins/src/structured_clone.rs index ae10992c..9eed7d9a 100644 --- a/crates/oxide_builtins/src/structured_clone.rs +++ b/crates/oxide_builtins/src/structured_clone.rs @@ -141,7 +141,7 @@ fn parse_transfer_list(vm: &mut H, options: JsValue, state: &mut Clon // 拷出后借用即结束,不跨 JS 调用。 let max_byte_length = unsafe { (*payload_ptr).max_byte_length }; let proto = crate::array_buffer::default_array_buffer_proto(vm); - let clone_ptr = alloc_buffer_object(vm, None, max_byte_length, false, proto); + let clone_ptr = alloc_buffer_object(vm, Vec::new(), max_byte_length, false, proto); state.seen.insert(src_ptr, clone_ptr); state.transfer_placeholders.push((src_ptr, clone_ptr)); Ok(()) @@ -169,12 +169,18 @@ fn finish_transfer(vm: &mut H, state: &mut CloneState) -> Result<(), .unwrap() }; // SAFETY: 两载荷指针均经校验为存活载荷盒;移动为标量操作,不跨 JS 调用。 - let data = unsafe { (*src_payload).data.take() }; + let src_detached = unsafe { (*src_payload).detached }; // 转移条目 detached 不可转移(规范报 DataCloneError)。 - let Some(data) = data else { + if src_detached { return Err(data_clone_error(vm, "detached buffer in transfer list")); }; - unsafe { (*clone_payload).data = Some(data) }; + let bytes = unsafe { std::mem::take(&mut (*src_payload).bytes) }; + unsafe { + // 源转 detached 态(字节区清空、标志置位),克隆占位填为源载荷。 + (*src_payload).detached = true; + (*clone_payload).bytes = bytes; + (*clone_payload).detached = false; + }; } Ok(()) } @@ -567,28 +573,29 @@ fn alloc_array_buffer_clone(vm: &mut H, src: &JsObject) -> Result<*mu }; // SAFETY: payload_ptr 经 array_buffer_payload_ptr 校验为存活载荷盒;标量 // 拷出后借用即结束,不跨 JS 调用。 - let (data, max_byte_length) = unsafe { + let (bytes, max_byte_length, detached) = unsafe { let p = &*payload_ptr; - (p.data.clone(), p.max_byte_length) + (p.bytes.clone(), p.max_byte_length, p.detached) }; // 值图内 detached 缓冲不可克隆(规范报 DataCloneError)。 - let Some(data) = data else { + if detached { return Err(data_clone_error(vm, "detached ArrayBuffer is not cloneable")); - }; + } let proto = crate::array_buffer::default_array_buffer_proto(vm); // 克隆是新缓冲:immutable 恒 false(不继承源标志),存储态上限照抄。 - let clone_ptr = alloc_buffer_object(vm, Some(data), max_byte_length, false, proto); + let clone_ptr = alloc_buffer_object(vm, bytes, max_byte_length, false, proto); Ok(clone_ptr) } -/// 分配携给定载荷形态(`data` 为 `None` 即 detached)的 ArrayBuffer 对象。 +/// 分配携给定载荷形态(字节区、存储态上限、immutable 标志)的 ArrayBuffer 对象。 fn alloc_buffer_object( - vm: &mut H, data: Option>, max_byte_length: usize, immutable: bool, proto: JsValue, + vm: &mut H, bytes: Vec, max_byte_length: usize, immutable: bool, proto: JsValue, ) -> *mut JsObject { let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto); obj.type_tag = JsObject::OBJ_TYPE_ARRAY_BUFFER; let payload = crate::array_buffer::ArrayBufferPayload { - data, + bytes, + detached: false, max_byte_length, immutable, }; @@ -903,7 +910,12 @@ mod tests { fn ab_bytes(ab: &JsObject) -> Option> { let ptr = crate::array_buffer::array_buffer_payload_ptr(ab)?; // SAFETY: ptr 经 array_buffer_payload_ptr 校验为存活载荷盒。 - unsafe { &*ptr }.data.as_ref().cloned() + let p = unsafe { &*ptr }; + if p.detached { + None + } else { + Some(p.bytes.clone()) + } } /// 读 TypedArray 视图状态盒(Copy)。 @@ -1015,9 +1027,17 @@ mod tests { crate::array_buffer::shared_array_buffer_payload_ptr(unsafe { &*v.as_js_object_ptr() }).unwrap(); let cl_payload = crate::array_buffer::shared_array_buffer_payload_ptr(cl).unwrap(); assert!(std::ptr::eq(src_payload, cl_payload), "克隆应共享同一载荷"); + // 内层字节缓冲为同一 Arc(克隆侧写入对源侧可见)。 + assert!( + std::ptr::eq( + Arc::as_ptr(&unsafe { &*src_payload }.buffer), + Arc::as_ptr(&unsafe { &*cl_payload }.buffer), + ), + "克隆应共享同一内层字节缓冲" + ); // SAFETY: 载荷盒存活。 - unsafe { (*cl_payload).data.as_mut().unwrap()[0] = 0xAB }; - assert_eq!(unsafe { &*src_payload }.data.as_ref().unwrap()[0], 0xAB); + unsafe { (*cl_payload).buffer.write_range(0, &[0xAB]).unwrap() }; + assert_eq!(unsafe { &*src_payload }.buffer.as_slice()[0], 0xAB); } #[test] diff --git a/crates/oxide_builtins/src/typed_array.rs b/crates/oxide_builtins/src/typed_array.rs index e8191b0e..ab4db7ad 100644 --- a/crates/oxide_builtins/src/typed_array.rs +++ b/crates/oxide_builtins/src/typed_array.rs @@ -1,12 +1,15 @@ use num_bigint::BigInt; use num_traits::ToPrimitive; use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_kernel::SharedBuffer; use oxide_types::object::{JsObject, NativeFnPtr, TypedArrayKind}; use oxide_types::private_key::{encode_symbol_key, int_key_value, is_int_key, WELL_KNOWN_SYMBOL_SPECIES}; use oxide_types::value::JsValue; use crate::array_buffer::{ - buffer_payload, buffer_payload_ptr, default_array_buffer_proto, new_array_buffer, MAX_ARRAY_BUFFER_LENGTH, + array_buffer_payload_ptr, buffer_store, buffer_store_mut_ptr, buffer_store_ptr, + default_array_buffer_proto, new_array_buffer, BufferStore, BufferStoreMut, + MAX_ARRAY_BUFFER_LENGTH, }; use oxide_runtime_api::{NativeResult, ProtoKind, VmHost}; @@ -242,19 +245,22 @@ fn absolute_byte_offset(view: TypedArrayData, index: usize) -> usize { } /// 读视图引用 buffer 的当前字节长度;ArrayBuffer/SharedArrayBuffer 双认, -/// buffer 两标签之外、无载荷或已 detach(`data` 为 `None`)时返回 `None`。 +/// buffer 两标签之外、无载荷或已 detach 时返回 `None`。 fn ta_buffer_byte_length(view: TypedArrayData) -> Option { let buffer_ptr = view.buffer.as_js_object_ptr(); if buffer_ptr.is_null() { return None; } // SAFETY: buffer 对象与视图同生命周期,此处只读载荷存活位与长度。 - let payload_ptr = buffer_payload_ptr(unsafe { &*buffer_ptr })?; - if payload_ptr.is_null() { - return None; + let store = buffer_store_ptr(unsafe { &*buffer_ptr })?; + if store.is_detached() { + None + } else { + match store { + BufferStore::Ab(payload) => Some(payload.bytes.len()), + BufferStore::Sab(payload) => Some(payload.buffer.len()), + } } - // SAFETY: payload_ptr 经 buffer_payload_ptr 校验为合法载荷盒。 - unsafe { (*payload_ptr).data.as_ref().map(|data| data.len()) } } /// 规范 TypedArrayLength:已 detach 恒 0;定长视图取静态长度(哪怕 buffer @@ -305,9 +311,9 @@ pub(crate) fn ta_validate( if writable { let buffer_ptr = view.buffer.as_js_object_ptr(); if !buffer_ptr.is_null() { - // SAFETY: buffer 对象与视图同生命周期,此处只读写守卫位。 - if let Some(payload_ptr) = buffer_payload_ptr(unsafe { &*buffer_ptr }) { - if !payload_ptr.is_null() && unsafe { (*payload_ptr).immutable } { + // SAFETY: buffer 对象与视图同生命周期,此处只读守卫位。 + if let Some(BufferStore::Ab(payload)) = buffer_store_ptr(unsafe { &*buffer_ptr }) { + if payload.immutable { return Err(type_error(vm, "ArrayBuffer is immutable")); } } @@ -356,15 +362,19 @@ pub fn typed_array_element_get(vm: &mut H, obj: &JsObject, index: u32 return Err("TypedArray buffer internal state invalid".to_string()); } // SAFETY: buffer 对象与视图同生命周期,此处只读载荷存活位与字节切片。 - let Some(payload_ptr) = buffer_payload_ptr(unsafe { &*buffer_ptr }) else { + let Some(store) = buffer_store_ptr(unsafe { &*buffer_ptr }) else { // 防御背板:构造期 buffer 必为 ArrayBuffer/SharedArrayBuffer 之一。 return Err("TypedArray buffer internal state invalid".to_string()); }; - let Some(buffer) = unsafe { &*payload_ptr }.data.as_deref() else { + if store.is_detached() { // 载荷缺失即 detach(live 界判后不可达,防御背板):按 [[Get]] 读 undefined。 return Ok(JsValue::undefined()); + } + let bytes: &[u8] = match &store { + BufferStore::Ab(payload) => &payload.bytes, + BufferStore::Sab(payload) => payload.buffer.as_slice(), }; - Ok(read_element(vm, view.kind, buffer, absolute_byte_offset(view, index as usize))) + Ok(read_element(vm, view.kind, bytes, absolute_byte_offset(view, index as usize))) } // ── 统一数值键门 ───────────────────────────────────────────────────────── @@ -607,16 +617,13 @@ fn write_typed_array_element( return Err("TypedArray buffer internal state invalid".to_string()); } // SAFETY: buffer 对象与视图同生命周期,此处只写载荷字节切片。 - let Some(payload_ptr) = buffer_payload_ptr(unsafe { &*buffer_ptr }) else { - // 防御背板:构造期 buffer 必为 ArrayBuffer/SharedArrayBuffer 之一。 - return Err("TypedArray buffer internal state invalid".to_string()); - }; - let Some(buffer) = unsafe { &mut *payload_ptr }.data.as_deref_mut() else { - // 载荷缺失即 detach(live 界判后不可达,防御背板):转换副作用已先 - // 发生,写按 [[Set]] 静默 no-op。 + let obj = unsafe { &*buffer_ptr }; + // 载荷缺失即 detach(live 界判后不可达,防御背板):转换副作用已先 + // 发生,写按 [[Set]] 静默 no-op。 + let Some(mut store) = buffer_store_mut_ptr(obj) else { return Ok(()); }; - write_element(vm, view.kind, buffer, absolute_byte_offset(view, index as usize), elem); + ta_store_write(vm, &mut store, view.kind, absolute_byte_offset(view, index as usize), elem); Ok(()) } @@ -754,6 +761,91 @@ pub(crate) fn write_element( } } +/// 把已转换值按元素类型编码后写入共享缓冲(SAB 臂):越界(含活长收缩后 +/// 越界)静默不写,与 [`write_element`] 的 live 视图越界语义同口径。 +pub(crate) fn write_element_shared( + vm: &mut H, kind: TypedArrayKind, buffer: &SharedBuffer, offset: usize, value: JsValue, +) { + // 活长可被并发推进/收缩:元素字节区越界时静默不写(live 视图越界语义)。 + if offset + kind.bytes_per_element() > buffer.len() { + return; + } + let n = oxide_runtime_api::to_number(value); + let mut bytes = [0u8; 8]; + let len = match kind { + TypedArrayKind::Int8 => { + bytes[0] = n as i32 as u8 as i8 as u8; + 1 + } + TypedArrayKind::Uint8 => { + bytes[0] = n as i32 as u8; + 1 + } + TypedArrayKind::Uint8Clamped => { + bytes[0] = n.clamp(0.0, 255.0).round() as u8; + 1 + } + TypedArrayKind::Int16 => { + bytes[..2].copy_from_slice(&(n as i32 as u16 as i16).to_ne_bytes()); + 2 + } + TypedArrayKind::Uint16 => { + bytes[..2].copy_from_slice(&(n as i32 as u16).to_ne_bytes()); + 2 + } + TypedArrayKind::Int32 => { + bytes[..4].copy_from_slice(&(n as i32).to_ne_bytes()); + 4 + } + TypedArrayKind::Uint32 => { + bytes[..4].copy_from_slice(&(n as u32).to_ne_bytes()); + 4 + } + TypedArrayKind::Float32 => { + bytes[..4].copy_from_slice(&(n as f32).to_ne_bytes()); + 4 + } + TypedArrayKind::Float64 => { + bytes[..8].copy_from_slice(&n.to_ne_bytes()); + 8 + } + TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 => { + // 取低 64 位位模式(与 [`write_element`] 同口径):BigInt64 按位模式 + // 重解释为 i64(二进制补码),非 BigInt 值按 ToBigInt 语义落 0。 + static ZERO_BI: std::sync::LazyLock = std::sync::LazyLock::new(|| BigInt::from(0_i64)); + let v: &BigInt = if value.is_bigint() { vm.bigint_value(value) } else { &ZERO_BI }; + let low = (v & (BigInt::from(u64::MAX))) + .to_u64() + .expect("与 u64::MAX 掩码后恒在 u64 范围"); + let bytes64 = if matches!(kind, TypedArrayKind::BigInt64) { + (low as i64).to_ne_bytes() + } else { + low.to_ne_bytes() + }; + bytes[..8].copy_from_slice(&bytes64); + 8 + } + }; + // 越界由 write_range 守卫(活长收缩后静默不写,不改变活长)。 + let _ = buffer.write_range(offset, &bytes[..len]); +} + +/// 按元素类型把已转换值写入缓冲区载荷(AB 臂对象字节区独占借用,SAB 臂共享 +/// 缓冲写);越界静默不写(live 视图越界语义)。detach 由调用方经 +/// [`buffer_store_mut_ptr`] 的 `None` 收口,此处载荷恒在场。 +fn ta_store_write( + vm: &mut H, store: &mut BufferStoreMut, kind: TypedArrayKind, offset: usize, value: JsValue, +) { + match store { + BufferStoreMut::Ab(bytes) => { + write_element(vm, kind, bytes, offset, value); + } + BufferStoreMut::Sab(buffer) => { + write_element_shared(vm, kind, buffer, offset, value); + } + } +} + /// 收集源对象的元素值。 /// /// # 步骤 @@ -783,17 +875,21 @@ fn collect_array_like( } if obj.is_typed_array_obj() { let view = get_typed_array_data(vm, value)?; - let payload_ptr = buffer_payload(vm, view.buffer)?; - // SAFETY: payload_ptr 经 buffer_payload 校验为合法缓冲区载荷(AB/SAB 双认)。 - let Some(buffer) = unsafe { &*payload_ptr }.data.as_deref() else { + let store = buffer_store(vm, view.buffer)?; + // SAFETY: store 经 buffer_store 校验为合法缓冲区载荷(AB/SAB 双认)。 + if store.is_detached() { // 源 buffer detach:按规范的源缓冲校验步抛 TypeError;各入口的 - // 校验/活长界判先行,此臂为防御背板。 + // 校验/活长界判先行,此判为防御背板。 return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); + } + let bytes: &[u8] = match &store { + BufferStore::Ab(payload) => &payload.bytes, + BufferStore::Sab(payload) => payload.buffer.as_slice(), }; // 源视图按 live 长度收集:buffer 收缩越界后视同空源(与构造器 // TypedArrayCreate 的数组元素收集口径一致)。 return Ok((0..ta_live_length(view)) - .map(|i| read_element(vm, view.kind, buffer, absolute_byte_offset(view, i))) + .map(|i| read_element(vm, view.kind, bytes, absolute_byte_offset(view, i))) .collect()); } @@ -882,7 +978,7 @@ fn typed_array_new(vm: &mut H, args: &[u8], kind: TypedArrayKind) -> // ArrayBuffer/SharedArrayBuffer 双认:长度/偏移/长度校验与定长臂同构; // SAB 无 detach 产出路径,载荷恒在(防御背板保留)。 if first_obj.is_array_buffer_obj() || first_obj.is_shared_array_buffer_obj() { - let Some(payload_ptr) = buffer_payload_ptr(first_obj) else { + let Some(store) = buffer_store_ptr(first_obj) else { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); }; // spec 步序:ToIndex(offset) → offset 模校验 → ToIndex(length) → @@ -906,22 +1002,24 @@ fn typed_array_new(vm: &mut H, args: &[u8], kind: TypedArrayKind) -> } else { native_try!(to_index(vm, vm.reg(args[3]), "TypedArray length out of bounds")) }; - // SAFETY: payload_ptr 经 buffer_payload_ptr 校验为合法缓冲区载荷。 - let payload = unsafe { &*payload_ptr }; - let Some(data) = payload.data.as_ref() else { - // 首参 buffer 载荷缺失(AB 转换窗口内已 detach;SAB 为防御背板): + // SAFETY: store 经 buffer_store_ptr 校验为合法缓冲区载荷。 + if store.is_detached() { + // 首参 buffer 载荷缺失(AB 转换窗口内已 detach): // 按规范的 IsDetached 步抛 TypeError。 return NativeResult::Err(type_error(vm, "ArrayBuffer internal state invalid")); + } + let (buffer_len, fixed) = match &store { + BufferStore::Ab(payload) => (payload.bytes.len(), payload.max_byte_length == 0), + BufferStore::Sab(payload) => (payload.buffer.len(), !payload.growable), }; - let buffer_len = data.len(); if byte_offset > buffer_len { return NativeResult::Err(range_error(vm, "TypedArray byteOffset out of bounds")); } let remaining = buffer_len - byte_offset; if auto_length { - // 定长 + 省略 length:剩余字节须被元素大小整除(max_byte_length - // 存储态 0 = 定长,AB/SAB 同编码);可增缓冲无此模校验。 - if payload.max_byte_length == 0 && remaining % bpe != 0 { + // 定长 + 省略 length:剩余字节须被元素大小整除;可增缓冲 + // 无此模校验。 + if fixed && remaining % bpe != 0 { return NativeResult::Err(range_error(vm, "TypedArray length out of bounds")); } length = remaining / bpe; @@ -941,15 +1039,19 @@ fn typed_array_new(vm: &mut H, args: &[u8], kind: TypedArrayKind) -> let byte_len = values.len().saturating_mul(bpe); let buffer = JsValue::from_js_object(new_array_buffer(vm, vec![0; byte_len], 0, default_array_buffer_proto(vm))); - let payload_ptr = native_try!(buffer_payload(vm, buffer)); - // SAFETY: payload_ptr 经 buffer_payload 校验为合法缓冲区载荷(AB/SAB 双认)。 - let Some(buffer_ref) = unsafe { &mut *payload_ptr }.data.as_deref_mut() else { + // 新建 buffer 必为 ArrayBuffer(非 SAB),直接经对象内载荷写元素。 + let buffer_ptr = buffer.as_js_object_ptr(); + // SAFETY: buffer 对象本 session 存活,此处只写载荷字节切片。 + let obj = unsafe { &*buffer_ptr }; + let Some(payload_ptr) = array_buffer_payload_ptr(obj) else { // 新建 buffer 载荷恒存活(无人可 detach),此臂为防御背板。 return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); }; + // SAFETY: payload_ptr 经 array_buffer_payload_ptr 校验为合法载荷盒,借用止于循环。 + let payload = unsafe { &mut *payload_ptr }; for (idx, value) in values.into_iter().enumerate() { let elem = native_try!(ta_element_value(vm, kind, value)); - write_element(vm, kind, buffer_ref, idx * bpe, elem); + write_element(vm, kind, &mut payload.bytes, idx * bpe, elem); } (buffer, 0, byte_len / bpe, false, proto) } @@ -1093,15 +1195,16 @@ pub fn typed_array_fill(vm: &mut H, args: &[u8]) -> NativeResult { native_try!(ta_validate(vm, view, true)); let start = clamp_index_to_len(start_raw, len); let end = clamp_index_to_len(end_raw, len).min(ta_spec_length(view)); - let payload_ptr = native_try!(buffer_payload(vm, view.buffer)); - // SAFETY: payload_ptr 经 buffer_payload 校验为合法缓冲区载荷(AB/SAB 双认)。 - let Some(buffer) = unsafe { &mut *payload_ptr }.data.as_deref_mut() else { - // 转换窗口内 detach:填充写退化为静默 no-op(循环内无重入窗,单次 - // 判即封窗),按 [[Set]] 语义正常返回 this。 + let buffer_ptr = view.buffer.as_js_object_ptr(); + // SAFETY: buffer 对象与视图同生命周期,此处只写载荷字节切片。 + let obj = unsafe { &*buffer_ptr }; + // 转换窗口内的 detach:填充写退化为静默 no-op(循环内无重入窗,单次 + // 判即封窗),按 [[Set]] 语义正常返回 this。 + let Some(mut store) = buffer_store_mut_ptr(obj) else { return NativeResult::Ok(this_val); }; for idx in start..end.max(start) { - write_element(vm, view.kind, buffer, absolute_byte_offset(view, idx), elem); + ta_store_write(vm, &mut store, view.kind, absolute_byte_offset(view, idx), elem); } NativeResult::Ok(this_val) } @@ -1268,8 +1371,11 @@ pub fn typed_array_subarray(vm: &mut H, args: &[u8]) -> NativeResult // 2 参构造(省略 count,子视图随 buffer 伸缩)仅当 end 未给、源视图为 // auto 且 buffer 可缩放;定长视图恒三参定死区间(窗口超 buffer 由构造器 // 抛 RangeError)。 - let resizable = match buffer_payload(vm, view.buffer) { - Ok(p) => unsafe { &*p }.max_byte_length != 0, + let resizable = match buffer_store(vm, view.buffer) { + Ok(store) => match store { + BufferStore::Ab(payload) => payload.max_byte_length != 0, + BufferStore::Sab(payload) => payload.growable, + }, Err(_) => false, }; let ctor_args = if end_undefined && view.auto_length && resizable { @@ -1332,12 +1438,13 @@ pub fn typed_array_set(vm: &mut H, args: &[u8]) -> NativeResult { if (source_len as u64) + (offset as u64) > len as u64 { return NativeResult::Err(range_error(vm, "TypedArray.set offset out of bounds")); } - let payload_ptr = native_try!(buffer_payload(vm, view.buffer)); - // SAFETY: payload_ptr 经 buffer_payload 校验为合法缓冲区载荷(AB/SAB 双认)。 - let mut buffer = unsafe { &mut *payload_ptr }.data.as_deref_mut(); + let buffer_ptr = view.buffer.as_js_object_ptr(); + // SAFETY: buffer 对象与视图同生命周期,此处只写载荷字节切片。 + let obj = unsafe { &*buffer_ptr }; // 逐元素惰性读源→转换→写:循环中的 detach/收缩使后续写入静默失效 // (目标越界不写,与规范 SetValueInBuffer 边界无操作同语义);载荷缺失 // 等同 detach,全循环退化为源读副作用与转换、零写入。 + let mut store = buffer_store_mut_ptr(obj); for k in 0..source_len { let value = match &source_kind { SetSourceKind::TypedArray(sview) => native_try!(ta_read(vm, *sview, k)), @@ -1346,8 +1453,8 @@ pub fn typed_array_set(vm: &mut H, args: &[u8]) -> NativeResult { }; let elem = native_try!(ta_element_value(vm, view.kind, value)); if offset + k < ta_live_length(view) { - if let Some(buffer) = buffer.as_deref_mut() { - write_element(vm, view.kind, buffer, absolute_byte_offset(view, offset + k), elem); + if let Some(s) = &mut store { + ta_store_write(vm, s, view.kind, absolute_byte_offset(view, offset + k), elem); } } } @@ -1490,14 +1597,15 @@ fn set_typed_array_element(vm: &mut H, ta: JsValue, index: usize, val if index >= ta_live_length(view) { return Ok(()); } - let payload_ptr = buffer_payload(vm, view.buffer)?; - // SAFETY: payload_ptr 经 buffer_payload 校验为合法缓冲区载荷(AB/SAB 双认)。 - let Some(buffer) = unsafe { &mut *payload_ptr }.data.as_deref_mut() else { - // 载荷缺失即 detach(live 界判后不可达,防御背板):转换副作用已先 - // 发生,写按 [[Set]] 静默 no-op。 + let buffer_ptr = view.buffer.as_js_object_ptr(); + // SAFETY: buffer 对象与视图同生命周期,此处只写载荷字节切片。 + let obj = unsafe { &*buffer_ptr }; + // 载荷缺失即 detach(live 界判后不可达,防御背板):转换副作用已先 + // 发生,写按 [[Set]] 静默 no-op。 + let Some(mut store) = buffer_store_mut_ptr(obj) else { return Ok(()); }; - write_element(vm, view.kind, buffer, absolute_byte_offset(view, index), elem); + ta_store_write(vm, &mut store, view.kind, absolute_byte_offset(view, index), elem); Ok(()) } @@ -1526,14 +1634,18 @@ fn ta_read(vm: &mut H, view: TypedArrayData, index: usize) -> Result< if index >= ta_live_length(view) { return Ok(JsValue::undefined()); } - let payload_ptr = buffer_payload(vm, view.buffer)?; - // SAFETY: payload_ptr 经 buffer_payload 校验为合法缓冲区载荷(AB/SAB 双认)。 - let Some(buffer) = unsafe { &*payload_ptr }.data.as_deref() else { + let store = buffer_store(vm, view.buffer)?; + // SAFETY: store 经 buffer_store 校验为合法缓冲区载荷(AB/SAB 双认)。 + if store.is_detached() { // 载荷缺失即 detach(live 界判后不可达,防御背板):按 [[Get]] 读 // undefined。 return Ok(JsValue::undefined()); + } + let bytes: &[u8] = match &store { + BufferStore::Ab(payload) => &payload.bytes, + BufferStore::Sab(payload) => payload.buffer.as_slice(), }; - Ok(read_element(vm, view.kind, buffer, absolute_byte_offset(view, index))) + Ok(read_element(vm, view.kind, bytes, absolute_byte_offset(view, index))) } /// 把已转换的值写入 TypedArray 指定索引(视图 data 已取出的形式,供原型方法内部使用)。 @@ -1543,13 +1655,14 @@ fn ta_write(vm: &mut H, view: TypedArrayData, index: usize, value: Js if index >= ta_live_length(view) { return Ok(()); } - let payload_ptr = buffer_payload(vm, view.buffer)?; - // SAFETY: payload_ptr 经 buffer_payload 校验为合法缓冲区载荷(AB/SAB 双认)。 - let Some(buffer) = unsafe { &mut *payload_ptr }.data.as_deref_mut() else { - // 载荷缺失即 detach(live 界判后不可达,防御背板):写静默 no-op。 + let buffer_ptr = view.buffer.as_js_object_ptr(); + // SAFETY: buffer 对象与视图同生命周期,此处只写载荷字节切片。 + let obj = unsafe { &*buffer_ptr }; + // 载荷缺失即 detach(live 界判后不可达,防御背板):写静默 no-op。 + let Some(mut store) = buffer_store_mut_ptr(obj) else { return Ok(()); }; - write_element(vm, view.kind, buffer, absolute_byte_offset(view, index), value); + ta_store_write(vm, &mut store, view.kind, absolute_byte_offset(view, index), value); Ok(()) } @@ -2625,36 +2738,44 @@ pub fn uint8array_to_base64(vm: &mut H, args: &[u8]) -> NativeResult .map_err(|e| crate::iterator::engine_error(vm, &e))); omit_padding = ta_to_boolean(v); } - let payload_ptr = native_try!(buffer_payload(vm, view.buffer)); - // SAFETY: payload_ptr 来自活动缓冲区对象(AB/SAB 双认),视图范围由构造保证界内。 - let Some(buffer) = unsafe { &*payload_ptr }.data.as_deref() else { + let store = native_try!(buffer_store(vm, view.buffer)); + // SAFETY: store 来自活动缓冲区对象(AB/SAB 双认),视图范围由构造保证界内。 + if store.is_detached() { // buffer detach:按规范的视图校验步抛 TypeError;入口校验先行, - // 此臂为防御背板。 + // 此判为防御背板。 return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); + } + let bytes: &[u8] = match &store { + BufferStore::Ab(payload) => &payload.bytes, + BufferStore::Sab(payload) => payload.buffer.as_slice(), }; // 视图范围按 live 长度与当前缓冲长度双收口(缓冲可被 resize 收缩、 // 视图可越界)。 - let start = view.byte_offset.min(buffer.len()); - let end = (view.byte_offset.saturating_add(ta_live_length(view))).min(buffer.len()); - NativeResult::Ok(vm.new_string_owned(crate::ta_codec::encode_base64(&buffer[start..end], alphabet, omit_padding))) + let start = view.byte_offset.min(bytes.len()); + let end = (view.byte_offset.saturating_add(ta_live_length(view))).min(bytes.len()); + NativeResult::Ok(vm.new_string_owned(crate::ta_codec::encode_base64(&bytes[start..end], alphabet, omit_padding))) } /// `%Uint8Array%.prototype.toHex()`:ValidateUint8Array 后编码小写两位 hex。 pub fn uint8array_to_hex(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(validate_uint8_array(vm, this_val)); - let payload_ptr = native_try!(buffer_payload(vm, view.buffer)); - // SAFETY: payload_ptr 来自活动缓冲区对象(AB/SAB 双认),视图范围由构造保证界内。 - let Some(buffer) = unsafe { &*payload_ptr }.data.as_deref() else { + let store = native_try!(buffer_store(vm, view.buffer)); + // SAFETY: store 来自活动缓冲区对象(AB/SAB 双认),视图范围由构造保证界内。 + if store.is_detached() { // buffer detach:按规范的视图校验步抛 TypeError;入口校验先行, - // 此臂为防御背板。 + // 此判为防御背板。 return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); + } + let bytes: &[u8] = match &store { + BufferStore::Ab(payload) => &payload.bytes, + BufferStore::Sab(payload) => payload.buffer.as_slice(), }; // 视图范围按 live 长度与当前缓冲长度双收口(缓冲可被 resize 收缩、 // 视图可越界)。 - let start = view.byte_offset.min(buffer.len()); - let end = (view.byte_offset.saturating_add(ta_live_length(view))).min(buffer.len()); - NativeResult::Ok(vm.new_string_owned(crate::ta_codec::encode_hex(&buffer[start..end]))) + let start = view.byte_offset.min(bytes.len()); + let end = (view.byte_offset.saturating_add(ta_live_length(view))).min(bytes.len()); + NativeResult::Ok(vm.new_string_owned(crate::ta_codec::encode_hex(&bytes[start..end]))) } /// `%Uint8Array%.prototype.setFromBase64(string, options)`:解码写入视图, @@ -2664,26 +2785,55 @@ pub fn uint8array_set_from_base64(vm: &mut H, args: &[u8]) -> NativeR let view = native_try!(validate_uint8_array(vm, this_val)); let string = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let options = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - let payload_ptr = native_try!(buffer_payload(vm, view.buffer)); + let buffer_ptr = view.buffer.as_js_object_ptr(); + // SAFETY: buffer 对象与视图同生命周期,此处只读写载荷。 + let obj = unsafe { &*buffer_ptr }; // 目标缓冲不可变(immutable):写前抛 TypeError(规范 ValidateUint8Array // 后的不可变守卫步)。 - if unsafe { (*payload_ptr).immutable } { - return NativeResult::Err(type_error(vm, "ArrayBuffer is immutable")); + if let Some(ptr) = array_buffer_payload_ptr(obj) { + if !ptr.is_null() { + // SAFETY: ptr 经 array_buffer_payload_ptr 校验为存活载荷盒。 + let payload = unsafe { &*ptr }; + if payload.immutable { + return NativeResult::Err(type_error(vm, "ArrayBuffer is immutable")); + } + } } - // SAFETY: payload_ptr 来自活动缓冲区对象(AB/SAB 双认),视图范围由构造保证界内。 - let Some(buffer) = unsafe { &mut *payload_ptr }.data.as_deref_mut() else { - // buffer detach:按规范的视图校验步抛 TypeError;入口校验先行, - // 此臂为防御背板。 + let start = view.byte_offset; + // buffer detach:按规范的视图校验步抛 TypeError;入口校验先行,此判为防御背板。 + let Some(store) = buffer_store_mut_ptr(obj) else { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); }; - let start = view.byte_offset; - let (read, written) = - native_try!(ta_decode_base64(vm, string, options, Some(ta_live_length(view)), &mut |idx, b| { - // 缓冲可被 resize 收缩:目标字节越界时静默不写(live 视图越界语义)。 - if start + idx < buffer.len() { - buffer[start + idx] = b; - } - })); + let (read, written) = match store { + BufferStoreMut::Ab(bytes) => { + native_try!(ta_decode_base64( + vm, + string, + options, + Some(ta_live_length(view)), + &mut |idx, b| { + // 缓冲可被 resize 收缩:目标字节越界时静默不写(live 视图越界语义)。 + if start + idx < bytes.len() { + bytes[start + idx] = b; + } + }, + )) + } + BufferStoreMut::Sab(buffer) => { + native_try!(ta_decode_base64( + vm, + string, + options, + Some(ta_live_length(view)), + &mut |idx, b| { + // 活长可被并发推进:目标字节越界时静默不写(live 视图越界语义)。 + if start + idx < buffer.len() { + let _ = buffer.write_range(start + idx, &[b]); + } + }, + )) + } + }; NativeResult::Ok(ta_read_written_result(vm, read, written)) } @@ -2696,28 +2846,49 @@ pub fn uint8array_set_from_hex(vm: &mut H, args: &[u8]) -> NativeResu if !string.is_string() { return NativeResult::Err(type_error(vm, "string argument required")); } - let payload_ptr = native_try!(buffer_payload(vm, view.buffer)); + let buffer_ptr = view.buffer.as_js_object_ptr(); + // SAFETY: buffer 对象与视图同生命周期,此处只读写载荷。 + let obj = unsafe { &*buffer_ptr }; // 目标缓冲不可变(immutable):写前抛 TypeError(规范 ValidateUint8Array // 后的不可变守卫步)。 - if unsafe { (*payload_ptr).immutable } { - return NativeResult::Err(type_error(vm, "ArrayBuffer is immutable")); + if let Some(ptr) = array_buffer_payload_ptr(obj) { + if !ptr.is_null() { + // SAFETY: ptr 经 array_buffer_payload_ptr 校验为存活载荷盒。 + let payload = unsafe { &*ptr }; + if payload.immutable { + return NativeResult::Err(type_error(vm, "ArrayBuffer is immutable")); + } + } } - // SAFETY: payload_ptr 来自活动缓冲区对象(AB/SAB 双认),视图范围由构造保证界内。 - let Some(buffer) = unsafe { &mut *payload_ptr }.data.as_deref_mut() else { - // buffer detach:按规范的视图校验步抛 TypeError;入口校验先行, - // 此臂为防御背板。 - return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); - }; let start = view.byte_offset; let units = vm.string_units(string).into_owned(); - let (read, written) = match crate::ta_codec::decode_hex(&units, Some(ta_live_length(view)), &mut |idx, b| { - // 缓冲可被 resize 收缩:目标字节越界时静默不写(live 视图越界语义)。 - if start + idx < buffer.len() { - buffer[start + idx] = b; + // buffer detach:按规范的视图校验步抛 TypeError;入口校验先行,此判为防御背板。 + let Some(store) = buffer_store_mut_ptr(obj) else { + return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); + }; + let (read, written) = match store { + BufferStoreMut::Ab(bytes) => { + match crate::ta_codec::decode_hex(&units, Some(ta_live_length(view)), &mut |idx, b| { + // 缓冲可被 resize 收缩:目标字节越界时静默不写(live 视图越界语义)。 + if start + idx < bytes.len() { + bytes[start + idx] = b; + } + }) { + Ok(r) => r, + Err(_) => return NativeResult::Err(crate::error::create_syntax_error(vm, "invalid hex input")), + } + } + BufferStoreMut::Sab(buffer) => { + match crate::ta_codec::decode_hex(&units, Some(ta_live_length(view)), &mut |idx, b| { + // 活长可被并发推进:目标字节越界时静默不写(live 视图越界语义)。 + if start + idx < buffer.len() { + let _ = buffer.write_range(start + idx, &[b]); + } + }) { + Ok(r) => r, + Err(_) => return NativeResult::Err(crate::error::create_syntax_error(vm, "invalid hex input")), + } } - }) { - Ok(r) => r, - Err(_) => return NativeResult::Err(crate::error::create_syntax_error(vm, "invalid hex input")), }; NativeResult::Ok(ta_read_written_result(vm, read, written)) } @@ -2772,7 +2943,8 @@ pub fn uint8array_from_hex(vm: &mut H, args: &[u8]) -> NativeResult { #[cfg(test)] mod tests { use super::*; - use crate::array_buffer::ArrayBufferPayload; + use std::sync::Arc; + use crate::array_buffer::{ArrayBufferPayload, SharedArrayBufferPayload}; fn gate(text: &str, length: usize) -> TaIndexGate { ta_index_gate_from_text(text, length) @@ -2782,22 +2954,33 @@ mod tests { fn ab_object_with_data(data: Option>) -> JsObject { let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()); obj.type_tag = JsObject::OBJ_TYPE_ARRAY_BUFFER; + let detached = data.is_none(); let payload = ArrayBufferPayload { - data, + bytes: data.unwrap_or_default(), + detached, max_byte_length: 0, immutable: false, }; - let payload_ptr = Box::into_raw(Box::new(payload)); + let payload_ptr = Arc::into_raw(Arc::new(payload)); // SAFETY: 载荷盒形态与 new_array_buffer 的 native_fn 槽存储一致,测试结束前恰好释放一次。 obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(payload_ptr as *const ()) })); obj } /// 手工构造带载荷盒的 SharedArrayBuffer 对象(不经 Vm,只验 live 长度纯核; - /// 载荷盒形态与 ArrayBuffer 臂同构)。 + /// 字节区经核账目登记,随对象释放)。 fn sab_object_with_data(data: Option>) -> JsObject { - let mut obj = ab_object_with_data(data); + let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined()); obj.type_tag = JsObject::OBJ_TYPE_SHARED_ARRAY_BUFFER; + let core = oxide_kernel::KernelCore::new(oxide_kernel::KernelConfig::minimal()); + let buffer = Arc::new(SharedBuffer::new(core, data.unwrap_or_default())); + let payload = SharedArrayBufferPayload { + buffer, + growable: false, + }; + let payload_ptr = Arc::into_raw(Arc::new(payload)); + // SAFETY: 载荷盒形态与 new_shared_array_buffer 的 native_fn 槽存储一致,测试结束前恰好释放一次。 + obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(payload_ptr as *const ()) })); obj } diff --git a/crates/oxide_runtime_api/Cargo.toml b/crates/oxide_runtime_api/Cargo.toml index 954b60e7..941253b6 100644 --- a/crates/oxide_runtime_api/Cargo.toml +++ b/crates/oxide_runtime_api/Cargo.toml @@ -9,6 +9,7 @@ description = "OxideJS - VmHost trait: the abstract Vm interface builtins are wr [dependencies] oxide_bytecode = { path = "../oxide_bytecode" } oxide_types = { path = "../oxide_types" } +oxide_kernel = { path = "../oxide_kernel" } num-bigint = "0.4" num-traits = "0.2" oxide_log = { path = "../oxide_log" } diff --git a/crates/oxide_runtime_api/src/host.rs b/crates/oxide_runtime_api/src/host.rs index 5162fd60..c2656d40 100644 --- a/crates/oxide_runtime_api/src/host.rs +++ b/crates/oxide_runtime_api/src/host.rs @@ -1,7 +1,9 @@ //! `VmHost` trait:builtins 依赖的 `Vm` 能力集合,trait 面向泛型而非对象安全。 use std::ffi::c_void; +use std::sync::Arc; +use oxide_kernel::KernelCore; use oxide_types::mem::P; use oxide_types::object::{Cell, JsObject, PropAttributes}; use oxide_types::value::JsValue; @@ -224,6 +226,9 @@ pub trait VmHost { fn string_default_iterator(&self) -> *const JsObject; /// 读当前 session 全局对象(引用计数持久指针,跨调用存活)。 fn global_object(&self) -> P; + /// 读内核核共享指针(SAB 字节区构造用:`SharedBuffer` 随核的 + /// `shared_buffer_bytes` 账目登记与释放)。 + fn kernel_core(&self) -> Arc; // 属性解析 fn property_key_si(&mut self, val: JsValue) -> u32; diff --git a/crates/oxide_vm/src/vm/vmhost.rs b/crates/oxide_vm/src/vm/vmhost.rs index 864421ea..8f9f0a0e 100644 --- a/crates/oxide_vm/src/vm/vmhost.rs +++ b/crates/oxide_vm/src/vm/vmhost.rs @@ -3,6 +3,7 @@ use std::ffi::c_void; use std::sync::Arc; +use oxide_kernel::KernelCore; use oxide_runtime_api::{ProtoKind, ShapeNode}; use oxide_types::mem::P; use oxide_types::object::{Cell, JsObject, PropAttributes}; @@ -152,6 +153,9 @@ impl oxide_runtime_api::VmHost for Vm { let session = self.session(); session.global_object().clone() } + fn kernel_core(&self) -> Arc { + Arc::clone(self.kernel_core()) + } fn pc(&self) -> usize { self.pc }