refactor: 拆分缓冲区载荷为 AB/SAB 双认入口,SAB 字节改走核账目

This commit is contained in:
2026-10-08 10:30:04 +00:00
parent 3f26995040
commit 04df4070fa
11 changed files with 731 additions and 382 deletions
+225 -135
View File
@@ -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<Vec<u8>>,
pub(crate) bytes: Vec<u8>,
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<SharedBuffer>,
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<BufferStoreMut<'a>> {
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<H: VmHost>(
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::<ArrayBufferPayload>() + payload.data.as_ref().map_or(0, |d| d.capacity())) as u64
(std::mem::size_of::<ArrayBufferPayload>() + payload.bytes.capacity()) as u64
}
/// 释放 ArrayBuffer 载荷盒(native_fn 槽),返回字节数;槽置空后重复调用零释放。
@@ -155,16 +220,22 @@ pub(crate) fn default_shared_array_buffer_proto<H: VmHost>(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<H: VmHost>(
vm: &mut H, data: Vec<u8>, 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<H: VmHost>(
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<BufferStore> {
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<H: VmHost>(vm: &mut H, this_val: JsValue) -> Result<*mut ArrayBufferPayload, JsValue> {
/// - SAB 无 detach 生产路径,载荷恒在场;detach 分叉由消费方经
/// `is_detached()` 探针现读处理。
/// - 调用方不得跨 JS 调用窗口持有返回载荷(晋升可改写对象,须重取)。
pub(crate) fn buffer_store<H: VmHost>(vm: &mut H, this_val: JsValue) -> Result<BufferStore, JsValue> {
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<H: VmHost>(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::<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;
};
if payload_ptr.is_null() {
return 0;
}
std::mem::size_of::<SharedArrayBufferPayload>() 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::<SharedArrayBufferPayload>() 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<H: VmHost>(vm: &mut H, args: &[u8]) -> Na
}
/// 读入口:校验 receiver 为 SharedArrayBuffer 并取载荷指针。SAB 无 detach
/// 生产路径,载荷 `data` 恒在场(`map_or` 兜底与 AB 读入口同形)。
/// 生产路径,载荷恒在场(`map_or` 兜底与 AB 读入口同形)。
pub(crate) fn shared_array_buffer_payload<H: VmHost>(
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<H: VmHost>(
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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(
vm: &mut H, this_val: JsValue,
) -> Result<*mut ArrayBufferPayload, JsValue> {
@@ -660,7 +732,8 @@ pub fn array_buffer_byte_length<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(
vm: &mut H, this_reg: u8, new_length: Option<JsValue>, keep: TransferKeep,
@@ -1046,7 +1128,8 @@ fn array_buffer_copy_and_detach<H: VmHost>(
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<H: VmHost>(
// 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<H: VmHost>(
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<u8> = 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<H: VmHost>(
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
+67 -53
View File
@@ -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<H: VmHost>(
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<H: VmHost>(
}
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<H: VmHost>(
vm: &mut H, payload_ptr: *mut ArrayBufferPayload, kind: TypedArrayKind, offset: usize,
vm: &mut H, store: &BufferStoreMut, kind: TypedArrayKind, offset: usize,
) -> Option<JsValue> {
// 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<H: VmHost>(
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<H: VmHost>(
///
/// # 副作用
/// 无(纯读)。
fn resolve_atomic_wait_access<H: VmHost>(
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<H: VmHost>(
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<H: VmHost>(
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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
pub fn atomics_load<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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();
+61 -35
View File
@@ -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<H: VmHost>(vm: &mut H, this_val: JsValue) -> Result<DataVi
}
/// 视图界判核(规范 IsViewOutOfBounds 与 GetViewByteLength 合一):缓冲已
/// detach(载荷 `data` 为 `None`)、auto 视图 offset 超 live、定长视图
/// detach(AB 臂 detached 标志)、auto 视图 offset 超 live、定长视图
/// offset + 静态长超 live(含溢出)→ TypeError;通过时返回视图长
/// (auto 视图现算 live − offset,定长视图返静态长)。
fn dv_view_bounds<H: VmHost>(
vm: &mut H, payload_ptr: *mut ArrayBufferPayload, view: DataViewData,
vm: &mut H, store: &BufferStore, view: DataViewData,
) -> Result<usize, JsValue> {
// 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<H: VmHost>(
/// set 面入口写守卫:immutable 缓冲 → TypeError(先于一切实参强转)。
fn dv_buffer_writable<H: VmHost>(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<H: VmHost>(vm: &mut H, view: DataViewData) -> Result<(), J
/// 返回(寄存器重读值、live 长、存储上限)。
fn revalidate_buffer<H: VmHost>(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<const N: usize, H: VmHost>(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<const N: usize, H: VmHost>(vm: &mut H, view: DataViewData, offs
fn write_bytes<const N: usize, H: VmHost>(
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<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
pub fn data_view_byte_offset_getter<H: VmHost>(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<H: VmHost>(vm: &mut H, args: &[u8]) -> Nativ
pub fn data_view_byte_length_getter<H: VmHost>(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))
}
+12 -7
View File
@@ -268,17 +268,22 @@ fn detach_array_buffer<H: VmHost>(
// 转移:移动源载荷入消息,源转 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]
+35 -15
View File
@@ -141,7 +141,7 @@ fn parse_transfer_list<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(
vm: &mut H, data: Option<Vec<u8>>, max_byte_length: usize, immutable: bool, proto: JsValue,
vm: &mut H, bytes: Vec<u8>, 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<Vec<u8>> {
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]
+300 -117
View File
@@ -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<usize> {
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<H: VmHost>(
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<H: VmHost>(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<H: VmHost>(
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<H: VmHost>(
}
}
/// 把已转换值按元素类型编码后写入共享缓冲(SAB 臂):越界(含活长收缩后
/// 越界)静默不写,与 [`write_element`] 的 live 视图越界语义同口径。
pub(crate) fn write_element_shared<H: VmHost>(
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<BigInt> = 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<H: VmHost>(
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<H: VmHost>(
}
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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<Vec<u8>>) -> 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<Vec<u8>>) -> 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
}
+1
View File
@@ -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" }
+5
View File
@@ -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<JsObject>;
/// 读内核核共享指针(SAB 字节区构造用:`SharedBuffer` 随核的
/// `shared_buffer_bytes` 账目登记与释放)。
fn kernel_core(&self) -> Arc<KernelCore>;
// 属性解析
fn property_key_si(&mut self, val: JsValue) -> u32;
+4
View File
@@ -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<KernelCore> {
Arc::clone(self.kernel_core())
}
fn pc(&self) -> usize {
self.pc
}