mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: ArrayBuffer 三访问器与 markImmutable 及 detach 宿主真化
This commit is contained in:
@@ -107,7 +107,7 @@ pub(crate) fn new_array_buffer<H: VmHost>(
|
|||||||
vm.alloc_object(obj)
|
vm.alloc_object(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn array_buffer_payload_ptr(obj: &JsObject) -> Option<*mut ArrayBufferPayload> {
|
pub(crate) fn array_buffer_payload_ptr(obj: &JsObject) -> Option<*mut ArrayBufferPayload> {
|
||||||
if !obj.is_array_buffer_obj() {
|
if !obj.is_array_buffer_obj() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -276,15 +276,14 @@ pub fn array_buffer_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRes
|
|||||||
NativeResult::Ok(JsValue::from_js_object(obj_ptr))
|
NativeResult::Ok(JsValue::from_js_object(obj_ptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `ArrayBuffer.prototype.byteLength` getter:返回缓冲区字节数。
|
/// `ArrayBuffer.prototype.byteLength` getter:返回缓冲区字节数;
|
||||||
|
/// detached 缓冲返回 0。
|
||||||
pub fn array_buffer_byte_length<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
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 this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||||
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||||||
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。
|
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷。
|
||||||
let Some(data) = unsafe { &*payload_ptr }.data.as_ref() else {
|
let len = unsafe { &*payload_ptr }.data.as_ref().map_or(0, |d| d.len());
|
||||||
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
NativeResult::Ok(JsValue::int(len as i32))
|
||||||
};
|
|
||||||
NativeResult::Ok(JsValue::int(data.len() as i32))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `ArrayBuffer.prototype.resizable` getter:返回缓冲区是否可 resize
|
/// `ArrayBuffer.prototype.resizable` getter:返回缓冲区是否可 resize
|
||||||
@@ -297,6 +296,73 @@ pub fn array_buffer_resizable<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
|||||||
NativeResult::Ok(JsValue::bool(resizable))
|
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 缓冲区原地调整长度,
|
/// `ArrayBuffer.prototype.resize(newLength)`:可 resize 缓冲区原地调整长度,
|
||||||
/// 前缀保留、增补零填充、缩小截断。
|
/// 前缀保留、增补零填充、缩小截断。
|
||||||
///
|
///
|
||||||
@@ -304,15 +370,16 @@ pub fn array_buffer_resizable<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
|||||||
/// 1. this 品牌校验(非对象/非 ArrayBuffer → TypeError)。
|
/// 1. this 品牌校验(非对象/非 ArrayBuffer → TypeError)。
|
||||||
/// 2. 载荷三状态位(detached / immutable / max)先于任何 JS 调用拷入局部
|
/// 2. 载荷三状态位(detached / immutable / max)先于任何 JS 调用拷入局部
|
||||||
/// 标量。
|
/// 标量。
|
||||||
/// 3. detached → TypeError(detach 路径由 transfer 族激活)。
|
/// 3. detached → TypeError。
|
||||||
/// 4. immutable → TypeError。
|
/// 4. immutable → TypeError。
|
||||||
/// 5. newByteLength = ToIntegerOrInfinity(newLength) 传播式(NaN → 0,
|
/// 5. newByteLength = ToIntegerOrInfinity(newLength) 传播式(NaN → 0,
|
||||||
/// ±Infinity 保留)。
|
/// ±Infinity 保留)。
|
||||||
/// 6. 定长(存储态上限 = 0)→ TypeError,先于界判。
|
/// 6. 定长(存储态上限 = 0)→ TypeError,先于界判。
|
||||||
/// 7. newByteLength < 0 或 > 真实上限(存储态 − 1)→ RangeError。
|
/// 7. newByteLength < 0 或 > 真实上限(存储态 − 1)→ RangeError。
|
||||||
/// 8. 重取载荷指针:步 5 的 JS 调用可触发 epoch 晋升,旧指针悬垂。
|
/// 8. 重取载荷指针:步 5 的 JS 调用可触发 epoch 晋升,旧指针悬垂。
|
||||||
/// 9. `Vec::resize` 一次调用收口目标长度。
|
/// 9. 载荷重检:强转调用可已 detach 本缓冲,detached 与步 3 同文本 TypeError。
|
||||||
/// 10. 返回 undefined。
|
/// 10. `Vec::resize` 一次调用收口目标长度。
|
||||||
|
/// 11. 返回 undefined。
|
||||||
pub fn array_buffer_resize<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
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_reg = if args.is_empty() { 0 } else { args[0] };
|
||||||
let this_val = vm.reg(this_reg);
|
let this_val = vm.reg(this_reg);
|
||||||
@@ -343,10 +410,12 @@ pub fn array_buffer_resize<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
// 重写,须重新读取后再取载荷指针(旧 this 值可能指向已释放 epoch 对象)。
|
// 重写,须重新读取后再取载荷指针(旧 this 值可能指向已释放 epoch 对象)。
|
||||||
let this_val = vm.reg(this_reg);
|
let this_val = vm.reg(this_reg);
|
||||||
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||||||
// SAFETY: 重取的 payload_ptr 指向存活载荷盒;detached 已在步 3 排除,
|
// SAFETY: 重取的 payload_ptr 指向存活载荷盒。
|
||||||
// data 必为 Some。
|
let Some(data) = (unsafe { &mut *payload_ptr }).data.as_mut() else {
|
||||||
let payload = unsafe { &mut *payload_ptr };
|
// 步 5 的强转调用可在中途 detach 本缓冲:重检不通过按步 3 同文本抛。
|
||||||
payload.data.as_mut().expect("detached 已排除").resize(new_length as usize, 0);
|
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||||||
|
};
|
||||||
|
data.resize(new_length as usize, 0);
|
||||||
NativeResult::Ok(JsValue::undefined())
|
NativeResult::Ok(JsValue::undefined())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,6 +470,7 @@ pub fn array_buffer_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use oxide_vm::vm::Vm;
|
||||||
|
|
||||||
/// 手工构造带载荷盒的 ArrayBuffer 对象(不经 Vm,只验 GC 三自由函数)。
|
/// 手工构造带载荷盒的 ArrayBuffer 对象(不经 Vm,只验 GC 三自由函数)。
|
||||||
fn ab_object_with_payload(payload: ArrayBufferPayload) -> JsObject {
|
fn ab_object_with_payload(payload: ArrayBufferPayload) -> JsObject {
|
||||||
@@ -495,4 +565,56 @@ mod tests {
|
|||||||
// SAFETY: 测试构造的盒,恰好释放一次。
|
// SAFETY: 测试构造的盒,恰好释放一次。
|
||||||
unsafe { drop(Box::from_raw(payload_ptr)) };
|
unsafe { drop(Box::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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// resize 后置 detach 重检钉:强转调用中途 detach 后按 TypeError 抛,
|
||||||
|
/// 不触碰已 detach 载荷(现树 expect 臂的 abort 钉改点)。
|
||||||
|
#[test]
|
||||||
|
fn resize_recheck_after_coercion_detach() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let e = eval_ab(
|
||||||
|
&mut vm,
|
||||||
|
"var ab = new ArrayBuffer(64, {maxByteLength: 1024}); \
|
||||||
|
try { ab.resize({ valueOf() { $262.detachArrayBuffer(ab); return 0; } }); \
|
||||||
|
'no-throw' } catch (err) { err.name }",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(vm.lookup_str(e).unwrap(), "TypeError");
|
||||||
|
let r = eval_ab(&mut vm, "ab.detached === true && ab.byteLength === 0").unwrap();
|
||||||
|
assert!(r.as_bool());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ use oxide_types::private_key::{int_key_value, is_int_key, make_well_known_symbol
|
|||||||
use oxide_types::value::JsValue;
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
use crate::array_buffer::{
|
use crate::array_buffer::{
|
||||||
array_buffer_payload, default_array_buffer_proto, new_array_buffer, MAX_ARRAY_BUFFER_LENGTH,
|
array_buffer_payload, array_buffer_payload_ptr, default_array_buffer_proto, new_array_buffer,
|
||||||
|
MAX_ARRAY_BUFFER_LENGTH,
|
||||||
};
|
};
|
||||||
|
|
||||||
use oxide_runtime_api::{NativeResult, VmHost};
|
use oxide_runtime_api::{NativeResult, VmHost};
|
||||||
@@ -336,7 +337,8 @@ pub fn ta_index_gate<H: VmHost>(vm: &H, obj: &JsObject, key_si: u32) -> TaIndexG
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 取 TypedArray 视图长度(供只需 length 不需门的枚举面消费方);
|
/// 取 TypedArray 视图长度(供只需 length 不需门的枚举面消费方);
|
||||||
/// 非 TypedArray 或内部状态无效返回 0。
|
/// 非 TypedArray 或内部状态无效返回 0。底层缓冲已 detach(载荷 `data`
|
||||||
|
/// 为 `None`)时视图长度统一为 0,界内键经门归数字无效。
|
||||||
pub(crate) fn ta_view_length<H: VmHost>(_vm: &H, obj: &JsObject) -> usize {
|
pub(crate) fn ta_view_length<H: VmHost>(_vm: &H, obj: &JsObject) -> usize {
|
||||||
let Some(ptr) = typed_array_data_ptr(obj) else {
|
let Some(ptr) = typed_array_data_ptr(obj) else {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -347,6 +349,16 @@ pub(crate) fn ta_view_length<H: VmHost>(_vm: &H, obj: &JsObject) -> usize {
|
|||||||
// SAFETY: ptr 非空,为 TypedArray 对象 native_fn 槽内的 Box<TypedArrayData>,
|
// SAFETY: ptr 非空,为 TypedArray 对象 native_fn 槽内的 Box<TypedArrayData>,
|
||||||
// 与对象同生命周期,此处只读拷贝视图字段。
|
// 与对象同生命周期,此处只读拷贝视图字段。
|
||||||
let view = unsafe { *ptr };
|
let view = unsafe { *ptr };
|
||||||
|
// 底层缓冲已 detach:视图长度统一 0。
|
||||||
|
let buffer_ptr = view.buffer.as_js_object_ptr();
|
||||||
|
if !buffer_ptr.is_null() {
|
||||||
|
// SAFETY: buffer 对象与视图同生命周期,此处只读载荷存活位。
|
||||||
|
if let Some(payload_ptr) = array_buffer_payload_ptr(unsafe { &*buffer_ptr }) {
|
||||||
|
if !payload_ptr.is_null() && unsafe { (*payload_ptr).data.is_none() } {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
view.length
|
view.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,8 +70,6 @@ fn is_blacklisted_harness(name: &str) -> bool {
|
|||||||
| "atomicsHelper.js"
|
| "atomicsHelper.js"
|
||||||
| "proxyTrapsHelper.js"
|
| "proxyTrapsHelper.js"
|
||||||
| "tcoHelper.js"
|
| "tcoHelper.js"
|
||||||
| "detachArrayBuffer.js"
|
|
||||||
| "resizableArrayBufferUtils.js"
|
|
||||||
| "byteConversionValues.js"
|
| "byteConversionValues.js"
|
||||||
| "compareIterator.js"
|
| "compareIterator.js"
|
||||||
| "iteratorZipUtils.js"
|
| "iteratorZipUtils.js"
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ pub fn bind_array_buffer(core: &Arc<KernelCore>, session: &KernelSession, global
|
|||||||
oxide_builtins::array_buffer::array_buffer_to_string::<crate::vm::Vm> as *const (),
|
oxide_builtins::array_buffer::array_buffer_to_string::<crate::vm::Vm> as *const (),
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"markImmutable",
|
||||||
|
oxide_builtins::array_buffer::array_buffer_mark_immutable::<crate::vm::Vm> as *const (),
|
||||||
|
0,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -79,6 +84,34 @@ pub fn bind_array_buffer(core: &Arc<KernelCore>, session: &KernelSession, global
|
|||||||
oxide_builtins::array_buffer::array_buffer_resizable::<crate::vm::Vm> as *const (),
|
oxide_builtins::array_buffer::array_buffer_resizable::<crate::vm::Vm> as *const (),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// maxByteLength 原型访问器(set 恒 undefined):detached 回 +0,定长回
|
||||||
|
// 当前字节数,resizable 回存储态上限 − 1。
|
||||||
|
bind_accessor_getter(
|
||||||
|
core,
|
||||||
|
session,
|
||||||
|
proto,
|
||||||
|
"maxByteLength",
|
||||||
|
oxide_builtins::array_buffer::array_buffer_max_byte_length::<crate::vm::Vm> as *const (),
|
||||||
|
);
|
||||||
|
|
||||||
|
// immutable 原型访问器(set 恒 undefined):读载荷 immutable 标志。
|
||||||
|
bind_accessor_getter(
|
||||||
|
core,
|
||||||
|
session,
|
||||||
|
proto,
|
||||||
|
"immutable",
|
||||||
|
oxide_builtins::array_buffer::array_buffer_immutable::<crate::vm::Vm> as *const (),
|
||||||
|
);
|
||||||
|
|
||||||
|
// detached 原型访问器(set 恒 undefined):读载荷 data 存活位。
|
||||||
|
bind_accessor_getter(
|
||||||
|
core,
|
||||||
|
session,
|
||||||
|
proto,
|
||||||
|
"detached",
|
||||||
|
oxide_builtins::array_buffer::array_buffer_detached::<crate::vm::Vm> as *const (),
|
||||||
|
);
|
||||||
|
|
||||||
bind_constructor!(
|
bind_constructor!(
|
||||||
core,
|
core,
|
||||||
global,
|
global,
|
||||||
|
|||||||
@@ -57,9 +57,11 @@ pub fn eval_script(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `$262.detachArrayBuffer(buf)`:ArrayBuffer detach 未实现,抛能力缺失错误。
|
/// `$262.detachArrayBuffer(buf)`:真 detach——品牌守卫后载荷 `data → None`,
|
||||||
pub fn detach_array_buffer(vm: &mut Vm, _args: &[u8]) -> NativeResult {
|
/// 返回 undefined。
|
||||||
not_supported(vm, "detachArrayBuffer")
|
pub fn detach_array_buffer(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||||
|
let this_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||||
|
oxide_builtins::array_buffer::detach_array_buffer_native::<Vm>(vm, this_val)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `$262.createRealm()`:跨 realm 未支持,抛能力缺失错误。
|
/// `$262.createRealm()`:跨 realm 未支持,抛能力缺失错误。
|
||||||
|
|||||||
@@ -475,3 +475,199 @@ fn ab_length_range_error_not_regressed() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(result.as_bool());
|
assert!(result.as_bool());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// maxByteLength 访问器钉:描述符 {get 函数, set undefined, e:0, c:1}、
|
||||||
|
/// name/length、定长 42→42 / 0→0、resizable(4,{max 8})→8、detached→0、
|
||||||
|
/// this 四形 TypeError。
|
||||||
|
#[test]
|
||||||
|
fn ab_maxbytelength_accessor_pins() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var d = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'maxByteLength'); \
|
||||||
|
var ok = d.set === undefined && typeof d.get === 'function' \
|
||||||
|
&& d.enumerable === false && d.configurable === true \
|
||||||
|
&& d.get.name === 'get maxByteLength' && d.get.length === 0; \
|
||||||
|
var t1 = false, t2 = false, t3 = false, t4 = false; \
|
||||||
|
try { d.get.call(ArrayBuffer.prototype); } catch (e) { t1 = e instanceof TypeError; } \
|
||||||
|
try { d.get.call(undefined); } catch (e) { t2 = e instanceof TypeError; } \
|
||||||
|
try { d.get.call({}); } catch (e) { t3 = e instanceof TypeError; } \
|
||||||
|
try { d.get.call([]); } catch (e) { t4 = e instanceof TypeError; } \
|
||||||
|
ok && t1 && t2 && t3 && t4 \
|
||||||
|
&& new ArrayBuffer(42).maxByteLength === 42 \
|
||||||
|
&& new ArrayBuffer(0).maxByteLength === 0 \
|
||||||
|
&& new ArrayBuffer(4, {maxByteLength: 8}).maxByteLength === 8",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
let result =
|
||||||
|
eval(&mut vm, "var ab = new ArrayBuffer(1); $262.detachArrayBuffer(ab); ab.maxByteLength === 0").unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// immutable 访问器钉:描述符形、markImmutable 前后 false→true、this 两形。
|
||||||
|
#[test]
|
||||||
|
fn ab_immutable_accessor_pins() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var d = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'immutable'); \
|
||||||
|
var ok = d.set === undefined && typeof d.get === 'function' \
|
||||||
|
&& d.enumerable === false && d.configurable === true \
|
||||||
|
&& d.get.name === 'get immutable' && d.get.length === 0; \
|
||||||
|
var ab = new ArrayBuffer(2); \
|
||||||
|
var before = ab.immutable; \
|
||||||
|
ab.markImmutable(); \
|
||||||
|
var t1 = false, t2 = false; \
|
||||||
|
try { d.get.call({}); } catch (e) { t1 = e instanceof TypeError; } \
|
||||||
|
try { d.get.call(undefined); } catch (e) { t2 = e instanceof TypeError; } \
|
||||||
|
ok && before === false && ab.immutable === true && t1 && t2",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// detached 访问器钉:描述符形、附着 false → $262 detach → true、this 两形。
|
||||||
|
#[test]
|
||||||
|
fn ab_detached_accessor_pins() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var d = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'detached'); \
|
||||||
|
var ok = d.set === undefined && typeof d.get === 'function' \
|
||||||
|
&& d.enumerable === false && d.configurable === true \
|
||||||
|
&& d.get.name === 'get detached' && d.get.length === 0; \
|
||||||
|
var ab = new ArrayBuffer(1); \
|
||||||
|
var before = ab.detached; \
|
||||||
|
$262.detachArrayBuffer(ab); \
|
||||||
|
var t1 = false, t2 = false; \
|
||||||
|
try { d.get.call({}); } catch (e) { t1 = e instanceof TypeError; } \
|
||||||
|
try { d.get.call(undefined); } catch (e) { t2 = e instanceof TypeError; } \
|
||||||
|
ok && before === false && ab.detached === true && t1 && t2",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// byteLength detached 臂改点钉:detach 后读 0(原 TypeError 臂)。
|
||||||
|
#[test]
|
||||||
|
fn ab_bytelength_detached_zero() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(&mut vm, "var ab = new ArrayBuffer(1); $262.detachArrayBuffer(ab); ab.byteLength === 0").unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// resizable detached 不受影响钉:resizable(1,{max 1}) detach 后仍 true。
|
||||||
|
#[test]
|
||||||
|
fn ab_resizable_detached_unchanged() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var ab = new ArrayBuffer(1, {maxByteLength: 1}); $262.detachArrayBuffer(ab); ab.resizable === true",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// own maxByteLength 数据属性与原型访问器并存读同值钉(resizable 专属)。
|
||||||
|
#[test]
|
||||||
|
fn ab_maxbytelength_own_still_resizable_only() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var ab = new ArrayBuffer(4, {maxByteLength: 8}); \
|
||||||
|
Object.getOwnPropertyDescriptor(ab, 'maxByteLength').value === 8 && ab.maxByteLength === 8",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// markImmutable 返回接收者并置位钉:返回 === ab、immutable true、二调幂等、
|
||||||
|
/// byteLength 不变。
|
||||||
|
#[test]
|
||||||
|
fn ab_markimmutable_returns_this_and_sets_flag() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&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!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// markImmutable resizable 源钉:TypeError。
|
||||||
|
#[test]
|
||||||
|
fn ab_markimmutable_resizable_type_error() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var t = false; \
|
||||||
|
try { new ArrayBuffer(4, {maxByteLength: 8}).markImmutable(); } catch (e) { t = e instanceof TypeError; } \
|
||||||
|
t",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// markImmutable detached 源钉:$262 detach 后 TypeError。
|
||||||
|
#[test]
|
||||||
|
fn ab_markimmutable_detached_type_error() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var ab = new ArrayBuffer(4); $262.detachArrayBuffer(ab); \
|
||||||
|
var t = false; \
|
||||||
|
try { ab.markImmutable(); } catch (e) { t = e instanceof TypeError; } \
|
||||||
|
t",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// markImmutable this 品牌校验钉:原型/undefined/{}/[] 四形 TypeError。
|
||||||
|
#[test]
|
||||||
|
fn ab_markimmutable_this_checks() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"var t1 = false, t2 = false, t3 = false, t4 = false; \
|
||||||
|
try { ArrayBuffer.prototype.markImmutable(); } catch (e) { t1 = e instanceof TypeError; } \
|
||||||
|
try { ArrayBuffer.prototype.markImmutable.call(undefined); } catch (e) { t2 = e instanceof TypeError; } \
|
||||||
|
try { ArrayBuffer.prototype.markImmutable.call({}); } catch (e) { t3 = e instanceof TypeError; } \
|
||||||
|
try { ArrayBuffer.prototype.markImmutable.call([]); } catch (e) { t4 = e instanceof TypeError; } \
|
||||||
|
t1 && t2 && t3 && t4",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// P2-1 门钉:TA 界内整数键 detached 前 NumericValid(0)、detached 后
|
||||||
|
/// NumericInvalid(232.19.2 Get 臂 detached 界内键用例的前置钉)。
|
||||||
|
#[test]
|
||||||
|
fn ab_ta_view_length_detached_zero() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
eval(
|
||||||
|
&mut vm,
|
||||||
|
"var ab = new ArrayBuffer(8); \
|
||||||
|
globalThis.__ab = ab; globalThis.__ta = new Uint8Array(ab); true",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let si = vm.kernel_core().perm_interner().intern("0").0;
|
||||||
|
let ta_val = eval(&mut vm, "globalThis.__ta").unwrap();
|
||||||
|
// SAFETY: __ta 为已晋升 session 的 TypedArray 对象,借用止于断言、不跨下一次 eval。
|
||||||
|
let ta_obj = unsafe { &*ta_val.as_js_object_ptr() };
|
||||||
|
assert_eq!(
|
||||||
|
oxide_builtins::typed_array::ta_index_gate(&vm, ta_obj, si),
|
||||||
|
oxide_builtins::typed_array::TaIndexGate::NumericValid(0)
|
||||||
|
);
|
||||||
|
eval(&mut vm, "$262.detachArrayBuffer(globalThis.__ab); true").unwrap();
|
||||||
|
let ta_val = eval(&mut vm, "globalThis.__ta").unwrap();
|
||||||
|
let ta_obj = unsafe { &*ta_val.as_js_object_ptr() };
|
||||||
|
assert_eq!(
|
||||||
|
oxide_builtins::typed_array::ta_index_gate(&vm, ta_obj, si),
|
||||||
|
oxide_builtins::typed_array::TaIndexGate::NumericInvalid
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -63,11 +63,6 @@ fn eval_script_syntax_error_is_catchable() {
|
|||||||
/// 未实现方法抛能力缺失错误(消息带 `not supported`),不抛 ReferenceError。
|
/// 未实现方法抛能力缺失错误(消息带 `not supported`),不抛 ReferenceError。
|
||||||
#[test]
|
#[test]
|
||||||
fn unsupported_methods_throw_not_supported() {
|
fn unsupported_methods_throw_not_supported() {
|
||||||
let r =
|
|
||||||
eval("try { $262.detachArrayBuffer(new ArrayBuffer(8)); 'no-throw' } catch (e) { e.name + ':' + e.message }");
|
|
||||||
assert!(r.contains("TypeError"), "期望 TypeError,实际 {r}");
|
|
||||||
assert!(r.contains("not supported"), "期望能力缺失消息,实际 {r}");
|
|
||||||
|
|
||||||
let r = eval("try { $262.createRealm(); 'no-throw' } catch (e) { e.name + ':' + e.message }");
|
let r = eval("try { $262.createRealm(); 'no-throw' } catch (e) { e.name + ':' + e.message }");
|
||||||
assert!(r.contains("TypeError"), "期望 TypeError,实际 {r}");
|
assert!(r.contains("TypeError"), "期望 TypeError,实际 {r}");
|
||||||
assert!(r.contains("not supported"), "期望能力缺失消息,实际 {r}");
|
assert!(r.contains("not supported"), "期望能力缺失消息,实际 {r}");
|
||||||
@@ -76,6 +71,15 @@ fn unsupported_methods_throw_not_supported() {
|
|||||||
assert!(r.contains("TypeError"), "期望 TypeError,实际 {r}");
|
assert!(r.contains("TypeError"), "期望 TypeError,实际 {r}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `detachArrayBuffer` 真实现钉:detach 后 `detached` 访问器读 true、
|
||||||
|
/// 非 ArrayBuffer 实参抛 TypeError。
|
||||||
|
#[test]
|
||||||
|
fn detach_array_buffer_detaches() {
|
||||||
|
assert_eq!(eval("var ab = new ArrayBuffer(8); $262.detachArrayBuffer(ab); ab.detached"), "true");
|
||||||
|
let r = eval("try { $262.detachArrayBuffer(1); 'no-throw' } catch (e) { e.name }");
|
||||||
|
assert_eq!(r, "TypeError");
|
||||||
|
}
|
||||||
|
|
||||||
/// `$262.gc()` 触发一次 session 回收且不抛错。
|
/// `$262.gc()` 触发一次 session 回收且不抛错。
|
||||||
#[test]
|
#[test]
|
||||||
fn gc_runs_without_error() {
|
fn gc_runs_without_error() {
|
||||||
|
|||||||
Reference in New Issue
Block a user