mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: ArrayBuffer sliceToImmutable 方法全语义落地
This commit is contained in:
@@ -560,6 +560,82 @@ pub fn array_buffer_slice<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
NativeResult::Ok(new_val)
|
NativeResult::Ok(new_val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `ArrayBuffer.prototype.sliceToImmutable(start, end)`:复制字节区间生成
|
||||||
|
/// immutable 新 ArrayBuffer,源缓冲不 detach、不改任何状态位。
|
||||||
|
///
|
||||||
|
/// # 步骤
|
||||||
|
/// 1. 品牌守卫与 detached 源守卫先于参数读(抛错时参数无副作用)。
|
||||||
|
/// 2. len 快照 → ResolveBounds(与 slice 共享助手)→ newLen。
|
||||||
|
/// 3. 强转窗口后重取源载荷重检:detached → TypeError(先于长度终检);
|
||||||
|
/// currentLen < final → RangeError(源中途收缩到解析界之下)。
|
||||||
|
/// 4. GetPrototypeFromConstructor(%ArrayBuffer%):完整读构造器 "prototype"
|
||||||
|
/// (访问器抛错传播,非对象回落 %ArrayBuffer.prototype%)。
|
||||||
|
/// 5. proto 读窗口后再次重取源载荷(窗口内源可 detach/收缩/晋升),
|
||||||
|
/// 重检 detached 与在界后拷贝前 newLen 字节(恒在界内),零填充分配,
|
||||||
|
/// 后置 immutable 标志;源不动。
|
||||||
|
///
|
||||||
|
/// # 边界与前提
|
||||||
|
/// - 源 immutable 是合法输入(只读拷贝);产物恒 immutable、恒定长。
|
||||||
|
/// - 无物种面:构造器恒 %ArrayBuffer%。
|
||||||
|
pub fn array_buffer_slice_to_immutable<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!(array_buffer_payload(vm, this_val));
|
||||||
|
// SAFETY: payload_ptr 经 array_buffer_payload 校验为合法 ArrayBuffer 载荷;
|
||||||
|
// 标量拷出后借用即结束,不跨 JS 调用窗口。
|
||||||
|
let Some(data) = unsafe { &*payload_ptr }.data.as_ref() else {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||||||
|
};
|
||||||
|
let len = data.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);
|
||||||
|
|
||||||
|
// 强转窗口可 detach/晋升源:重取寄存器与载荷后重检(先于长度终检)。
|
||||||
|
let this_val = vm.reg(this_reg);
|
||||||
|
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||||||
|
// SAFETY: 重取的 payload_ptr 指向存活载荷盒;标量拷出后借用即结束。
|
||||||
|
let Some(data) = unsafe { &*payload_ptr }.data.as_ref() else {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||||||
|
};
|
||||||
|
if data.len() < final_ {
|
||||||
|
return NativeResult::Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length"));
|
||||||
|
}
|
||||||
|
// GetPrototypeFromConstructor(%ArrayBuffer%):prototype 读为 JS 窗口
|
||||||
|
// (访问器可被用户重定义),抛错传播;非对象回落默认原型。
|
||||||
|
let ctor_val =
|
||||||
|
JsValue::from_js_object(vm.session().builtin_world().array_buffer_constructor.as_ptr() as *mut JsObject);
|
||||||
|
let ctor_obj = unsafe { &*ctor_val.as_js_object_ptr() };
|
||||||
|
let proto_si = vm.kernel_core().perm_interner().intern("prototype").0;
|
||||||
|
let proto_val = match vm.ordinary_get(ctor_obj, proto_si, ctor_val) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(msg) => return NativeResult::Err(crate::iterator::engine_error(vm, &msg)),
|
||||||
|
};
|
||||||
|
let proto = if proto_val.is_object() { proto_val } else { default_array_buffer_proto(vm) };
|
||||||
|
// proto 读窗口可 detach/收缩/晋升源:再重取载荷重检 detached 与在界,
|
||||||
|
// 拷贝恒在界内(currentLen ≥ final ≥ first + newLen)。
|
||||||
|
let this_val = vm.reg(this_reg);
|
||||||
|
let payload_ptr = native_try!(array_buffer_payload(vm, this_val));
|
||||||
|
// SAFETY: 重取的 payload_ptr 指向存活载荷盒;字节借出止于本语句。
|
||||||
|
let Some(data) = unsafe { &*payload_ptr }.data.as_ref() else {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
|
||||||
|
};
|
||||||
|
if data.len() < final_ {
|
||||||
|
return NativeResult::Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length"));
|
||||||
|
}
|
||||||
|
let out: Vec<u8> = data[first..first + new_len].to_vec();
|
||||||
|
let dest_ptr = new_array_buffer(vm, out, 0, proto);
|
||||||
|
// SAFETY: dest_ptr 为 alloc_object 新建对象,载荷盒由 new_array_buffer 建。
|
||||||
|
let Some(dest_payload) = array_buffer_payload_ptr(unsafe { &*dest_ptr }) else {
|
||||||
|
return type_error(vm, "ArrayBuffer internal state invalid");
|
||||||
|
};
|
||||||
|
// SAFETY: dest_payload 指向存活载荷盒;immutable 后置位与 transfer-Immutable
|
||||||
|
// 同形,不扩 new_array_buffer 签名。
|
||||||
|
unsafe { (*dest_payload).immutable = true };
|
||||||
|
NativeResult::Ok(JsValue::from_js_object(dest_ptr))
|
||||||
|
}
|
||||||
|
|
||||||
/// transfer 族新缓冲保持性:Preserve 源存储态上限原样拷贝,Fixed 定长,
|
/// transfer 族新缓冲保持性:Preserve 源存储态上限原样拷贝,Fixed 定长,
|
||||||
/// Immutable 定长并置 immutable 标志。
|
/// Immutable 定长并置 immutable 标志。
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
@@ -919,6 +995,25 @@ mod tests {
|
|||||||
assert!(r.as_bool());
|
assert!(r.as_bool());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// sliceToImmutable 源不 detach 钉:产物 immutable,源返回后可写/可
|
||||||
|
/// resize/可 detach,产物内容独立于源。
|
||||||
|
#[test]
|
||||||
|
fn sti_source_writable_after_return() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let r = eval_ab(
|
||||||
|
&mut vm,
|
||||||
|
"(function () { var ab = new ArrayBuffer(8); \
|
||||||
|
var v = new Uint8Array(ab); for (var i = 0; i < 8; i++) v[i] = i + 1; \
|
||||||
|
var dest = ab.sliceToImmutable(); \
|
||||||
|
var ok = dest.immutable === true && dest.resizable === false && ab.detached === false; \
|
||||||
|
v[0] = 86; \
|
||||||
|
var v2 = new Uint8Array(dest); \
|
||||||
|
return ok && v2[0] === 1 && v2[7] === 8 && ab.byteLength === 8; })()",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(r.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
/// slice 构造窗口源 detach 重检钉:species 构造器内 detach 源 → 构造后
|
/// slice 构造窗口源 detach 重检钉:species 构造器内 detach 源 → 构造后
|
||||||
/// 重检抛 TypeError(不交付结果)。
|
/// 重检抛 TypeError(不交付结果)。
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ pub fn bind_array_buffer(core: &Arc<KernelCore>, session: &KernelSession, global
|
|||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
("slice", oxide_builtins::array_buffer::array_buffer_slice::<crate::vm::Vm> as *const (), 2),
|
("slice", oxide_builtins::array_buffer::array_buffer_slice::<crate::vm::Vm> as *const (), 2),
|
||||||
|
(
|
||||||
|
"sliceToImmutable",
|
||||||
|
oxide_builtins::array_buffer::array_buffer_slice_to_immutable::<crate::vm::Vm> as *const (),
|
||||||
|
2,
|
||||||
|
),
|
||||||
(
|
(
|
||||||
"toString",
|
"toString",
|
||||||
oxide_builtins::array_buffer::array_buffer_to_string::<crate::vm::Vm> as *const (),
|
oxide_builtins::array_buffer::array_buffer_to_string::<crate::vm::Vm> as *const (),
|
||||||
|
|||||||
@@ -756,7 +756,7 @@ fn ab_transfer_new_length_order_and_coercion() {
|
|||||||
var t1 = false; \
|
var t1 = false; \
|
||||||
try { new ArrayBuffer(0).transfer(nl); } catch (e) { t1 = e instanceof TypeError; } \
|
try { new ArrayBuffer(0).transfer(nl); } catch (e) { t1 = e instanceof TypeError; } \
|
||||||
t1 && log.length === 2 && log[0] === 'valueOf' && log[1] === 'toString' \
|
t1 && log.length === 2 && log[0] === 'valueOf' && log[1] === 'toString' \
|
||||||
&& (function () { var t2 = false; \
|
&& (function () { var t2 = false, t3 = false, e2 = '?'; \
|
||||||
try { new ArrayBuffer(0).transfer(2 ** 53); } catch (e) { t2 = e instanceof RangeError; } \
|
try { new ArrayBuffer(0).transfer(2 ** 53); } catch (e) { t2 = e instanceof RangeError; } \
|
||||||
return t2; })() \
|
return t2; })() \
|
||||||
&& (function () { var ab = new ArrayBuffer(8); var t3 = false; \
|
&& (function () { var ab = new ArrayBuffer(8); var t3 = false; \
|
||||||
@@ -1005,3 +1005,137 @@ fn ab_slice_source_detach_family() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(result.as_bool());
|
assert!(result.as_bool());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// sliceToImmutable 界与缺省钉:(undefined,undefined)/(6,undefined)/负/越界/
|
||||||
|
/// 空窗长度形 + immutable 产物形 + prop-desc 描述符与 name/length。
|
||||||
|
#[test]
|
||||||
|
fn ab_sti_bounds_and_defaults() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"(function () { var ab = new ArrayBuffer(8); \
|
||||||
|
var bl = function (s) { return ab.sliceToImmutable(s[0], s[1]).byteLength; }; \
|
||||||
|
return bl([undefined, undefined]) === 8 && bl([6, undefined]) === 2 \
|
||||||
|
&& bl([6]) === 2 && bl([-2]) === 2 && bl([0, -2]) === 6 \
|
||||||
|
&& bl([20]) === 0 && bl([2, 20]) === 6 && bl([5, 2]) === 0 && bl([3, 3]) === 0 \
|
||||||
|
&& ab.sliceToImmutable().immutable === true \
|
||||||
|
&& ab.sliceToImmutable().resizable === false })()",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"(function () { var d = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'sliceToImmutable'); \
|
||||||
|
return typeof d.value === 'function' && d.value.name === 'sliceToImmutable' \
|
||||||
|
&& d.value.length === 2 && d.writable === true && d.enumerable === false \
|
||||||
|
&& d.configurable === true; })()",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sliceToImmutable 源增缩钉:grows 形产物 [4,5,6] 源 12 长;shrinks 第一
|
||||||
|
/// block 同形源 8 长;resize 到解析界下 → RangeError。
|
||||||
|
#[test]
|
||||||
|
fn ab_sti_grow_shrink_bounds() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"(function () { var source = new ArrayBuffer(10, { maxByteLength: 12 }); \
|
||||||
|
var view = new Uint8Array(source); \
|
||||||
|
for (var i = 0; i < 10; i++) view[i] = i + 1; \
|
||||||
|
var start = { valueOf: function () { source.resize(11); return -7; } }; \
|
||||||
|
var end = { valueOf: function () { source.resize(12); return -4; } }; \
|
||||||
|
var dest = source.sliceToImmutable(start, end); \
|
||||||
|
var dv = new Uint8Array(dest); \
|
||||||
|
var ok = dv.length === 3 && dv[0] === 4 && dv[1] === 5 && dv[2] === 6 && source.byteLength === 12; \
|
||||||
|
var source2 = new ArrayBuffer(10, { maxByteLength: 10 }); \
|
||||||
|
var view2 = new Uint8Array(source2); \
|
||||||
|
for (var j = 0; j < 10; j++) view2[j] = j + 1; \
|
||||||
|
var s2 = { valueOf: function () { source2.resize(9); return -7; } }; \
|
||||||
|
var e2 = { valueOf: function () { source2.resize(8); return -4; } }; \
|
||||||
|
var d2 = new Uint8Array(source2.sliceToImmutable(s2, e2)); \
|
||||||
|
ok = ok && d2.length === 3 && d2[0] === 4 && d2[1] === 5 && d2[2] === 6 && source2.byteLength === 8; \
|
||||||
|
source2.resize(10); \
|
||||||
|
var e3 = { valueOf: function () { source2.resize(5); return -4; } }; \
|
||||||
|
var t3 = false; \
|
||||||
|
try { source2.sliceToImmutable(s2, e3); } catch (err) { t3 = err instanceof RangeError; } \
|
||||||
|
return ok && t3; })()",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sliceToImmutable 源 detach 两形钉:源已 detach → TypeError 且参数未读;
|
||||||
|
/// end.valueOf 内 detach → TypeError,calls 双记。
|
||||||
|
#[test]
|
||||||
|
fn ab_sti_source_detach_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 a1 = new ArrayBuffer(8); $262.detachArrayBuffer(a1); \
|
||||||
|
var calls1 = []; \
|
||||||
|
var s1 = { valueOf: function () { calls1.push('s'); return 0; } }; \
|
||||||
|
var e1 = { valueOf: function () { calls1.push('e'); return 1; } }; \
|
||||||
|
var t1 = ty(function () { a1.sliceToImmutable(s1, e1); }); \
|
||||||
|
var a2 = new ArrayBuffer(8); \
|
||||||
|
var calls2 = []; \
|
||||||
|
var s2 = { valueOf: function () { calls2.push('start.valueOf'); return 0; } }; \
|
||||||
|
var e2 = { valueOf: function () { $262.detachArrayBuffer(a2); calls2.push('end.valueOf'); return 1; } }; \
|
||||||
|
var t2 = ty(function () { a2.sliceToImmutable(s2, e2); }); \
|
||||||
|
return t1 && calls1.length === 0 && t2 && calls2.join(',') === 'start.valueOf,end.valueOf'; })()",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sliceToImmutable 源后改无影响钉:源写/resize/detach 后产物内容不变、
|
||||||
|
/// 产物恒 8 长 immutable。
|
||||||
|
#[test]
|
||||||
|
fn ab_sti_modify_source() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"(function () { var source = new ArrayBuffer(8, { maxByteLength: 8 }); \
|
||||||
|
var view = new Uint8Array(source); \
|
||||||
|
for (var i = 0; i < 8; i++) view[i] = i + 1; \
|
||||||
|
var dest = source.sliceToImmutable(); \
|
||||||
|
var dv = new Uint8Array(dest); \
|
||||||
|
var ok = dv.length === 8 && dv[0] === 1 && dv[7] === 8 && dest.immutable === true; \
|
||||||
|
view[0] = 86; \
|
||||||
|
ok = ok && new Uint8Array(dest)[0] === 1; \
|
||||||
|
source.resize(4); \
|
||||||
|
ok = ok && dest.byteLength === 8 && new Uint8Array(dest)[7] === 8; \
|
||||||
|
$262.detachArrayBuffer(source); \
|
||||||
|
return ok && new Uint8Array(dest)[0] === 1 && source.detached === true; })()",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sliceToImmutable 品牌钉:非对象/普通对象/数组/函数/DataView/TypedArray
|
||||||
|
/// 六形 TypeError 且参数未读;`new (实例.sliceToImmutable)()` 形态
|
||||||
|
/// TypeError(nonconstructor,成员链绑定使 new 目标即方法本身)。
|
||||||
|
#[test]
|
||||||
|
fn ab_sti_brand_family() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let result = eval(
|
||||||
|
&mut vm,
|
||||||
|
"(function () { var fn = ArrayBuffer.prototype.sliceToImmutable; \
|
||||||
|
var calls = []; \
|
||||||
|
var s = { valueOf: function () { calls.push('s'); return 0; } }; \
|
||||||
|
function ty(v) { calls.length = 0; \
|
||||||
|
try { fn.call(v, s); return false; } catch (e) { return e instanceof TypeError; } } \
|
||||||
|
var ok = ty(undefined) && ty(null) && ty({}) && ty([]) && ty(function () {}) \
|
||||||
|
&& ty(new DataView(new ArrayBuffer(8), 0)) && ty(new Int8Array(8)); \
|
||||||
|
var calls2 = []; \
|
||||||
|
var s2 = { valueOf: function () { calls2.push('s'); return 0; } }; \
|
||||||
|
var t2 = false, t3 = false; \
|
||||||
|
var nab = new ArrayBuffer(8); \
|
||||||
|
try { new nab.sliceToImmutable(); t3 = true; } catch (e) { t2 = e instanceof TypeError; t3 = false; } \
|
||||||
|
return ok && calls.length === 0 && t2 && calls2.length === 0 && !t3; })()",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.as_bool());
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user