mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: SAB 原型 slice 完整语义(species 默认 SAB 与结果三检直拷)
This commit is contained in:
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user