Files
OxideJS-New/crates/oxide_builtins/src/array_buffer.rs
T

1371 lines
66 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use std::sync::Arc;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
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 载荷:字节缓冲与状态位。`data` 为 `None` 即缓冲区已 detach;
/// `max_byte_length` 为存储态上限:0 即定长缓冲(resizable 判据),非 0 存
/// 请求上限 + 1(上限 0 与定长必须可分);`immutable` 为字节缓冲写守卫标志。
/// 载荷经 `Arc::into_raw` 存于对象 `native_fn` 槽(引用计数盒:SharedArrayBuffer
/// 克隆共享同一载荷,计数归零统一释放),GC 两自由函数(size/drop)对整结构体
/// 操作。
#[derive(Clone)]
pub(crate) struct ArrayBufferPayload {
pub(crate) data: Option<Vec<u8>>,
pub(crate) max_byte_length: usize,
pub(crate) immutable: bool,
}
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 {
data: Some(data),
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.data.as_ref().map_or(0, |d| d.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 数据
/// 属性)。
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 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 ArrayBufferPayload> {
if !obj.is_shared_array_buffer_obj() {
return None;
}
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut ArrayBufferPayload)
}
/// 缓冲区载荷指针单收口:ArrayBuffer 与 SharedArrayBuffer 共用同一载荷
/// 结构,双标签双认,返回统一形态的载荷盒指针;两标签之外(含槽位空)
/// 返回 `None`。
pub(crate) fn buffer_payload_ptr(obj: &JsObject) -> Option<*mut ArrayBufferPayload> {
if obj.is_array_buffer_obj() {
return array_buffer_payload_ptr(obj);
}
shared_array_buffer_payload_ptr(obj)
}
/// 缓冲区载荷读入口(两标签双认):校验 receiver 为 ArrayBuffer 或
/// 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> {
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(payload_ptr) = buffer_payload_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)
}
/// SharedArrayBuffer 载荷盒字节数(`native_fn` 槽);非 SAB 或已释放 → 0。
pub fn shared_array_buffer_native_size(obj: &JsObject) -> u64 {
let payload_ptr = match shared_array_buffer_payload_ptr(obj) {
Some(p) => p,
None => return 0,
};
if payload_ptr.is_null() {
return 0;
}
// SAFETY: payload_ptr 非空且指向存活载荷盒。
let payload = unsafe { &*payload_ptr };
(std::mem::size_of::<ArrayBufferPayload>() + payload.data.as_ref().map_or(0, |d| d.capacity())) as u64
}
/// 释放 SharedArrayBuffer 载荷盒(native_fn 槽),返回字节数;槽置空后重复调用零释放。
pub fn drop_shared_array_buffer_native(obj: &mut JsObject) -> u64 {
let bytes = shared_array_buffer_native_size(obj);
if bytes == 0 {
return 0;
}
let Some(payload_ptr) = shared_array_buffer_payload_ptr(obj) else {
return 0;
};
// SAFETY: payload_ptr 非空(size 已验证),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
/// 生产路径,载荷 `data` 恒在场(`map_or` 兜底与 AB 读入口同形)。
pub(crate) fn shared_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,
"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 ArrayBufferPayload;
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 臂归后续面)。
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());
NativeResult::Ok(JsValue::int(len as i32))
}
/// `SharedArrayBuffer.prototype.growable` getter:返回缓冲区是否 growable
/// (载荷存储态上限非 0:0 = 定长,非 0 = 请求上限 + 1)。
/// 非 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))
}
/// `SharedArrayBuffer.prototype.maxByteLength` getter:定长(存储态上限 0)
/// 返回当前字节数;growable 返回真实上限(存储态 − 1 解码)。SAB 无
/// detached 臂(载荷 `data` 恒在场)。
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())
} else {
payload.max_byte_length - 1
};
NativeResult::Ok(JsValue::int(value as i32))
}
/// `SharedArrayBuffer.prototype.grow(newLength)`:growable 缓冲区原地增长到
/// newLength,前缀保留、增补零填;只增不缩(缩为 RangeError)。
///
/// # 步骤
/// 1. this 品牌校验(非对象/非 SAB → TypeError)。
/// 2. 非 growable(存储态上限 0)→ TypeError,先于强转。
/// 3. newByteLength = ToIntegerOrInfinity(newLength) 传播式(NaN → 0,
/// ±Infinity 保留)。
/// 4. 重取 this 寄存器 + 载荷指针(强转 JS 调用窗可晋升——AB resize 同式;
/// SAB 无 detach 面,仅晋升面)。
/// 5. 重读存储态上限 0 → TypeError。
/// 6. newByteLength < 0 或 > 真实上限(重读存储态 − 1)或 < 当前字节数
/// (grow-only)→ RangeError。
/// 7. `Vec::resize` 一次调用收口目标长度。
/// 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 }.max_byte_length == 0 {
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 { &mut *payload_ptr };
if payload.max_byte_length == 0 {
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 {
return NativeResult::Err(crate::error::create_range_error(vm, "invalid SharedArrayBuffer length"));
}
data.resize(new_length as usize, 0);
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 }.data.as_ref().map_or(0, |d| d.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 }.data.as_ref();
// 结果三检: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 }.data.as_ref().map_or(0, |d| d.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]);
}
NativeResult::Ok(new_val)
}
/// 读入口:校验 receiver 为 ArrayBuffer 并取载荷指针。detach(载荷 `data` 为
/// `None`)不在此分叉,由消费方现读 `data` 时按既有错误形态处理。
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 len = unsafe { &*payload_ptr }.data.as_ref().map_or(0, |d| d.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.data.is_none() {
return NativeResult::Ok(JsValue::int(0));
}
let value = if payload.max_byte_length == 0 {
payload.data.as_ref().map_or(0, |d| d.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 判据即载荷 `data` 为
/// `None`。
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()))
}
/// `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.data.is_none() {
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 生产路径:品牌守卫后载荷 `data → None`(字节缓冲随
/// `Option` 置空释放,载荷盒本体存活至对象 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 };
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;
let Some(data) = payload.data.as_mut() else {
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"));
}
data.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 Some(data) = unsafe { &*payload_ptr }.data.as_ref() else {
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
};
let len = data.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 Some(data) = unsafe { &*payload_ptr }.data.as_ref() else {
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
};
// 结果五检: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.data.is_none() {
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.data.as_ref().map_or(0, |d| d.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 臂已先行
// 排除,data 必为 Some,可变借用止于本语句。
if let Some(dest) = unsafe { (*new_payload).data.as_mut() } {
dest[..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 Some(data) = unsafe { &*payload_ptr }.data.as_ref() else {
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
};
let len = data.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 Some(data) = unsafe { &*payload_ptr }.data.as_ref() else {
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
};
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 Some(data) = unsafe { &*payload_ptr }.data.as_ref() else {
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
};
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(`data → None`):分配可已晋升源对象,新缓冲
/// 持独立拷贝,源 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 cur_len = unsafe { &*payload_ptr }.data.as_ref().map_or(0, |d| d.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;
(p.data.as_ref().map_or(0, |d| d.len()), p.max_byte_length, p.immutable, p.data.is_none())
};
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 已排除,data 必为
// Some。字节借出止于本语句。
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());
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).data = None };
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 {
data: None,
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 {
data: Some(vec![0u8; 8]),
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 {
data: Some(vec![0u8; 4]),
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.data.as_ref().expect("产物应附着").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");
}
}