Files
OxideJS-New/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs
T

821 lines
30 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_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))
}
/// 原型 byteLength 描述符钉:get 为函数、set 缺失、不可枚举、可配置。
#[test]
fn ab_bytelength_proto_is_accessor() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var d = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength'); \
typeof d.get === 'function' && d.set === undefined \
&& d.enumerable === false && d.configurable === true",
)
.unwrap();
assert!(result.as_bool());
}
/// 访问器 getter 函数对象元数据钉:name 标签与 length。
#[test]
fn ab_bytelength_getter_name_and_length() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var d = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength'); \
d.get.name === 'get byteLength' && d.get.length === 0",
)
.unwrap();
assert!(result.as_bool());
}
/// 访问器返回值钉:构造长度经原型访问器读回。
#[test]
fn ab_bytelength_returns_constructed_len() {
let mut vm = Vm::new();
let result = eval(&mut vm, "new ArrayBuffer(0).byteLength === 0 && new ArrayBuffer(42).byteLength === 42").unwrap();
assert!(result.as_bool());
}
/// receiver 校验钉:非 ArrayBuffer this(原型自身 / undefined)抛 TypeError。
#[test]
fn ab_bytelength_this_checks() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var d = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength'); \
var proto_threw = false; var undef_threw = false; \
try { ArrayBuffer.prototype.byteLength } catch (e) { proto_threw = e instanceof TypeError; } \
try { d.get.call(undefined) } catch (e) { undef_threw = e instanceof TypeError; } \
proto_threw && undef_threw",
)
.unwrap();
assert!(result.as_bool());
}
/// 实例无 own byteLength 属性钉(构造器不写数据属性)。
#[test]
fn ab_instance_has_no_own_bytelength() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Object.getOwnPropertyNames(new ArrayBuffer(8)).length === 0").unwrap();
assert!(result.as_bool());
}
/// 可 resize 实例 own maxByteLength 数据属性钉:描述符 {value, w:0, e:0, c:0};
/// 定长实例无该 own 属性。
#[test]
fn ab_ctor_options_own_max_property() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var ab = new ArrayBuffer(4, {maxByteLength: 8}); \
var d = Object.getOwnPropertyDescriptor(ab, 'maxByteLength'); \
d !== undefined && d.value === 8 && d.writable === false \
&& d.enumerable === false && d.configurable === false \
&& Object.getOwnPropertyDescriptor(new ArrayBuffer(4), 'maxByteLength') === undefined",
)
.unwrap();
assert!(result.as_bool());
}
/// options 非对象钉:null/boolean/symbol/bigint/string/number/undefined 一律定长。
#[test]
fn ab_ctor_options_non_object() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"[null, true, Symbol(3), 1n, 'string', 9, undefined] \
.every(function (o) { return new ArrayBuffer(0, o).resizable === false; })",
)
.unwrap();
assert!(result.as_bool());
}
/// options.maxByteLength 为 undefined 或 options 为空对象 → 定长。
#[test]
fn ab_ctor_options_max_undefined() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"new ArrayBuffer(0, {}).resizable === false \
&& new ArrayBuffer(0, {maxByteLength: undefined}).resizable === false",
)
.unwrap();
assert!(result.as_bool());
}
/// length > maxByteLength → RangeError。
#[test]
fn ab_ctor_len_gt_max_range_error() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var t1 = false, t2 = false; \
try { new ArrayBuffer(1, {maxByteLength: 0}); } catch (e) { t1 = e instanceof RangeError; } \
try { new ArrayBuffer(5, {maxByteLength: 4}); } catch (e) { t2 = e instanceof RangeError; } \
t1 && t2",
)
.unwrap();
assert!(result.as_bool());
}
/// maxByteLength: 0 是合法可 resize 上限(非定长):resizable true、
/// resize(0) 为 no-op、resize(1) 越界 RangeError。
#[test]
fn ab_ctor_max_zero_is_resizable() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var ab = new ArrayBuffer(0, {maxByteLength: 0}); \
var ok1 = ab.resizable === true && ab.maxByteLength === 0; \
ab.resize(0); \
var ok2 = ab.byteLength === 0 && ab.resizable === true; \
var threw = false; \
try { ab.resize(1); } catch (e) { threw = e instanceof RangeError; } \
ok1 && ok2 && threw",
)
.unwrap();
assert!(result.as_bool());
}
/// options.maxByteLength 经 ToIndex 强转:字符串 '8' → 8,分数 4.5 → 截断 4。
#[test]
fn ab_ctor_max_coerced() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var s = new ArrayBuffer(4, {maxByteLength: '8'}); \
var f = new ArrayBuffer(4, {maxByteLength: 4.5}); \
s.resizable === true && s.maxByteLength === 8 \
&& f.resizable === true && f.maxByteLength === 4",
)
.unwrap();
assert!(result.as_bool());
}
/// options.maxByteLength 访问器抛错 → 原异常值传播。
#[test]
fn ab_ctor_max_poisoned_propagates() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var options = { get maxByteLength() { throw new TypeError('poisoned'); } }; \
var threw = false; \
try { new ArrayBuffer(0, options); } catch (e) { threw = e instanceof TypeError && e.message === 'poisoned'; } \
threw",
)
.unwrap();
assert!(result.as_bool());
}
/// options.maxByteLength 为 Symbol → TypeError;对象形强转序 valueOf → toString。
#[test]
fn ab_ctor_max_symbol_type_error() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var threw = false; \
try { new ArrayBuffer(0, {maxByteLength: Symbol()}); } catch (e) { threw = e instanceof TypeError; } \
var log = []; \
var opts = { maxByteLength: { \
toString: function() { log.push('toString'); return {}; }, \
valueOf: function() { log.push('valueOf'); return {}; } } }; \
try { new ArrayBuffer(0, opts); } catch (e) {} \
threw && log.length === 2 && log[0] === 'valueOf' && log[1] === 'toString'",
)
.unwrap();
assert!(result.as_bool());
}
/// length 参 ToIndex 语义钉:负分数截 0、字符串/NaN/分数按截断。
#[test]
fn ab_ctor_length_toindex() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"new ArrayBuffer(-0.1).byteLength === 0 \
&& new ArrayBuffer('42').byteLength === 42 \
&& new ArrayBuffer(NaN).byteLength === 0 \
&& new ArrayBuffer(0.9).byteLength === 0",
)
.unwrap();
assert!(result.as_bool());
}
/// length 参强转期抛错 → 原异常值传播。
#[test]
fn ab_ctor_length_abort_propagates() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var len = { valueOf: function() { throw new RangeError('abort'); } }; \
var threw = false; \
try { new ArrayBuffer(len); } catch (e) { threw = e instanceof RangeError && e.message === 'abort'; } \
threw",
)
.unwrap();
assert!(result.as_bool());
}
/// 分配期上界钉:2^32(过 ToIndex 后撞引擎上限)与 2^53(ToLength 夹取失配)均 RangeError。
#[test]
fn ab_ctor_length_alloc_limit() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var t1 = false, t2 = false; \
try { new ArrayBuffer(2 ** 32); } catch (e) { t1 = e instanceof RangeError; } \
try { new ArrayBuffer(2 ** 53); } catch (e) { t2 = e instanceof RangeError; } \
t1 && t2",
)
.unwrap();
assert!(result.as_bool());
}
/// GetPrototypeFromConstructor 钉:newTarget 为 Object → 原型取 Object.prototype;
/// newTarget.prototype 非对象 → 回落 %ArrayBuffer.prototype%。
#[test]
fn ab_ctor_newtarget_proto() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"Object.getPrototypeOf(Reflect.construct(ArrayBuffer, [8], Object)) === Object.prototype \
&& (function () { function nt() {} nt.prototype = undefined; \
return Object.getPrototypeOf(Reflect.construct(ArrayBuffer, [1], nt)) === ArrayBuffer.prototype; })()",
)
.unwrap();
assert!(result.as_bool());
}
/// 普通调用形态(new.target 缺失)→ TypeError。
#[test]
fn ab_ctor_call_throws() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var t1 = false, t2 = false; \
try { ArrayBuffer(); } catch (e) { t1 = e instanceof TypeError; } \
try { ArrayBuffer(10); } catch (e) { t2 = e instanceof TypeError; } \
t1 && t2",
)
.unwrap();
assert!(result.as_bool());
}
/// 语义序钉:原型读先于分配期上界拒绝(7PiB length + 抛错 prototype getter)。
#[test]
fn ab_ctor_proto_before_alloc() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"function DummyError() {} \
var newTarget = function() {}.bind(null); \
Object.defineProperty(newTarget, 'prototype', { get: function() { throw new DummyError(); } }); \
var threw = false; \
try { Reflect.construct(ArrayBuffer, [7 * 1125899906842624], newTarget); } catch (e) { threw = e instanceof DummyError; } \
threw",
)
.unwrap();
assert!(result.as_bool());
}
/// resize grow 钉:前缀保留 + 零填充,视图 live 见新长度。
#[test]
fn ab_resize_grow_preserves_and_zeros() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var ab = new ArrayBuffer(2, {maxByteLength: 5}); \
var view = new Uint8Array(ab); view[0] = 1; view[1] = 2; \
ab.resize(5); \
var v2 = new Uint8Array(ab); \
ab.byteLength === 5 && v2.length === 5 \
&& v2[0] === 1 && v2[1] === 2 && v2[2] === 0 && v2[3] === 0 && v2[4] === 0",
)
.unwrap();
assert!(result.as_bool());
}
/// resize shrink 钉:截断丢尾字节,再 grow 前缀保留 + 零填充。
#[test]
fn ab_resize_shrink_truncate() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var ab = new ArrayBuffer(4, {maxByteLength: 8}); \
var view = new Uint8Array(ab); view[0] = 1; view[1] = 2; \
ab.resize(1); \
var ok1 = ab.byteLength === 1 && new Uint8Array(ab).length === 1; \
ab.resize(4); \
var v2 = new Uint8Array(ab); \
ok1 && ab.byteLength === 4 && v2[0] === 1 && v2[1] === 0 && v2[2] === 0 && v2[3] === 0",
)
.unwrap();
assert!(result.as_bool());
}
/// resize(0) 不 detach 钉:byteLength 0、resizable 保持、slice 不抛、可再 grow。
#[test]
fn ab_resize_zero_stays_attached() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var ab = new ArrayBuffer(2, {maxByteLength: 4}); \
ab.resize(0); \
var ok1 = ab.byteLength === 0 && ab.resizable === true && ab.slice().byteLength === 0; \
ab.resize(2); \
ok1 && ab.byteLength === 2",
)
.unwrap();
assert!(result.as_bool());
}
/// resize 界判钉:负值/超 max → RangeError;分数截断、NaN → 0。
#[test]
fn ab_resize_range_checks() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var ab = new ArrayBuffer(4, {maxByteLength: 8}); \
var t1 = false, t2 = false; \
try { ab.resize(-1); } catch (e) { t1 = e instanceof RangeError; } \
try { ab.resize(9); } catch (e) { t2 = e instanceof RangeError; } \
ab.resize(2.5); \
var t3 = ab.byteLength === 2; \
ab.resize(NaN); \
var t4 = ab.byteLength === 0; \
ab.resize(-0.5); \
t1 && t2 && t3 && t4 && ab.byteLength === 0",
)
.unwrap();
assert!(result.as_bool());
}
/// 定长缓冲 resize 钉:0/3/4/5 四形均 TypeError,长度不变。
#[test]
fn ab_resize_non_resizable_type_error() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var ab = new ArrayBuffer(4); \
var t0 = false, t3 = false, t4 = false, t5 = false; \
try { ab.resize(0); } catch (e) { t0 = e instanceof TypeError; } \
try { ab.resize(3); } catch (e) { t3 = e instanceof TypeError; } \
try { ab.resize(4); } catch (e) { t4 = e instanceof TypeError; } \
try { ab.resize(5); } catch (e) { t5 = e instanceof TypeError; } \
t0 && t3 && t4 && t5 && ab.byteLength === 4",
)
.unwrap();
assert!(result.as_bool());
}
/// resize this 品牌校验钉:原型/undefined/{}/[] 四形均 TypeError。
#[test]
fn ab_resize_this_checks() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var t1 = false, t2 = false, t3 = false, t4 = false; \
try { ArrayBuffer.prototype.resize(); } catch (e) { t1 = e instanceof TypeError; } \
try { ArrayBuffer.prototype.resize.call(undefined); } catch (e) { t2 = e instanceof TypeError; } \
try { ArrayBuffer.prototype.resize.call({}); } catch (e) { t3 = e instanceof TypeError; } \
try { ArrayBuffer.prototype.resize.call([]); } catch (e) { t4 = e instanceof TypeError; } \
t1 && t2 && t3 && t4",
)
.unwrap();
assert!(result.as_bool());
}
/// resizable 访问器钉:描述符 {get 函数, set undefined, e:0, c:1}、name/length、
/// 定长 false / 可 resize true、this 校验两形。
#[test]
fn ab_resizable_accessor_pins() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var d = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'resizable'); \
var ok = d.set === undefined && typeof d.get === 'function' \
&& d.enumerable === false && d.configurable === true \
&& d.get.name === 'get resizable' && d.get.length === 0; \
var fixed = new ArrayBuffer(1); \
var resizable = new ArrayBuffer(1, {maxByteLength: 1}); \
var getter = d.get; \
var t1 = false, t2 = false; \
try { getter.call({}); } catch (e) { t1 = e instanceof TypeError; } \
try { getter.call(undefined); } catch (e) { t2 = e instanceof TypeError; } \
ok && fixed.resizable === false && resizable.resizable === true && t1 && t2",
)
.unwrap();
assert!(result.as_bool());
}
/// BigInt 参钉:ctor 与 slice 单参/双参/中间参四形均抛 TypeError。
#[test]
fn ab_bigint_argument_type_error() {
let mut vm = Vm::new();
let err = eval(&mut vm, "new ArrayBuffer(8n)").unwrap_err();
assert!(err.contains("TypeError"), "ctor: {}", err);
for src in [
"new ArrayBuffer(8).slice(2n)",
"new ArrayBuffer(8).slice(2n, 4n)",
"new ArrayBuffer(8).slice(1n, 2n)",
] {
let err = eval(&mut vm, src).unwrap_err();
assert!(err.contains("TypeError"), "{}: {}", src, err);
}
}
/// valueOf 异常透传钉:ctor 与 slice 的转换异常保留原异常值。
#[test]
fn ab_value_of_abort_passes_through() {
let mut vm = Vm::new();
let v = eval(&mut vm, "try { new ArrayBuffer({valueOf(){ throw 42 }}) } catch (e) { e }").unwrap();
assert_eq!(v.as_int(), 42);
let v = eval(&mut vm, "try { new ArrayBuffer(8).slice({valueOf(){ throw 7 }}) } catch (e) { e }").unwrap();
assert_eq!(v.as_int(), 7);
}
/// fold 保留钉:slice ±Infinity 饱和 0/len,Number 分支语义不回退。
#[test]
fn ab_slice_infinity_fold_preserved() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"new ArrayBuffer(8).slice(Infinity).byteLength === 0 \
&& new ArrayBuffer(8).slice(-Infinity).byteLength === 8 \
&& new ArrayBuffer(8).slice(-Infinity, 3).byteLength === 3 \
&& new ArrayBuffer(8).slice(2, Infinity).byteLength === 6",
)
.unwrap();
assert!(result.as_bool());
}
/// RangeError 不回退钉:负值/超引擎上限抛 RangeError,分数截断、字符串强转。
#[test]
fn ab_length_range_error_not_regressed() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var t1 = false, t2 = false; \
try { new ArrayBuffer(-1); } catch (e) { t1 = e instanceof RangeError; } \
try { new ArrayBuffer(2 ** 40); } catch (e) { t2 = e instanceof RangeError; } \
t1 && t2 \
&& new ArrayBuffer(4.9).byteLength === 4 \
&& new ArrayBuffer('8').byteLength === 8",
)
.unwrap();
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
);
}
/// transfer 拷贝/增减/零填充/源 detach 钉:grow 补零、same 原样、shrink
/// 截断;transfer(0) 产物附着 0 长(区别于 detach)。
#[test]
fn ab_transfer_copies_grows_shrinks_and_detaches() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"function fill(ab) { var v = new Uint8Array(ab); v[0]=1; v[1]=2; v[2]=3; v[3]=4; return ab; } \
var a = new Uint8Array(fill(new ArrayBuffer(4)).transfer(5)); \
a[0]===1 && a[1]===2 && a[2]===3 && a[3]===4 && a[4]===0 \
&& new Uint8Array(fill(new ArrayBuffer(4)).transfer(4)).length === 4 \
&& new Uint8Array(fill(new ArrayBuffer(4)).transfer(2)).join(',') === '1,2' \
&& fill(new ArrayBuffer(4)).transfer(0).detached === false \
&& fill(new ArrayBuffer(4)).transfer(0).byteLength === 0",
)
.unwrap();
assert!(result.as_bool());
let result = eval(
&mut vm,
"var ab = new ArrayBuffer(4); ab.transfer(2); ab.detached === true && ab.byteLength === 0",
)
.unwrap();
assert!(result.as_bool());
}
/// transfer 保持性钉:resizable 源 → 产物 resizable 同上限,产物附着。
#[test]
fn ab_transfer_preserves_resizability() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var dest = new ArrayBuffer(4, {maxByteLength: 8}).transfer(5); \
dest.resizable === true && dest.maxByteLength === 8 \
&& dest.detached === false && dest.byteLength === 5",
)
.unwrap();
assert!(result.as_bool());
}
/// transferToFixedLength 钉:resizable 源 → 产物恒定长、源 detach。
#[test]
fn ab_transfer_to_fixed_length_fixed() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var ab = new ArrayBuffer(4, {maxByteLength: 8}); \
var dest = ab.transferToFixedLength(6); \
dest.resizable === false && dest.maxByteLength === 6 && dest.byteLength === 6 \
&& ab.detached === true && ab.byteLength === 0",
)
.unwrap();
assert!(result.as_bool());
}
/// transferToImmutable 钉:产物 immutable + 恒定长、源 detach;显式 longer
/// 零填充。
#[test]
fn ab_transfer_to_immutable_sets_flag() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var ab = new ArrayBuffer(4); \
var dest = ab.transferToImmutable(); \
dest.immutable === true && dest.resizable === false && ab.detached === true \
&& new Uint8Array(new ArrayBuffer(4).transferToImmutable(9)).length === 9 \
&& new Uint8Array(new ArrayBuffer(4).transferToImmutable(9))[8] === 0",
)
.unwrap();
assert!(result.as_bool());
}
/// newLength 求值序与强转钉:valueOf→toString 序 TypeError、2^53 RangeError、
/// valueOf 内 detach 后走 detached 判定(求值先于守卫)。
#[test]
fn ab_transfer_new_length_order_and_coercion() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var log = []; \
var nl = { toString() { log.push('toString'); return {}; }, \
valueOf() { log.push('valueOf'); return {}; } }; \
var t1 = false; \
try { new ArrayBuffer(0).transfer(nl); } catch (e) { t1 = e instanceof TypeError; } \
t1 && log.length === 2 && log[0] === 'valueOf' && log[1] === 'toString' \
&& (function () { var t2 = false; \
try { new ArrayBuffer(0).transfer(2 ** 53); } catch (e) { t2 = e instanceof RangeError; } \
return t2; })() \
&& (function () { var ab = new ArrayBuffer(8); var t3 = false; \
try { ab.transfer({ valueOf() { $262.detachArrayBuffer(ab); return 1; } }); } \
catch (e) { t3 = e instanceof TypeError; } \
return t3; })()",
)
.unwrap();
assert!(result.as_bool());
}
/// detached 源钉:三方法同形 TypeError。
#[test]
fn ab_transfer_detached_source_type_error() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var a = new ArrayBuffer(4); a.transfer(); \
var t1 = false, t2 = false, t3 = false; \
try { a.transfer(); } catch (e) { t1 = e instanceof TypeError; } \
try { a.transferToFixedLength(); } catch (e) { t2 = e instanceof TypeError; } \
try { a.transferToImmutable(); } catch (e) { t3 = e instanceof TypeError; } \
t1 && t2 && t3",
)
.unwrap();
assert!(result.as_bool());
}
/// immutable 源钉:三方法同形 TypeError,newLength 读取先于 mutability 守卫。
#[test]
fn ab_transfer_immutable_source_type_error() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var iab = new ArrayBuffer(4).transferToImmutable(); \
var calls = []; \
var nl = { valueOf() { calls.push('valueOf'); return 1; } }; \
var t1 = false, t2 = false, t3 = false; \
try { iab.transfer(nl); } catch (e) { t1 = e instanceof TypeError; } \
try { iab.transferToFixedLength(); } catch (e) { t2 = e instanceof TypeError; } \
try { iab.transferToImmutable(); } catch (e) { t3 = e instanceof TypeError; } \
t1 && t2 && t3 && calls.length === 1 && calls[0] === 'valueOf'",
)
.unwrap();
assert!(result.as_bool());
}
/// 界校验钉:resizable 源超真实上限 RangeError、超引擎分配上界 RangeError。
#[test]
fn ab_transfer_range_and_alloc_limits() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var t1 = false, t2 = false; \
try { new ArrayBuffer(4, {maxByteLength: 8}).transfer(9); } catch (e) { t1 = e instanceof RangeError; } \
try { new ArrayBuffer(0).transfer(2 ** 32); } catch (e) { t2 = e instanceof RangeError; } \
t1 && t2",
)
.unwrap();
assert!(result.as_bool());
}