diff --git a/crates/oxide_builtins/src/typed_array.rs b/crates/oxide_builtins/src/typed_array.rs index 396c714f..95bb9f91 100644 --- a/crates/oxide_builtins/src/typed_array.rs +++ b/crates/oxide_builtins/src/typed_array.rs @@ -1388,7 +1388,10 @@ fn allocate_typed_array(vm: &mut H, c: JsValue, len: usize) -> Result return Err(type_error(vm, "TypedArray.of/from constructor did not return a TypedArray")); } let view = get_typed_array_data(vm, result)?; - if view.length < len { + // 结果长校验取 live 口径(规范 TypedArrayCreateFromConstructor:长度取 + // TypedArrayLength 实时值,构造器窗口内 auto 视图可已收缩);越界(含 + // detach)恒 0,自然落入长度不足 TypeError。 + if ta_live_length(view) < len { return Err(type_error( vm, "TypedArray.of/from constructor returned a TypedArray with insufficient length", @@ -1488,12 +1491,12 @@ fn ta_to_number(vm: &mut H, value: JsValue) -> Result { pub fn typed_array_for_each(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); + native_try!(ta_validate(vm, view, false)); if args.len() < 2 { return NativeResult::Err(type_error(vm, "callback is not a function")); } let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1]))); let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - native_try!(ta_validate(vm, view, false)); // 入口见证:循环长度一次捕获,回调期 buffer 伸缩不改本轮范围。 let len = ta_spec_length(view); for i in 0..len { @@ -1508,12 +1511,12 @@ pub fn typed_array_for_each(vm: &mut H, args: &[u8]) -> NativeResult pub fn typed_array_map(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); + native_try!(ta_validate(vm, view, false)); if args.len() < 2 { return NativeResult::Err(type_error(vm, "callback is not a function")); } let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1]))); let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - native_try!(ta_validate(vm, view, false)); // 入口见证:目标长度 = 源见证长度,回调期 buffer 伸缩不改本轮范围。 let len = ta_spec_length(view); // 目标对象经 species 构造(长度 = 源长度);逐元素"回调 → 写入" @@ -1539,12 +1542,12 @@ pub fn typed_array_map(vm: &mut H, args: &[u8]) -> NativeResult { pub fn typed_array_filter(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); + native_try!(ta_validate(vm, view, false)); if args.len() < 2 { return NativeResult::Err(type_error(vm, "callback is not a function")); } let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1]))); let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - native_try!(ta_validate(vm, view, false)); // 先全量回调得通过元素(规范序:count 先于目标构造),再经 species // 构造目标(长度 = 通过数)并顺序写入;循环走入口见证长度。 let len = ta_spec_length(view); @@ -1575,6 +1578,7 @@ pub fn typed_array_filter(vm: &mut H, args: &[u8]) -> NativeResult { pub fn typed_array_reduce(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); + native_try!(ta_validate(vm, view, false)); // 见证长度一次捕获:空检与循环同口径(shrink 到 0 的 auto 视图按空抛错)。 let len = ta_spec_length(view); if len == 0 && args.len() < 3 { @@ -1584,7 +1588,6 @@ pub fn typed_array_reduce(vm: &mut H, args: &[u8]) -> NativeResult { return NativeResult::Err(type_error(vm, "callback is not a function")); } let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1]))); - native_try!(ta_validate(vm, view, false)); let (mut accumulator, start_idx) = if args.len() > 2 { (vm.reg(args[2]), 0usize) } else { @@ -1606,6 +1609,7 @@ pub fn typed_array_reduce(vm: &mut H, args: &[u8]) -> NativeResult { pub fn typed_array_reduce_right(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); + native_try!(ta_validate(vm, view, false)); // 见证长度一次捕获:空检与循环同口径(shrink 到 0 的 auto 视图按空抛错)。 let len = ta_spec_length(view); if len == 0 && args.len() < 3 { @@ -1615,7 +1619,6 @@ pub fn typed_array_reduce_right(vm: &mut H, args: &[u8]) -> NativeRes return NativeResult::Err(type_error(vm, "callback is not a function")); } let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1]))); - native_try!(ta_validate(vm, view, false)); let (mut accumulator, start_idx): (JsValue, i32) = if args.len() > 2 { (vm.reg(args[2]), len as i32 - 1) } else { @@ -1637,12 +1640,12 @@ pub fn typed_array_reduce_right(vm: &mut H, args: &[u8]) -> NativeRes pub fn typed_array_every(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); + native_try!(ta_validate(vm, view, false)); if args.len() < 2 { return NativeResult::Err(type_error(vm, "callback is not a function")); } let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1]))); let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - native_try!(ta_validate(vm, view, false)); let len = ta_spec_length(view); for i in 0..len { let elem = native_try!(ta_read(vm, view, i)); @@ -1658,12 +1661,12 @@ pub fn typed_array_every(vm: &mut H, args: &[u8]) -> NativeResult { pub fn typed_array_some(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); + native_try!(ta_validate(vm, view, false)); if args.len() < 2 { return NativeResult::Err(type_error(vm, "callback is not a function")); } let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1]))); let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - native_try!(ta_validate(vm, view, false)); let len = ta_spec_length(view); for i in 0..len { let elem = native_try!(ta_read(vm, view, i)); @@ -1679,12 +1682,12 @@ pub fn typed_array_some(vm: &mut H, args: &[u8]) -> NativeResult { pub fn typed_array_find(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); + native_try!(ta_validate(vm, view, false)); if args.len() < 2 { return NativeResult::Err(type_error(vm, "callback is not a function")); } let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1]))); let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - native_try!(ta_validate(vm, view, false)); let len = ta_spec_length(view); for i in 0..len { let elem = native_try!(ta_read(vm, view, i)); @@ -1700,12 +1703,12 @@ pub fn typed_array_find(vm: &mut H, args: &[u8]) -> NativeResult { pub fn typed_array_find_index(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); + native_try!(ta_validate(vm, view, false)); if args.len() < 2 { return NativeResult::Err(type_error(vm, "callback is not a function")); } let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1]))); let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - native_try!(ta_validate(vm, view, false)); let len = ta_spec_length(view); for i in 0..len { let elem = native_try!(ta_read(vm, view, i)); @@ -1721,12 +1724,12 @@ pub fn typed_array_find_index(vm: &mut H, args: &[u8]) -> NativeResul pub fn typed_array_find_last(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); + native_try!(ta_validate(vm, view, false)); if args.len() < 2 { return NativeResult::Err(type_error(vm, "callback is not a function")); } let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1]))); let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - native_try!(ta_validate(vm, view, false)); let len = ta_spec_length(view); for i in (0..len).rev() { let elem = native_try!(ta_read(vm, view, i)); @@ -1743,12 +1746,12 @@ pub fn typed_array_find_last(vm: &mut H, args: &[u8]) -> NativeResult pub fn typed_array_find_last_index(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); + native_try!(ta_validate(vm, view, false)); if args.len() < 2 { return NativeResult::Err(type_error(vm, "callback is not a function")); } let callback = native_try!(crate::array::require_callback(vm, vm.reg(args[1]))); let this_arg = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - native_try!(ta_validate(vm, view, false)); let len = ta_spec_length(view); for i in (0..len).rev() { let elem = native_try!(ta_read(vm, view, i)); diff --git a/crates/oxide_vm/tests/builtins_typed_array_tests.rs b/crates/oxide_vm/tests/builtins_typed_array_tests.rs index a5d9816a..dfdd780d 100644 --- a/crates/oxide_vm/tests/builtins_typed_array_tests.rs +++ b/crates/oxide_vm/tests/builtins_typed_array_tests.rs @@ -692,6 +692,45 @@ fn typed_array_plain_call_and_construct_zero_drift() { assert!(result.as_bool()); } +#[test] +fn typed_array_of_from_result_length_live_check() { + // of/from 结果长校验取 live 口径:构造器窗口内把 auto 视图收缩到请求 + // 长度之下时抛 TypeError(长度不足);未收缩的正常臂不受影响。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { \ + function C() { var ab = new ArrayBuffer(16, { maxByteLength: 16 }); \ + var t = new Int8Array(ab); ab.resize(2); return t; } \ + var o, f; \ + try { o = Int8Array.of.call(C, 1, 2, 3); } catch (e) { o = e instanceof TypeError; } \ + try { f = Int8Array.from.call(C, [1, 2, 3]); } catch (e) { f = e instanceof TypeError; } \ + var ok = o === true && f === true; \ + var n = Int8Array.of(1, 2, 3); \ + return ok && n.length === 3 && n.at(0) === 1 && n.at(2) === 3; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +#[test] +fn typed_array_detached_entry_validate_before_callback_check() { + // 回调族入口校验先于回调检查:detached 源上非函数回调与有效回调均抛 + // TypeError(detached 守卫臂),种类与消息不回归。 + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { \ + var ab = new ArrayBuffer(8); var ta = new Int8Array(ab); ab.transfer(); \ + var t1, t2; \ + try { ta.map('nope'); } catch (e) { t1 = e instanceof TypeError; } \ + try { ta.forEach(function (v) { return v; }); } catch (e) { t2 = e instanceof TypeError; } \ + return t1 === true && t2 === true; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + #[test] fn typed_array_plain_member_call_in_class_ctor_not_construct() { // 类构造器帧内对 TA 构造器的成员式普通调用是普通形态:返回全新 TA,