feat: SAB 原型 slice 完整语义(species 默认 SAB 与结果三检直拷)

This commit is contained in:
2026-09-24 00:16:53 +00:00
parent 8e52a4688e
commit 885c67428d
3 changed files with 309 additions and 20 deletions
+101 -10
View File
@@ -471,6 +471,97 @@ pub fn shared_array_buffer_grow<H: VmHost>(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<H: VmHost>(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<H: VmHost>(
@@ -739,24 +830,22 @@ fn ab_resolve_bounds<H: VmHost>(
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<H: VmHost>(vm: &mut H, o_val: JsValue) -> Result<JsValue, JsValue> {
/// SpeciesConstructor(O, default_ctor):完整 Get O.constructor(访问器/异常
/// 传播);undefined 回落调用方给定的默认构造器,其余非对象抛 TypeError;
/// 完整 Get C[Symbol.species](null 归一为 undefined);undefined 回落默认
/// 构造器,非构造器抛 TypeError。
fn ab_species_constructor<H: VmHost>(vm: &mut H, o_val: JsValue, default_ctor: JsValue) -> Result<JsValue, JsValue> {
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<H: VmHost>(vm: &mut H, o_val: JsValue) -> Result<JsVal
if s.is_undefined() {
Ok(default_ctor)
} else if !crate::array::is_constructor_value(s) {
Err(crate::error::create_type_error(vm, "ArrayBuffer species is not a constructor"))
Err(crate::error::create_type_error(vm, "buffer species is not a constructor"))
} else {
Ok(s)
}
@@ -809,7 +898,9 @@ pub fn array_buffer_slice<H: VmHost>(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),
@@ -18,16 +18,24 @@ pub fn bind_shared_array_buffer(core: &Arc<KernelCore>, 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::<crate::vm::Vm> as *const (),
1,
)],
&[
(
"grow",
oxide_builtins::array_buffer::shared_array_buffer_grow::<crate::vm::Vm> as *const (),
1,
),
(
"slice",
oxide_builtins::array_buffer::shared_array_buffer_slice::<crate::vm::Vm> as *const (),
2,
),
],
);
// byteLength/maxByteLength 两枚访问器(set 恒 undefined)读载荷字节数 /
@@ -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());