diff --git a/crates/oxide_builtins/src/atomics.rs b/crates/oxide_builtins/src/atomics.rs index 8dc0855c..42e40c8e 100644 --- a/crates/oxide_builtins/src/atomics.rs +++ b/crates/oxide_builtins/src/atomics.rs @@ -65,10 +65,10 @@ fn is_bigint_kind(kind: TypedArrayKind) -> bool { /// # 步骤 /// 1. `get_typed_array_data` 品牌校验(非 TA 对象抛 TypeError)。 /// 2. 整数宽度守卫(浮点/clamped 视图抛 TypeError)。 -/// 3. 索引经 ToIntegerOrInfinity(valueOf/toString 抛错原值上抛,先于越界判)。 -/// 4. 相对字节偏移 = view.byte_offset + index·bpe(f64 运算);越界 +/// 3. 写族加查写守卫(immutable/detach 抛 TypeError),先于索引/值 coercion。 +/// 4. 索引经 ToIntegerOrInfinity(valueOf/toString 抛错原值上抛,先于越界判)。 +/// 5. 相对字节偏移 = view.byte_offset + index·bpe(f64 运算);越界 /// (`< 0` 或 `>= buffer 字节长`)抛 RangeError,detach 视同越界。 -/// 5. 写族加查写守卫(immutable/detach 抛 TypeError)。 /// /// # 边界与前提 /// - 返回的载荷指针、视图与偏移在 session 独占期内有效,调用方同一语句内消费。 @@ -83,6 +83,11 @@ fn resolve_atomic_access( if !is_integer_kind(view.kind) { return Err(type_error(vm, "Atomics operations require an integer TypedArray type")); } + // 写守卫(不可变缓冲 TypeError)先于索引/值 coercion 求值:守卫抛错时 + // index/value 的 valueOf 不得被调用(语料 calls 序列口径)。 + if writable { + ta_validate(vm, view, true)?; + } let index = ta_to_integer_or_infinity(vm, index_val)?; // 缓冲字节长:AB/SAB 双认(buffer_payload_ptr),载荷缺失视同越界。 @@ -102,9 +107,6 @@ fn resolve_atomic_access( if offset < 0.0 || offset >= buffer_len as f64 { return Err(range_error(vm, "invalid indexed access to Atomics")); } - if writable { - ta_validate(vm, view, true)?; - } Ok((payload_ptr, view, offset as usize)) } @@ -161,18 +163,18 @@ fn arithmetic_write_value( /// `Atomics.load(typedArray, index)`:读指定索引元素。 pub fn atomics_load(vm: &mut H, args: &[u8]) -> NativeResult { - let this_val = arg_at(vm, args, 0); - let index_val = arg_at(vm, args, 1); - let (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, this_val, false, index_val)); + let ta_val = arg_at(vm, args, 1); + let index_val = arg_at(vm, args, 2); + let (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, false, index_val)); NativeResult::Ok(atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined())) } /// `Atomics.store(typedArray, index, value)`:写值后读回返回(宽度归一,-0 → +0)。 pub fn atomics_store(vm: &mut H, args: &[u8]) -> NativeResult { - let this_val = arg_at(vm, args, 0); - let index_val = arg_at(vm, args, 1); - let value_val = arg_at(vm, args, 2); - let (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, this_val, true, index_val)); + 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 (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); let value = native_try!(ta_element_value(vm, view.kind, value_val)); atomic_write(vm, payload_ptr, view.kind, offset, value); NativeResult::Ok(atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(value)) @@ -180,10 +182,10 @@ pub fn atomics_store(vm: &mut H, args: &[u8]) -> NativeResult { /// `Atomics.exchange(typedArray, index, value)`:读旧值、写新值、返回旧值。 pub fn atomics_exchange(vm: &mut H, args: &[u8]) -> NativeResult { - let this_val = arg_at(vm, args, 0); - let index_val = arg_at(vm, args, 1); - let value_val = arg_at(vm, args, 2); - let (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, this_val, true, index_val)); + 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 (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); let old = atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined()); let value = native_try!(ta_element_value(vm, view.kind, value_val)); atomic_write(vm, payload_ptr, view.kind, offset, value); @@ -217,10 +219,10 @@ pub fn atomics_xor(vm: &mut H, args: &[u8]) -> NativeResult { /// add/sub/and/or/xor 共享核:读旧值 → 运算值转换 → 读-改-写 → 返回旧值。 fn atomic_arithmetic(vm: &mut H, args: &[u8], op: &str) -> NativeResult { - let this_val = arg_at(vm, args, 0); - let index_val = arg_at(vm, args, 1); - let value_val = arg_at(vm, args, 2); - let (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, this_val, true, index_val)); + 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 (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); let old = atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined()); let value = native_try!(ta_element_value(vm, view.kind, value_val)); let result = arithmetic_write_value(vm, view.kind, op, old, value); @@ -231,11 +233,11 @@ fn atomic_arithmetic(vm: &mut H, args: &[u8], op: &str) -> NativeResu /// `Atomics.compareExchange(typedArray, index, expected, replacement)`:读旧值; /// 相等(数值宽度 `==`、BigInt 宽度 BigInt 相等)则写 replacement;返回旧值。 pub fn atomics_compare_exchange(vm: &mut H, args: &[u8]) -> NativeResult { - let this_val = arg_at(vm, args, 0); - let index_val = arg_at(vm, args, 1); - let expected_val = arg_at(vm, args, 2); - let replacement_val = arg_at(vm, args, 3); - let (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, this_val, true, index_val)); + let ta_val = arg_at(vm, args, 1); + let index_val = arg_at(vm, args, 2); + let expected_val = arg_at(vm, args, 3); + let replacement_val = arg_at(vm, args, 4); + let (payload_ptr, view, offset) = native_try!(resolve_atomic_access(vm, ta_val, true, index_val)); let old = atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined()); let expected = native_try!(ta_element_value(vm, view.kind, expected_val)); let matched = if is_bigint_kind(view.kind) { diff --git a/crates/oxide_builtins/src/stubs.rs b/crates/oxide_builtins/src/stubs.rs index 2e7bbfc3..272c11fe 100644 --- a/crates/oxide_builtins/src/stubs.rs +++ b/crates/oxide_builtins/src/stubs.rs @@ -25,7 +25,3 @@ pub fn weakref_stub(vm: &mut H, _args: &[u8]) -> NativeResult { pub fn finalization_registry_stub(vm: &mut H, _args: &[u8]) -> NativeResult { stub_error(vm, "FinalizationRegistry") } -/// `Atomics` 全局占位:始终抛 TypeError。按设计延后实现。 -pub fn atomics_stub(vm: &mut H, _args: &[u8]) -> NativeResult { - stub_error(vm, "Atomics") -} diff --git a/crates/oxide_kernel/src/builtin/world.rs b/crates/oxide_kernel/src/builtin/world.rs index aae86ea9..c05a3699 100644 --- a/crates/oxide_kernel/src/builtin/world.rs +++ b/crates/oxide_kernel/src/builtin/world.rs @@ -725,10 +725,7 @@ mod tests { assert!(!dirty.shared_array_buffer); // 脏家族换新对象,未脏家族指针原样保留。 - assert!(!std::ptr::eq( - atomics, - session.builtin_world.atomics_object.as_ptr() as *mut JsObject - )); + assert!(!std::ptr::eq(atomics, session.builtin_world.atomics_object.as_ptr() as *mut JsObject)); assert!(std::ptr::eq(ab_proto, session.builtin_world.array_buffer_proto.as_ptr() as *mut JsObject)); } } diff --git a/crates/oxide_test262/src/harness.rs b/crates/oxide_test262/src/harness.rs index ff0e0123..2e46fbce 100644 --- a/crates/oxide_test262/src/harness.rs +++ b/crates/oxide_test262/src/harness.rs @@ -1,4 +1,4 @@ -//! test262 harness 内嵌注册表与前缀拼接/缓存:`HarnessSources`(23 处 `include_str!`)、`HARNESS`、黑名单、 +//! test262 harness 内嵌注册表与前缀拼接/缓存:`HarnessSources`(24 处 `include_str!`)、`HARNESS`、黑名单、 //! 前缀拼接与缓存。全部 `include_str!` 路径相对上 3 级到仓库根,依赖本文件平铺 `src/` 一级,禁子目录。 use crate::meta::TestMeta; @@ -46,6 +46,7 @@ impl HarnessSources { sources.insert("typeCoercion.js", include_str!("../../../tests/test262/harness/typeCoercion.js")); sources.insert("deepEqual.js", include_str!("../../../tests/test262/harness/deepEqual.js")); sources.insert("testTypedArray.js", include_str!("../../../tests/test262/harness/testTypedArray.js")); + sources.insert("testAtomics.js", include_str!("../../../tests/test262/harness/testAtomics.js")); sources.insert("temporalHelpers.js", include_str!("../../../tests/test262/harness/temporalHelpers.js")); sources.insert("asyncHelpers.js", include_str!("../../../tests/test262/harness/asyncHelpers.js")); sources.insert("doneprintHandle.js", include_str!("../../../tests/test262/harness/doneprintHandle.js")); @@ -74,7 +75,6 @@ fn is_blacklisted_harness(name: &str) -> bool { matches!( name, "testIntl.js" - | "testAtomics.js" | "atomicsHelper.js" | "proxyTrapsHelper.js" | "tcoHelper.js" diff --git a/crates/oxide_vm/src/bindings/bind_atomics.rs b/crates/oxide_vm/src/bindings/bind_atomics.rs new file mode 100644 index 00000000..2be737ff --- /dev/null +++ b/crates/oxide_vm/src/bindings/bind_atomics.rs @@ -0,0 +1,41 @@ +use std::sync::Arc; + +use oxide_kernel::bind_methods; +use oxide_kernel::kernel::{KernelCore, KernelSession}; +use oxide_types::object::JsObject; +use oxide_types::value::JsValue; + +use crate::bindings::bind_global_value; + +/// 把 Atomics 全局纯对象及其 10 个原子方法绑定到 global。 +/// +/// Atomics 非函数对象:原型链落 Object.prototype(`wire_builtin_world_links`), +/// `@@toStringTag` 由 `install_to_string_tags` 统一补装;此处只装方法与全局槽。 +pub fn bind_atomics(core: &Arc, session: &KernelSession, global: &mut JsObject) { + let atomics_ptr = session.builtin_world().atomics_object.as_ptr() as *mut JsObject; + let atomics = unsafe { &mut *atomics_ptr }; + + let bw = session.builtin_world(); + let sf = core.perm_interner().as_ref(); + let sh = core.shape_forge().as_ref(); + + bind_methods!( + bw, + atomics, + sf, + sh, + ("load", oxide_builtins::atomics::atomics_load::, 2), + ("store", oxide_builtins::atomics::atomics_store::, 3), + ("exchange", oxide_builtins::atomics::atomics_exchange::, 3), + ("add", oxide_builtins::atomics::atomics_add::, 3), + ("sub", oxide_builtins::atomics::atomics_sub::, 3), + ("and", oxide_builtins::atomics::atomics_and::, 3), + ("or", oxide_builtins::atomics::atomics_or::, 3), + ("xor", oxide_builtins::atomics::atomics_xor::, 3), + ("compareExchange", oxide_builtins::atomics::atomics_compare_exchange::, 4), + ("isLockFree", oxide_builtins::atomics::atomics_is_lock_free::, 1), + ); + + let a_val = JsValue::from_js_object(atomics_ptr); + bind_global_value(core, global, "Atomics", a_val); +} diff --git a/crates/oxide_vm/src/bindings/bind_stubs.rs b/crates/oxide_vm/src/bindings/bind_stubs.rs index b223ac7f..cd6bfaa1 100644 --- a/crates/oxide_vm/src/bindings/bind_stubs.rs +++ b/crates/oxide_vm/src/bindings/bind_stubs.rs @@ -8,7 +8,7 @@ use oxide_types::value::JsValue; use crate::bindings::{bind_global_value, configure_native_constructor}; -const STUBS: [(&str, *const (), u8); 6] = [ +const STUBS: [(&str, *const (), u8); 5] = [ ("Proxy", oxide_builtins::stubs::proxy_stub:: as *const (), 2), ("WeakMap", oxide_builtins::stubs::weakmap_stub:: as *const (), 0), ("WeakSet", oxide_builtins::stubs::weakset_stub:: as *const (), 0), @@ -18,10 +18,9 @@ const STUBS: [(&str, *const (), u8); 6] = [ oxide_builtins::stubs::finalization_registry_stub:: as *const (), 1, ), - ("Atomics", oxide_builtins::stubs::atomics_stub:: as *const (), 0), ]; -/// 把未实现内置(Proxy/WeakMap/WeakSet/WeakRef/FinalizationRegistry/Atomics) +/// 把未实现内置(Proxy/WeakMap/WeakSet/WeakRef/FinalizationRegistry) /// 的 stub 构造器绑定到 global,并登记到 `stub_objects` 供快照跟踪。 pub fn bind_stubs(core: &Arc, session: &mut KernelSession, global: &mut JsObject) { let builtin_world = Arc::get_mut(&mut session.builtin_world) diff --git a/crates/oxide_vm/src/bindings/mod.rs b/crates/oxide_vm/src/bindings/mod.rs index 8ed2c60d..cd2d9dba 100644 --- a/crates/oxide_vm/src/bindings/mod.rs +++ b/crates/oxide_vm/src/bindings/mod.rs @@ -8,6 +8,8 @@ pub mod bind_array; pub mod bind_array_buffer; /// AsyncDisposableStack 构造器与原型的 native 方法绑定(含 dirty reset 同步)。 pub mod bind_async_disposable_stack; +/// Atomics 全局纯对象及其 10 个原子方法绑定。 +pub mod bind_atomics; /// BigInt 构造器与原型的 native 方法绑定。 pub mod bind_bigint; /// Boolean 构造器与原型的 native 方法绑定。 @@ -634,7 +636,7 @@ fn install_to_string_tags(core: &Arc, session: &KernelSession) { let sh = core.shape_forge().as_ref(); let tag_id = oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG; let tag_key = oxide_types::private_key::make_well_known_symbol_key(tag_id); - let cases: [(*mut JsObject, &str); 13] = [ + let cases: [(*mut JsObject, &str); 14] = [ (world.map_proto.as_ptr() as *mut JsObject, "Map"), (world.array_buffer_proto.as_ptr() as *mut JsObject, "ArrayBuffer"), (world.shared_array_buffer_proto.as_ptr() as *mut JsObject, "SharedArrayBuffer"), @@ -648,6 +650,7 @@ fn install_to_string_tags(core: &Arc, session: &KernelSession) { (world.math_object.as_ptr() as *mut JsObject, "Math"), (world.json_object.as_ptr() as *mut JsObject, "JSON"), (world.symbol_proto.as_ptr() as *mut JsObject, "Symbol"), + (world.atomics_object.as_ptr() as *mut JsObject, "Atomics"), ]; for (ptr, tag) in cases { // SAFETY: ptr 为当前 builtin world 的内置原型,session 独占期间始终有效。 @@ -887,7 +890,6 @@ fn bind_stub_globals(core: &Arc, session: &KernelSession, global: &m oxide_builtins::stubs::finalization_registry_stub:: as *const (), 1, ), - ("Atomics", oxide_builtins::stubs::atomics_stub:: as *const (), 0), ] .into_iter() .enumerate() @@ -997,6 +999,7 @@ pub fn bind_global_builtin_slots(core: &Arc, session: &KernelSession ("Math", JsValue::from_js_object(world.math_object.as_ptr() as *mut JsObject)), ("JSON", JsValue::from_js_object(world.json_object.as_ptr() as *mut JsObject)), ("Temporal", JsValue::from_js_object(world.temporal_object.as_ptr() as *mut JsObject)), + ("Atomics", JsValue::from_js_object(world.atomics_object.as_ptr() as *mut JsObject)), ] { bind_existing_global(core, global, name, value); } @@ -1188,6 +1191,9 @@ pub fn rebind_dirty_builtins(core: &Arc, session: &mut KernelSession if dirty.map_or(true, |d| d.math) { bind_math::bind_math(core, session, global); } + if dirty.map_or(true, |d| d.atomics) { + bind_atomics::bind_atomics(core, session, global); + } if dirty.map_or(true, |d| d.json) { bind_json::bind_json(core, session, global); } diff --git a/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs b/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs index cf992a72..5ab72092 100644 --- a/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs +++ b/crates/oxide_vm/tests/builtins_arraybuffer_state_tests.rs @@ -1303,20 +1303,21 @@ fn sab_full_reset_rebinds() { assert_eq!(text, "true|true|true|true"); } -/// STUBS 表收缩钉:剩余 6 枚 stub 全局仍为 function 且 Atomics 调用仍抛 -/// TypeError(占位行为未随 SAB 移出而漂移)。 +/// STUBS 表收缩钉:Atomics 移出 stub 后剩 5 枚 stub 全局仍为 function; +/// Atomics 现为纯对象(typeof 'object'),对其调用抛 TypeError(非函数)。 #[test] -fn sab_stubs_shrunk_six_remain() { +fn sab_stubs_shrunk_five_remain() { let mut vm = Vm::new(); let result = eval( &mut vm, "(function () { \ - var names = ['Proxy', 'WeakMap', 'WeakSet', 'WeakRef', 'FinalizationRegistry', 'Atomics']; \ + var names = ['Proxy', 'WeakMap', 'WeakSet', 'WeakRef', 'FinalizationRegistry']; \ 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 { Atomics.add(new SharedArrayBuffer(4), 0, 1); } catch (e) { threw = e instanceof TypeError; } \ + try { globalThis.Atomics(); } catch (e) { threw = e instanceof TypeError; } \ return threw; })()", ) .unwrap(); diff --git a/crates/oxide_vm/tests/builtins_atomics_tests.rs b/crates/oxide_vm/tests/builtins_atomics_tests.rs new file mode 100644 index 00000000..c2451e04 --- /dev/null +++ b/crates/oxide_vm/tests/builtins_atomics_tests.rs @@ -0,0 +1,179 @@ +use std::sync::Arc; + +use oxide_compiler::compiler::Compiler; +use oxide_types::value::JsValue; +use oxide_vm::vm::Vm; + +fn eval(vm: &mut Vm, source: &str) -> Result { + let allocator = oxide_parser::Allocator::default(); + let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?; + let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?; + vm.run(&Arc::new(module)) +} + +fn truthy(vm: &mut Vm, source: &str) { + let result = eval(vm, source).unwrap(); + assert!(result.as_bool(), "expected truthy for: {}", source); +} + +/// Atomics 为全局纯对象:typeof 'object'、proto 落 Object.prototype、 +/// `@@toStringTag` 为 "Atomics",对其调用抛 TypeError(非函数)。 +#[test] +fn atomics_is_plain_object() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "typeof Atomics === 'object' \ + && Object.getPrototypeOf(Atomics) === Object.prototype \ + && Atomics[Symbol.toStringTag] === 'Atomics'", + ); + truthy( + &mut vm, + "(function () { \ + var threw = false; \ + try { Atomics(); } catch (e) { threw = e instanceof TypeError; } \ + return threw; })()", + ); +} + +/// 方法元数据钉:name 标签与 length(语料 length 口径)。 +#[test] +fn atomics_method_name_and_length() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "Atomics.load.name === 'load' && Atomics.load.length === 2 \ + && Atomics.store.length === 3 && Atomics.exchange.length === 3 \ + && Atomics.add.length === 3 && Atomics.compareExchange.length === 4 \ + && Atomics.isLockFree.length === 1", + ); +} + +/// load:读指定索引(ArrayBuffer 后接整数视图可操作,不强制共享)。 +#[test] +fn atomics_load_int32() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "(function () { \ + var ta = new Int32Array(new ArrayBuffer(16)); \ + ta[2] = 0x01008081; \ + return Atomics.load(ta, 2) === 0x01008081 && Atomics.load(ta, 0) === 0; })()", + ); +} + +/// store:写入后读回返回(宽度归一,语料同值原样返回)。 +#[test] +fn atomics_store_returns_stored_value() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "(function () { \ + var ta = new Int32Array(new ArrayBuffer(16)); \ + var v = Atomics.store(ta, 1, 0x01008081); \ + return v === 0x01008081 && ta[1] === 0x01008081; })()", + ); +} + +/// exchange:返回旧值、写入新值。 +#[test] +fn atomics_exchange_returns_old_value() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "(function () { \ + var ta = new Int32Array(new ArrayBuffer(16)); \ + ta[0] = 7; \ + var old = Atomics.exchange(ta, 0, 42); \ + return old === 7 && ta[0] === 42; })()", + ); +} + +/// add/sub:返回旧值、写入结果(有符号域)。 +#[test] +fn atomics_add_sub_return_old() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "(function () { \ + var ta = new Int32Array(new ArrayBuffer(16)); \ + ta[0] = 10; \ + var r1 = Atomics.add(ta, 0, 5); var a1 = ta[0]; \ + var r2 = Atomics.sub(ta, 0, 3); var a2 = ta[0]; \ + return r1 === 10 && a1 === 15 && r2 === 15 && a2 === 12; })()", + ); +} + +/// and/or/xor:返回旧值、按位运算写入。 +#[test] +fn atomics_and_or_xor_return_old() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "(function () { \ + var ta = new Uint8Array(new ArrayBuffer(16)); \ + ta[0] = 0b11001100; \ + var r1 = Atomics.and(ta, 0, 0b10101010); var a1 = ta[0]; \ + var r2 = Atomics.or(ta, 0, 0b00000001); var a2 = ta[0]; \ + var r3 = Atomics.xor(ta, 0, 0b00000110); var a3 = ta[0]; \ + return r1 === 0b11001100 && a1 === 0b10001000 \ + && r2 === 0b10001000 && a2 === 0b10001001 \ + && r3 === 0b10001001 && a3 === 0b10001111; })()", + ); +} + +/// compareExchange:相等才写替换值,恒返回旧值。 +#[test] +fn atomics_compare_exchange_semantics() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "(function () { \ + var ta = new Int32Array(new ArrayBuffer(16)); \ + ta[0] = 5; \ + var no = Atomics.compareExchange(ta, 0, 9, 100); var b0 = ta[0]; \ + var yes = Atomics.compareExchange(ta, 0, 5, 100); var b1 = ta[0]; \ + return no === 5 && b0 === 5 && yes === 5 && b1 === 100; })()", + ); +} + +/// BigInt64 宽度:BigInt 域运算、返回旧值。 +#[test] +fn atomics_bigint64_load_add() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "(function () { \ + var ta = new BigInt64Array(new ArrayBuffer(32)); \ + ta[0] = 10n; \ + var r = Atomics.add(ta, 0, 5n); \ + return Atomics.load(ta, 0) === 15n && r === 10n; })()", + ); +} + +/// isLockFree:1/2/4/8 真、余假。 +#[test] +fn atomics_is_lock_free_values() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "Atomics.isLockFree(1) === true && Atomics.isLockFree(2) === true \ + && Atomics.isLockFree(4) === true && Atomics.isLockFree(8) === true \ + && Atomics.isLockFree(0) === false && Atomics.isLockFree(3) === false \ + && Atomics.isLockFree(16) === false", + ); +} + +/// 非整数宽度视图(Float32/Float64/Uint8Clamped)抛 TypeError。 +#[test] +fn atomics_rejects_non_integer_views() { + let mut vm = Vm::new(); + truthy( + &mut vm, + "(function () { \ + function throws(fn) { try { fn(); return false; } catch (e) { return e instanceof TypeError; } } \ + return throws(function () { Atomics.load(new Float32Array(new ArrayBuffer(4)), 0); }) \ + && throws(function () { Atomics.store(new Float64Array(new ArrayBuffer(8)), 0, 1); }) \ + && throws(function () { Atomics.load(new Uint8ClampedArray(new ArrayBuffer(4)), 0); }); })()", + ); +}