diff --git a/crates/oxide_builtins/src/typed_array.rs b/crates/oxide_builtins/src/typed_array.rs index 7a7f8524..70323aaa 100644 --- a/crates/oxide_builtins/src/typed_array.rs +++ b/crates/oxide_builtins/src/typed_array.rs @@ -40,18 +40,21 @@ fn range_error(vm: &mut H, msg: &str) -> JsValue { crate::error::create_range_error(vm, msg) } +/// ToIndex(spec 7.1.24):ToNumber 传播式(Symbol 抛 TypeError、BigInt +/// 走 lossy 数值、对象经 ToPrimitive);NaN → +0、向零截断;负值与 +/// ≥ 2^53(含 +Infinity)→ RangeError。 fn to_index(vm: &mut H, value: JsValue, msg: &str) -> Result { - let n = match vm.coerce_number_bounded(value) { + let n = match oxide_runtime_api::to_number_full(value, vm) { Ok(n) => n, Err(e) => return Err(crate::iterator::engine_error(vm, &e)), }; - if n.is_nan() { - return Ok(0); - } - if !n.is_finite() || n < 0.0 { + // ToIntegerOrInfinity:NaN → +0,±Infinity 原样,有限值向零截断。 + let integer = if n.is_nan() { 0.0 } else { n.trunc() }; + // 合法域 [0, 2^53):负值与 ≥ 2^53(含 +Infinity)→ RangeError。 + if !(0.0..9_007_199_254_740_992.0_f64).contains(&integer) { return Err(range_error(vm, msg)); } - Ok(n.trunc() as usize) + Ok(integer as usize) } fn typed_array_proto_ptr(vm: &mut H, kind: TypedArrayKind) -> *mut JsObject { @@ -73,6 +76,29 @@ fn typed_array_proto_ptr(vm: &mut H, kind: TypedArrayKind) -> *mut Js /// 取指定类型的内建构造器值(`[[TypedArrayName]]` → world 槽),species 默认臂 /// 的构造目标。 +/// GetPrototypeFromConstructor(newTarget, 该 kind 内建原型):经 ordinary_get +/// 传播式读 newTarget 的 "prototype"(访问器副作用与异常原值传播); +/// newTarget 非对象、或读取结果非对象时回落该 kind 的内建原型。 +/// +/// # 边界与前提 +/// - 仅读寄存器 255(new.target),构造帧内由 NEW/SUPER 机制置位;普通调用 +/// 入口经构造守卫拦截,不到达本函数。 +fn ta_gpf_proto(vm: &mut H, kind: TypedArrayKind) -> Result { + let default_proto = JsValue::from_js_object(typed_array_proto_ptr(vm, kind)); + let new_target = vm.reg(255); + if !new_target.is_object() { + return Ok(default_proto); + } + let nt_ptr = new_target.as_js_object_ptr(); + // SAFETY: is_object 保证指针非空且对象本 session 存活。 + let nt_obj = unsafe { &*nt_ptr }; + let proto_si = vm.kernel_core().perm_interner().intern("prototype").0; + match vm.ordinary_get(nt_obj, proto_si, new_target) { + Ok(v) => Ok(if v.is_object() { v } else { default_proto }), + Err(err) => Err(crate::iterator::engine_error(vm, &err)), + } +} + fn typed_array_ctor_value(vm: &mut H, kind: TypedArrayKind) -> JsValue { let world = vm.session().builtin_world(); let ctor = match kind { @@ -114,9 +140,9 @@ fn materialize_typed_array( fn create_typed_array( vm: &mut H, kind: TypedArrayKind, buffer: JsValue, byte_offset: usize, length: usize, auto_length: bool, + proto: JsValue, ) -> *mut JsObject { - let proto = typed_array_proto_ptr(vm, kind); - let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto)); + let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto); materialize_typed_array(&mut obj, kind, buffer, byte_offset, length, auto_length); vm.alloc_object(obj) } @@ -128,7 +154,8 @@ fn create_same_type_typed_array(vm: &mut H, kind: TypedArrayKind, len let bpe = kind.bytes_per_element(); let buffer = JsValue::from_js_object(new_array_buffer(vm, vec![0; length * bpe], 0, default_array_buffer_proto(vm))); - create_typed_array(vm, kind, buffer, 0, length, false) + let proto = JsValue::from_js_object(typed_array_proto_ptr(vm, kind)); + create_typed_array(vm, kind, buffer, 0, length, false, proto) } fn typed_array_data_ptr(obj: &JsObject) -> Option<*mut TypedArrayData> { @@ -769,8 +796,13 @@ pub(crate) fn write_element( /// (含用户自定义迭代器);否则按 array-like 读 `length` 逐索引取值。 /// /// # 边界 -/// 源须为对象,否则抛 TypeError。 -fn collect_array_like(vm: &mut H, value: JsValue, consult_iterator: bool) -> Result, JsValue> { +/// - 源须为对象,否则抛 TypeError。 +/// - `max_elements` 为 AllocateTypedArrayBuffer 上界(元素数 = 引擎缓冲上限 +/// ÷ 元素大小):ToLength(length) 值超出时抛 RangeError,先于密集上限截断; +/// None 表示入口无上界(非构造器路径)。 +fn collect_array_like( + vm: &mut H, value: JsValue, consult_iterator: bool, max_elements: Option, +) -> Result, JsValue> { if !value.is_object() { return Err(type_error(vm, "TypedArray source must be array-like or iterable")); } @@ -825,6 +857,13 @@ fn collect_array_like(vm: &mut H, value: JsValue, consult_iterator: b .ordinary_get(obj, length_si, value) .map_err(|e| crate::iterator::engine_error(vm, &e))?; let n = oxide_runtime_api::to_number_full(len_val, vm).map_err(|e| crate::iterator::engine_error(vm, &e))?; + // AllocateTypedArrayBuffer 上界:ToLength 值超元素数上界即抛,先于 + // 密集上限截断(截断后的长度永不可触上界,检点必须在此)。 + if let Some(max_elements) = max_elements { + if to_length(n) > max_elements as u64 { + return Err(range_error(vm, "invalid TypedArray length")); + } + } let len = to_collect_len(n); let mut values = Vec::with_capacity(len); for i in 0..len { @@ -838,29 +877,40 @@ fn collect_array_like(vm: &mut H, value: JsValue, consult_iterator: b Ok(values) } -/// 按 ToLength 语义把 length 数值夹到收集上限:NaN/非正取 0,超出密集上限截断。 -fn to_collect_len(n: f64) -> usize { +/// ToLength(spec 7.1.20):NaN/非正取 0,+Infinity 夹取 2^53−1, +/// 有限值向零截断。 +fn to_length(n: f64) -> u64 { if n.is_nan() || n <= 0.0 { 0 } else { - (n.min(9_007_199_254_740_991.0).trunc() as u64).min(oxide_types::object::MAX_DENSE_PROPS as u64) as usize + n.min(9_007_199_254_740_991.0).trunc() as u64 } } +/// 按 ToLength 语义把 length 数值夹到收集上限:NaN/非正取 0,超出密集上限截断。 +fn to_collect_len(n: f64) -> usize { + to_length(n).min(oxide_types::object::MAX_DENSE_PROPS as u64) as usize +} + fn typed_array_new(vm: &mut H, args: &[u8], kind: TypedArrayKind) -> NativeResult { + // spec 步 1:NewTarget 缺失(普通调用)→ TypeError,先于一切参数求值。 + if !vm.constructing_native() { + return NativeResult::Err(type_error(vm, "TypedArray constructor requires new")); + } let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); // 调用形态取构造入口标记(NEW/SUPER native 臂与 construct_with 三入口调用前 // 置位、普通调用入口调用前清零),不推断 new.target 寄存器:native 调用与 // 调用方共享寄存器文件,类构造器帧内 new.target 槽残留类构造器对象,按 // 寄存器推断会把成员式普通调用误判为构造(双物化 receiver、旧数据盒泄漏)。 - // 构造形态下视图数据物化到 receiver(其原型 = new.target.prototype,派生类 - // super() 与 species 构造由此拿到子类实例);普通形态按规范 TypedArrayCreate - // 忽略 this、自建新对象。 - let in_construct = vm.constructing_native() && this_val.is_object(); + // 构造形态下视图数据物化到 receiver(派生类 super() 与 species 构造由此 + // 拿到子类实例);普通形态按规范 TypedArrayCreate 忽略 this、自建新对象。 + let in_construct = this_val.is_object(); let first = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::int(0) }; let bpe = kind.bytes_per_element(); - let (buffer, byte_offset, length, auto_length) = if first.is_object() { + let (buffer, byte_offset, length, auto_length, proto) = if first.is_object() { + // GpFC 先于本臂一切参数转换:newTarget 原型读异常原值上抛。 + let proto = native_try!(ta_gpf_proto(vm, kind)); let first_ptr = first.as_js_object_ptr(); let first_obj = unsafe { &*first_ptr }; // ArrayBuffer/SharedArrayBuffer 双认:长度/偏移/长度校验与定长臂同构; @@ -869,41 +919,59 @@ fn typed_array_new(vm: &mut H, args: &[u8], kind: TypedArrayKind) -> let Some(payload_ptr) = buffer_payload_ptr(first_obj) else { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); }; - // SAFETY: payload_ptr 经 buffer_payload_ptr 校验为合法缓冲区载荷。 - let Some(data) = unsafe { &*payload_ptr }.data.as_ref() else { - // 首参 buffer 载荷缺失(AB 已 detach;SAB 为防御背板):按规范的 - // ArrayBuffer 校验步抛 TypeError(构造器入口的 detach 守卫即本臂)。 - return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); - }; - let buffer_len = data.len(); + // spec 步序:ToIndex(offset) → offset 模校验 → ToIndex(length) → + // 重读载荷 detach 检 → 剩余长度/模校验;转换窗口内的 detach + // 在 IsDetached 步抛 TypeError。 let byte_offset = if args.len() > 2 { native_try!(to_index(vm, vm.reg(args[2]), "TypedArray byteOffset out of bounds")) } else { 0 }; - if byte_offset > buffer_len || byte_offset % bpe != 0 { + if byte_offset % bpe != 0 { return NativeResult::Err(range_error(vm, "TypedArray byteOffset out of bounds")); } - let remaining = buffer_len - byte_offset; // 规范以 "length is not undefined" 分支:显式 undefined 与省略同义, // 取整缓冲剩余长度(live 全长),不落入 ToIndex(undefined)=0。 // auto 口径:省略/显式 undefined → 长度随 buffer 伸缩;显式给定 // 定死。 let auto_length = args.len() <= 3 || vm.reg(args[3]).is_undefined(); - let length = if auto_length { - remaining / bpe + let mut length = if auto_length { + 0 } else { native_try!(to_index(vm, vm.reg(args[3]), "TypedArray length out of bounds")) }; - let Some(byte_length) = length.checked_mul(bpe) else { - return NativeResult::Err(range_error(vm, "TypedArray length out of bounds")); + // SAFETY: payload_ptr 经 buffer_payload_ptr 校验为合法缓冲区载荷。 + let payload = unsafe { &*payload_ptr }; + let Some(data) = payload.data.as_ref() else { + // 首参 buffer 载荷缺失(AB 转换窗口内已 detach;SAB 为防御背板): + // 按规范的 IsDetached 步抛 TypeError。 + return NativeResult::Err(type_error(vm, "ArrayBuffer internal state invalid")); }; - if byte_length > remaining { - return NativeResult::Err(range_error(vm, "TypedArray length out of bounds")); + let buffer_len = data.len(); + if byte_offset > buffer_len { + return NativeResult::Err(range_error(vm, "TypedArray byteOffset out of bounds")); } - (first, byte_offset, length, auto_length) + let remaining = buffer_len - byte_offset; + if auto_length { + // 定长 + 省略 length:剩余字节须被元素大小整除(max_byte_length + // 存储态 0 = 定长,AB/SAB 同编码);可增缓冲无此模校验。 + if payload.max_byte_length == 0 && remaining % bpe != 0 { + return NativeResult::Err(range_error(vm, "TypedArray length out of bounds")); + } + length = remaining / bpe; + } else { + let Some(byte_length) = length.checked_mul(bpe) else { + return NativeResult::Err(range_error(vm, "TypedArray length out of bounds")); + }; + if byte_length > remaining { + return NativeResult::Err(range_error(vm, "TypedArray length out of bounds")); + } + } + (first, byte_offset, length, auto_length, proto) } else { - let values = native_try!(collect_array_like(vm, first, true)); + // AllocateTypedArrayBuffer 上界先于密集上限截断:ToLength 值 × + // 元素大小超引擎缓冲上限 → RangeError。 + let values = native_try!(collect_array_like(vm, first, true, Some(MAX_ARRAY_BUFFER_LENGTH / bpe))); let byte_len = values.len().saturating_mul(bpe); let buffer = JsValue::from_js_object(new_array_buffer(vm, vec![0; byte_len], 0, default_array_buffer_proto(vm))); @@ -917,15 +985,18 @@ fn typed_array_new(vm: &mut H, args: &[u8], kind: TypedArrayKind) -> let elem = native_try!(ta_element_value(vm, kind, value)); write_element(vm, kind, buffer_ref, idx * bpe, elem); } - (buffer, 0, byte_len / bpe, false) + (buffer, 0, byte_len / bpe, false, proto) } } else { // 非对象第一参数统一按 ToIndex 语义处理(bool→0/1、null→0、BigInt→数值、 - // 字符串→解析、undefined→0);Symbol 按规范抛 TypeError。 + // 字符串→解析、undefined→0);Symbol 按规范抛 TypeError,先于 GpFC 读。 if first.is_symbol() { return NativeResult::Err(type_error(vm, "invalid TypedArray length")); } let len = native_try!(to_index(vm, first, "invalid TypedArray length")); + // GpFC 在 ToIndex 之后(spec 步序:非对象臂 AllocateTypedArray 在 + // ToIndex 之后);转换异常与原型读异常不组合钉序,符号守卫已在先。 + let proto = native_try!(ta_gpf_proto(vm, kind)); let Some(byte_len) = len.checked_mul(bpe) else { return NativeResult::Err(range_error(vm, "invalid TypedArray length")); }; @@ -934,13 +1005,18 @@ fn typed_array_new(vm: &mut H, args: &[u8], kind: TypedArrayKind) -> } let buffer = JsValue::from_js_object(new_array_buffer(vm, vec![0; byte_len], 0, default_array_buffer_proto(vm))); - (buffer, 0, len, false) + (buffer, 0, len, false, proto) }; if in_construct { // SAFETY: receiver 是构造帧分配的 this(本 session 存活),此处只写 // type_tag 与 native_fn 两个槽位,与调用方无别名。 let obj = unsafe { &mut *this_val.as_js_object_ptr() }; + // 通用构造机制的原型读吞访问器异常且非对象回落 Object.prototype; + // 按 spec 结果重设(派生类 super() 的恒等重设是 no-op,无漂移)。 + if let Err(msg) = obj.set_proto(proto) { + return NativeResult::Err(type_error(vm, msg)); + } materialize_typed_array(obj, kind, buffer, byte_offset, length, auto_length); NativeResult::Ok(this_val) } else { @@ -951,6 +1027,7 @@ fn typed_array_new(vm: &mut H, args: &[u8], kind: TypedArrayKind) -> byte_offset, length, auto_length, + proto, ))) } } @@ -2678,6 +2755,7 @@ pub fn uint8array_from_base64(vm: &mut H, args: &[u8]) -> NativeResul native_try!(ta_decode_base64(vm, string, options, None, &mut |_, b| bytes.push(b))); let len = bytes.len(); let buffer = JsValue::from_js_object(new_array_buffer(vm, bytes, 0, default_array_buffer_proto(vm))); + let proto = JsValue::from_js_object(typed_array_proto_ptr(vm, TypedArrayKind::Uint8)); NativeResult::Ok(JsValue::from_js_object(create_typed_array( vm, TypedArrayKind::Uint8, @@ -2685,6 +2763,7 @@ pub fn uint8array_from_base64(vm: &mut H, args: &[u8]) -> NativeResul 0, len, false, + proto, ))) } @@ -2702,6 +2781,7 @@ pub fn uint8array_from_hex(vm: &mut H, args: &[u8]) -> NativeResult { } let len = bytes.len(); let buffer = JsValue::from_js_object(new_array_buffer(vm, bytes, 0, default_array_buffer_proto(vm))); + let proto = JsValue::from_js_object(typed_array_proto_ptr(vm, TypedArrayKind::Uint8)); NativeResult::Ok(JsValue::from_js_object(create_typed_array( vm, TypedArrayKind::Uint8, @@ -2709,6 +2789,7 @@ pub fn uint8array_from_hex(vm: &mut H, args: &[u8]) -> NativeResult { 0, len, false, + proto, ))) } diff --git a/crates/oxide_vm/src/session_gc/tests.rs b/crates/oxide_vm/src/session_gc/tests.rs index 9569b74d..569dbde7 100644 --- a/crates/oxide_vm/src/session_gc/tests.rs +++ b/crates/oxide_vm/src/session_gc/tests.rs @@ -749,7 +749,11 @@ fn session_gc_keeps_shared_array_buffer_alive_through_view_native_edges() { vm.constructing_native = false; vm.regs[1] = buffer; + // 同 AB 臂:TA 构造器普通调用按规范抛 TypeError,直调须夹持 NEW 标记 + // (reg(0) 未置 this,走自建对象臂)。 + vm.constructing_native = true; let typed = native_ok(typed_array::int32array_constructor(&mut vm, &[0, 1])); + vm.constructing_native = false; vm.regs[1] = buffer; data_view_this(&mut vm, 0); diff --git a/crates/oxide_vm/tests/builtins_typed_array_tests.rs b/crates/oxide_vm/tests/builtins_typed_array_tests.rs index a4ddaf2f..5d6d3979 100644 --- a/crates/oxide_vm/tests/builtins_typed_array_tests.rs +++ b/crates/oxide_vm/tests/builtins_typed_array_tests.rs @@ -317,13 +317,13 @@ fn typed_array_numeric_write_bigint_throws() { #[test] fn typed_array_constructor_bigint_length() { let mut vm = Vm::new(); - // 长度参数走 ToIndex:BigInt 实参在 ToNumber 步抛 TypeError,不截断建数组; + // 长度参数走 ToIndex:BigInt 经 ToNumber 取 lossy 数值(spec ToIndex(5n)=5), // 布尔/null 等原语按数值转换语义建数组。 let result = eval( &mut vm, - "(function () { var t = false; \ - try { new BigInt64Array(5n); } catch (e) { t = e instanceof TypeError; } \ - return t && new Int8Array(true).length === 1 && new Int8Array(null).length === 0; })()", + "(function () { \ + return new BigInt64Array(5n).length === 5 && \ + new Int8Array(true).length === 1 && new Int8Array(null).length === 0; })()", ) .unwrap(); assert!(result.as_bool()); @@ -707,15 +707,17 @@ fn typed_array_same_type_immutable_methods_ignore_species() { } #[test] -fn typed_array_plain_call_and_construct_zero_drift() { - // 普通调用(无 new)与构造调用双路径零漂移:结果均为内建实例; - // 派生类显式构造器 super() 后可继续扩展属性。 +fn typed_array_plain_call_throws_construct_ok() { + // 普通调用(无 new、NewTarget 缺失)按规范步 1 抛 TypeError;构造调用 + // 正常出内建实例;派生类显式构造器 super() 后可继续扩展属性。 let mut vm = Vm::new(); let result = eval( &mut vm, - "(function () { var plain = Uint8Array(2); var constructed = new Uint8Array(2); \ - if (!(plain instanceof Uint8Array && constructed instanceof Uint8Array)) return false; \ - if (plain.length !== 2 || constructed.length !== 2) return false; \ + "(function () { var t = false; \ + try { Uint8Array(2); } catch (e) { t = e instanceof TypeError; } \ + var constructed = new Uint8Array(2); \ + if (!(t && constructed instanceof Uint8Array)) return false; \ + if (constructed.length !== 2) return false; \ class S extends Uint8Array { constructor(len) { super(len); this.marked = true; } } \ var s = new S(2); var f = S.from([7, 8]); \ return s instanceof S && s.marked === true && f instanceof S && \ @@ -766,19 +768,23 @@ fn typed_array_detached_entry_validate_before_callback_check() { #[test] fn typed_array_plain_member_call_in_class_ctor_not_construct() { - // 类构造器帧内对 TA 构造器的成员式普通调用是普通形态:返回全新 TA, - // receiver 不物化(长度与 buffer 不动),receiver 为普通对象时亦不改建。 + // 类构造器帧内对 TA 构造器的成员式普通调用是普通形态(NewTarget 缺失): + // 按规范步 1 抛 TypeError,receiver 不物化(长度与 buffer 不动), + // receiver 为普通对象时亦不改建。 let mut vm = Vm::new(); let result = eval( &mut vm, "(function () { class Sub extends Uint8Array { constructor() { \ - super(4); this.f = Uint8Array; var r = this.f(2); this.r = r; this.len = this.length; \ - var o = {}; o.f = Uint8Array; var p = o.f(2); this.p = p; this.o = o; } } \ + super(4); this.f = Uint8Array; \ + var o = {}; o.f = Uint8Array; \ + var t1, t2; \ + try { this.f(2); } catch (e) { t1 = e instanceof TypeError; } \ + try { o.f(2); } catch (e) { t2 = e instanceof TypeError; } \ + this.t1 = t1; this.t2 = t2; this.o = o; } } \ var s = new Sub(); \ - return s.r !== s && s.r instanceof Uint8Array && \ - Object.getPrototypeOf(s.r) === Uint8Array.prototype && s.r.length === 2 && \ - s.len === 4 && s.length === 4 && s.buffer.byteLength === 4 && \ - s.p !== s.o && s.p.length === 2 && typeof s.o.length === 'undefined'; })()", + return s.t1 === true && s.t2 === true && \ + s.length === 4 && s.buffer.byteLength === 4 && \ + typeof s.o.length === 'undefined'; })()", ) .unwrap(); assert!(result.as_bool()); @@ -2274,3 +2280,121 @@ fn ta_sab_base64() { .unwrap(); assert!(result.as_bool()); } + +#[test] +fn typed_array_ctor_plain_call_all_forms_throw() { + // NewTarget 缺失守卫(spec 步 1):buffer/length/对象/TA 源/无参五形 + // 普通调用全抛 TypeError,先于一切参数求值。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var ab = new ArrayBuffer(4); var t = true; \ + try { Int8Array(ab); } catch (e) { t = t && e instanceof TypeError; } \ + try { Int8Array(2); } catch (e) { t = t && e instanceof TypeError; } \ + try { Int8Array([1, 2]); } catch (e) { t = t && e instanceof TypeError; } \ + try { Int8Array(new Int8Array(2)); } catch (e) { t = t && e instanceof TypeError; } \ + try { Int8Array(); } catch (e) { t = t && e instanceof TypeError; } \ + return t; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn typed_array_ctor_gpf_getter_throws_propagates() { + // newTarget.prototype 访问器抛值原值传播(buffer 形 + length 形): + // 构造器体内 GpFC 自重读,不依赖通用构造机制的原型读。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var nt = function () {}; \ + Object.defineProperty(nt, 'prototype', { \ + get: function () { throw new RangeError('gpf'); } }); \ + var ab = new ArrayBuffer(4); var t1, t2; \ + try { Reflect.construct(Int8Array, [ab], nt); } \ + catch (e) { t1 = e instanceof RangeError && e.message === 'gpf'; } \ + try { Reflect.construct(Int8Array, [2], nt); } \ + catch (e) { t2 = e instanceof RangeError && e.message === 'gpf'; } \ + return t1 && t2; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn typed_array_ctor_gpf_proto_fallback_and_respect() { + // GpFC 交付:newTarget.prototype 非对象回落该 kind 内建原型(构造形), + // 自定义对象原型被尊重;回落实例的 constructor/原型断言同源。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var nt = function () {}; nt.prototype = null; \ + var c = Reflect.construct(Int8Array, [2], nt); \ + var o = { p: 1 }; var nt2 = function () {}; nt2.prototype = o; \ + var d = Reflect.construct(Int8Array, [2], nt2); \ + return Object.getPrototypeOf(c) === Int8Array.prototype && \ + c.constructor === Int8Array && c.length === 2 && \ + Object.getPrototypeOf(d) === o; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn typed_array_ctor_to_index_truncation_and_bounds() { + // ToIndex 语义:向零截断(-0.1→0、8.9→8)、true→1、BigInt lossy(2n→2); + // 负整数与 ≥2^53 → RangeError;Symbol → TypeError。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var t = new Int8Array(-0.1).length === 0 && \ + new Int8Array(8.9).length === 8 && \ + new Int8Array(true).length === 1 && new Int8Array(2n).length === 2; \ + try { new Int8Array(-5); } catch (e) { t = t && e instanceof RangeError; } \ + try { new Int8Array(9007199254740992); } catch (e) { t = t && e instanceof RangeError; } \ + try { new Int8Array(Symbol()); } catch (e) { t = t && e instanceof TypeError; } \ + return t; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn typed_array_ctor_fixed_missing_length_modulo() { + // 定长缓冲 + 省略 length:剩余字节 % bpe ≠ 0 → RangeError(AB/SAB 同形); + // 可增 SAB 无模校验,auto 短视图(1 字节 / bpe=8 → 长度 0)。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var t = true; \ + try { new Float64Array(new ArrayBuffer(1)); } \ + catch (e) { t = t && e instanceof RangeError; } \ + try { new Float64Array(new SharedArrayBuffer(1)); } \ + catch (e) { t = t && e instanceof RangeError; } \ + var sab = new SharedArrayBuffer(1, { maxByteLength: 8 }); \ + var v = new Float64Array(sab); \ + return t && v.length === 0; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn typed_array_ctor_object_arg_excessive_length() { + // 对象臂 AllocateTypedArrayBuffer 上界:ToLength(length)×bpe 超引擎缓冲 + // 上限 → RangeError(BigInt 源不再误抛 TypeError);上界下正常成对象, + // 长度按密集上限截断。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var t = true; \ + try { new Int8Array({ length: 9007199254740992 }); } \ + catch (e) { t = t && e instanceof RangeError; } \ + try { new BigInt64Array({ length: 9007199254740992 }); } \ + catch (e) { t = t && e instanceof RangeError; } \ + var ok = new Int8Array({ length: 268435456 }); \ + return t && ok.length === 1000000; })()", + ) + .unwrap(); + assert!(result.as_bool()); +}