mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: Atomics.waitAsync/pause 异步臂与 notify 唤醒面
This commit is contained in:
@@ -4,7 +4,8 @@
|
||||
//! 阻塞、无 waiter 面);宽度截断与元素写路径(`write_element`)结构同构。
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use oxide_types::object::TypedArrayKind;
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_types::object::{JsObject, TypedArrayKind};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::array_buffer::{buffer_payload_ptr, ArrayBufferPayload};
|
||||
@@ -243,19 +244,26 @@ pub fn atomics_wait<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
|
||||
/// `Atomics.notify(typedArray, index, count)`:值验证后 count 经
|
||||
/// ToIntegerOrInfinity(毒传播,NaN → +∞、有限负值 → 0);非 SAB 提前返 0
|
||||
/// (先于本早返的越界/类型异常照常抛出);单线程无 waiter 面,恒返 0。
|
||||
/// (先于本早返的越界/类型异常照常抛出);SAB 臂唤醒 (缓冲, 偏移) 处登记的
|
||||
/// waitAsync waiter,返唤醒数(无登记恒 0)。
|
||||
pub fn atomics_notify<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let ta_val = arg_at(vm, args, 1);
|
||||
let index_val = arg_at(vm, args, 2);
|
||||
let count_val = arg_at(vm, args, 3);
|
||||
let (_payload_ptr, _view, _offset, is_sab) = native_try!(resolve_atomic_wait_access(vm, ta_val, index_val, false));
|
||||
// count 归一:单线程下无观察差,只承载毒传播(语料钉:非 SAB 亦先评估
|
||||
// count 再返 0)。
|
||||
native_try!(ta_to_integer_or_infinity(vm, count_val));
|
||||
let (_payload_ptr, view, offset, is_sab) = native_try!(resolve_atomic_wait_access(vm, ta_val, index_val, false));
|
||||
// count 实参缺省 → +∞(全唤醒);显式传入才走强转(毒传播语料钉:
|
||||
// 非 SAB 亦先评估 count 再返 0)。
|
||||
let count = if args.len() > 3 {
|
||||
native_try!(ta_to_integer_or_infinity(vm, count_val))
|
||||
} else {
|
||||
f64::INFINITY
|
||||
};
|
||||
if !is_sab {
|
||||
return NativeResult::Ok(JsValue::int(0));
|
||||
}
|
||||
NativeResult::Ok(JsValue::int(0))
|
||||
// 与登记侧同一读径取缓冲现指针,同 run 无 GC 时恒匹配。
|
||||
let buffer_ptr = view.buffer.as_js_object_ptr();
|
||||
NativeResult::Ok(JsValue::int(vm.atomics_wake_waiters(buffer_ptr, offset, count) as i32))
|
||||
}
|
||||
|
||||
/// `Atomics.load(typedArray, index)`:读指定索引元素。
|
||||
@@ -357,3 +365,67 @@ pub fn atomics_is_lock_free<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
let n = native_try!(ta_to_integer_or_infinity(vm, size_val));
|
||||
NativeResult::Ok(JsValue::bool(matches!(n, 1.0 | 2.0 | 4.0 | 8.0)))
|
||||
}
|
||||
|
||||
/// waitAsync 结果对象:普通对象(proto = Object.prototype),own 属性
|
||||
/// async/value 双 data 属性(普通创建路径默认形)。
|
||||
fn wait_async_result_object<H: VmHost>(vm: &mut H, async_arm: bool, value: JsValue) -> JsValue {
|
||||
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let obj_ptr = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
|
||||
// SAFETY: alloc_object 返回存活 arena 指针,借出期间无别名。
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
let async_si = vm.string_key_si("async");
|
||||
let value_si = vm.string_key_si("value");
|
||||
vm.set_or_create_prop_value(obj, async_si, JsValue::bool(async_arm));
|
||||
vm.set_or_create_prop_value(obj, value_si, value);
|
||||
JsValue::from_js_object(obj_ptr)
|
||||
}
|
||||
|
||||
/// `Atomics.waitAsync(typedArray, index, value, timeout)`:值强转(毒传播)
|
||||
/// 后读载荷比较——不等值返 {async:false, value:"not-equal"};等值再归一 timeout
|
||||
/// (NaN → +∞、<0 → 0),≤0 返 {async:false, value:"timed-out"};等值且 >0
|
||||
/// 建 pending Promise 登记 waiter 表(notify 唤醒结算 "ok"),返
|
||||
/// {async:true, value:Promise}。timeout 强转在不等值早返之后(spec 步序)。
|
||||
pub fn atomics_wait_async<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let ta_val = arg_at(vm, args, 1);
|
||||
let index_val = arg_at(vm, args, 2);
|
||||
let value_val = arg_at(vm, args, 3);
|
||||
let timeout_val = arg_at(vm, args, 4);
|
||||
let (payload_ptr, view, offset, _) = native_try!(resolve_atomic_wait_access(vm, ta_val, index_val, true));
|
||||
let value = native_try!(ta_element_value(vm, view.kind, value_val));
|
||||
let old = atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined());
|
||||
let equal = if is_bigint_kind(view.kind) {
|
||||
let old_bi = vm.bigint_value(old).clone();
|
||||
let value_bi = vm.bigint_value(value).clone();
|
||||
old_bi.cmp(&value_bi) == std::cmp::Ordering::Equal
|
||||
} else {
|
||||
oxide_runtime_api::to_number(old) == oxide_runtime_api::to_number(value)
|
||||
};
|
||||
if !equal {
|
||||
let str = vm.new_string("not-equal");
|
||||
return NativeResult::Ok(wait_async_result_object(vm, false, str));
|
||||
}
|
||||
// 超时 ToNumber 归一:毒传播 + NaN → +∞、负值 → 0。
|
||||
let timeout = native_try!(
|
||||
oxide_runtime_api::to_number_full(timeout_val, vm).map_err(|e| crate::iterator::engine_error(vm, &e))
|
||||
);
|
||||
let normalized = if timeout.is_nan() {
|
||||
f64::INFINITY
|
||||
} else if timeout < 0.0 {
|
||||
0.0
|
||||
} else {
|
||||
timeout
|
||||
};
|
||||
if normalized <= 0.0 {
|
||||
let str = vm.new_string("timed-out");
|
||||
return NativeResult::Ok(wait_async_result_object(vm, false, str));
|
||||
}
|
||||
// 登记期取视图 buffer 现指针为键:同 run 无 GC 时 notify 侧同读径恒匹配。
|
||||
let promise = vm.atomics_new_waiter_promise();
|
||||
vm.atomics_register_waiter(view.buffer.as_js_object_ptr(), offset, promise);
|
||||
NativeResult::Ok(wait_async_result_object(vm, true, promise))
|
||||
}
|
||||
|
||||
/// `Atomics.pause(hint)`:零参语义(不校验、不延迟),任意实参形直返 undefined。
|
||||
pub fn atomics_pause<H: VmHost>(_vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
@@ -243,4 +243,18 @@ pub trait VmHost {
|
||||
fn symbol_lookup_global(&self, key: &str) -> Option<u32>;
|
||||
fn symbol_register_global(&mut self, key: String, idx: u32);
|
||||
fn symbol_key_for_id(&self, idx: u32) -> Option<String>;
|
||||
|
||||
// Atomics waiter 面(waitAsync 登记 / notify 唤醒)
|
||||
/// 新建 pending Promise,供 `Atomics.waitAsync` 异步臂的 value 槽;结算不
|
||||
/// 经能力闭包,由 notify 唤醒直调引擎结算核。
|
||||
fn atomics_new_waiter_promise(&mut self) -> JsValue;
|
||||
/// 登记 waitAsync waiter:键 = (缓冲对象指针, 元素字节偏移),同键 FIFO 追加。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// `buffer` 须为登记时刻视图的 `buffer` 现指针(与 notify 侧同一读径),
|
||||
/// 同 run 无 GC 时两侧指针恒匹配。
|
||||
fn atomics_register_waiter(&mut self, buffer: *mut JsObject, offset: usize, promise: JsValue);
|
||||
/// 唤醒 (缓冲, 偏移) 处登记的前 `count` 个 waiter(`<= 0` 不唤醒、`+Inf`
|
||||
/// 全唤醒),逐个以 "ok" 结算(反应入队随 run 收尾 drain);返回唤醒数。
|
||||
fn atomics_wake_waiters(&mut self, buffer: *mut JsObject, offset: usize, count: f64) -> usize;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
//! Atomics.waitAsync waiter 核:per-(缓冲对象, 偏移) 的 promise FIFO 表、登记与
|
||||
//! notify 唤醒(直调 `fulfill_promise` 结算 "ok",反应入队随 run 收尾 FIFO drain)。
|
||||
//!
|
||||
//! 键 = 登记时刻视图的 `buffer` 指针:同 run 无 GC 时 notify 侧读同一指针恒匹配;
|
||||
//! run 内 GC 晋升后指针漂移则唤醒退化 0(单线程语义,无界外观察钉)。
|
||||
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
use crate::vm::Vm;
|
||||
|
||||
impl Vm {
|
||||
/// 新建 pending Promise(waitAsync 异步臂的 value 槽)。
|
||||
///
|
||||
/// resolve/reject 闭包弃用:结算路径是 notify 唤醒直调 [`Self::fulfill_promise`],
|
||||
/// 不经闭包。
|
||||
pub(crate) fn atomics_new_waiter_promise(&mut self) -> JsValue {
|
||||
let (promise, _resolve, _reject) = self.new_promise_capability();
|
||||
promise
|
||||
}
|
||||
|
||||
/// 登记 waitAsync waiter:键 = (缓冲对象指针, 元素字节偏移),同键 FIFO 追加。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// 调用方须传登记时刻视图的 `buffer` 现指针(与 notify 侧同一读径);表在
|
||||
/// run 边界清空(`clear_execution_state`),清位后 promise 无强根自然回收。
|
||||
pub(crate) fn atomics_register_waiter(&mut self, buffer: *mut JsObject, offset: usize, promise: JsValue) {
|
||||
self.atomics_waiters.entry((buffer as u64, offset)).or_default().push(promise);
|
||||
}
|
||||
|
||||
/// 唤醒 (缓冲, 偏移) 处登记的前 `count` 个 waiter(`<= 0` 不唤醒、`+Inf` 全唤醒),
|
||||
/// 逐个直调 `fulfill_promise` 结算 "ok"(反应入队随 run 收尾 drain 执行)。
|
||||
///
|
||||
/// # 返回
|
||||
/// 实际唤醒数(队列不存在或不足时为 0 / 队列长)。
|
||||
pub(crate) fn atomics_wake_waiters(&mut self, buffer: *mut JsObject, offset: usize, count: f64) -> usize {
|
||||
// 唤醒数先独立判定(借用即时结束),结算串创建不跨表借用。
|
||||
let n = {
|
||||
let Some(waiters) = self.atomics_waiters.get_mut(&(buffer as u64, offset)) else {
|
||||
return 0;
|
||||
};
|
||||
if count <= 0.0 {
|
||||
0
|
||||
} else if count.is_infinite() {
|
||||
waiters.len()
|
||||
} else {
|
||||
(count as usize).min(waiters.len())
|
||||
}
|
||||
};
|
||||
if n == 0 {
|
||||
return 0;
|
||||
}
|
||||
let ok = self.new_string("ok");
|
||||
let Some(waiters) = self.atomics_waiters.get_mut(&(buffer as u64, offset)) else {
|
||||
return 0;
|
||||
};
|
||||
let woken: Vec<JsValue> = waiters.drain(..n).collect();
|
||||
for promise in woken {
|
||||
let _ = self.fulfill_promise(promise, ok);
|
||||
}
|
||||
n
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,8 @@ pub fn bind_atomics(core: &Arc<KernelCore>, session: &KernelSession, global: &mu
|
||||
("isLockFree", oxide_builtins::atomics::atomics_is_lock_free::<crate::vm::Vm>, 1),
|
||||
("wait", oxide_builtins::atomics::atomics_wait::<crate::vm::Vm>, 4),
|
||||
("notify", oxide_builtins::atomics::atomics_notify::<crate::vm::Vm>, 3),
|
||||
("waitAsync", oxide_builtins::atomics::atomics_wait_async::<crate::vm::Vm>, 4),
|
||||
("pause", oxide_builtins::atomics::atomics_pause::<crate::vm::Vm>, 0),
|
||||
);
|
||||
|
||||
let a_val = JsValue::from_js_object(atomics_ptr);
|
||||
|
||||
@@ -11,6 +11,7 @@ mod async_disposable;
|
||||
mod async_from_sync;
|
||||
mod async_func;
|
||||
mod async_generator;
|
||||
mod atomics;
|
||||
/// 内置对象绑定模块:向 session 的 global 对象安装 Object/Array/... 及各原型。
|
||||
pub mod bindings;
|
||||
mod dispatch;
|
||||
|
||||
@@ -75,6 +75,12 @@ impl Vm {
|
||||
for job in &self.job_queue {
|
||||
crate::promise::for_each_job_value(job, &mut f);
|
||||
}
|
||||
// Atomics waiter 表登记的 promise 是 GC 根(run 内跨调用存活)。
|
||||
for promises in self.atomics_waiters.values() {
|
||||
for p in promises {
|
||||
f(*p);
|
||||
}
|
||||
}
|
||||
for iter in &self.iters.for_in_iters {
|
||||
if iter.is_null() {
|
||||
continue;
|
||||
@@ -150,6 +156,12 @@ impl Vm {
|
||||
for job in &mut self.job_queue {
|
||||
crate::promise::rewrite_job_values(job, &mut rewrite);
|
||||
}
|
||||
// Atomics waiter 表中的 promise 随 sweep 重写(与 mark 段一一对应)。
|
||||
for promises in self.atomics_waiters.values_mut() {
|
||||
for p in promises {
|
||||
*p = rewrite(*p);
|
||||
}
|
||||
}
|
||||
for iter in &mut self.iters.for_in_iters {
|
||||
if iter.is_null() {
|
||||
continue;
|
||||
|
||||
@@ -189,6 +189,10 @@ pub struct Vm {
|
||||
pub async_generator_function_proto: P<JsObject>,
|
||||
/// 微任务队列(Promise reactions / thenable 委托),`run()` 末尾 FIFO drain。
|
||||
pub(crate) job_queue: VecDeque<crate::promise::Microtask>,
|
||||
/// Atomics.waitAsync waiter 表:键 = (缓冲对象指针, 元素字节偏移),值 =
|
||||
/// 登记的 promise FIFO。键取登记时刻视图的 `buffer` 现指针(同 run 无 GC 时
|
||||
/// notify 侧读同一指针恒匹配);run 边界清空,清位后 promise 无强根自然回收。
|
||||
pub(crate) atomics_waiters: HashMap<(u64, usize), Vec<JsValue>>,
|
||||
pub math_rng_state: u64,
|
||||
/// 子模块平表的表代际注册表:键 = 表代际(`current_gen` 为当前 run 正在
|
||||
/// 装载的代际)。函数对象创建时记录自身所属代际(`JsObject::table_gen`),
|
||||
|
||||
@@ -216,4 +216,13 @@ impl oxide_runtime_api::VmHost for Vm {
|
||||
fn symbol_key_for_id(&self, idx: u32) -> Option<String> {
|
||||
self.symbols.key_for_id(idx)
|
||||
}
|
||||
fn atomics_new_waiter_promise(&mut self) -> JsValue {
|
||||
self.atomics_new_waiter_promise()
|
||||
}
|
||||
fn atomics_register_waiter(&mut self, buffer: *mut JsObject, offset: usize, promise: JsValue) {
|
||||
self.atomics_register_waiter(buffer, offset, promise)
|
||||
}
|
||||
fn atomics_wake_waiters(&mut self, buffer: *mut JsObject, offset: usize, count: f64) -> usize {
|
||||
self.atomics_wake_waiters(buffer, offset, count)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ impl Vm {
|
||||
async_generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
async_generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
job_queue: VecDeque::new(),
|
||||
atomics_waiters: std::collections::HashMap::new(),
|
||||
math_rng_state: 0,
|
||||
// gen 0 预登记空表占位:函数对象恒在 run 内创建(彼时 current_gen
|
||||
// ≥ 1),gen 0 表只是首 run 前路径的占位,首 run 边界即被回收。
|
||||
@@ -199,6 +200,7 @@ impl Vm {
|
||||
async_generator_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
async_generator_function_proto: P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||
job_queue: VecDeque::new(),
|
||||
atomics_waiters: std::collections::HashMap::new(),
|
||||
math_rng_state: 0,
|
||||
// gen 0 预登记空表占位:函数对象恒在 run 内创建(彼时 current_gen
|
||||
// ≥ 1),gen 0 表只是首 run 前路径的占位,首 run 边界即被回收。
|
||||
@@ -544,6 +546,8 @@ impl Vm {
|
||||
self.inline_reg_pool = None;
|
||||
// 微任务队列是执行期状态:跨 run 不保留。
|
||||
self.job_queue.clear();
|
||||
// Atomics waiter 表同属执行期状态:未结算 promise 清位后无强根。
|
||||
self.atomics_waiters.clear();
|
||||
}
|
||||
|
||||
/// 轻量重置:清空执行状态并回收 epoch 内存,但保留 session 字符串与 builtin。
|
||||
|
||||
@@ -253,3 +253,153 @@ fn atomics_wait_notify_name_and_length() {
|
||||
&& Atomics.notify.name === 'notify' && Atomics.notify.length === 3",
|
||||
);
|
||||
}
|
||||
|
||||
/// waitAsync:不等值 → 同步结果对象 {async:false, value:"not-equal"}。
|
||||
#[test]
|
||||
fn eval_waitasync_not_equal() {
|
||||
let mut vm = Vm::new();
|
||||
truthy(
|
||||
&mut vm,
|
||||
"(function () { \
|
||||
var ta = new Int32Array(new SharedArrayBuffer(8)); \
|
||||
ta[0] = 7; \
|
||||
var r = Atomics.waitAsync(ta, 0, 8, 100); \
|
||||
return r.async === false && r.value === 'not-equal' && !(r.value instanceof Promise); })()",
|
||||
);
|
||||
}
|
||||
|
||||
/// waitAsync:等值 + 归一 timeout ≤ 0 → {async:false, value:"timed-out"};
|
||||
/// NaN/缺省 timeout → +∞ 走异步臂。
|
||||
#[test]
|
||||
fn eval_waitasync_timeout_zero() {
|
||||
let mut vm = Vm::new();
|
||||
truthy(
|
||||
&mut vm,
|
||||
"(function () { \
|
||||
var ta = new Int32Array(new SharedArrayBuffer(8)); \
|
||||
ta[0] = 7; \
|
||||
var r0 = Atomics.waitAsync(ta, 0, 7, 0); \
|
||||
var rneg = Atomics.waitAsync(ta, 0, 7, -5); \
|
||||
var rnan = Atomics.waitAsync(ta, 0, 7, NaN); \
|
||||
var rdef = Atomics.waitAsync(ta, 0, 7); \
|
||||
return r0.async === false && r0.value === 'timed-out' \
|
||||
&& rneg.async === false && rneg.value === 'timed-out' \
|
||||
&& rnan.async === true && rnan.value instanceof Promise \
|
||||
&& rdef.async === true && rdef.value instanceof Promise; })()",
|
||||
);
|
||||
}
|
||||
|
||||
/// waitAsync:等值 + timeout > 0 → {async:true, value:<pending Promise>}。
|
||||
#[test]
|
||||
fn eval_waitasync_async_arm() {
|
||||
let mut vm = Vm::new();
|
||||
truthy(
|
||||
&mut vm,
|
||||
"(function () { \
|
||||
var ta = new Int32Array(new SharedArrayBuffer(8)); \
|
||||
ta[0] = 7; \
|
||||
var r = Atomics.waitAsync(ta, 0, 7, 100); \
|
||||
return r.async === true && r.value instanceof Promise; })()",
|
||||
);
|
||||
}
|
||||
|
||||
/// waitAsync "ok" 结算核心钉:同 run 内 notify 唤醒 → drain 执行 then 处理器
|
||||
/// 写 global;跨 run 断言(同 Vm 双段)。
|
||||
#[test]
|
||||
fn eval_waitasync_notify_wakes() {
|
||||
let mut vm = Vm::new();
|
||||
eval(
|
||||
&mut vm,
|
||||
"var ta = new Int32Array(new SharedArrayBuffer(8)); \
|
||||
ta[0] = 0; \
|
||||
var r = Atomics.waitAsync(ta, 0, 0, 100); \
|
||||
globalThis.__w2 = Atomics.notify(ta, 0); \
|
||||
r.value.then(function (v) { globalThis.__w3 = (v === 'ok'); });",
|
||||
)
|
||||
.unwrap();
|
||||
truthy(&mut vm, "globalThis.__w2 === 1 && globalThis.__w3 === true");
|
||||
}
|
||||
|
||||
/// waitAsync:非 SAB 视图 → TypeError 同步抛(非 Promise reject),且毒 value
|
||||
/// 不评估(非 SAB 早返先于值强转)。
|
||||
#[test]
|
||||
fn eval_waitasync_non_sab_throws() {
|
||||
let mut vm = Vm::new();
|
||||
truthy(
|
||||
&mut vm,
|
||||
&format!(
|
||||
"(function () {{ \
|
||||
var ta = new Int32Array(new ArrayBuffer(8)); \
|
||||
var calls = 0; var poison = {{ valueOf: function () {{ calls = 1; }} }}; \
|
||||
return {} && calls === 0; }})()",
|
||||
throws("Atomics.waitAsync(ta, 0, poison, 1)", "TypeError"),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// waitAsync:run 边界清位——run1 登记不 notify,run2 同 Vm notify 恒 0。
|
||||
#[test]
|
||||
fn eval_waitasync_per_run_clear() {
|
||||
let mut vm = Vm::new();
|
||||
eval(
|
||||
&mut vm,
|
||||
"var ta = new Int32Array(new SharedArrayBuffer(8)); \
|
||||
ta[0] = 0; \
|
||||
Atomics.waitAsync(ta, 0, 0, 100);",
|
||||
)
|
||||
.unwrap();
|
||||
truthy(&mut vm, "Atomics.notify(ta, 0) === 0");
|
||||
}
|
||||
|
||||
/// pause:任意实参形(含缺省)恒 undefined。
|
||||
#[test]
|
||||
fn eval_pause_returns_undefined() {
|
||||
let mut vm = Vm::new();
|
||||
truthy(
|
||||
&mut vm,
|
||||
"Atomics.pause(undefined) === undefined && Atomics.pause(42) === undefined \
|
||||
&& Atomics.pause(0) === undefined && Atomics.pause(-0) === undefined \
|
||||
&& Atomics.pause(9007199254740991) === undefined && Atomics.pause() === undefined",
|
||||
);
|
||||
}
|
||||
|
||||
/// pause:零参语义不校验(true/小数/NaN/串 均不抛)。
|
||||
#[test]
|
||||
fn eval_pause_no_validation() {
|
||||
let mut vm = Vm::new();
|
||||
truthy(
|
||||
&mut vm,
|
||||
"Atomics.pause(true) === undefined && Atomics.pause(42.42) === undefined \
|
||||
&& Atomics.pause(NaN) === undefined && Atomics.pause('42') === undefined \
|
||||
&& Atomics.pause({}) === undefined",
|
||||
);
|
||||
}
|
||||
|
||||
/// pause 方法元数据钉:length 0、name "pause"。
|
||||
#[test]
|
||||
fn eval_pause_meta() {
|
||||
let mut vm = Vm::new();
|
||||
truthy(&mut vm, "Atomics.pause.length === 0 && Atomics.pause.name === 'pause'");
|
||||
}
|
||||
|
||||
/// waitAsync:值强转毒传播(BigInt64 视图配数值/非整串;Int32 视图配 BigInt),
|
||||
/// 防共享核回归。
|
||||
#[test]
|
||||
fn eval_waitasync_value_coercion() {
|
||||
let mut vm = Vm::new();
|
||||
truthy(
|
||||
&mut vm,
|
||||
&format!(
|
||||
"(function () {{ \
|
||||
var ta = new BigInt64Array(new SharedArrayBuffer(8)); \
|
||||
ta[0] = 5n; \
|
||||
var ab = new Int32Array(new SharedArrayBuffer(4)); \
|
||||
ab[0] = 3; \
|
||||
return {} && {} && {} && {}; }})()",
|
||||
throws("Atomics.waitAsync(ta, 0, 0.5, 1)", "TypeError"),
|
||||
throws("Atomics.waitAsync(ta, 0, null, 1)", "TypeError"),
|
||||
throws("Atomics.waitAsync(ta, 0, 'x', 1)", "SyntaxError"),
|
||||
throws("Atomics.waitAsync(ab, 0, 1n, 1)", "TypeError"),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user