mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
1590 lines
75 KiB
Rust
1590 lines
75 KiB
Rust
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;
|
||
|
||
use oxide_runtime_api::{NativeResult, ProtoKind, VmHost};
|
||
|
||
use crate::array::to_integer_or_infinity_bounded;
|
||
|
||
pub(crate) const MAX_ARRAY_BUFFER_LENGTH: usize = 1 << 30;
|
||
|
||
/// 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) 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 {
|
||
Ok(value) => value,
|
||
Err(err) => return NativeResult::Err(err),
|
||
}
|
||
};
|
||
}
|
||
|
||
fn type_error<H: VmHost>(vm: &mut H, msg: &str) -> NativeResult {
|
||
NativeResult::Err(crate::error::create_type_error(vm, msg))
|
||
}
|
||
|
||
/// ToIndex:undefined → 0;ToInteger 传播式(NaN → +0、trunc、±Infinity
|
||
/// 保留);负值与 ToLength 夹取后不可同值表示(≥ 2^53,含 +Infinity)→
|
||
/// RangeError。分配期上界(引擎上限)不在本阶段校验。
|
||
fn to_index<H: VmHost>(vm: &mut H, value: JsValue) -> Result<usize, JsValue> {
|
||
if value.is_undefined() {
|
||
return Ok(0);
|
||
}
|
||
let n = match vm.coerce_number_bounded(value) {
|
||
Ok(n) => n,
|
||
Err(err) => return Err(crate::iterator::engine_error(vm, &err)),
|
||
};
|
||
// ToInteger:NaN → +0,±Infinity 原样,有限值截断。
|
||
let integer = if n.is_nan() { 0.0 } else { n.trunc() };
|
||
if integer < 0.0 {
|
||
return Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length"));
|
||
}
|
||
// ToLength 夹取到 2^53-1:≥ 2^53 的整数与夹取值不可同值,拒绝。
|
||
if integer >= 9_007_199_254_740_992.0_f64 {
|
||
return Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length"));
|
||
}
|
||
Ok(integer as usize)
|
||
}
|
||
|
||
/// ToIntegerOrInfinity:ToNumber 传播式(用户转换异常原值恢复),NaN → +0、
|
||
/// trunc,±Infinity 保留——界判由调用方上界比较收口。
|
||
fn to_integer_or_infinity<H: VmHost>(vm: &mut H, value: JsValue) -> Result<f64, JsValue> {
|
||
let n = match vm.coerce_number_bounded(value) {
|
||
Ok(n) => n,
|
||
Err(err) => return Err(crate::iterator::engine_error(vm, &err)),
|
||
};
|
||
Ok(if n.is_nan() { 0.0 } else { n.trunc() })
|
||
}
|
||
|
||
/// ToIntegerOrInfinity:ToNumber 传播式(BigInt/Symbol/转换异常向上传播),
|
||
/// NaN → 0、trunc、±Infinity 保留——负值归 0、正值归 len 由调用侧折叠。
|
||
fn normalize_index<H: VmHost>(vm: &mut H, value: JsValue, len: usize) -> Result<usize, JsValue> {
|
||
let n = to_integer_or_infinity_bounded(vm, value)?;
|
||
// ±Infinity 不截断,负无穷归 0、正无穷归 len,
|
||
// 避免饱和成 isize::MIN 后取负溢出。
|
||
if n.is_infinite() {
|
||
return Ok(if n < 0.0 { 0 } else { len });
|
||
}
|
||
let int = n.trunc() as isize;
|
||
if int < 0 {
|
||
Ok(len.saturating_sub(int.unsigned_abs()))
|
||
} else {
|
||
Ok((int as usize).min(len))
|
||
}
|
||
}
|
||
|
||
/// 默认缓冲区原型 %ArrayBuffer.prototype%(构造器回落路径与各定长建点共用)。
|
||
pub(crate) fn default_array_buffer_proto<H: VmHost>(vm: &H) -> JsValue {
|
||
JsValue::from_js_object(vm.builtin_proto(ProtoKind::ArrayBufferProto))
|
||
}
|
||
|
||
/// 分配携给定 proto 与载荷形态(`max_byte_length` 为存储态:0 定长,非 0 可
|
||
/// resize)的 ArrayBuffer 对象;byteLength 经原型访问器读(不写 own 数据属性)。
|
||
pub(crate) fn new_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_ARRAY_BUFFER;
|
||
let payload = ArrayBufferPayload {
|
||
bytes: data,
|
||
detached: false,
|
||
max_byte_length,
|
||
immutable: false,
|
||
};
|
||
let payload_ptr = Arc::into_raw(Arc::new(payload));
|
||
// SAFETY: ArrayBuffer 对象不可调用,故 native_fn 槽复用作不透明载荷盒
|
||
// 指针,与既有 RegExp 存储模式一致。
|
||
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(payload_ptr as *const ()) }));
|
||
vm.alloc_object(obj)
|
||
}
|
||
|
||
pub(crate) fn array_buffer_payload_ptr(obj: &JsObject) -> Option<*mut ArrayBufferPayload> {
|
||
if !obj.is_array_buffer_obj() {
|
||
return None;
|
||
}
|
||
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut ArrayBufferPayload)
|
||
}
|
||
|
||
/// 只读核算 ArrayBuffer 载荷字节(不释放):结构体尺寸 + 附着缓冲容量,
|
||
/// detached 载荷仅计结构体尺寸。
|
||
pub fn array_buffer_native_size(obj: &JsObject) -> u64 {
|
||
let Some(payload_ptr) = array_buffer_payload_ptr(obj) else {
|
||
return 0;
|
||
};
|
||
if payload_ptr.is_null() {
|
||
return 0;
|
||
}
|
||
// SAFETY: payload_ptr 非空且指向存活载荷盒。
|
||
let payload = unsafe { &*payload_ptr };
|
||
(std::mem::size_of::<ArrayBufferPayload>() + payload.bytes.capacity()) as u64
|
||
}
|
||
|
||
/// 释放 ArrayBuffer 载荷盒(native_fn 槽),返回字节数;槽置空后重复调用零释放。
|
||
pub fn drop_array_buffer_native(obj: &mut JsObject) -> u64 {
|
||
let bytes = array_buffer_native_size(obj);
|
||
if bytes == 0 {
|
||
return 0;
|
||
}
|
||
let Some(payload_ptr) = array_buffer_payload_ptr(obj) else {
|
||
return 0;
|
||
};
|
||
// SAFETY: payload_ptr 非空(array_buffer_native_size 已验证),Arc::from_raw 恰好减引用一次。
|
||
let payload = unsafe { Arc::from_raw(payload_ptr) };
|
||
drop(payload);
|
||
obj.set_native_fn(None);
|
||
bytes
|
||
}
|
||
|
||
/// 默认缓冲区原型 %SharedArrayBuffer.prototype%(构造器回落路径与各定长建点共用)。
|
||
pub(crate) fn default_shared_array_buffer_proto<H: VmHost>(vm: &H) -> JsValue {
|
||
JsValue::from_js_object(vm.builtin_proto(ProtoKind::SharedArrayBufferProto))
|
||
}
|
||
|
||
/// 分配携给定 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 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 槽复用为不透明载荷盒
|
||
// 指针,与 ArrayBuffer 存储模式一致。
|
||
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(payload_ptr as *const ()) }));
|
||
vm.alloc_object(obj)
|
||
}
|
||
|
||
pub(crate) fn shared_array_buffer_payload_ptr(obj: &JsObject) -> Option<*mut SharedArrayBufferPayload> {
|
||
if !obj.is_shared_array_buffer_obj() {
|
||
return None;
|
||
}
|
||
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut SharedArrayBufferPayload)
|
||
}
|
||
|
||
/// 缓冲区载荷统一双认入口:ArrayBuffer 与 SharedArrayBuffer 双标签双认,
|
||
/// 克隆出统一形态的载荷(AB 臂克隆字节区,SAB 臂仅增 Arc 计数);两标签
|
||
/// 之外(含槽位空)返回 `None`。
|
||
pub(crate) fn buffer_store_ptr(obj: &JsObject) -> Option<BufferStore> {
|
||
if obj.is_array_buffer_obj() {
|
||
let ptr = array_buffer_payload_ptr(obj)?;
|
||
if ptr.is_null() {
|
||
return None;
|
||
}
|
||
// SAFETY: ptr 非空且指向存活载荷盒,克隆只读标量与字节区。
|
||
return Some(BufferStore::Ab(unsafe { (*ptr).clone() }));
|
||
}
|
||
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` 与
|
||
/// `array_buffer_payload` 路径同函数同序(逐位同形),SAB 臂经
|
||
/// `shared_array_buffer_payload_ptr`;非对象 / 双标签外 / 槽位空(防御背板)
|
||
/// → TypeError。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 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"));
|
||
}
|
||
let obj_ptr = this_val.as_js_object_ptr();
|
||
if obj_ptr.is_null() {
|
||
return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||
}
|
||
// SAFETY: is_object 保证指针非空且对象本 session 存活。
|
||
let obj = unsafe { &*obj_ptr };
|
||
let Some(store) = buffer_store_ptr(obj) else {
|
||
return Err(crate::error::create_type_error(vm, "ArrayBuffer method called on incompatible receiver"));
|
||
};
|
||
Ok(store)
|
||
}
|
||
|
||
/// SharedArrayBuffer 载荷盒字节数(`native_fn` 槽):只计结构体(字节区
|
||
/// 账目在核的 `shared_buffer_bytes`);非 SAB 或已释放 → 0。
|
||
pub fn shared_array_buffer_native_size(obj: &JsObject) -> u64 {
|
||
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);
|
||
obj.set_native_fn(None);
|
||
bytes
|
||
}
|
||
|
||
/// `SharedArrayBuffer(length [, options])`:构造语义仅 `new`;length 缺省 0,
|
||
/// 经 ToIndex 传播式(强转副作用原值上抛);`maxByteLength` 选项经传播式
|
||
/// Get + ToIndex,`resizable` 键恒忽略(SAB 面不读);分配期上界校验在原型
|
||
/// 读之后。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 非构造形态(普通调用,new.target 缺失)→ TypeError。
|
||
/// 2. length = ToIndex(length) 传播式;undefined/缺省 → 0。
|
||
/// 3. GetArrayBufferMaxByteLengthOption:options 非对象 → 定长;Get
|
||
/// "maxByteLength" 传播式;undefined → 定长;否则 ToIndex 传播式。
|
||
/// 4. length > max → RangeError(先于原型读与对象创建)。
|
||
/// 5. GetPrototypeFromConstructor:经 ordinary_get 读 new.target 的
|
||
/// "prototype"(访问器触发,异常原值传播);非对象结果回落
|
||
/// %SharedArrayBuffer.prototype%。
|
||
/// 6. 分配期上界校验(length 与 max 各一条,超引擎上限 → RangeError)。
|
||
/// 7. 对象创建(零填充,定长/growable 由选项是否在场决定);不设 own
|
||
/// maxByteLength 数据属性(ArrayBuffer 构造器有此步,SAB 面经原型访问器
|
||
/// 读)。
|
||
pub fn shared_array_buffer_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
if !vm.constructing_native() {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer must be called with new"));
|
||
}
|
||
let length = if args.len() > 1 { native_try!(to_index(vm, vm.reg(args[1]))) } else { 0 };
|
||
// GetArrayBufferMaxByteLengthOption:None = 空(定长),Some(v) = 请求的
|
||
// 上限(v 可为 0,与"空"语义相异:length > v 的界判须生效)。
|
||
let max_opt = if args.len() > 2 {
|
||
let options = vm.reg(args[2]);
|
||
if !options.is_object() {
|
||
None
|
||
} else {
|
||
let options_ptr = options.as_js_object_ptr();
|
||
// SAFETY: is_object 保证指针非空且对象本 session 存活。
|
||
let options_obj = unsafe { &*options_ptr };
|
||
let si = vm.perm_intern("maxByteLength");
|
||
let max_val = match vm.ordinary_get(options_obj, si, options) {
|
||
Ok(v) => v,
|
||
Err(err) => return NativeResult::Err(crate::iterator::engine_error(vm, &err)),
|
||
};
|
||
if max_val.is_undefined() {
|
||
None
|
||
} else {
|
||
Some(native_try!(to_index(vm, max_val)))
|
||
}
|
||
}
|
||
} else {
|
||
None
|
||
};
|
||
if let Some(max) = max_opt {
|
||
if length > max {
|
||
return NativeResult::Err(crate::error::create_range_error(vm, "invalid SharedArrayBuffer length"));
|
||
}
|
||
}
|
||
// GetPrototypeFromConstructor:prototype getter 可抛错,异常原值传播;
|
||
// 结果非对象时回落默认原型。
|
||
let default_proto = default_shared_array_buffer_proto(vm);
|
||
let new_target = vm.reg(255);
|
||
let proto = if new_target.is_object() {
|
||
let nt_ptr = new_target.as_js_object_ptr();
|
||
// SAFETY: is_object 保证指针非空且对象本 session 存活。
|
||
let nt_obj = unsafe { &*nt_ptr };
|
||
let proto_si = vm.perm_intern("prototype");
|
||
let proto_val = match vm.ordinary_get(nt_obj, proto_si, new_target) {
|
||
Ok(v) => v,
|
||
Err(err) => return NativeResult::Err(crate::iterator::engine_error(vm, &err)),
|
||
};
|
||
if proto_val.is_object() {
|
||
proto_val
|
||
} else {
|
||
default_proto
|
||
}
|
||
} else {
|
||
default_proto
|
||
};
|
||
// 分配期上界:length 与 max 超引擎上限一律拒绝(ToIndex 阶段不校验)。
|
||
if length > MAX_ARRAY_BUFFER_LENGTH || max_opt.is_some_and(|m| m > MAX_ARRAY_BUFFER_LENGTH) {
|
||
return NativeResult::Err(crate::error::create_range_error(vm, "invalid SharedArrayBuffer length"));
|
||
}
|
||
// 存储态上限 = 请求上限 + 1(上限 0 与定长可分;定长存 0)。
|
||
let obj_ptr = new_shared_array_buffer(vm, vec![0; length], max_opt.map_or(0, |m| m + 1), proto);
|
||
NativeResult::Ok(JsValue::from_js_object(obj_ptr))
|
||
}
|
||
|
||
/// 读入口:校验 receiver 为 SharedArrayBuffer 并取载荷指针。SAB 无 detach
|
||
/// 生产路径,载荷恒在场(`map_or` 兜底与 AB 读入口同形)。
|
||
pub(crate) fn shared_array_buffer_payload<H: VmHost>(
|
||
vm: &mut H, this_val: JsValue,
|
||
) -> Result<*mut SharedArrayBufferPayload, JsValue> {
|
||
if !this_val.is_object() {
|
||
return Err(crate::error::create_type_error(
|
||
vm,
|
||
"SharedArrayBuffer method called on incompatible receiver",
|
||
));
|
||
}
|
||
let obj_ptr = this_val.as_js_object_ptr();
|
||
if obj_ptr.is_null() {
|
||
return Err(crate::error::create_type_error(vm, "SharedArrayBuffer internal state invalid"));
|
||
}
|
||
let obj = unsafe { &*obj_ptr };
|
||
if !obj.is_shared_array_buffer_obj() {
|
||
return Err(crate::error::create_type_error(
|
||
vm,
|
||
"SharedArrayBuffer method called on incompatible receiver",
|
||
));
|
||
}
|
||
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 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:返回缓冲区活字节数
|
||
/// (定长与 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 }.buffer.len();
|
||
NativeResult::Ok(JsValue::int(len as i32))
|
||
}
|
||
|
||
/// `SharedArrayBuffer.prototype.growable` getter:返回缓冲区是否 growable
|
||
/// (载荷 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 }.growable))
|
||
}
|
||
|
||
/// `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.growable { payload.buffer.max_len() } else { payload.buffer.len() };
|
||
NativeResult::Ok(JsValue::int(value as i32))
|
||
}
|
||
|
||
/// `SharedArrayBuffer.prototype.grow(newLength)`:growable 缓冲区原地增长到
|
||
/// newLength,前缀保留、增补零填;只增不缩(缩为 RangeError)。
|
||
///
|
||
/// # 步骤
|
||
/// 1. this 品牌校验(非对象/非 SAB → TypeError)。
|
||
/// 2. 非 growable(载荷 growable 位未置)→ TypeError,先于强转。
|
||
/// 3. newByteLength = ToIntegerOrInfinity(newLength) 传播式(NaN → 0,
|
||
/// ±Infinity 保留)。
|
||
/// 4. 重取 this 寄存器 + 载荷指针(强转 JS 调用窗可晋升——AB resize 同式;
|
||
/// SAB 无 detach 面,仅晋升面)。
|
||
/// 5. 重读 growable 位 0 → TypeError。
|
||
/// 6. newByteLength < 0 或 > 真实上限(预分配真实上限)或 < 当前活长
|
||
/// (grow-only)→ RangeError。
|
||
/// 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] };
|
||
let this_val = vm.reg(this_reg);
|
||
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 }.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() };
|
||
let new_length = native_try!(to_integer_or_infinity(vm, new_val));
|
||
// 强转调用可能已把本缓冲区晋升进 session:接收者寄存器与载荷指针均须
|
||
// 重取重检,不得消费求值前快照。
|
||
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 { &*payload_ptr };
|
||
if !payload.growable {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer is not growable"));
|
||
}
|
||
// 真实上限即预分配上限;当前活长之下拒缩。
|
||
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"));
|
||
}
|
||
payload.buffer.grow(new_length as usize);
|
||
NativeResult::Ok(JsValue::undefined())
|
||
}
|
||
|
||
/// `SharedArrayBuffer.prototype.slice(start, end)`:复制字节区间生成新
|
||
/// SharedArrayBuffer。
|
||
///
|
||
/// # 步骤
|
||
/// 1. this 品牌校验(非对象/非 SAB → TypeError);len = 活字节长快照
|
||
/// (growable 源取当前长非上限)。
|
||
/// 2. ResolveBounds(start undefined/缺省 → 0、end undefined/缺省 → len,
|
||
/// 不调强转)→ newLen = max(final - first, 0)。
|
||
/// 3. SpeciesConstructor(O, %SharedArrayBuffer%)(构造器/@@species 完整 Get,
|
||
/// 异常传播;undefined 臂回落 %SharedArrayBuffer%;非对象/非构造器
|
||
/// TypeError)。
|
||
/// 4. Construct(ctor, «newLen») 经 vm.construct_ctor(native 值传递 / bytecode
|
||
/// 压构造帧,含 derived 构造器 super() 语义;构造器抛出值原样上抛)。
|
||
/// 5. 构造窗口后重取源寄存器与载荷(窗口内晋升使旧指针悬垂;SAB 无 detach
|
||
/// 生产路径,无重检臂)。
|
||
/// 6. 结果三检:SAB 品牌槽 → SameValue(new, O) → 活长 ≥ newLen,均
|
||
/// TypeError(SAB 无 detached/immutable 检项)。
|
||
/// 7. 直拷 newLen 字节入结果前区。
|
||
///
|
||
/// # 边界与前提
|
||
/// - SAB grow-only(grow 只增不缩):步 1 快照长 ≤ 拷贝时活长,
|
||
/// first..first+newLen 恒在界,直拷无需活长夹取。
|
||
/// - 源对象经寄存器根保活;每次 JS 调用窗口后重新读接收者寄存器与载荷指针。
|
||
pub fn shared_array_buffer_slice<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_reg = if args.is_empty() { 0 } else { args[0] };
|
||
let this_val = vm.reg(this_reg);
|
||
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 }.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));
|
||
let new_len = final_.saturating_sub(first);
|
||
|
||
// 物种读与构造各是 JS 调用窗口:getter/构造器可晋升源。
|
||
let default_ctor = JsValue::from_js_object(vm.builtin_proto(ProtoKind::SharedArrayBufferConstructor));
|
||
let ctor = native_try!(ab_species_constructor(vm, this_val, default_ctor));
|
||
let new_val = match vm.construct_ctor(ctor, &[JsValue::int(new_len as i32)]) {
|
||
Ok(v) => v,
|
||
Err(err) => return NativeResult::Err(err),
|
||
};
|
||
|
||
// 构造窗口后重取接收者与载荷(源对象可被晋升进 session,旧指针悬垂)。
|
||
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 }.buffer.as_slice();
|
||
// 结果三检:SAB 品牌槽 → SameValue → 活长。
|
||
if !new_val.is_object() {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"SharedArrayBuffer slice result is not a SharedArrayBuffer",
|
||
));
|
||
}
|
||
// SAFETY: new_val 为对象且指针非空,对象本 session 存活。
|
||
let Some(new_payload) = shared_array_buffer_payload_ptr(unsafe { &*new_val.as_js_object_ptr() }) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"SharedArrayBuffer slice result is not a SharedArrayBuffer",
|
||
));
|
||
};
|
||
if new_payload.is_null() {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"SharedArrayBuffer slice result is not a SharedArrayBuffer",
|
||
));
|
||
}
|
||
if new_val == this_val {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"SharedArrayBuffer slice result must not be the source buffer",
|
||
));
|
||
}
|
||
// SAFETY: new_payload 经 shared_array_buffer_payload_ptr 校验为 SAB 载荷盒。
|
||
if unsafe { &*new_payload }.buffer.len() < new_len {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer slice result is too small"));
|
||
}
|
||
// 直拷 newLen 字节(grow-only 恒在界,见 边界与前提)。
|
||
// SAFETY: new_payload 经 shared_array_buffer_payload_ptr 校验;SAB 无
|
||
// 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(detached 标志
|
||
/// 置位)不在此分叉,由消费方现读标志时按既有错误形态处理。
|
||
pub(crate) fn array_buffer_payload<H: VmHost>(
|
||
vm: &mut H, this_val: JsValue,
|
||
) -> Result<*mut ArrayBufferPayload, JsValue> {
|
||
if !this_val.is_object() {
|
||
return Err(crate::error::create_type_error(vm, "ArrayBuffer method called on incompatible receiver"));
|
||
}
|
||
let obj_ptr = this_val.as_js_object_ptr();
|
||
if obj_ptr.is_null() {
|
||
return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||
}
|
||
let obj = unsafe { &*obj_ptr };
|
||
if !obj.is_array_buffer_obj() {
|
||
return Err(crate::error::create_type_error(vm, "ArrayBuffer method called on incompatible receiver"));
|
||
}
|
||
let Some(payload_ptr) = obj.native_fn() else {
|
||
return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||
};
|
||
let payload_ptr = payload_ptr.as_ptr() as *mut ArrayBufferPayload;
|
||
if payload_ptr.is_null() {
|
||
return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||
}
|
||
Ok(payload_ptr)
|
||
}
|
||
|
||
/// `ArrayBuffer(length [ , options ])` 构造逻辑。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 非构造形态(普通调用,new.target 缺失)→ TypeError。
|
||
/// 2. length = ToIndex(length) 传播式;undefined/缺省 → 0。
|
||
/// 3. GetArrayBufferMaxByteLengthOption:options 非对象 → 0;Get
|
||
/// "maxByteLength" 传播式;undefined → 0;否则 ToIndex 传播式。
|
||
/// 4. length > max → RangeError(先于原型读与对象创建)。
|
||
/// 5. GetPrototypeFromConstructor:经 ordinary_get 读 new.target 的
|
||
/// "prototype"(访问器触发,异常原值传播);非对象结果回落
|
||
/// %ArrayBuffer.prototype%。
|
||
/// 6. 对象创建(定长/可 resize 由选项是否在场决定);分配期上界校验
|
||
/// (length 与 max 各一条,超引擎上限 → RangeError)。
|
||
/// 7. 选项在场 → own maxByteLength 数据属性 {w:0, e:0, c:0}(值为 ToIndex
|
||
/// 后整数)。
|
||
pub fn array_buffer_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
if !vm.constructing_native() {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer must be called with new"));
|
||
}
|
||
let length = if args.len() > 1 { native_try!(to_index(vm, vm.reg(args[1]))) } else { 0 };
|
||
// GetArrayBufferMaxByteLengthOption:None = 空(定长),Some(v) = 请求的
|
||
// 上限(v 可为 0,与"空"语义相异:length > v 的界判与 own 属性均须生效)。
|
||
let max_opt = if args.len() > 2 {
|
||
let options = vm.reg(args[2]);
|
||
if !options.is_object() {
|
||
None
|
||
} else {
|
||
let options_ptr = options.as_js_object_ptr();
|
||
// SAFETY: is_object 保证指针非空且对象本 session 存活。
|
||
let options_obj = unsafe { &*options_ptr };
|
||
let si = vm.perm_intern("maxByteLength");
|
||
let max_val = match vm.ordinary_get(options_obj, si, options) {
|
||
Ok(v) => v,
|
||
Err(err) => return NativeResult::Err(crate::iterator::engine_error(vm, &err)),
|
||
};
|
||
if max_val.is_undefined() {
|
||
None
|
||
} else {
|
||
Some(native_try!(to_index(vm, max_val)))
|
||
}
|
||
}
|
||
} else {
|
||
None
|
||
};
|
||
if let Some(max) = max_opt {
|
||
if length > max {
|
||
return NativeResult::Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length"));
|
||
}
|
||
}
|
||
// GetPrototypeFromConstructor:prototype getter 可抛错,异常原值传播;
|
||
// 结果非对象时回落默认原型。
|
||
let default_proto = default_array_buffer_proto(vm);
|
||
let new_target = vm.reg(255);
|
||
let proto = if new_target.is_object() {
|
||
let nt_ptr = new_target.as_js_object_ptr();
|
||
// SAFETY: is_object 保证指针非空且对象本 session 存活。
|
||
let nt_obj = unsafe { &*nt_ptr };
|
||
let proto_si = vm.perm_intern("prototype");
|
||
let proto_val = match vm.ordinary_get(nt_obj, proto_si, new_target) {
|
||
Ok(v) => v,
|
||
Err(err) => return NativeResult::Err(crate::iterator::engine_error(vm, &err)),
|
||
};
|
||
if proto_val.is_object() {
|
||
proto_val
|
||
} else {
|
||
default_proto
|
||
}
|
||
} else {
|
||
default_proto
|
||
};
|
||
// 分配期上界:length 与 max 超引擎上限一律拒绝(ToIndex 阶段不校验)。
|
||
if length > MAX_ARRAY_BUFFER_LENGTH || max_opt.is_some_and(|m| m > MAX_ARRAY_BUFFER_LENGTH) {
|
||
return NativeResult::Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length"));
|
||
}
|
||
// 存储态上限 = 请求上限 + 1(上限 0 与定长可分;定长存 0)。
|
||
let obj_ptr = new_array_buffer(vm, vec![0; length], max_opt.map_or(0, |m| m + 1), proto);
|
||
// SAFETY: obj_ptr 为 alloc_object 新建对象,无别名。
|
||
let obj = unsafe { &mut *obj_ptr };
|
||
if let Some(max) = max_opt {
|
||
let si = vm.perm_intern("maxByteLength");
|
||
if let Err(err) = vm.define_data_property(
|
||
obj,
|
||
si,
|
||
JsValue::int(max as i32),
|
||
oxide_types::object::PropAttributes::new(false, false, false),
|
||
) {
|
||
return NativeResult::Err(crate::iterator::engine_error(vm, &err));
|
||
}
|
||
}
|
||
NativeResult::Ok(JsValue::from_js_object(obj_ptr))
|
||
}
|
||
|
||
/// `ArrayBuffer.prototype.byteLength` getter:返回缓冲区字节数;
|
||
/// detached 缓冲返回 0。
|
||
pub fn 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!(array_buffer_payload(vm, this_val));
|
||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。
|
||
let payload = unsafe { &*payload_ptr };
|
||
let len = if payload.detached { 0 } else { payload.bytes.len() };
|
||
NativeResult::Ok(JsValue::int(len as i32))
|
||
}
|
||
|
||
/// `ArrayBuffer.prototype.resizable` getter:返回缓冲区是否可 resize
|
||
/// (载荷存储态上限非 0:0 = 定长,非 0 = 请求上限 + 1)。
|
||
pub fn array_buffer_resizable<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 载荷。
|
||
let resizable = unsafe { &*payload_ptr }.max_byte_length != 0;
|
||
NativeResult::Ok(JsValue::bool(resizable))
|
||
}
|
||
|
||
/// `ArrayBuffer.prototype.maxByteLength` getter:detached → +0;定长 →
|
||
/// 当前字节数;resizable → 真实上限(存储态 − 1 解码)。
|
||
pub fn 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!(array_buffer_payload(vm, this_val));
|
||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。
|
||
let payload = unsafe { &*payload_ptr };
|
||
if payload.detached {
|
||
return NativeResult::Ok(JsValue::int(0));
|
||
}
|
||
let value = if payload.max_byte_length == 0 {
|
||
payload.bytes.len()
|
||
} else {
|
||
payload.max_byte_length - 1
|
||
};
|
||
NativeResult::Ok(JsValue::int(value as i32))
|
||
}
|
||
|
||
/// `ArrayBuffer.prototype.immutable` getter:返回载荷 immutable 标志。
|
||
pub fn array_buffer_immutable<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 }.immutable))
|
||
}
|
||
|
||
/// `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 }.detached))
|
||
}
|
||
|
||
/// `ArrayBuffer.prototype.markImmutable()`:定长附着缓冲区置 immutable 标志
|
||
/// 并返回接收者;已置位再调为幂等 no-op。
|
||
///
|
||
/// # 步骤
|
||
/// 1. this 品牌校验(非对象/非 ArrayBuffer → TypeError)。
|
||
/// 2. detached → TypeError。
|
||
/// 3. resizable(存储态上限非 0)→ TypeError。
|
||
/// 4. 置 immutable 标志并返回 O。
|
||
pub fn array_buffer_mark_immutable<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 载荷。
|
||
let payload = unsafe { &mut *payload_ptr };
|
||
if payload.detached {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||
}
|
||
if payload.max_byte_length != 0 {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer is not resizable"));
|
||
}
|
||
payload.immutable = true;
|
||
NativeResult::Ok(this_val)
|
||
}
|
||
|
||
/// 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).bytes.clear();
|
||
(*payload_ptr).detached = true;
|
||
};
|
||
NativeResult::Ok(JsValue::undefined())
|
||
}
|
||
|
||
/// `ArrayBuffer.prototype.resize(newLength)`:可 resize 缓冲区原地调整长度,
|
||
/// 前缀保留、增补零填充、缩小截断。
|
||
///
|
||
/// # 步骤
|
||
/// 1. this 品牌校验(非对象/非 ArrayBuffer → TypeError)。
|
||
/// 2. immutable 状态位先于任何 JS 调用拷出并守卫(该守卫在 newLength
|
||
/// 读取之前:强转副作用不先于 immutable 观测)。
|
||
/// 3. newByteLength = ToIntegerOrInfinity(newLength) 传播式(NaN → 0,
|
||
/// ±Infinity 保留)。
|
||
/// 4. 重取载荷指针(强转调用可触发收集,旧指针悬垂);detached
|
||
/// 守卫在求值之后:强转内 detach 与本步前已 detach 同形 TypeError。
|
||
/// 5. 定长(重读存储态上限 = 0)→ TypeError,先于界判。
|
||
/// 6. newByteLength < 0 或 > 真实上限(重读存储态 − 1)→ RangeError。
|
||
/// 7. `Vec::resize` 一次调用收口目标长度。
|
||
/// 8. 返回 undefined。
|
||
pub fn array_buffer_resize<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_reg = if args.is_empty() { 0 } else { args[0] };
|
||
let this_val = vm.reg(this_reg);
|
||
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷;
|
||
// 状态位拷出后本次借用即结束,不跨 JS 调用点。
|
||
let immutable = unsafe { &*payload_ptr }.immutable;
|
||
if immutable {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer is immutable"));
|
||
}
|
||
let new_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||
let new_length = native_try!(to_integer_or_infinity(vm, new_val));
|
||
// 强转调用可能已把本缓冲区晋升进 session 或 detach:接收者寄存器与
|
||
// 状态位均须重取重检,不得消费求值前快照。
|
||
let this_val = vm.reg(this_reg);
|
||
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||
// SAFETY: 重取的 payload_ptr 指向存活载荷盒。
|
||
let payload = unsafe { &mut *payload_ptr };
|
||
let max = payload.max_byte_length;
|
||
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"));
|
||
}
|
||
// 存储态上限 +1 编码:真实上限 = max - 1(上限 0 的缓冲区只许 resize(0))。
|
||
let real_max = max - 1;
|
||
if new_length < 0.0 || new_length > real_max as f64 {
|
||
return NativeResult::Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length"));
|
||
}
|
||
payload.bytes.resize(new_length as usize, 0);
|
||
NativeResult::Ok(JsValue::undefined())
|
||
}
|
||
|
||
/// ResolveBounds(len, start, end) 共享核:start 为 undefined(或缺省)归 0、
|
||
/// end 为 undefined(或缺省)归 len,在场臂 ToIntegerOrInfinity 上下夹取
|
||
/// (NaN → 0、±Infinity 饱和 0/len、负值按 len 偏移)。slice 与
|
||
/// sliceToImmutable 同源共用。
|
||
fn ab_resolve_bounds<H: VmHost>(
|
||
vm: &mut H, len: usize, start: Option<JsValue>, end: Option<JsValue>,
|
||
) -> Result<(usize, usize), JsValue> {
|
||
let start_val = start.unwrap_or(JsValue::undefined());
|
||
let first = if start_val.is_undefined() { 0 } else { normalize_index(vm, start_val, len)? };
|
||
let end_val = end.unwrap_or(JsValue::undefined());
|
||
let final_ = if end_val.is_undefined() { len } else { normalize_index(vm, end_val, len)? };
|
||
Ok((first, final_))
|
||
}
|
||
|
||
/// SpeciesConstructor(O, default_ctor):完整 Get O.constructor(访问器/异常
|
||
/// 传播);undefined 回落调用方给定的默认构造器,其余非对象抛 TypeError;
|
||
/// 完整 Get C[Symbol.species](null 归一为 undefined);undefined 回落默认
|
||
/// 构造器,非构造器抛 TypeError。
|
||
fn ab_species_constructor<H: VmHost>(vm: &mut H, o_val: JsValue, default_ctor: JsValue) -> Result<JsValue, JsValue> {
|
||
let o_obj = unsafe { &*o_val.as_js_object_ptr() };
|
||
let ctor_key = vm.perm_intern("constructor");
|
||
let c = match vm.ordinary_get(o_obj, ctor_key, o_val) {
|
||
Ok(v) => v,
|
||
Err(msg) => return Err(crate::iterator::engine_error(vm, &msg)),
|
||
};
|
||
if c.is_undefined() {
|
||
return Ok(default_ctor);
|
||
}
|
||
if !c.is_object() {
|
||
return Err(crate::error::create_type_error(vm, "buffer constructor is not an object"));
|
||
}
|
||
let species_key = encode_symbol_key(vm.realm_id(), WELL_KNOWN_SYMBOL_SPECIES);
|
||
let s = match vm.ordinary_get(unsafe { &*c.as_js_object_ptr() }, species_key, c) {
|
||
Ok(v) => v,
|
||
Err(msg) => return Err(crate::iterator::engine_error(vm, &msg)),
|
||
};
|
||
let s = if s.is_null() { JsValue::undefined() } else { s };
|
||
if s.is_undefined() {
|
||
Ok(default_ctor)
|
||
} else if !crate::array::is_constructor_value(s) {
|
||
Err(crate::error::create_type_error(vm, "buffer species is not a constructor"))
|
||
} else {
|
||
Ok(s)
|
||
}
|
||
}
|
||
|
||
/// `ArrayBuffer.prototype.slice(start, end)`:复制字节区间生成新 ArrayBuffer。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 品牌守卫与 detached 源守卫先于参数读(抛错时参数无副作用)。
|
||
/// 2. len 快照 → ResolveBounds(start undefined/缺省 → 0、end undefined/缺省
|
||
/// → len,不调强转)→ newLen = max(final - first, 0)。
|
||
/// 3. SpeciesConstructor(O, %ArrayBuffer%)(构造器/@@species 完整 Get,异常
|
||
/// 传播;undefined 臂回落 %ArrayBuffer%;非对象/非构造器 TypeError)。
|
||
/// 4. Construct(ctor, «newLen») 经 vm.construct_ctor(native 值传递 / bytecode
|
||
/// 压构造帧,含 derived 构造器 super() 语义;构造器抛出值原样上抛)。
|
||
/// 5. 结果五检:AB 载荷槽 → detached → immutable → SameValue(new, O) →
|
||
/// new.byteLength < newLen,均 TypeError。
|
||
/// 6. 构造窗口后重取源载荷(窗口内源可被 detach/晋升):detached →
|
||
/// TypeError;按活 currentLen 夹拷贝(count = min(newLen, currentLen -
|
||
/// first),first < currentLen 守卫),余位零填充。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 源 immutable 是合法输入(只读拷贝);对 new 的 immutable 检只挡物种
|
||
/// 构造器返回的 immutable 产物。
|
||
/// - 源对象经寄存器根保活;每次 JS 调用窗口后重新读接收者寄存器与载荷指针。
|
||
pub fn array_buffer_slice<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_reg = if args.is_empty() { 0 } else { args[0] };
|
||
let this_val = vm.reg(this_reg);
|
||
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷;
|
||
// 标量拷出后借用即结束,不跨 JS 调用窗口。
|
||
let payload = unsafe { &*payload_ptr };
|
||
if payload.detached {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||
}
|
||
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));
|
||
let new_len = final_.saturating_sub(first);
|
||
|
||
// 物种读与构造各是 JS 调用窗口:getter/构造器可 detach、resize、晋升源。
|
||
let default_ctor = JsValue::from_js_object(vm.builtin_proto(ProtoKind::ArrayBufferConstructor));
|
||
let ctor = native_try!(ab_species_constructor(vm, this_val, default_ctor));
|
||
let new_val = match vm.construct_ctor(ctor, &[JsValue::int(new_len as i32)]) {
|
||
Ok(v) => v,
|
||
Err(err) => return NativeResult::Err(err),
|
||
};
|
||
|
||
// 构造窗口后重取接收者与载荷(源对象可被晋升进 session,旧指针悬垂)。
|
||
let this_val = vm.reg(this_reg);
|
||
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||
// SAFETY: 重取的 payload_ptr 指向存活载荷盒;标量拷出后借用即结束。
|
||
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() {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"ArrayBuffer slice result is not an ArrayBuffer",
|
||
));
|
||
}
|
||
// SAFETY: new_val 为对象且指针非空,对象本 session 存活。
|
||
let Some(new_payload) = array_buffer_payload_ptr(unsafe { &*new_ptr }) else {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"ArrayBuffer slice result is not an ArrayBuffer",
|
||
));
|
||
};
|
||
if new_payload.is_null() {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"ArrayBuffer slice result is not an ArrayBuffer",
|
||
));
|
||
}
|
||
// SAFETY: new_payload 经 array_buffer_payload_ptr 校验为 ArrayBuffer 载荷盒。
|
||
let new_state = unsafe { &*new_payload };
|
||
if new_state.detached {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer slice result is detached"));
|
||
}
|
||
if new_state.immutable {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer slice result is immutable"));
|
||
}
|
||
if new_val == this_val {
|
||
return NativeResult::Err(crate::error::create_type_error(
|
||
vm,
|
||
"ArrayBuffer slice result must not be the source buffer",
|
||
));
|
||
}
|
||
if new_state.bytes.len() < new_len {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer slice result is too small"));
|
||
}
|
||
// 活长度夹拷贝写入结果缓冲前区(构造器已零填充):源中途收缩只拷现存
|
||
// 字节,first 越界零拷。
|
||
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 臂已先行
|
||
// 排除,字节区恒在场,可变借用止于本语句。
|
||
unsafe { (&mut (*new_payload).bytes)[..count].copy_from_slice(&data[first..first + count]) };
|
||
NativeResult::Ok(new_val)
|
||
}
|
||
|
||
/// `ArrayBuffer.prototype.sliceToImmutable(start, end)`:复制字节区间生成
|
||
/// immutable 新 ArrayBuffer,源缓冲不 detach、不改任何状态位。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 品牌守卫与 detached 源守卫先于参数读(抛错时参数无副作用)。
|
||
/// 2. len 快照 → ResolveBounds(与 slice 共享助手)→ newLen。
|
||
/// 3. 强转窗口后重取源载荷重检:detached → TypeError(先于长度终检);
|
||
/// currentLen < final → RangeError(源中途收缩到解析界之下)。
|
||
/// 4. GetPrototypeFromConstructor(%ArrayBuffer%):完整读构造器 "prototype"
|
||
/// (访问器抛错传播,非对象回落 %ArrayBuffer.prototype%)。
|
||
/// 5. proto 读窗口后再次重取源载荷(窗口内源可 detach/收缩/晋升),
|
||
/// 重检 detached 与在界后拷贝前 newLen 字节(恒在界内),零填充分配,
|
||
/// 后置 immutable 标志;源不动。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 源 immutable 是合法输入(只读拷贝);产物恒 immutable、恒定长。
|
||
/// - 无物种面:构造器恒 %ArrayBuffer%。
|
||
pub fn array_buffer_slice_to_immutable<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_reg = if args.is_empty() { 0 } else { args[0] };
|
||
let this_val = vm.reg(this_reg);
|
||
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷;
|
||
// 标量拷出后借用即结束,不跨 JS 调用窗口。
|
||
let payload = unsafe { &*payload_ptr };
|
||
if payload.detached {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||
}
|
||
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));
|
||
let new_len = final_.saturating_sub(first);
|
||
|
||
// 强转窗口可 detach/晋升源:重取寄存器与载荷后重检(先于长度终检)。
|
||
let this_val = vm.reg(this_reg);
|
||
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||
// SAFETY: 重取的 payload_ptr 指向存活载荷盒;标量拷出后借用即结束。
|
||
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"));
|
||
}
|
||
// GetPrototypeFromConstructor(%ArrayBuffer%):prototype 读为 JS 窗口
|
||
// (访问器可被用户重定义),抛错传播;非对象回落默认原型。
|
||
let ctor_val = JsValue::from_js_object(vm.builtin_proto(ProtoKind::ArrayBufferConstructor));
|
||
let ctor_obj = unsafe { &*ctor_val.as_js_object_ptr() };
|
||
let proto_si = vm.perm_intern("prototype");
|
||
let proto_val = match vm.ordinary_get(ctor_obj, proto_si, ctor_val) {
|
||
Ok(v) => v,
|
||
Err(msg) => return NativeResult::Err(crate::iterator::engine_error(vm, &msg)),
|
||
};
|
||
let proto = if proto_val.is_object() { proto_val } else { default_array_buffer_proto(vm) };
|
||
// proto 读窗口可 detach/收缩/晋升源:再重取载荷重检 detached 与在界,
|
||
// 拷贝恒在界内(currentLen ≥ final ≥ first + newLen)。
|
||
let this_val = vm.reg(this_reg);
|
||
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||
// SAFETY: 重取的 payload_ptr 指向存活载荷盒;字节借出止于本语句。
|
||
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"));
|
||
}
|
||
let out: Vec<u8> = data[first..first + new_len].to_vec();
|
||
let dest_ptr = new_array_buffer(vm, out, 0, proto);
|
||
// SAFETY: dest_ptr 为 alloc_object 新建对象,载荷盒由 new_array_buffer 建。
|
||
let Some(dest_payload) = array_buffer_payload_ptr(unsafe { &*dest_ptr }) else {
|
||
return type_error(vm, "ArrayBuffer internal state invalid");
|
||
};
|
||
// SAFETY: dest_payload 指向存活载荷盒;immutable 后置位与 transfer-Immutable
|
||
// 同形,不扩 new_array_buffer 签名。
|
||
unsafe { (*dest_payload).immutable = true };
|
||
NativeResult::Ok(JsValue::from_js_object(dest_ptr))
|
||
}
|
||
|
||
/// transfer 族新缓冲保持性:Preserve 源存储态上限原样拷贝,Fixed 定长,
|
||
/// Immutable 定长并置 immutable 标志。
|
||
#[derive(Clone, Copy)]
|
||
enum TransferKeep {
|
||
Preserve,
|
||
Fixed,
|
||
Immutable,
|
||
}
|
||
|
||
/// `ArrayBufferCopyAndDetach(O, newLength, keep)` 共享核:品牌守卫 →
|
||
/// newLength 求值 → detached/immutable 判定 → 界校验 → 前缀拷贝零填充建新
|
||
/// 缓冲 → 源 detach。
|
||
///
|
||
/// # 步骤
|
||
/// 1. this 品牌校验(非对象/非 ArrayBuffer → TypeError)。
|
||
/// 2. newLength 缺省(含显式 undefined)→ 源当前字节长;其余在场 → ToIndex
|
||
/// 传播式(强转副作用先于一切守卫观测)。
|
||
/// 3. 重取源载荷指针(求值可已晋升/detach/改 immutable),重检状态位:
|
||
/// detached → TypeError;immutable → TypeError。
|
||
/// 4. 界校验:仅保持性原样臂——resizable 源且 newByteLength > 真实上限
|
||
/// (重读存储态 − 1)→ RangeError;newByteLength > 引擎分配上界 →
|
||
/// RangeError(全保持性)。
|
||
/// 5. 物化拷贝(载荷指针新鲜,字节借出止于本步)。
|
||
/// 6. 新建缓冲(proto 取 %ArrayBuffer.prototype%,保持性按 keep),字节
|
||
/// 序列零填充至 newByteLength。
|
||
/// 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,
|
||
) -> NativeResult {
|
||
let this_val = vm.reg(this_reg);
|
||
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷;
|
||
// 只读当前字节长,借用即结束,不跨 JS 调用点。
|
||
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 与
|
||
// 缺省同义,直接取源当前字节长。
|
||
None => cur_len,
|
||
Some(v) if v.is_undefined() => cur_len,
|
||
Some(v) => native_try!(to_index(vm, v)),
|
||
};
|
||
// 求值可已把本缓冲区晋升进 session、detach 或置 immutable:接收者寄存器
|
||
// 与状态位均须重取重检,不得消费求值前快照。
|
||
let this_val = vm.reg(this_reg);
|
||
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||
// SAFETY: 重取的 payload_ptr 指向存活载荷盒;标量拷出后借用即结束。
|
||
let (cur_len, stored_max, immutable, detached) = unsafe {
|
||
let p = &*payload_ptr;
|
||
(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"));
|
||
}
|
||
if immutable {
|
||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer is immutable"));
|
||
}
|
||
// 保持性原样臂:resizable 源的真实上限(存储态 − 1)须容纳新长度
|
||
// (ttf/tti 定长分配无上限约束)。
|
||
if matches!(keep, TransferKeep::Preserve) && stored_max != 0 && new_len > stored_max - 1 {
|
||
return NativeResult::Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length"));
|
||
}
|
||
if new_len > MAX_ARRAY_BUFFER_LENGTH {
|
||
return NativeResult::Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length"));
|
||
}
|
||
// SAFETY: 重取的 payload_ptr 指向存活载荷盒;detached 已排除,字节区恒
|
||
// 在场。字节借出止于本语句。
|
||
let copy_len = new_len.min(cur_len);
|
||
let mut new_data: Vec<u8> = unsafe { &*payload_ptr }
|
||
.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),
|
||
TransferKeep::Fixed => (0, false),
|
||
TransferKeep::Immutable => (0, true),
|
||
};
|
||
let proto = default_array_buffer_proto(vm);
|
||
let dest_ptr = new_array_buffer(vm, new_data, dest_max, proto);
|
||
// SAFETY: dest_ptr 为 alloc_object 新建对象,载荷盒由 new_array_buffer 建。
|
||
if dest_immutable {
|
||
let Some(p) = array_buffer_payload_ptr(unsafe { &*dest_ptr }) else {
|
||
return type_error(vm, "ArrayBuffer internal state invalid");
|
||
};
|
||
unsafe { (*p).immutable = true };
|
||
}
|
||
// 分配可已将源晋升: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).bytes.clear();
|
||
(*payload_ptr).detached = true;
|
||
};
|
||
NativeResult::Ok(JsValue::from_js_object(dest_ptr))
|
||
}
|
||
|
||
/// `ArrayBuffer.prototype.transfer([newLength])`:拷贝并 detach,保持性
|
||
/// 原样(定长→定长,resizable 源→resizable 同上限)。
|
||
pub fn array_buffer_transfer<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_reg = if args.is_empty() { 0 } else { args[0] };
|
||
let new_length = if args.len() > 1 { Some(vm.reg(args[1])) } else { None };
|
||
array_buffer_copy_and_detach(vm, this_reg, new_length, TransferKeep::Preserve)
|
||
}
|
||
|
||
/// `ArrayBuffer.prototype.transferToFixedLength([newLength])`:拷贝并
|
||
/// detach,新缓冲恒定长。
|
||
pub fn array_buffer_transfer_to_fixed_length<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_reg = if args.is_empty() { 0 } else { args[0] };
|
||
let new_length = if args.len() > 1 { Some(vm.reg(args[1])) } else { None };
|
||
array_buffer_copy_and_detach(vm, this_reg, new_length, TransferKeep::Fixed)
|
||
}
|
||
|
||
/// `ArrayBuffer.prototype.transferToImmutable([newLength])`:拷贝并
|
||
/// detach,新缓冲恒定长且 immutable。
|
||
pub fn array_buffer_transfer_to_immutable<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_reg = if args.is_empty() { 0 } else { args[0] };
|
||
let new_length = if args.len() > 1 { Some(vm.reg(args[1])) } else { None };
|
||
array_buffer_copy_and_detach(vm, this_reg, new_length, TransferKeep::Immutable)
|
||
}
|
||
|
||
/// `ArrayBuffer.isView(value)`:参数是 DataView 或 TypedArray 才返回 true。
|
||
pub fn array_buffer_is_view<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||
if !value.is_object() {
|
||
return NativeResult::Ok(JsValue::bool(false));
|
||
}
|
||
let ptr = value.as_js_object_ptr();
|
||
if ptr.is_null() {
|
||
return NativeResult::Ok(JsValue::bool(false));
|
||
}
|
||
let obj = unsafe { &*ptr };
|
||
NativeResult::Ok(JsValue::bool(obj.is_data_view_obj() || obj.is_typed_array_obj()))
|
||
}
|
||
|
||
/// `ArrayBuffer.prototype.toString`:校验 receiver 后返回 `[object ArrayBuffer]`。
|
||
pub fn array_buffer_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||
if array_buffer_payload(vm, this_val).is_err() {
|
||
return type_error(vm, "ArrayBuffer.prototype.toString called on incompatible receiver");
|
||
}
|
||
NativeResult::Ok(vm.new_string("[object ArrayBuffer]"))
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use oxide_vm::vm::Vm;
|
||
|
||
/// 手工构造带载荷盒的 ArrayBuffer 对象(不经 Vm,只验 GC 两自由函数)。
|
||
fn ab_object_with_payload(payload: ArrayBufferPayload) -> JsObject {
|
||
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined());
|
||
obj.type_tag = JsObject::OBJ_TYPE_ARRAY_BUFFER;
|
||
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
|
||
}
|
||
|
||
#[test]
|
||
fn detached_size_and_drop_idempotent() {
|
||
let mut obj = ab_object_with_payload(ArrayBufferPayload {
|
||
bytes: vec![],
|
||
detached: true,
|
||
max_byte_length: 0,
|
||
immutable: false,
|
||
});
|
||
let struct_bytes = std::mem::size_of::<ArrayBufferPayload>() as u64;
|
||
assert_eq!(array_buffer_native_size(&obj), struct_bytes);
|
||
// 首次 drop 释放结构体(无缓冲容量);槽置空后二次 drop 零释放。
|
||
assert_eq!(drop_array_buffer_native(&mut obj), struct_bytes);
|
||
assert_eq!(drop_array_buffer_native(&mut obj), 0);
|
||
}
|
||
|
||
#[test]
|
||
fn attached_size_and_drop() {
|
||
let mut obj = ab_object_with_payload(ArrayBufferPayload {
|
||
bytes: vec![0u8; 8],
|
||
detached: false,
|
||
max_byte_length: 0,
|
||
immutable: false,
|
||
});
|
||
let bytes = (std::mem::size_of::<ArrayBufferPayload>() + 8) as u64;
|
||
assert_eq!(array_buffer_native_size(&obj), bytes);
|
||
assert_eq!(drop_array_buffer_native(&mut obj), bytes);
|
||
assert_eq!(drop_array_buffer_native(&mut obj), 0);
|
||
}
|
||
|
||
#[test]
|
||
fn payload_ptr_brand_guard() {
|
||
// 非 ArrayBuffer 类型标签(PLAIN)对象取载荷指针恒 None。
|
||
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::undefined());
|
||
let payload_ptr = Arc::into_raw(Arc::new(ArrayBufferPayload {
|
||
bytes: vec![0u8; 4],
|
||
detached: false,
|
||
max_byte_length: 0,
|
||
immutable: false,
|
||
}));
|
||
// SAFETY: 同 ab_object_with_payload 的载荷盒形态。
|
||
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(payload_ptr as *const ()) }));
|
||
assert!(array_buffer_payload_ptr(&obj).is_none());
|
||
// SAFETY: 测试构造的盒,恰好释放一次。
|
||
unsafe { drop(Arc::from_raw(payload_ptr)) };
|
||
}
|
||
|
||
/// JS 求值辅助:单脚本编译执行,返回完成值。
|
||
fn eval_ab(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||
let allocator = oxide_parser::Allocator::default();
|
||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("parse: {:?}", e[0].message))?;
|
||
let module = oxide_compiler::compiler::Compiler::new()
|
||
.compile(&program)
|
||
.map_err(|e| format!("compile: {e}"))?;
|
||
vm.run(&std::sync::Arc::new(module))
|
||
}
|
||
|
||
/// markImmutable 标志面钉:置位返回 O、二调幂等、resizable/detached 两
|
||
/// TypeError 形。
|
||
#[test]
|
||
fn mark_immutable_flag_and_guard() {
|
||
let mut vm = Vm::new();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"var ab = new ArrayBuffer(4); \
|
||
var ret = ab.markImmutable(); \
|
||
ret === ab && ab.immutable === true && ab.markImmutable() === ab \
|
||
&& ab.immutable === true && ab.byteLength === 4",
|
||
)
|
||
.unwrap();
|
||
assert!(r.as_bool());
|
||
let e = eval_ab(&mut vm, "new ArrayBuffer(4, {maxByteLength: 8}).markImmutable()").unwrap_err();
|
||
assert!(e.contains("TypeError"), "resizable 应抛 TypeError: {e}");
|
||
let e = eval_ab(
|
||
&mut vm,
|
||
"var ab = new ArrayBuffer(4); $262.detachArrayBuffer(ab); \
|
||
try { ab.markImmutable(); 'no-throw' } catch (err) { err.name }",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(vm.lookup_str(e).unwrap(), "TypeError");
|
||
}
|
||
|
||
/// transfer 族新缓冲存储态字段表:Preserve 源上限原样(+1 编码不变)、
|
||
/// Fixed/Immutable 定长,仅 Immutable 置位;源均 detach。
|
||
#[test]
|
||
fn transfer_dest_payload_field_table() {
|
||
let mut vm = Vm::new();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"var src = new ArrayBuffer(4, {maxByteLength: 8}); \
|
||
var v = new Uint8Array(src); v[0] = 9; \
|
||
var dest = src.transfer(5); \
|
||
var a = new Uint8Array(dest)[0]; \
|
||
var src1 = new ArrayBuffer(4, {maxByteLength: 8}); \
|
||
var d1 = src1.transferToFixedLength(5).maxByteLength; \
|
||
var src2 = new ArrayBuffer(4, {maxByteLength: 8}); \
|
||
var d2 = src2.transferToImmutable(5); \
|
||
var fixed = new ArrayBuffer(3); \
|
||
var d3 = fixed.transfer(3).resizable; \
|
||
[a, src.detached, d1, d2.immutable, d2.resizable, fixed.detached, d3].join(',')",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(vm.lookup_str(r).unwrap(), "9,true,5,true,false,true,false");
|
||
// 载荷存储态直读:transfer 产物 max = 8 + 1(+1 编码),ttf/tti 产物 = 0。
|
||
let dest_val = eval_ab(
|
||
&mut vm,
|
||
"var s = new ArrayBuffer(4, {maxByteLength: 8}); \
|
||
globalThis.__d = s.transfer(5); globalThis.__d",
|
||
)
|
||
.unwrap();
|
||
// SAFETY: __d 为已晋升 session 的 ArrayBuffer 对象,借用止于断言、不跨下一次 eval。
|
||
let dest_obj = unsafe { &*dest_val.as_js_object_ptr() };
|
||
let p = array_buffer_payload_ptr(dest_obj).expect("产物应携带载荷盒");
|
||
// SAFETY: p 指向存活载荷盒。
|
||
let payload = unsafe { &*p };
|
||
assert_eq!(payload.max_byte_length, 9);
|
||
assert!(!payload.immutable);
|
||
assert_eq!(payload.bytes.len(), 5);
|
||
}
|
||
|
||
/// resize detach 守卫序钉:守卫在 newLength 求值之后——强转内 detach
|
||
/// 与求值前已 detach 两形均先跑完 valueOf 再按 TypeError 抛;
|
||
/// immutable 守卫在求值之前(强转不跑)。
|
||
#[test]
|
||
fn resize_guard_order_pins() {
|
||
let mut vm = Vm::new();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"(function () { var calls = 0; var ab = new ArrayBuffer(64, {maxByteLength: 1024}); \
|
||
try { ab.resize({ valueOf() { calls++; $262.detachArrayBuffer(ab); return 0; } }); \
|
||
return 'no-throw' + calls; } \
|
||
catch (err) { return (err.name === 'TypeError' ? 'T' : 'X') + calls; } })() \
|
||
+ '|' + \
|
||
(function () { var c2 = 0; var ab2 = new ArrayBuffer(64, {maxByteLength: 1024}); \
|
||
$262.detachArrayBuffer(ab2); \
|
||
try { ab2.resize({ valueOf() { c2++; return 0; } }); return 'no-throw' + c2; } \
|
||
catch (err) { return (err.name === 'TypeError' ? 'T' : 'X') + c2; } })()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(vm.lookup_str(r).unwrap(), "T1|T1");
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"var calls = 0; \
|
||
var ab = new ArrayBuffer(4).transferToImmutable(); \
|
||
try { ab.resize({ valueOf() { calls++; return 0; } }); 'no-throw' } \
|
||
catch (err) { (err.name === 'TypeError' ? 'T' : 'X') + calls }",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(vm.lookup_str(r).unwrap(), "T0");
|
||
}
|
||
|
||
/// slice end 缺省臂钉:end 为 undefined 归 len 不经强转(6, undefined) → 2;
|
||
/// start/end 全缺省 → 全长。
|
||
#[test]
|
||
fn slice_end_undefined_defaults_len() {
|
||
let mut vm = Vm::new();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"new ArrayBuffer(8).slice(6, undefined).byteLength === 2 \
|
||
&& new ArrayBuffer(8).slice(undefined).byteLength === 8 \
|
||
&& new ArrayBuffer(8).slice(6).byteLength === 2 \
|
||
&& new ArrayBuffer(8).slice(undefined, undefined).byteLength === 8",
|
||
)
|
||
.unwrap();
|
||
assert!(r.as_bool());
|
||
}
|
||
|
||
/// sliceToImmutable 源不 detach 钉:产物 immutable,源返回后可写/可
|
||
/// resize/可 detach,产物内容独立于源。
|
||
#[test]
|
||
fn sti_source_writable_after_return() {
|
||
let mut vm = Vm::new();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"(function () { var ab = new ArrayBuffer(8); \
|
||
var v = new Uint8Array(ab); for (var i = 0; i < 8; i++) v[i] = i + 1; \
|
||
var dest = ab.sliceToImmutable(); \
|
||
var ok = dest.immutable === true && dest.resizable === false && ab.detached === false; \
|
||
v[0] = 86; \
|
||
var v2 = new Uint8Array(dest); \
|
||
return ok && v2[0] === 1 && v2[7] === 8 && ab.byteLength === 8; })()",
|
||
)
|
||
.unwrap();
|
||
assert!(r.as_bool());
|
||
}
|
||
|
||
/// slice 构造窗口源 detach 重检钉:species 构造器内 detach 源 → 构造后
|
||
/// 重检抛 TypeError(不交付结果)。
|
||
#[test]
|
||
fn slice_source_detach_in_construct_throws() {
|
||
let mut vm = Vm::new();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"(function () { var ab = new ArrayBuffer(8); \
|
||
var c = {}; c[Symbol.species] = function (len) { \
|
||
$262.detachArrayBuffer(ab); return new ArrayBuffer(len); }; \
|
||
ab.constructor = c; \
|
||
try { ab.slice(); return 'no-throw'; } catch (err) { return err.name; } })()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(vm.lookup_str(r).unwrap(), "TypeError");
|
||
}
|
||
|
||
/// growable 构造器预分配账目钉:byteLength 读活长、maxByteLength 读真实
|
||
/// 上限、growable 为真;核的 `shared_buffer_bytes` 增量等于真实上限
|
||
/// (预分配一次性到位),而非初始活长。定长对照臂:增量即活长、
|
||
/// growable 为假、maxByteLength 解码取活长。
|
||
#[test]
|
||
fn growable_constructor_preallocates_to_max() {
|
||
let mut vm = Vm::new();
|
||
let before = vm.kernel_core().shared_buffer_bytes();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"var sab = new SharedArrayBuffer(2, {maxByteLength: 8}); \
|
||
globalThis.__sab = sab; \
|
||
[sab.byteLength, sab.maxByteLength, sab.growable].join(',')",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(vm.lookup_str(r).unwrap(), "2,8,true");
|
||
assert_eq!(
|
||
vm.kernel_core().shared_buffer_bytes() - before,
|
||
8,
|
||
"growable 预分配账目是真实上限而非初始活长"
|
||
);
|
||
// 定长对照臂:账目即活长,growable 为假,maxByteLength 解码取活长。
|
||
let before_fixed = vm.kernel_core().shared_buffer_bytes();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"var fixed = new SharedArrayBuffer(4); \
|
||
globalThis.__fixed = fixed; \
|
||
[fixed.byteLength, fixed.maxByteLength, fixed.growable].join(',')",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(vm.lookup_str(r).unwrap(), "4,4,false");
|
||
assert_eq!(vm.kernel_core().shared_buffer_bytes() - before_fixed, 4);
|
||
}
|
||
|
||
/// grow 前缀保留与零填充钉:写入前缀后 grow,前缀字节不变、增补区
|
||
/// 全零、活长推进到目标值。
|
||
#[test]
|
||
fn grow_preserves_prefix_and_zero_fills() {
|
||
let mut vm = Vm::new();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"(function () { var sab = new SharedArrayBuffer(2, {maxByteLength: 8}); \
|
||
var v = new Uint8Array(sab); v[0] = 0xAA; v[1] = 0xBB; \
|
||
sab.grow(6); \
|
||
var v2 = new Uint8Array(sab); \
|
||
return v2[0] === 0xAA && v2[1] === 0xBB && v2[2] === 0 && v2[3] === 0 \
|
||
&& v2[4] === 0 && v2[5] === 0 && sab.byteLength === 6; })()",
|
||
)
|
||
.unwrap();
|
||
assert!(r.as_bool());
|
||
}
|
||
|
||
/// grow-only 与超上限 RangeError 钉:缩长(当前活长以下任意值)与超
|
||
/// 真实上限各抛 RangeError,恰好到上限成功;恰好等于当前活长是 no-op
|
||
/// 成功(只增不缩,相等放行)。
|
||
#[test]
|
||
fn grow_only_and_over_max_range_errors() {
|
||
let mut vm = Vm::new();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"(function () { var sab = new SharedArrayBuffer(4, {maxByteLength: 8}); \
|
||
var out = ''; \
|
||
try { sab.grow(3); out += 'no-throw'; } \
|
||
catch (e) { out += (e.name === 'RangeError' ? 'R' : 'X'); } \
|
||
try { sab.grow(4); out += 'ok'; } \
|
||
catch (e) { out += 'X'; } \
|
||
try { sab.grow(9); out += 'no-throw'; } \
|
||
catch (e) { out += (e.name === 'RangeError' ? 'R' : 'X'); } \
|
||
try { sab.grow(8); out += 'ok'; } \
|
||
catch (e) { out += 'X'; } \
|
||
return out + '|' + sab.byteLength; })()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(vm.lookup_str(r).unwrap(), "RokRok|8");
|
||
}
|
||
|
||
/// grow 非 growable 守卫钉:定长 SAB 调 grow 抛 TypeError,守卫先于
|
||
/// 参数强转(valueOf 副作用不运行)。
|
||
#[test]
|
||
fn grow_non_growable_guard_before_coercion() {
|
||
let mut vm = Vm::new();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"(function () { var calls = 0; \
|
||
var sab = new SharedArrayBuffer(4); \
|
||
try { sab.grow({ valueOf() { calls++; return 8; } }); \
|
||
return 'no-throw' + calls; } \
|
||
catch (err) { return (err.name === 'TypeError' ? 'T' : 'X') + calls; } })()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(vm.lookup_str(r).unwrap(), "T0");
|
||
}
|
||
|
||
/// grow 强转窗口重取钉:界判消费强转后重取的活长——valueOf 内先
|
||
/// grow 推进活长,外层 grow 到重取活长之下抛 RangeError(若消费
|
||
/// 求值前快照则会成功)。
|
||
#[test]
|
||
fn grow_coercion_window_refetch() {
|
||
let mut vm = Vm::new();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"(function () { var sab = new SharedArrayBuffer(4, {maxByteLength: 8}); \
|
||
try { sab.grow({ valueOf() { sab.grow(6); return 5; } }); \
|
||
return 'no-throw|' + sab.byteLength; } \
|
||
catch (e) { return (e.name === 'RangeError' ? 'R' : 'X') + '|' + sab.byteLength; } })()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(vm.lookup_str(r).unwrap(), "R|6");
|
||
}
|
||
|
||
/// SAB 无 detach 面钉:$262.detachArrayBuffer 对 SAB 抛 TypeError
|
||
/// (读入口标签校验臂),SAB 原型无 AB 专属方法(transfer / resize /
|
||
/// markImmutable / sliceToImmutable 均 undefined),detach 失败后缓冲
|
||
/// 状态不变。
|
||
#[test]
|
||
fn sab_has_no_detach_surface() {
|
||
let mut vm = Vm::new();
|
||
let r = eval_ab(
|
||
&mut vm,
|
||
"(function () { var sab = new SharedArrayBuffer(4, {maxByteLength: 8}); \
|
||
var out = ''; \
|
||
try { $262.detachArrayBuffer(sab); out += 'no-throw'; } \
|
||
catch (e) { out += (e.name === 'TypeError' ? 'T' : 'X'); } \
|
||
out += ',' + (sab.transfer === undefined ? 'u' : 'x') \
|
||
+ '|' + (sab.resize === undefined ? 'u' : 'x') \
|
||
+ '|' + (sab.markImmutable === undefined ? 'u' : 'x') \
|
||
+ '|' + (sab.sliceToImmutable === undefined ? 'u' : 'x'); \
|
||
return out + '|' + sab.byteLength + '|' + sab.growable; })()",
|
||
)
|
||
.unwrap();
|
||
assert_eq!(vm.lookup_str(r).unwrap(), "T,u|u|u|u|4|true");
|
||
}
|
||
}
|