feat: SAB 构造器 maxByteLength 选项面 grow 方法与访问器真值臂落地

This commit is contained in:
2026-09-23 21:46:36 +00:00
parent c568eed769
commit b892c59a75
3 changed files with 302 additions and 23 deletions
+115 -19
View File
@@ -164,14 +164,17 @@ pub(crate) fn default_shared_array_buffer_proto<H: VmHost>(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<H: VmHost>(vm: &mut H, data: Vec<u8>, proto: JsValue) -> *mut JsObject {
/// 分配携给定 proto 与载荷形态(`max_byte_length` 为存储态:0 定长,非 0 可
/// grow)的 SharedArrayBuffer 对象;byteLength 经原型访问器读(不写 own 数据
/// 属性)。
pub(crate) fn new_shared_array_buffer<H: VmHost>(
vm: &mut H, data: Vec<u8>, 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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<H: VmHost>(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<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 调用点。
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` 为
@@ -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<KernelCore>, 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::<crate::vm::Vm> 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<KernelCore>, session: &KernelSession,
oxide_builtins::array_buffer::shared_array_buffer_max_byte_length::<crate::vm::Vm> as *const (),
);
// growable 定长臂:恒 false,品牌校验抛 TypeError(proto 自身访问即抛)。
// growable 真值臂:读载荷存储态上限(0 定长 false / 非 0 growable true)。
bind_accessor_getter(
core,
session,
@@ -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]