From ed9ae06566707c521a70e893dcdc18d320a1a930 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Tue, 22 Sep 2026 22:20:36 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20ArrayBuffer=20slice=20BigInt=20=E5=8F=82?= =?UTF-8?q?=E6=94=B9=E4=BC=A0=E6=92=AD=20TypeError?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/array_buffer.rs | 31 ++++++---- .../tests/builtins_arraybuffer_state_tests.rs | 60 ++++++++++++++++++- 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/crates/oxide_builtins/src/array_buffer.rs b/crates/oxide_builtins/src/array_buffer.rs index 8b14b537..62d97a1e 100644 --- a/crates/oxide_builtins/src/array_buffer.rs +++ b/crates/oxide_builtins/src/array_buffer.rs @@ -4,6 +4,8 @@ use oxide_types::value::JsValue; use oxide_runtime_api::{NativeResult, VmHost}; +use crate::array::to_integer_or_infinity_bounded; + pub(crate) const MAX_ARRAY_BUFFER_LENGTH: usize = 1 << 30; /// ArrayBuffer 载荷:字节缓冲与状态位。`data` 为 `None` 即缓冲区已 detach; @@ -64,21 +66,20 @@ fn to_integer_or_infinity(vm: &mut H, value: JsValue) -> Result(vm: &mut H, value: JsValue, len: usize) -> usize { - let n = vm.coerce_number_bounded(value).unwrap_or(f64::NAN); - if n.is_nan() { - return 0; - } - // ToIntegerOrInfinity:±Infinity 不截断,负无穷归 0、正无穷归 len, +/// ToIntegerOrInfinity:ToNumber 传播式(BigInt/Symbol/转换异常向上传播), +/// NaN → 0、trunc、±Infinity 保留——负值归 0、正值归 len 由调用侧折叠。 +fn normalize_index(vm: &mut H, value: JsValue, len: usize) -> Result { + let n = to_integer_or_infinity_bounded(vm, value)?; + // ±Infinity 不截断,负无穷归 0、正无穷归 len, // 避免饱和成 isize::MIN 后取负溢出。 if n.is_infinite() { - return if n < 0.0 { 0 } else { len }; + return Ok(if n < 0.0 { 0 } else { len }); } let int = n.trunc() as isize; if int < 0 { - len.saturating_sub(int.unsigned_abs()) + Ok(len.saturating_sub(int.unsigned_abs())) } else { - (int as usize).min(len) + Ok((int as usize).min(len)) } } @@ -358,8 +359,16 @@ pub fn array_buffer_slice(vm: &mut H, args: &[u8]) -> NativeResult { return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); }; let len = data.len(); - let start = if args.len() > 1 { normalize_index(vm, vm.reg(args[1]), len) } else { 0 }; - let end = if args.len() > 2 { normalize_index(vm, vm.reg(args[2]), len) } else { len }; + let start = if args.len() > 1 { + native_try!(normalize_index(vm, vm.reg(args[1]), len)) + } else { + 0 + }; + let end = if args.len() > 2 { + native_try!(normalize_index(vm, vm.reg(args[2]), len)) + } else { + len + }; let end = end.max(start); // 切片产物恒定长,原型取默认 %ArrayBuffer.prototype%。 let proto = default_array_buffer_proto(vm); diff --git a/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs b/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs index bcd75c4b..f8226496 100644 --- a/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs +++ b/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs @@ -412,7 +412,65 @@ fn ab_resizable_accessor_pins() { 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", + 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());