mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: DataView 活界判与构造器 proto 读后终校验落地
This commit is contained in:
@@ -5,15 +5,19 @@ use oxide_types::value::JsValue;
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
use crate::array_buffer::array_buffer_payload;
|
||||
use crate::array_buffer::{array_buffer_payload, ArrayBufferPayload};
|
||||
|
||||
use oxide_runtime_api::{NativeResult, VmHost};
|
||||
|
||||
/// DataView 视图状态盒。`byte_length` 语义随 `length_is_auto` 分流:
|
||||
/// 定长视图存静态长;auto 视图(构造时 byteLength 缺省且构造期缓冲可 resize)
|
||||
/// 存构造期 live − offset 初值,活读一律现算 live − offset。
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct DataViewData {
|
||||
buffer: JsValue,
|
||||
byte_offset: usize,
|
||||
byte_length: usize,
|
||||
length_is_auto: bool,
|
||||
}
|
||||
|
||||
macro_rules! native_try {
|
||||
@@ -106,50 +110,91 @@ fn get_data_view_data<H: VmHost>(vm: &mut H, this_val: JsValue) -> Result<DataVi
|
||||
Ok(unsafe { *data_ptr })
|
||||
}
|
||||
|
||||
fn checked_absolute_offset<H: VmHost>(
|
||||
vm: &mut H, view: DataViewData, offset: usize, width: usize,
|
||||
/// 视图界判核(规范 IsViewOutOfBounds 与 GetViewByteLength 合一):缓冲已
|
||||
/// detach(载荷 `data` 为 `None`)、auto 视图 offset 超 live、定长视图
|
||||
/// offset + 静态长超 live(含溢出)→ TypeError;通过时返回视图长
|
||||
/// (auto 视图现算 live − offset,定长视图返静态长)。
|
||||
fn dv_view_bounds<H: VmHost>(
|
||||
vm: &mut H, payload_ptr: *mut ArrayBufferPayload, view: DataViewData,
|
||||
) -> Result<usize, JsValue> {
|
||||
let Some(end) = offset.checked_add(width) else {
|
||||
return Err(crate::error::create_range_error(vm, "DataView offset out of bounds"));
|
||||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。
|
||||
let Some(data) = unsafe { &*payload_ptr }.data.as_deref() else {
|
||||
return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||||
};
|
||||
if end > view.byte_length {
|
||||
return Err(crate::error::create_range_error(vm, "DataView offset out of bounds"));
|
||||
let live = data.len();
|
||||
if view.length_is_auto {
|
||||
if view.byte_offset > live {
|
||||
return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||||
}
|
||||
return Ok(live - view.byte_offset);
|
||||
}
|
||||
match view.byte_offset.checked_add(view.byte_length) {
|
||||
Some(end) if end <= live => Ok(view.byte_length),
|
||||
_ => Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")),
|
||||
}
|
||||
Ok(view.byte_offset + offset)
|
||||
}
|
||||
|
||||
/// GetViewValue 后半:按已校验的视图与偏移读 N 字节(越界抛 RangeError)。
|
||||
/// detach 守卫先于范围校验(规范序:IsDetachedBuffer 检查先于越界判定,
|
||||
/// detach 后越界偏移也抛 TypeError 而非 RangeError)。
|
||||
/// set 面入口写守卫:immutable 缓冲 → TypeError(先于一切实参强转)。
|
||||
fn dv_buffer_writable<H: VmHost>(vm: &mut H, view: DataViewData) -> Result<(), JsValue> {
|
||||
let payload_ptr = array_buffer_payload(vm, view.buffer)?;
|
||||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。
|
||||
if unsafe { &*payload_ptr }.immutable {
|
||||
return Err(crate::error::create_type_error(vm, "ArrayBuffer is immutable"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// JS 调用点后的缓冲重取纪律:重读寄存器再重取载荷(epoch 晋升改写
|
||||
/// 寄存器,旧 JsValue 滞留 epoch 克隆旧盒);detached → TypeError。
|
||||
/// 返回(寄存器重读值、live 长、存储上限)。
|
||||
fn revalidate_buffer<H: VmHost>(vm: &mut H, buf_reg: u8) -> Result<(JsValue, usize, usize), JsValue> {
|
||||
let buffer = vm.reg(buf_reg);
|
||||
let payload_ptr = array_buffer_payload(vm, buffer)?;
|
||||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。
|
||||
let payload = unsafe { &*payload_ptr };
|
||||
let Some(data) = payload.data.as_deref() else {
|
||||
return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||||
};
|
||||
Ok((buffer, data.len(), payload.max_byte_length))
|
||||
}
|
||||
|
||||
/// GetViewValue 后半:按已校验的视图与偏移读 N 字节。界判核先
|
||||
/// (detach/OOB → TypeError,live 长口径),后视图相对越界
|
||||
/// (→ RangeError,判活视图长)。
|
||||
fn read_bytes_at<const N: usize, H: VmHost>(vm: &mut H, view: DataViewData, offset: usize) -> Result<[u8; N], JsValue> {
|
||||
let payload_ptr = array_buffer_payload(vm, view.buffer)?;
|
||||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。
|
||||
let Some(buffer) = unsafe { &*payload_ptr }.data.as_deref() else {
|
||||
return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||||
};
|
||||
let abs = checked_absolute_offset(vm, view, offset, N)?;
|
||||
if abs + N > buffer.len() {
|
||||
return Err(crate::error::create_range_error(vm, "DataView offset out of bounds"));
|
||||
let view_len = dv_view_bounds(vm, payload_ptr, view)?;
|
||||
match offset.checked_add(N) {
|
||||
Some(end) if end <= view_len => {}
|
||||
_ => return Err(crate::error::create_range_error(vm, "DataView offset out of bounds")),
|
||||
}
|
||||
let abs = view.byte_offset + offset;
|
||||
let mut out = [0u8; N];
|
||||
out.copy_from_slice(&buffer[abs..abs + N]);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// SetViewValue 后半:按已校验的视图与偏移写 N 字节(越界抛 RangeError)。
|
||||
/// detach 守卫先于范围校验(规范序同读路径)。
|
||||
/// SetViewValue 后半:按已校验的视图与偏移写 N 字节(读路径同序)。
|
||||
fn write_bytes<const N: usize, H: VmHost>(
|
||||
vm: &mut H, view: DataViewData, offset: usize, bytes: [u8; N],
|
||||
) -> Result<(), JsValue> {
|
||||
let payload_ptr = array_buffer_payload(vm, view.buffer)?;
|
||||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。
|
||||
// 界判核共享借用先于可变借用建立,避免活别名。
|
||||
let view_len = dv_view_bounds(vm, payload_ptr, view)?;
|
||||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷;
|
||||
// 界判核已证 data 在场,下方守卫为结构性兜底。
|
||||
let Some(buffer) = unsafe { &mut *payload_ptr }.data.as_deref_mut() else {
|
||||
return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||||
};
|
||||
let abs = checked_absolute_offset(vm, view, offset, N)?;
|
||||
if abs + N > buffer.len() {
|
||||
return Err(crate::error::create_range_error(vm, "DataView offset out of bounds"));
|
||||
match offset.checked_add(N) {
|
||||
Some(end) if end <= view_len => {}
|
||||
_ => return Err(crate::error::create_range_error(vm, "DataView offset out of bounds")),
|
||||
}
|
||||
let abs = view.byte_offset + offset;
|
||||
buffer[abs..abs + N].copy_from_slice(&bytes);
|
||||
Ok(())
|
||||
}
|
||||
@@ -183,18 +228,26 @@ fn has_data_view_proto<H: VmHost>(vm: &mut H, this_val: JsValue) -> bool {
|
||||
/// # 步骤
|
||||
/// 1. receiver 原型链不含 %DataView.prototype% → TypeError(普通调用面:
|
||||
/// this = globalThis 不满足;构造路径恒传 DataView 原型链上的 receiver)。
|
||||
/// 2. buffer 品牌校验(ArrayBufferData)。
|
||||
/// 3. offset = ToIndex(byteOffset) 传播式;detached 缓冲 → TypeError(在
|
||||
/// 偏移强转之后);offset > bufferLen → RangeError。
|
||||
/// 4. byteLength 缺省/undefined → bufferLen - offset,否则 ToIndex 传播式;
|
||||
/// offset + byteLength > bufferLen → RangeError。
|
||||
/// 2. buffer 品牌校验(ArrayBufferData);首个载荷取用后即弃指针,
|
||||
/// 不持借用跨 JS 调用。
|
||||
/// 3. offset = ToIndex(byteOffset) 传播式;其后重取(重读寄存器 + 载荷重取):
|
||||
/// detached 缓冲 → TypeError(在偏移强转之后);offset > live → RangeError。
|
||||
/// 4. byteLength 缺省/undefined → 缺省长 live − offset,auto 位按构造期缓冲
|
||||
/// resizable 定;否则 ToIndex 传播式,其后重取按新 live 复验
|
||||
/// offset + byteLength → RangeError。
|
||||
/// 5. GetPrototypeFromConstructor:经 `ordinary_get` 读 NewTarget(regs[255])
|
||||
/// 的 `prototype`(触发访问器,读期异常原值传播);非对象回落 %DataView.prototype%。
|
||||
/// 6. 分配 OBJ_TYPE_DATA_VIEW 对象并挂 DataViewData 状态盒。
|
||||
/// 6. 终校验(proto 读后再重取一次):detached → TypeError;offset > live' →
|
||||
/// RangeError;显式长 offset + byteLength > live' → RangeError;auto 视图
|
||||
/// 存储长按 live' − offset 重算。
|
||||
/// 7. 分配 OBJ_TYPE_DATA_VIEW 对象并挂 DataViewData 状态盒(终校验先于
|
||||
/// 分配,raise 路径无半成品对象)。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 原型读取在全部校验之后:越界 RangeError 先于抛错的 prototype getter。
|
||||
/// - 原型读取在全部初校验之后:越界 RangeError 先于抛错的 prototype getter。
|
||||
/// - NewTarget 非对象(直调/缺省面)时直接回落默认原型,不抛错。
|
||||
/// - 每个 JS 调用点后重取缓冲(重读寄存器 + 载荷重取):晋升改写寄存器,
|
||||
/// 旧值滞留 epoch 克隆旧盒。
|
||||
pub fn data_view_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !has_data_view_proto(vm, this_val) {
|
||||
@@ -205,37 +258,34 @@ pub fn data_view_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "DataView requires an ArrayBuffer"));
|
||||
}
|
||||
let buffer = vm.reg(args[1]);
|
||||
let payload_ptr = native_try!(array_buffer_payload(vm, buffer));
|
||||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。
|
||||
let buf = unsafe { &*payload_ptr }.data.as_deref();
|
||||
let buffer_len = buf.map_or(0, |d| d.len());
|
||||
// 首个载荷取用只做品牌校验,指针即弃,不持借用跨 JS 调用。
|
||||
native_try!(array_buffer_payload(vm, buffer));
|
||||
|
||||
let byte_offset = if args.len() > 2 {
|
||||
native_try!(to_index(vm, vm.reg(args[2]), "DataView byteOffset out of bounds"))
|
||||
} else {
|
||||
0
|
||||
};
|
||||
// detach 守卫在偏移强转之后、越界判定之前(规范序:detached 缓冲构造
|
||||
// 抛 TypeError,且偏移的 ToNumber 已恰好发生一次)。
|
||||
if buf.is_none() {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||||
}
|
||||
if byte_offset > buffer_len {
|
||||
|
||||
// 偏移强转后的重取:detached 缓冲构造抛 TypeError,且偏移的 ToNumber
|
||||
// 已恰好发生一次(规范序)。
|
||||
let (_, live, max_byte_length) = native_try!(revalidate_buffer(vm, args[1]));
|
||||
if byte_offset > live {
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "DataView byteOffset out of bounds"));
|
||||
}
|
||||
let default_len = buffer_len - byte_offset;
|
||||
// byteLength 显式 undefined 视同缺省(ToIndex(undefined) = 0 会吞掉视图长度)。
|
||||
let byte_length = if args.len() > 3 {
|
||||
let bl_val = vm.reg(args[3]);
|
||||
if bl_val.is_undefined() {
|
||||
default_len
|
||||
} else {
|
||||
native_try!(to_index(vm, bl_val, "DataView byteLength out of bounds"))
|
||||
|
||||
// byteLength 显式 undefined 视同缺省(ToIndex(undefined) = 0 会吞掉视图长度);
|
||||
// 缺省臂 auto 位按构造期缓冲 resizable 判定。
|
||||
let explicit = args.len() > 3 && !vm.reg(args[3]).is_undefined();
|
||||
let length_is_auto = !explicit && max_byte_length != 0;
|
||||
let mut byte_length = live - byte_offset;
|
||||
if explicit {
|
||||
byte_length = native_try!(to_index(vm, vm.reg(args[3]), "DataView byteLength out of bounds"));
|
||||
// byteLength 强转后的重取,按新 live 复验。
|
||||
let (_, live, _) = native_try!(revalidate_buffer(vm, args[1]));
|
||||
if byte_offset + byte_length > live {
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "DataView byteLength out of bounds"));
|
||||
}
|
||||
} else {
|
||||
default_len
|
||||
};
|
||||
if byte_offset + byte_length > buffer_len {
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "DataView byteLength out of bounds"));
|
||||
}
|
||||
|
||||
// GetPrototypeFromConstructor:普通读触发原型链上的访问器 getter,
|
||||
@@ -259,12 +309,24 @@ pub fn data_view_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
default_proto
|
||||
};
|
||||
|
||||
// 终校验:proto getter 是用户代码窗(detach/resize 可发生),再重取一次,
|
||||
// 全部判定用 live';auto 视图存储长按 live' − offset 重算。
|
||||
let (buffer, live, _) = native_try!(revalidate_buffer(vm, args[1]));
|
||||
if byte_offset > live {
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "DataView byteOffset out of bounds"));
|
||||
}
|
||||
if !length_is_auto && byte_offset + byte_length > live {
|
||||
return NativeResult::Err(crate::error::create_range_error(vm, "DataView byteLength out of bounds"));
|
||||
}
|
||||
let byte_length = if length_is_auto { live - byte_offset } else { byte_length };
|
||||
|
||||
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto);
|
||||
obj.type_tag = JsObject::OBJ_TYPE_DATA_VIEW;
|
||||
let data = Box::into_raw(Box::new(DataViewData {
|
||||
buffer,
|
||||
byte_offset,
|
||||
byte_length,
|
||||
length_is_auto,
|
||||
}));
|
||||
// SAFETY: DataView 对象不可调用,native_fn 存不透明 `Box<DataViewData>` 指针,
|
||||
// 与 ArrayBuffer / RegExp 的类型化存储模式一致。
|
||||
@@ -502,10 +564,12 @@ pub fn data_view_get_big_uint64<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRes
|
||||
|
||||
/// `DataView.prototype.setInt8(byteOffset, value)`:写入 1 字节有符号整数。
|
||||
///
|
||||
/// 步序:品牌 → ToIndex(byteOffset) → ToInt32(value) 回卷 → 越界 RangeError → 写。
|
||||
/// 步序:品牌 → immutable 写守卫 → ToIndex(byteOffset) → ToInt32(value)
|
||||
/// 回卷 → 越界 RangeError → 写。
|
||||
pub fn data_view_set_int8<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
native_try!(dv_buffer_writable(vm, view));
|
||||
let offset = native_try!(to_index_arg(vm, args, 1));
|
||||
let n = to_int32(native_try!(coerce_value_arg(vm, args, 2)));
|
||||
native_try!(write_bytes::<1, H>(vm, view, offset, [n as u8]));
|
||||
@@ -516,6 +580,7 @@ pub fn data_view_set_int8<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn data_view_set_uint8<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
native_try!(dv_buffer_writable(vm, view));
|
||||
let offset = native_try!(to_index_arg(vm, args, 1));
|
||||
let n = to_int32(native_try!(coerce_value_arg(vm, args, 2)));
|
||||
native_try!(write_bytes::<1, H>(vm, view, offset, [n as u8]));
|
||||
@@ -526,6 +591,7 @@ pub fn data_view_set_uint8<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn data_view_set_int16<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
native_try!(dv_buffer_writable(vm, view));
|
||||
let offset = native_try!(to_index_arg(vm, args, 1));
|
||||
let n = to_int32(native_try!(coerce_value_arg(vm, args, 2)));
|
||||
let bytes = if is_little_endian(vm, args, 3) {
|
||||
@@ -541,6 +607,7 @@ pub fn data_view_set_int16<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn data_view_set_uint16<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
native_try!(dv_buffer_writable(vm, view));
|
||||
let offset = native_try!(to_index_arg(vm, args, 1));
|
||||
let n = to_int32(native_try!(coerce_value_arg(vm, args, 2)));
|
||||
let bytes = if is_little_endian(vm, args, 3) {
|
||||
@@ -556,6 +623,7 @@ pub fn data_view_set_uint16<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
pub fn data_view_set_int32<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
native_try!(dv_buffer_writable(vm, view));
|
||||
let offset = native_try!(to_index_arg(vm, args, 1));
|
||||
let n = to_int32(native_try!(coerce_value_arg(vm, args, 2)));
|
||||
let bytes = if is_little_endian(vm, args, 3) { n.to_le_bytes() } else { n.to_be_bytes() };
|
||||
@@ -567,6 +635,7 @@ pub fn data_view_set_int32<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn data_view_set_uint32<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
native_try!(dv_buffer_writable(vm, view));
|
||||
let offset = native_try!(to_index_arg(vm, args, 1));
|
||||
let n = to_int32(native_try!(coerce_value_arg(vm, args, 2)));
|
||||
let bytes = if is_little_endian(vm, args, 3) {
|
||||
@@ -582,6 +651,7 @@ pub fn data_view_set_uint32<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
pub fn data_view_set_float32<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
native_try!(dv_buffer_writable(vm, view));
|
||||
let offset = native_try!(to_index_arg(vm, args, 1));
|
||||
let value = native_try!(coerce_value_arg(vm, args, 2)) as f32;
|
||||
let bytes = if is_little_endian(vm, args, 3) {
|
||||
@@ -598,6 +668,7 @@ pub fn data_view_set_float32<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
pub fn data_view_set_float16<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
native_try!(dv_buffer_writable(vm, view));
|
||||
let offset = native_try!(to_index_arg(vm, args, 1));
|
||||
let value = native_try!(coerce_value_arg(vm, args, 2));
|
||||
let bits = crate::math::f64_to_f16_bits(value);
|
||||
@@ -614,6 +685,7 @@ pub fn data_view_set_float16<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
pub fn data_view_set_float64<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
native_try!(dv_buffer_writable(vm, view));
|
||||
let offset = native_try!(to_index_arg(vm, args, 1));
|
||||
let value = native_try!(coerce_value_arg(vm, args, 2));
|
||||
let bytes = if is_little_endian(vm, args, 3) {
|
||||
@@ -628,10 +700,12 @@ pub fn data_view_set_float64<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
/// `DataView.prototype.setBigInt64(byteOffset, value, littleEndian)`:按 ToBigInt
|
||||
/// 语义接收值(Number 入参抛 TypeError),取低 64 位位模式写入 8 字节有符号整数。
|
||||
///
|
||||
/// 步序:品牌 → ToIndex(byteOffset) → ToBigInt(value) → 越界 RangeError → 写。
|
||||
/// 步序:品牌 → immutable 写守卫 → ToIndex(byteOffset) → ToBigInt(value)
|
||||
/// → 越界 RangeError → 写。
|
||||
pub fn data_view_set_big_int64<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
native_try!(dv_buffer_writable(vm, view));
|
||||
let offset = native_try!(to_index_arg(vm, args, 1));
|
||||
let value = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
let b =
|
||||
@@ -651,6 +725,7 @@ pub fn data_view_set_big_int64<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
|
||||
pub fn data_view_set_big_uint64<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
native_try!(dv_buffer_writable(vm, view));
|
||||
let offset = native_try!(to_index_arg(vm, args, 1));
|
||||
let value = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
let b =
|
||||
@@ -686,21 +761,22 @@ pub fn data_view_buffer_getter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
|
||||
}
|
||||
|
||||
/// `DataView.prototype.byteOffset` getter:返回视图起始字节在 buffer 内的偏移。
|
||||
/// 缓冲已 detach 或视图越界(live 长口径)→ TypeError。
|
||||
pub fn data_view_byte_offset_getter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
let payload_ptr = native_try!(array_buffer_payload(vm, view.buffer));
|
||||
native_try!(dv_view_bounds(vm, payload_ptr, view));
|
||||
NativeResult::Ok(JsValue::float(view.byte_offset as f64))
|
||||
}
|
||||
|
||||
/// `DataView.prototype.byteLength` getter:返回视图覆盖的字节长度。
|
||||
/// 缓冲已 detach → TypeError(规范序:IsDetachedBuffer 检查先于返回值)。
|
||||
/// 缓冲已 detach 或视图越界 → TypeError;auto 视图返 live − offset 活读
|
||||
/// (存储值仅构造期语义)。
|
||||
pub fn data_view_byte_length_getter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
let view = native_try!(get_data_view_data(vm, this_val));
|
||||
let payload_ptr = native_try!(array_buffer_payload(vm, view.buffer));
|
||||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。
|
||||
if unsafe { &*payload_ptr }.data.is_none() {
|
||||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||||
}
|
||||
NativeResult::Ok(JsValue::float(view.byte_length as f64))
|
||||
let view_len = native_try!(dv_view_bounds(vm, payload_ptr, view));
|
||||
NativeResult::Ok(JsValue::float(view_len as f64))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(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 error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&Arc::new(module))
|
||||
}
|
||||
|
||||
/// 构造器终校验 detach 形:proto getter 内 detach → TypeError,
|
||||
/// 偏移 ToNumber 恰发生一次(重取点在强转之后)。
|
||||
#[test]
|
||||
fn dv_ctor_custom_proto_detach_throws() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var buffer = new ArrayBuffer(8); \
|
||||
var calls = 0; \
|
||||
var byteOffset = { valueOf() { calls++; return 0; } }; \
|
||||
var newTarget = function() {}.bind(null); \
|
||||
Object.defineProperty(newTarget, 'prototype', { \
|
||||
get() { $262.detachArrayBuffer(buffer); return DataView.prototype; } \
|
||||
}); \
|
||||
var err = null; \
|
||||
try { Reflect.construct(DataView, [buffer, byteOffset], newTarget); } catch (e) { err = e; } \
|
||||
err instanceof TypeError && calls === 1",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// auto 缺省长终校验重算:proto getter 内 resize(2) 后存储长按 live' − offset 重算。
|
||||
#[test]
|
||||
fn dv_ctor_custom_proto_resize_valid_by_offset() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var buffer = new ArrayBuffer(3, { maxByteLength: 3 }); \
|
||||
var newTarget = function() {}.bind(null); \
|
||||
Object.defineProperty(newTarget, 'prototype', { \
|
||||
get: function() { buffer.resize(2); return DataView.prototype; } \
|
||||
}); \
|
||||
var result = Reflect.construct(DataView, [buffer, 2], newTarget); \
|
||||
result.constructor === DataView && result.byteLength === 0 && result.byteOffset === 2",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// 显式长终校验:resize 至 2 后 1 + 1 ≤ 2 → 成功且 byteLength 保持静态 1。
|
||||
#[test]
|
||||
fn dv_ctor_custom_proto_resize_valid_by_length() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var buffer = new ArrayBuffer(3, { maxByteLength: 3 }); \
|
||||
var newTarget = function() {}.bind(null); \
|
||||
Object.defineProperty(newTarget, 'prototype', { \
|
||||
get: function() { buffer.resize(2); return DataView.prototype; } \
|
||||
}); \
|
||||
var result = Reflect.construct(DataView, [buffer, 1, 1], newTarget); \
|
||||
result.constructor === DataView && result.byteLength === 1",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// 终校验 offset 臂:resize(1) 后 offset 2 > live' → RangeError。
|
||||
#[test]
|
||||
fn dv_ctor_custom_proto_resize_invalid_by_offset() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var buffer = new ArrayBuffer(3, { maxByteLength: 3 }); \
|
||||
var newTarget = function() {}.bind(null); \
|
||||
Object.defineProperty(newTarget, 'prototype', { \
|
||||
get: function() { buffer.resize(1); return DataView.prototype; } \
|
||||
}); \
|
||||
var err = null; \
|
||||
try { Reflect.construct(DataView, [buffer, 2], newTarget); } catch (e) { err = e; } \
|
||||
err instanceof RangeError",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// 终校验显式长臂:resize(2) 后 1 + 2 > live' → RangeError。
|
||||
#[test]
|
||||
fn dv_ctor_custom_proto_resize_invalid_by_length() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var buffer = new ArrayBuffer(3, { maxByteLength: 3 }); \
|
||||
var newTarget = function() {}.bind(null); \
|
||||
Object.defineProperty(newTarget, 'prototype', { \
|
||||
get: function() { buffer.resize(2); return DataView.prototype; } \
|
||||
}); \
|
||||
var err = null; \
|
||||
try { Reflect.construct(DataView, [buffer, 1, 2], newTarget); } catch (e) { err = e; } \
|
||||
err instanceof RangeError",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// byteLength auto 活读序列:3 → grow 4 → shrink 2 → 边界 0 → 越界 TypeError。
|
||||
#[test]
|
||||
fn dv_bytelength_auto_tracks_resize() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var ab = new ArrayBuffer(4, { maxByteLength: 5 }); \
|
||||
var dv = new DataView(ab, 1); \
|
||||
var ok = dv.byteLength === 3; \
|
||||
ab.resize(5); ok = ok && dv.byteLength === 4; \
|
||||
ab.resize(3); ok = ok && dv.byteLength === 2; \
|
||||
ab.resize(1); ok = ok && dv.byteLength === 0; \
|
||||
ab.resize(0); \
|
||||
try { dv.byteLength; ok = false; } catch (e) { ok = ok && e instanceof TypeError; } \
|
||||
ok",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// byteLength fixed 视图:shrink 到 end 之下 → TypeError,grow 回静态长。
|
||||
#[test]
|
||||
fn dv_bytelength_fixed_shrink_typeerror() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var ab = new ArrayBuffer(4, { maxByteLength: 5 }); \
|
||||
var dv = new DataView(ab, 1, 2); \
|
||||
var ok = dv.byteLength === 2; \
|
||||
ab.resize(5); ok = ok && dv.byteLength === 2; \
|
||||
ab.resize(3); ok = ok && dv.byteLength === 2; \
|
||||
ab.resize(2); \
|
||||
try { dv.byteLength; ok = false; } catch (e) { ok = ok && e instanceof TypeError; } \
|
||||
ab.resize(3); ok = ok && dv.byteLength === 2; \
|
||||
ok",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// byteOffset getter detach 守卫:detached 缓冲 → TypeError。
|
||||
#[test]
|
||||
fn dv_byteoffset_detach_throws() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var buffer = new ArrayBuffer(1); \
|
||||
var sample = new DataView(buffer, 0); \
|
||||
$262.detachArrayBuffer(buffer); \
|
||||
var err = null; \
|
||||
try { sample.byteOffset; } catch (e) { err = e; } \
|
||||
err instanceof TypeError",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// byteOffset getter OOB 守卫:auto 视图 shrink 至 offset 之下 → TypeError。
|
||||
#[test]
|
||||
fn dv_byteoffset_auto_shrink_typeerror() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var ab = new ArrayBuffer(4, { maxByteLength: 5 }); \
|
||||
var dv = new DataView(ab, 1); \
|
||||
var ok = dv.byteOffset === 1; \
|
||||
ab.resize(0); \
|
||||
try { dv.byteOffset; ok = false; } catch (e) { ok = ok && e instanceof TypeError; } \
|
||||
ok",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// get 面 OOB:fixed 视图(0,16)resize(8) 后读 → TypeError(非 RangeError)。
|
||||
#[test]
|
||||
fn dv_get_shrunk_buffer_typeerror() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var buffer = new ArrayBuffer(24, { maxByteLength: 32 }); \
|
||||
var sample = new DataView(buffer, 0, 16); \
|
||||
buffer.resize(16); \
|
||||
var ok = sample.getInt8(0) === 0; \
|
||||
buffer.resize(8); \
|
||||
try { sample.getInt8(0); ok = false; } catch (e) { ok = ok && e instanceof TypeError; } \
|
||||
ok",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// set 面 OOB:同形写面 → TypeError。
|
||||
#[test]
|
||||
fn dv_set_shrunk_buffer_typeerror() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var buffer = new ArrayBuffer(24, { maxByteLength: 32 }); \
|
||||
var sample = new DataView(buffer, 0, 16); \
|
||||
buffer.resize(8); \
|
||||
var err = null; \
|
||||
try { sample.setInt8(0, 30); } catch (e) { err = e; } \
|
||||
err instanceof TypeError",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// set 面 immutable 守卫先于一切强转:偏移与值 valueOf 零调用。
|
||||
#[test]
|
||||
fn dv_set_immutable_before_conversion() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var iab = (new ArrayBuffer(8)).transferToImmutable(); \
|
||||
var view = new DataView(iab); \
|
||||
var calls = []; \
|
||||
var byteOffset = { valueOf() { calls.push('byteOffset.valueOf'); return 0; } }; \
|
||||
var value = { valueOf() { calls.push('value.valueOf'); return '1'; } }; \
|
||||
view.getInt8(byteOffset) === 0 && \
|
||||
(function() { \
|
||||
calls = []; \
|
||||
try { view.setInt8(byteOffset, value); return false; } catch (e) { return e instanceof TypeError; } \
|
||||
})() && calls.length === 0",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// get 面允许 immutable 缓冲:正常读回初值。
|
||||
#[test]
|
||||
fn dv_get_immutable_ok() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var iab = (new ArrayBuffer(8)).transferToImmutable(); \
|
||||
var src = new DataView(new ArrayBuffer(8)); \
|
||||
src.setInt8(3, 42); \
|
||||
var view = new DataView(iab); \
|
||||
view.getInt8(0) === 0 && new DataView(iab).byteLength === 8",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// 缓冲完好时视图相对越界保持 RangeError 种类;auto 视图 grow 后新区可读。
|
||||
#[test]
|
||||
fn dv_get_oob_kind_split() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var buffer = new ArrayBuffer(12); \
|
||||
var sample = new DataView(buffer, 0); \
|
||||
var err = null; \
|
||||
try { sample.getInt8(12); } catch (e) { err = e; } \
|
||||
var ok = err instanceof RangeError; \
|
||||
var ab = new ArrayBuffer(4, { maxByteLength: 8 }); \
|
||||
var auto = new DataView(ab, 0); \
|
||||
ab.resize(8); \
|
||||
ok = ok && auto.byteLength === 8 && auto.getUint8(7) === 0; \
|
||||
ok",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
/// 定长缓冲缺省长构造 + 初校验序钉:offset > bufferByteLength 的
|
||||
/// RangeError 先于 proto getter(getter 抛 Test262Error 不可见)。
|
||||
#[test]
|
||||
fn dv_ctor_fixed_default_len_static() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var ok = new DataView(new ArrayBuffer(8), 2).byteLength === 6; \
|
||||
var buffer = new ArrayBuffer(3); \
|
||||
var getterCalled = false; \
|
||||
var newTarget = function() {}.bind(null); \
|
||||
Object.defineProperty(newTarget, 'prototype', { \
|
||||
get: function() { getterCalled = true; throw new Error('getter'); } \
|
||||
}); \
|
||||
var err = null; \
|
||||
try { Reflect.construct(DataView, [buffer, 4], newTarget); } catch (e) { err = e; } \
|
||||
ok = ok && err instanceof RangeError && !getterCalled; \
|
||||
ok",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
Reference in New Issue
Block a user