From 885c67428de6e8cce02469c7447fa844e6514710 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Thu, 24 Sep 2026 00:16:53 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20SAB=20=E5=8E=9F=E5=9E=8B=20slice=20?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E8=AF=AD=E4=B9=89=EF=BC=88species=20?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=20SAB=20=E4=B8=8E=E7=BB=93=E6=9E=9C=E4=B8=89?= =?UTF-8?q?=E6=A3=80=E7=9B=B4=E6=8B=B7=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/array_buffer.rs | 111 +++++++++- .../src/bindings/bind_shared_array_buffer.rs | 20 +- .../tests/builtins_arraybuffer_state_tests.rs | 198 +++++++++++++++++- 3 files changed, 309 insertions(+), 20 deletions(-) diff --git a/crates/oxide_builtins/src/array_buffer.rs b/crates/oxide_builtins/src/array_buffer.rs index c606f5d7..3e697b89 100644 --- a/crates/oxide_builtins/src/array_buffer.rs +++ b/crates/oxide_builtins/src/array_buffer.rs @@ -471,6 +471,97 @@ pub fn shared_array_buffer_grow(vm: &mut H, args: &[u8]) -> NativeRes NativeResult::Ok(JsValue::undefined()) } +/// `SharedArrayBuffer.prototype.slice(start, end)`:复制字节区间生成新 +/// SharedArrayBuffer。 +/// +/// # 步骤 +/// 1. this 品牌校验(非对象/非 SAB → TypeError);len = 活字节长快照 +/// (growable 源取当前长非上限)。 +/// 2. ResolveBounds(start undefined/缺省 → 0、end undefined/缺省 → len, +/// 不调强转)→ newLen = max(final - first, 0)。 +/// 3. SpeciesConstructor(O, %SharedArrayBuffer%)(构造器/@@species 完整 Get, +/// 异常传播;undefined 臂回落 %SharedArrayBuffer%;非对象/非构造器 +/// TypeError)。 +/// 4. Construct(ctor, «newLen») 经 vm.construct_ctor(native 值传递 / bytecode +/// 压构造帧,含 derived 构造器 super() 语义;构造器抛出值原样上抛)。 +/// 5. 构造窗口后重取源寄存器与载荷(窗口内晋升使旧指针悬垂;SAB 无 detach +/// 生产路径,无重检臂)。 +/// 6. 结果三检:SAB 品牌槽 → SameValue(new, O) → 活长 ≥ newLen,均 +/// TypeError(SAB 无 detached/immutable 检项)。 +/// 7. 直拷 newLen 字节入结果前区。 +/// +/// # 边界与前提 +/// - SAB grow-only(grow 只增不缩):步 1 快照长 ≤ 拷贝时活长, +/// first..first+newLen 恒在界,直拷无需活长夹取。 +/// - 源对象经寄存器根保活;每次 JS 调用窗口后重新读接收者寄存器与载荷指针。 +pub fn shared_array_buffer_slice(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 调用窗口。 + let len = unsafe { &*payload_ptr }.data.as_ref().map_or(0, |d| d.len()); + let start = if args.len() > 1 { Some(vm.reg(args[1])) } else { None }; + let end = if args.len() > 2 { Some(vm.reg(args[2])) } else { None }; + let (first, final_) = native_try!(ab_resolve_bounds(vm, len, start, end)); + let new_len = final_.saturating_sub(first); + + // 物种读与构造各是 JS 调用窗口:getter/构造器可晋升源。 + let default_ctor = + JsValue::from_js_object(vm.session().builtin_world().shared_array_buffer_constructor.as_ptr() as *mut JsObject); + let ctor = native_try!(ab_species_constructor(vm, this_val, default_ctor)); + let new_val = match vm.construct_ctor(ctor, &[JsValue::int(new_len as i32)]) { + Ok(v) => v, + Err(err) => return NativeResult::Err(err), + }; + + // 构造窗口后重取接收者与载荷(源对象可被晋升进 session,旧指针悬垂)。 + let this_val = vm.reg(this_reg); + let payload_ptr = native_try!(shared_array_buffer_payload(vm, this_val)); + // SAFETY: 重取的 payload_ptr 指向存活载荷盒;字节借出止于拷贝语句。 + let data = unsafe { &*payload_ptr }.data.as_ref(); + // 结果三检:SAB 品牌槽 → SameValue → 活长。 + if !new_val.is_object() { + return NativeResult::Err(crate::error::create_type_error( + vm, + "SharedArrayBuffer slice result is not a SharedArrayBuffer", + )); + } + // SAFETY: new_val 为对象且指针非空,对象本 session 存活。 + let Some(new_payload) = shared_array_buffer_payload_ptr(unsafe { &*new_val.as_js_object_ptr() }) else { + return NativeResult::Err(crate::error::create_type_error( + vm, + "SharedArrayBuffer slice result is not a SharedArrayBuffer", + )); + }; + if new_payload.is_null() { + return NativeResult::Err(crate::error::create_type_error( + vm, + "SharedArrayBuffer slice result is not a SharedArrayBuffer", + )); + } + if new_val == this_val { + return NativeResult::Err(crate::error::create_type_error( + vm, + "SharedArrayBuffer slice result must not be the source buffer", + )); + } + // SAFETY: new_payload 经 shared_array_buffer_payload_ptr 校验为 SAB 载荷盒。 + if unsafe { &*new_payload }.data.as_ref().map_or(0, |d| d.len()) < new_len { + return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer slice result is too small")); + } + let Some(data) = data else { + return NativeResult::Err(crate::error::create_type_error(vm, "SharedArrayBuffer internal state invalid")); + }; + // 直拷 newLen 字节(grow-only 恒在界,见 边界与前提)。 + // SAFETY: new_payload 经 shared_array_buffer_payload_ptr 校验;SAB 无 + // detach 臂,data 必为 Some,可变借用止于本语句。 + if let Some(dest) = unsafe { (*new_payload).data.as_mut() } { + dest[..new_len].copy_from_slice(&data[first..first + new_len]); + } + NativeResult::Ok(new_val) +} + /// 读入口:校验 receiver 为 ArrayBuffer 并取载荷指针。detach(载荷 `data` 为 /// `None`)不在此分叉,由消费方现读 `data` 时按既有错误形态处理。 pub(crate) fn array_buffer_payload( @@ -739,24 +830,22 @@ fn ab_resolve_bounds( Ok((first, final_)) } -/// SpeciesConstructor(O, %ArrayBuffer%):完整 Get O.constructor(访问器/异常 -/// 传播);undefined 回落 %ArrayBuffer%,其余非对象抛 TypeError;完整 Get -/// C[Symbol.species](null 归一为 undefined);undefined 回落 %ArrayBuffer%, -/// 非构造器抛 TypeError。 -fn ab_species_constructor(vm: &mut H, o_val: JsValue) -> Result { +/// SpeciesConstructor(O, default_ctor):完整 Get O.constructor(访问器/异常 +/// 传播);undefined 回落调用方给定的默认构造器,其余非对象抛 TypeError; +/// 完整 Get C[Symbol.species](null 归一为 undefined);undefined 回落默认 +/// 构造器,非构造器抛 TypeError。 +fn ab_species_constructor(vm: &mut H, o_val: JsValue, default_ctor: JsValue) -> Result { let o_obj = unsafe { &*o_val.as_js_object_ptr() }; let ctor_key = vm.kernel_core().perm_interner().intern("constructor").0; let c = match vm.ordinary_get(o_obj, ctor_key, o_val) { Ok(v) => v, Err(msg) => return Err(crate::iterator::engine_error(vm, &msg)), }; - let default_ctor = - JsValue::from_js_object(vm.session().builtin_world().array_buffer_constructor.as_ptr() as *mut JsObject); if c.is_undefined() { return Ok(default_ctor); } if !c.is_object() { - return Err(crate::error::create_type_error(vm, "ArrayBuffer constructor is not an object")); + return Err(crate::error::create_type_error(vm, "buffer constructor is not an object")); } let species_key = make_well_known_symbol_key(WELL_KNOWN_SYMBOL_SPECIES); let s = match vm.ordinary_get(unsafe { &*c.as_js_object_ptr() }, species_key, c) { @@ -767,7 +856,7 @@ fn ab_species_constructor(vm: &mut H, o_val: JsValue) -> Result(vm: &mut H, args: &[u8]) -> NativeResult { let new_len = final_.saturating_sub(first); // 物种读与构造各是 JS 调用窗口:getter/构造器可 detach、resize、晋升源。 - let ctor = native_try!(ab_species_constructor(vm, this_val)); + let default_ctor = + JsValue::from_js_object(vm.session().builtin_world().array_buffer_constructor.as_ptr() as *mut JsObject); + let ctor = native_try!(ab_species_constructor(vm, this_val, default_ctor)); let new_val = match vm.construct_ctor(ctor, &[JsValue::int(new_len as i32)]) { Ok(v) => v, Err(err) => return NativeResult::Err(err), 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 6825bf28..9b9e2761 100644 --- a/crates/oxide_vm/src/bindings/bind_shared_array_buffer.rs +++ b/crates/oxide_vm/src/bindings/bind_shared_array_buffer.rs @@ -18,16 +18,24 @@ pub fn bind_shared_array_buffer(core: &Arc, session: &KernelSession, 1, ); - // grow 方法(slice 归后续面):growable 缓冲区原地增长,只增不缩。 + // grow/slice 方法:grow 原地增长(只增不缩);slice 复制字节区间生成 + // 新 SAB。 apply_binding_table( session.builtin_world(), proto, core, - &[( - "grow", - oxide_builtins::array_buffer::shared_array_buffer_grow:: as *const (), - 1, - )], + &[ + ( + "grow", + oxide_builtins::array_buffer::shared_array_buffer_grow:: as *const (), + 1, + ), + ( + "slice", + oxide_builtins::array_buffer::shared_array_buffer_slice:: as *const (), + 2, + ), + ], ); // byteLength/maxByteLength 两枚访问器(set 恒 undefined)读载荷字节数 / diff --git a/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs b/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs index bcbdfe92..dc38610f 100644 --- a/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs +++ b/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs @@ -1486,10 +1486,200 @@ fn sab_stubs_shrunk_five_remain() { for (var i = 0; i < names.length; i++) { \ if (typeof globalThis[names[i]] !== 'function') { return false; } \ } \ - if (typeof globalThis.Atomics !== 'object') { return false; } \ - var threw = false; \ - try { globalThis.Atomics(); } catch (e) { threw = e instanceof TypeError; } \ - return threw; })()", + if (typeof globalThis.Atomics !== 'object') { return false; } \ + var threw = false; \ + try { globalThis.Atomics(); } catch (e) { threw = e instanceof TypeError; } \ + return threw; })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB slice 窗口钉:全窗/前缀/后缀/负 start/负 end/start 越界/end 越界/ +/// start 超 end/空窗九形长度,产物恒 SAB 品牌且定长。 +#[test] +fn sab_slice_window_pins() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var sab = new SharedArrayBuffer(8); \ + var chk = function (s) { var r = sab.slice(s[0], s[1]); \ + return r instanceof SharedArrayBuffer && r.growable === false ? r.byteLength : -1; }; \ + return chk([undefined, undefined]) === 8 && chk([0, 3]) === 3 && chk([6, undefined]) === 2 \ + && chk([-2]) === 2 && chk([0, -2]) === 6 && chk([20]) === 0 \ + && chk([2, 20]) === 6 && chk([5, 2]) === 0 && chk([3, 3]) === 0 })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB slice 内容钉:[1..8] 源 slice(2,6) → [3,4,5,6],产物独立于源。 +#[test] +fn sab_slice_content_copy() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var sab = new SharedArrayBuffer(8); \ + var v = new Uint8Array(sab); \ + for (var i = 0; i < 8; i++) v[i] = i + 1; \ + var d = new Uint8Array(sab.slice(2, 6)); \ + return d.length === 4 && d[0] === 3 && d[1] === 4 && d[2] === 5 && d[3] === 6 \ + && v[0] === 1 })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB slice 物种回落三形钉:constructor undefined / species undefined / +/// species null → 默认构造器 %SharedArrayBuffer%(proto === SAB.prototype) +/// 且内容正确。 +#[test] +fn sab_slice_species_default_fallbacks() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { \ + var s1 = new SharedArrayBuffer(8); s1.constructor = undefined; \ + var r1 = s1.slice(); \ + var c2 = {}; var s2 = new SharedArrayBuffer(8); s2.constructor = c2; \ + var r2 = s2.slice(); \ + var c3 = {}; c3[Symbol.species] = null; \ + var s3 = new SharedArrayBuffer(8); s3.constructor = c3; \ + var r3 = s3.slice(); \ + var v = new Uint8Array(s1); v[0] = 7; \ + var d = new Uint8Array(s1.slice()); \ + return Object.getPrototypeOf(r1) === SharedArrayBuffer.prototype && r1.byteLength === 8 \ + && Object.getPrototypeOf(r2) === SharedArrayBuffer.prototype \ + && Object.getPrototypeOf(r3) === SharedArrayBuffer.prototype \ + && d[0] === 7 })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB slice 物种交付钉:构造器实参 «8» 捕获、返回自建 SAB → 交付原值(同一 +/// 对象)且内容拷贝入产物。 +#[test] +fn sab_slice_species_custom_returns() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var calls = []; var made = null; var c = {}; \ + c[Symbol.species] = function (len) { calls.push(len); made = new SharedArrayBuffer(8); return made; }; \ + var sab = new SharedArrayBuffer(8); \ + var v = new Uint8Array(sab); \ + for (var i = 0; i < 8; i++) v[i] = i + 1; \ + sab.constructor = c; \ + var r = sab.slice(); \ + var d = new Uint8Array(r); \ + return r === made && r.byteLength === 8 && calls.join(',') === '8' \ + && d[0] === 1 && d[7] === 8 })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB slice 物种 TypeError 五形钉:constructor null/true/\"\"、species +/// 对象/Function.prototype 均 TypeError。 +#[test] +fn sab_slice_species_type_error_family() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { function ty(fn) { try { fn(); return false; } catch (e) { return e instanceof TypeError; } } \ + var sab = new SharedArrayBuffer(8); \ + sab.constructor = null; var t1 = ty(function () { sab.slice(); }); \ + sab.constructor = true; var t2 = ty(function () { sab.slice(); }); \ + sab.constructor = ''; var t3 = ty(function () { sab.slice(); }); \ + var c = {}; sab.constructor = c; \ + c[Symbol.species] = {}; var t4 = ty(function () { sab.slice(); }); \ + c[Symbol.species] = Function.prototype; var t5 = ty(function () { sab.slice(); }); \ + return t1 && t2 && t3 && t4 && t5 })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB slice 结果三检前四形钉:物种返回非 SAB / 返回源自身 / 返回短 SAB → +/// TypeError;返回长 SAB → 交付(内容只填前缀)。 +#[test] +fn sab_slice_species_result_checks() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { function ty(fn) { try { fn(); return false; } catch (e) { return e instanceof TypeError; } } \ + var a1 = new SharedArrayBuffer(8); var c1 = {}; \ + c1[Symbol.species] = function () { return {}; }; a1.constructor = c1; \ + var t1 = ty(function () { a1.slice(); }); \ + var a2 = new SharedArrayBuffer(8); var c2 = {}; \ + c2[Symbol.species] = function () { return a2; }; a2.constructor = c2; \ + var t2 = ty(function () { a2.slice(); }); \ + var a3 = new SharedArrayBuffer(8); var c3 = {}; \ + c3[Symbol.species] = function () { return new SharedArrayBuffer(4); }; a3.constructor = c3; \ + var t3 = ty(function () { a3.slice(); }); \ + var a4 = new SharedArrayBuffer(8); var v = new Uint8Array(a4); v[0] = 5; \ + var c4 = {}; c4[Symbol.species] = function () { return new SharedArrayBuffer(10); }; a4.constructor = c4; \ + var r4 = a4.slice(); var d4 = new Uint8Array(r4); \ + return t1 && t2 && t3 && r4.byteLength === 10 && d4[0] === 5 && d4[8] === 0 })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB slice growable 源窗钉:窗取活长非上限(slice(0,6) 活长 4 → 4); +/// grow 后 end 缺省按新活长(slice(6) → 2);构造窗内 grow 不改变已定窗 +/// (结果长 = newLen)。 +#[test] +fn sab_slice_growable_source_window() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { \ + var g = new SharedArrayBuffer(4, { maxByteLength: 8 }); \ + if (g.slice(0, 6).byteLength !== 4) { return false; } \ + g.grow(8); \ + if (g.slice(6).byteLength !== 2) { return false; } \ + var src = new SharedArrayBuffer(4, { maxByteLength: 8 }); \ + var c = {}; \ + c[Symbol.species] = function (len) { src.grow(8); return new SharedArrayBuffer(len); }; \ + src.constructor = c; \ + return src.slice().byteLength === 4 })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB slice 品牌两臂钉:slice.call(undefined) / call({}) / +/// call(ArrayBuffer) 均 TypeError(AB 标签非 SAB)。 +#[test] +fn sab_slice_brand_family() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { function ty(fn) { try { fn(); return false; } catch (e) { return e instanceof TypeError; } } \ + var t1 = ty(function () { SharedArrayBuffer.prototype.slice.call(undefined); }); \ + var t2 = ty(function () { SharedArrayBuffer.prototype.slice.call({}); }); \ + var t3 = ty(function () { SharedArrayBuffer.prototype.slice.call(new ArrayBuffer(8)); }); \ + return t1 && t2 && t3 })()", + ) + .unwrap(); + assert!(result.as_bool()); +} + +/// SAB slice meta 钉:描述符 {w:1,e:0,c:1}、length 2、name 'slice'、 +/// nonconstructor(new 形态 TypeError)。 +#[test] +fn sab_slice_meta_pins() { + let mut vm = Vm::new(); + let result = eval( + &mut vm, + "(function () { var d = Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'slice'); \ + if (!(typeof d.value === 'function' && d.writable === true \ + && d.enumerable === false && d.configurable === true)) { return false; } \ + if (d.value.length !== 2 || d.value.name !== 'slice') { return false; } \ + var threw = false; \ + try { new SharedArrayBuffer.prototype.slice(); } catch (e) { threw = e instanceof TypeError; } \ + return threw })()", ) .unwrap(); assert!(result.as_bool());