From b892c59a753c0431acb5ecd1e7752bdbe9ddc660 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 23 Sep 2026 21:46:36 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20SAB=20=E6=9E=84=E9=80=A0=E5=99=A8=20max?= =?UTF-8?q?ByteLength=20=E9=80=89=E9=A1=B9=E9=9D=A2=20grow=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E4=B8=8E=E8=AE=BF=E9=97=AE=E5=99=A8=E7=9C=9F=E5=80=BC?= =?UTF-8?q?=E8=87=82=E8=90=BD=E5=9C=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/array_buffer.rs | 134 ++++++++++++-- .../src/bindings/bind_shared_array_buffer.rs | 20 +- .../tests/builtins_arraybuffer_state_tests.rs | 171 ++++++++++++++++++ 3 files changed, 302 insertions(+), 23 deletions(-) diff --git a/crates/oxide_builtins/src/array_buffer.rs b/crates/oxide_builtins/src/array_buffer.rs index 7591ce3d..0b37e6a4 100644 --- a/crates/oxide_builtins/src/array_buffer.rs +++ b/crates/oxide_builtins/src/array_buffer.rs @@ -164,14 +164,17 @@ pub(crate) fn default_shared_array_buffer_proto(vm: &H) -> JsValue { JsValue::from_js_object(vm.session().builtin_world().shared_array_buffer_proto.as_ptr() as *mut JsObject) } -/// 分配携给定 proto 与载荷形态(`max_byte_length` 恒 0 定长)的 -/// SharedArrayBuffer 对象;byteLength 经原型访问器读(不写 own 数据属性)。 -pub(crate) fn new_shared_array_buffer(vm: &mut H, data: Vec, proto: JsValue) -> *mut JsObject { +/// 分配携给定 proto 与载荷形态(`max_byte_length` 为存储态:0 定长,非 0 可 +/// grow)的 SharedArrayBuffer 对象;byteLength 经原型访问器读(不写 own 数据 +/// 属性)。 +pub(crate) fn new_shared_array_buffer( + vm: &mut H, data: Vec, max_byte_length: usize, proto: JsValue, +) -> *mut JsObject { let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto); obj.type_tag = JsObject::OBJ_TYPE_SHARED_ARRAY_BUFFER; let payload = ArrayBufferPayload { data: Some(data), - max_byte_length: 0, + max_byte_length, immutable: false, }; let payload_ptr = Box::into_raw(Box::new(payload)); @@ -242,24 +245,57 @@ pub fn clone_shared_array_buffer_native(old_obj: &JsObject, new_obj: &mut JsObje new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned_ptr as *const ()) })); } -/// `SharedArrayBuffer(length)`:构造语义仅 `new`;length 缺省 0,经 ToIndex -/// 传播式(强转副作用原值上抛),超引擎上界 → RangeError。 +/// `SharedArrayBuffer(length [, options])`:构造语义仅 `new`;length 缺省 0, +/// 经 ToIndex 传播式(强转副作用原值上抛);`maxByteLength` 选项经传播式 +/// Get + ToIndex,`resizable` 键恒忽略(SAB 面不读);分配期上界校验在原型 +/// 读之后。 /// /// # 步骤 /// 1. 非构造形态(普通调用,new.target 缺失)→ TypeError。 /// 2. length = ToIndex(length) 传播式;undefined/缺省 → 0。 -/// 3. 分配期上界校验(超引擎上限 → RangeError)。 -/// 4. GetPrototypeFromConstructor:经 ordinary_get 读 new.target 的 +/// 3. GetArrayBufferMaxByteLengthOption:options 非对象 → 定长;Get +/// "maxByteLength" 传播式;undefined → 定长;否则 ToIndex 传播式。 +/// 4. length > max → RangeError(先于原型读与对象创建)。 +/// 5. GetPrototypeFromConstructor:经 ordinary_get 读 new.target 的 /// "prototype"(访问器触发,异常原值传播);非对象结果回落 /// %SharedArrayBuffer.prototype%。 -/// 5. 对象创建(零填充定长,无 resizable 选项面)。 +/// 6. 分配期上界校验(length 与 max 各一条,超引擎上限 → RangeError)。 +/// 7. 对象创建(零填充,定长/growable 由选项是否在场决定);不设 own +/// maxByteLength 数据属性(ArrayBuffer 构造器有此步,SAB 面经原型访问器 +/// 读)。 pub fn shared_array_buffer_constructor(vm: &mut H, args: &[u8]) -> NativeResult { if !vm.constructing_native() { return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer must be called with new")); } let length = if args.len() > 1 { native_try!(to_index(vm, vm.reg(args[1]))) } else { 0 }; - if length > MAX_ARRAY_BUFFER_LENGTH { - return NativeResult::Err(crate::error::create_range_error(vm, "invalid SharedArrayBuffer length")); + // GetArrayBufferMaxByteLengthOption:None = 空(定长),Some(v) = 请求的 + // 上限(v 可为 0,与"空"语义相异:length > v 的界判须生效)。 + let max_opt = if args.len() > 2 { + let options = vm.reg(args[2]); + if !options.is_object() { + None + } else { + let options_ptr = options.as_js_object_ptr(); + // SAFETY: is_object 保证指针非空且对象本 session 存活。 + let options_obj = unsafe { &*options_ptr }; + let si = vm.kernel_core().perm_interner().intern("maxByteLength").0; + let max_val = match vm.ordinary_get(options_obj, si, options) { + Ok(v) => v, + Err(err) => return NativeResult::Err(crate::iterator::engine_error(vm, &err)), + }; + if max_val.is_undefined() { + None + } else { + Some(native_try!(to_index(vm, max_val))) + } + } + } else { + None + }; + if let Some(max) = max_opt { + if length > max { + return NativeResult::Err(crate::error::create_range_error(vm, "invalid SharedArrayBuffer length")); + } } // GetPrototypeFromConstructor:prototype getter 可抛错,异常原值传播; // 结果非对象时回落默认原型。 @@ -282,7 +318,12 @@ pub fn shared_array_buffer_constructor(vm: &mut H, args: &[u8]) -> Na } else { default_proto }; - let obj_ptr = new_shared_array_buffer(vm, vec![0; length], proto); + // 分配期上界:length 与 max 超引擎上限一律拒绝(ToIndex 阶段不校验)。 + if length > MAX_ARRAY_BUFFER_LENGTH || max_opt.is_some_and(|m| m > MAX_ARRAY_BUFFER_LENGTH) { + return NativeResult::Err(crate::error::create_range_error(vm, "invalid SharedArrayBuffer length")); + } + // 存储态上限 = 请求上限 + 1(上限 0 与定长可分;定长存 0)。 + let obj_ptr = new_shared_array_buffer(vm, vec![0; length], max_opt.map_or(0, |m| m + 1), proto); NativeResult::Ok(JsValue::from_js_object(obj_ptr)) } @@ -328,23 +369,78 @@ pub fn shared_array_buffer_byte_length(vm: &mut H, args: &[u8]) -> Na NativeResult::Ok(JsValue::int(len as i32)) } -/// `SharedArrayBuffer.prototype.growable` getter:定长臂恒 false -/// (载荷存储态上限 0;growable 真值臂归后续面)。 +/// `SharedArrayBuffer.prototype.growable` getter:返回缓冲区是否 growable +/// (载荷存储态上限非 0:0 = 定长,非 0 = 请求上限 + 1)。 /// 非 SAB receiver 经品牌校验抛 TypeError(RequireInternalSlot 同语义)。 pub fn shared_array_buffer_growable(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); - native_try!(shared_array_buffer_payload(vm, this_val)); - NativeResult::Ok(JsValue::bool(false)) + let payload_ptr = native_try!(shared_array_buffer_payload(vm, this_val)); + // SAFETY: payload_ptr 经 shared_array_buffer_payload 校验为合法 SAB 载荷。 + NativeResult::Ok(JsValue::bool(unsafe { &*payload_ptr }.max_byte_length != 0)) } -/// `SharedArrayBuffer.prototype.maxByteLength` getter:非 growable -/// (载荷存储态上限 0)返回当前字节数;growable 臂归后续面。 +/// `SharedArrayBuffer.prototype.maxByteLength` getter:定长(存储态上限 0) +/// 返回当前字节数;growable 返回真实上限(存储态 − 1 解码)。SAB 无 +/// detached 臂(载荷 `data` 恒在场)。 pub fn shared_array_buffer_max_byte_length(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let payload_ptr = native_try!(shared_array_buffer_payload(vm, this_val)); // SAFETY: payload_ptr 经 shared_array_buffer_payload 校验为合法 SAB 载荷。 let payload = unsafe { &*payload_ptr }; - NativeResult::Ok(JsValue::int(payload.data.as_ref().map_or(0, |d| d.len()) as i32)) + let value = if payload.max_byte_length == 0 { + payload.data.as_ref().map_or(0, |d| d.len()) + } else { + payload.max_byte_length - 1 + }; + NativeResult::Ok(JsValue::int(value as i32)) +} + +/// `SharedArrayBuffer.prototype.grow(newLength)`:growable 缓冲区原地增长到 +/// newLength,前缀保留、增补零填;只增不缩(缩为 RangeError)。 +/// +/// # 步骤 +/// 1. this 品牌校验(非对象/非 SAB → TypeError)。 +/// 2. 非 growable(存储态上限 0)→ TypeError,先于强转。 +/// 3. newByteLength = ToIntegerOrInfinity(newLength) 传播式(NaN → 0, +/// ±Infinity 保留)。 +/// 4. 重取 this 寄存器 + 载荷指针(强转 JS 调用窗可晋升——AB resize 同式; +/// SAB 无 detach 面,仅晋升面)。 +/// 5. 重读存储态上限 0 → TypeError。 +/// 6. newByteLength < 0 或 > 真实上限(重读存储态 − 1)或 < 当前字节数 +/// (grow-only)→ RangeError。 +/// 7. `Vec::resize` 一次调用收口目标长度。 +/// 8. 返回 undefined。 +pub fn shared_array_buffer_grow(vm: &mut H, args: &[u8]) -> NativeResult { + let this_reg = if args.is_empty() { 0 } else { args[0] }; + let this_val = vm.reg(this_reg); + let payload_ptr = native_try!(shared_array_buffer_payload(vm, this_val)); + // SAFETY: payload_ptr 经 shared_array_buffer_payload 校验为合法 SAB 载荷; + // 状态位拷出后本次借用即结束,不跨 JS 调用点。 + if unsafe { &*payload_ptr }.max_byte_length == 0 { + return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer is not growable")); + } + let new_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let new_length = native_try!(to_integer_or_infinity(vm, new_val)); + // 强转调用可能已把本缓冲区晋升进 session:接收者寄存器与载荷指针均须 + // 重取重检,不得消费求值前快照。 + let this_val = vm.reg(this_reg); + let payload_ptr = native_try!(shared_array_buffer_payload(vm, this_val)); + // SAFETY: 重取的 payload_ptr 指向存活载荷盒。 + let payload = unsafe { &mut *payload_ptr }; + if payload.max_byte_length == 0 { + return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer is not growable")); + } + let Some(data) = payload.data.as_mut() else { + return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer internal state invalid")); + }; + // 存储态上限 +1 编码:真实上限 = max - 1(上限 0 的缓冲区只许 grow(0)); + // 当前长度之下拒缩。 + let real_max = payload.max_byte_length - 1; + if new_length < 0.0 || new_length > real_max as f64 || new_length < data.len() as f64 { + return NativeResult::Err(crate::error::create_range_error(vm, "invalid SharedArrayBuffer length")); + } + data.resize(new_length as usize, 0); + NativeResult::Ok(JsValue::undefined()) } /// 读入口:校验 receiver 为 ArrayBuffer 并取载荷指针。detach(载荷 `data` 为 diff --git a/crates/oxide_vm/src/bindings/bind_shared_array_buffer.rs b/crates/oxide_vm/src/bindings/bind_shared_array_buffer.rs index 8a177f03..6825bf28 100644 --- a/crates/oxide_vm/src/bindings/bind_shared_array_buffer.rs +++ b/crates/oxide_vm/src/bindings/bind_shared_array_buffer.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use crate::bind_constructor; -use crate::bindings::{bind_accessor_getter, configure_native_constructor}; +use crate::bindings::{apply_binding_table, bind_accessor_getter, configure_native_constructor}; use oxide_kernel::kernel::{KernelCore, KernelSession}; use oxide_types::object::JsObject; @@ -18,8 +18,20 @@ pub fn bind_shared_array_buffer(core: &Arc, session: &KernelSession, 1, ); - // SAB 原型当前无静态方法(grow/growable 臂与 slice 归后续面); - // byteLength/maxByteLength 两枚访问器(set 恒 undefined)读载荷字节数。 + // grow 方法(slice 归后续面):growable 缓冲区原地增长,只增不缩。 + apply_binding_table( + session.builtin_world(), + proto, + core, + &[( + "grow", + oxide_builtins::array_buffer::shared_array_buffer_grow:: as *const (), + 1, + )], + ); + + // byteLength/maxByteLength 两枚访问器(set 恒 undefined)读载荷字节数 / + // 存储态上限,品牌校验抛 TypeError(proto 自身访问即抛)。 bind_accessor_getter( core, session, @@ -36,7 +48,7 @@ pub fn bind_shared_array_buffer(core: &Arc, session: &KernelSession, oxide_builtins::array_buffer::shared_array_buffer_max_byte_length:: as *const (), ); - // growable 定长臂:恒 false,品牌校验抛 TypeError(proto 自身访问即抛)。 + // growable 真值臂:读载荷存储态上限(0 定长 false / 非 0 growable true)。 bind_accessor_getter( core, session, diff --git a/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs b/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs index 5ab72092..bcbdfe92 100644 --- a/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs +++ b/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs @@ -1303,6 +1303,177 @@ fn sab_full_reset_rebinds() { assert_eq!(text, "true|true|true|true"); } +/// SAB 构造器 options 真值臂钉:maxByteLength 在场 → growable true 且 +/// maxByteLength 回请求上限;undefined / 非对象 options → 定长。 +#[test] +fn sab_constructor_options_growable_and_fixed() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { \ + var g = new SharedArrayBuffer(4, { maxByteLength: 5 }); \ + var u = new SharedArrayBuffer(4, { maxByteLength: undefined }); \ + var n = new SharedArrayBuffer(0, 9); \ + return g.growable === true && g.maxByteLength === 5 \ + && u.growable === false && u.maxByteLength === 4 \ + && n.growable === false && n.maxByteLength === 0; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB options 抛形钉:length > max / max 负值 / max 2^53−1(引擎上界) +/// 均 RangeError;options getter 抛错原值传播。 +#[test] +fn sab_constructor_options_errors() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { \ + var t1 = false; \ + try { new SharedArrayBuffer(1, { maxByteLength: 0 }); } catch (e) { t1 = e instanceof RangeError; } \ + var t2 = false; \ + try { new SharedArrayBuffer(0, { maxByteLength: -1 }); } catch (e) { t2 = e instanceof RangeError; } \ + var t3 = false; \ + try { new SharedArrayBuffer(0, { maxByteLength: 9007199254740991 }); } catch (e) { t3 = e instanceof RangeError; } \ + function Test262Error() {} \ + var t4 = false; \ + var options = { get maxByteLength() { throw new Test262Error(); } }; \ + try { new SharedArrayBuffer(0, options); } catch (e) { t4 = e instanceof Test262Error; } \ + return t1 && t2 && t3 && t4; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB length>max 先于对象创建钉:max 在场且 length 超限时 RangeError 先出, +/// 抛错 prototype getter 不得先出。 +#[test] +fn sab_constructor_length_gt_max_before_gpfc() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { \ + function Test262Error() {} \ + var newTarget = function() {}.bind(null); \ + Object.defineProperty(newTarget, 'prototype', { get: function () { throw new Test262Error(); } }); \ + var t = false; \ + try { Reflect.construct(SharedArrayBuffer, [10, { maxByteLength: 0 }], newTarget); } catch (e) { t = e instanceof RangeError; } \ + return t; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB 分配期上界移序钉:合法 length/max 下抛错 prototype getter 先出; +/// 7 PiB length(超引擎上界)同样 prototype getter 先出(上界后移于原型读)。 +#[test] +fn sab_constructor_gpfc_before_allocation_limit() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { \ + function DummyError() {} \ + var newTarget = function() {}.bind(null); \ + Object.defineProperty(newTarget, 'prototype', { get: function () { throw new DummyError(); } }); \ + var t1 = false; \ + try { Reflect.construct(SharedArrayBuffer, [8, { maxByteLength: 16 }], newTarget); } catch (e) { t1 = e instanceof DummyError; } \ + var t2 = false; \ + try { Reflect.construct(SharedArrayBuffer, [7 * 1125899906842624], newTarget); } catch (e) { t2 = e instanceof DummyError; } \ + return t1 && t2; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB grow 守卫序钉:品牌 / 非 growable / receiver 类型均 TypeError; +/// growable 界判 grow-only(超上限 / 负值 / 缩长均 RangeError)。 +#[test] +fn sab_grow_guard_order() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { \ + var fixed = new SharedArrayBuffer(4); \ + var t1 = false; \ + try { fixed.grow(5); } catch (e) { t1 = e instanceof TypeError; } \ + var t2 = false; \ + try { SharedArrayBuffer.prototype.grow.call(new ArrayBuffer(4)); } catch (e) { t2 = e instanceof TypeError; } \ + var t3 = false; \ + try { SharedArrayBuffer.prototype.grow.call({}); } catch (e) { t3 = e instanceof TypeError; } \ + var g = new SharedArrayBuffer(4, { maxByteLength: 5 }); \ + var t4 = false; \ + try { g.grow(6); } catch (e) { t4 = e instanceof RangeError; } \ + var t5 = false; \ + try { g.grow(-1); } catch (e) { t5 = e instanceof RangeError; } \ + var t6 = false; \ + try { g.grow(3); } catch (e) { t6 = e instanceof RangeError; } \ + return t1 && t2 && t3 && t4 && t5 && t6; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB grow 原地语义钉:grow(上限) 后 byteLength 更新且回 undefined; +/// 同长 no-op;零长 growable 缓冲 grow(0)/grow(上限) 均合法。 +#[test] +fn sab_grow_inplace_semantics() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { \ + var g = new SharedArrayBuffer(4, { maxByteLength: 5 }); \ + if (g.grow(5) !== undefined || g.byteLength !== 5) { return false; } \ + if (g.grow(5) !== undefined || g.byteLength !== 5) { return false; } \ + var z = new SharedArrayBuffer(0, { maxByteLength: 4 }); \ + if (z.grow(0) !== undefined || z.byteLength !== 0) { return false; } \ + if (z.grow(4) !== undefined || z.byteLength !== 4) { return false; } \ + return true; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB grow NaN 归零钉:ToIntegerOrInfinity 将 NaN 折为 0——非零长 growable +/// 经 grow-only 界判 RangeError,零长 growable 同长成功(node 20 同形)。 +#[test] +fn sab_grow_nan_folds_to_zero() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { \ + var g = new SharedArrayBuffer(4, { maxByteLength: 5 }); \ + var t1 = false; \ + try { g.grow(NaN); } catch (e) { t1 = e instanceof RangeError; } \ + if (g.byteLength !== 4) { return false; } \ + var z = new SharedArrayBuffer(0, { maxByteLength: 4 }); \ + var r = z.grow(NaN); \ + return t1 && r === undefined && z.byteLength === 0; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB growable / maxByteLength 访问器真值臂钉:growable 双形态、 +/// maxByteLength 0/23/42 三形态与语料同值。 +#[test] +fn sab_growable_and_max_bytelength_truth_arms() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { \ + var s1 = new SharedArrayBuffer(1); \ + var s2 = new SharedArrayBuffer(1, { maxByteLength: 1 }); \ + var m0 = new SharedArrayBuffer(0, { maxByteLength: 0 }); \ + var m23 = new SharedArrayBuffer(0, { maxByteLength: 23 }); \ + var m42 = new SharedArrayBuffer(42, { maxByteLength: 42 }); \ + return s1.growable === false && s2.growable === true \ + && m0.maxByteLength === 0 && m23.maxByteLength === 23 && m42.maxByteLength === 42; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + /// STUBS 表收缩钉:Atomics 移出 stub 后剩 5 枚 stub 全局仍为 function; /// Atomics 现为纯对象(typeof 'object'),对其调用抛 TypeError(非函数)。 #[test]