mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
454 lines
16 KiB
Rust
454 lines
16 KiB
Rust
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());
|
||
}
|
||
|
||
/// DV-over-SAB 构造基础形:缺省长视图,buffer 指向 SAB 本体。
|
||
#[test]
|
||
fn dv_sab_ctor_basic() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"var sab = new SharedArrayBuffer(8); \
|
||
var dv = new DataView(sab); \
|
||
dv.byteLength === 8 && dv.byteOffset === 0 && dv.buffer === sab \
|
||
&& Object.getPrototypeOf(dv) === DataView.prototype",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
/// DV-over-SAB 显式 offset/length 形:视图界与 buffer 指向。
|
||
#[test]
|
||
fn dv_sab_ctor_offset_length() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"var sab = new SharedArrayBuffer(8); \
|
||
var dv = new DataView(sab, 2, 4); \
|
||
dv.byteLength === 4 && dv.byteOffset === 2 && dv.buffer === sab",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
/// DV-over-SAB 构造越界三形:offset > live / 显式长越界 / 负 offset 均 RangeError。
|
||
#[test]
|
||
fn dv_sab_ctor_oob() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"(function () { \
|
||
var sab = new SharedArrayBuffer(4); \
|
||
function kind(expr) { try { expr(); return 'none'; } catch (e) { return e.name; } } \
|
||
return kind(function () { new DataView(sab, 5); }) === 'RangeError' \
|
||
&& kind(function () { new DataView(sab, 0, 5); }) === 'RangeError' \
|
||
&& kind(function () { new DataView(sab, -1); }) === 'RangeError'; })()",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
/// DV-over-SAB 读写核回卷:各宽 get/set 位模式与同形 AB 视图对照同值。
|
||
#[test]
|
||
fn dv_sab_get_set_roundtrip() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"(function () { \
|
||
var sab = new SharedArrayBuffer(32); \
|
||
var dv = new DataView(sab); \
|
||
dv.setInt8(0, -7); \
|
||
dv.setUint8(1, 200); \
|
||
dv.setInt32(2, 0x01020304, true); \
|
||
dv.setInt32(6, 0x01020304, false); \
|
||
dv.setFloat64(8, 1.5); \
|
||
dv.setBigInt64(16, 42n); \
|
||
dv.setBigUint64(24, 18446744073709551615n); \
|
||
var ab = new ArrayBuffer(32); \
|
||
var ref = new DataView(ab); \
|
||
ref.setInt8(0, -7); \
|
||
ref.setUint8(1, 200); \
|
||
ref.setInt32(2, 0x01020304, true); \
|
||
ref.setInt32(6, 0x01020304, false); \
|
||
ref.setFloat64(8, 1.5); \
|
||
ref.setBigInt64(16, 42n); \
|
||
ref.setBigUint64(24, 18446744073709551615n); \
|
||
return dv.getInt8(0) === ref.getInt8(0) \
|
||
&& dv.getUint8(1) === ref.getUint8(1) \
|
||
&& dv.getInt32(2, true) === ref.getInt32(2, true) \
|
||
&& dv.getInt32(6, false) === ref.getInt32(6, false) \
|
||
&& dv.getFloat64(8, true) === ref.getFloat64(8, true) \
|
||
&& dv.getBigInt64(16, true) === ref.getBigInt64(16, true) \
|
||
&& dv.getBigUint64(24, false) === ref.getBigUint64(24, false); })()",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
/// DV-over-SAB 读越界:跨视图尾 getInt32 → RangeError。
|
||
#[test]
|
||
fn dv_sab_get_oob() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"(function () { \
|
||
var dv = new DataView(new SharedArrayBuffer(4)); \
|
||
try { dv.getInt32(1); return false; } catch (e) { return e instanceof RangeError; } })()",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
/// DV-over-SAB live 读:定长视图静态界 + growable SAB auto 视图随 grow 活读。
|
||
#[test]
|
||
fn dv_sab_live_getters() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"(function () { \
|
||
var sab = new SharedArrayBuffer(8); \
|
||
var fixed = new DataView(sab, 1, 3); \
|
||
var ok = fixed.byteOffset === 1 && fixed.byteLength === 3; \
|
||
var growable = new SharedArrayBuffer(4, { maxByteLength: 8 }); \
|
||
var auto = new DataView(growable); \
|
||
ok = ok && auto.byteLength === 4 && auto.byteOffset === 0; \
|
||
growable.grow(8); \
|
||
return ok && auto.byteLength === 8 && auto.byteOffset === 0; })()",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
/// DV-over-SAB 原型链:buffer 属性返回 SAB 本体 + custom-proto 臂
|
||
/// (newTarget 自定义原型经 GpFC 落位)。
|
||
#[test]
|
||
fn dv_sab_proto_chain() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"(function () { \
|
||
var sab = new SharedArrayBuffer(4); \
|
||
var dv = new DataView(sab); \
|
||
var ok = dv.buffer === sab; \
|
||
var newTarget = function() {}.bind(null); \
|
||
newTarget.prototype = {}; \
|
||
var dv2 = Reflect.construct(DataView, [sab], newTarget); \
|
||
return ok && Object.getPrototypeOf(dv2) === newTarget.prototype; })()",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
/// DV-over-SAB 品牌负钉:非缓冲区对象(裸对象 / TypedArray / null)构造
|
||
/// 首检均 TypeError。
|
||
#[test]
|
||
fn dv_sab_brand_reject() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(
|
||
&mut vm,
|
||
"(function () { \
|
||
function kind(expr) { try { expr(); return 'none'; } catch (e) { return e.name; } } \
|
||
var ta = new Int8Array(4); \
|
||
return kind(function () { new DataView({}); }) === 'TypeError' \
|
||
&& kind(function () { new DataView(ta); }) === 'TypeError' \
|
||
&& kind(function () { new DataView(null); }) === 'TypeError'; })()",
|
||
)
|
||
.unwrap();
|
||
assert!(result.as_bool());
|
||
}
|