mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 绑定 Atomics 全局对象方法并接入 test262
This commit is contained in:
@@ -65,10 +65,10 @@ fn is_bigint_kind(kind: TypedArrayKind) -> bool {
|
|||||||
/// # 步骤
|
/// # 步骤
|
||||||
/// 1. `get_typed_array_data` 品牌校验(非 TA 对象抛 TypeError)。
|
/// 1. `get_typed_array_data` 品牌校验(非 TA 对象抛 TypeError)。
|
||||||
/// 2. 整数宽度守卫(浮点/clamped 视图抛 TypeError)。
|
/// 2. 整数宽度守卫(浮点/clamped 视图抛 TypeError)。
|
||||||
/// 3. 索引经 ToIntegerOrInfinity(valueOf/toString 抛错原值上抛,先于越界判)。
|
/// 3. 写族加查写守卫(immutable/detach 抛 TypeError),先于索引/值 coercion。
|
||||||
/// 4. 相对字节偏移 = view.byte_offset + index·bpe(f64 运算);越界
|
/// 4. 索引经 ToIntegerOrInfinity(valueOf/toString 抛错原值上抛,先于越界判)。
|
||||||
|
/// 5. 相对字节偏移 = view.byte_offset + index·bpe(f64 运算);越界
|
||||||
/// (`< 0` 或 `>= buffer 字节长`)抛 RangeError,detach 视同越界。
|
/// (`< 0` 或 `>= buffer 字节长`)抛 RangeError,detach 视同越界。
|
||||||
/// 5. 写族加查写守卫(immutable/detach 抛 TypeError)。
|
|
||||||
///
|
///
|
||||||
/// # 边界与前提
|
/// # 边界与前提
|
||||||
/// - 返回的载荷指针、视图与偏移在 session 独占期内有效,调用方同一语句内消费。
|
/// - 返回的载荷指针、视图与偏移在 session 独占期内有效,调用方同一语句内消费。
|
||||||
@@ -83,6 +83,11 @@ fn resolve_atomic_access<H: VmHost>(
|
|||||||
if !is_integer_kind(view.kind) {
|
if !is_integer_kind(view.kind) {
|
||||||
return Err(type_error(vm, "Atomics operations require an integer TypedArray type"));
|
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)?;
|
let index = ta_to_integer_or_infinity(vm, index_val)?;
|
||||||
|
|
||||||
// 缓冲字节长:AB/SAB 双认(buffer_payload_ptr),载荷缺失视同越界。
|
// 缓冲字节长:AB/SAB 双认(buffer_payload_ptr),载荷缺失视同越界。
|
||||||
@@ -102,9 +107,6 @@ fn resolve_atomic_access<H: VmHost>(
|
|||||||
if offset < 0.0 || offset >= buffer_len as f64 {
|
if offset < 0.0 || offset >= buffer_len as f64 {
|
||||||
return Err(range_error(vm, "invalid indexed access to Atomics"));
|
return Err(range_error(vm, "invalid indexed access to Atomics"));
|
||||||
}
|
}
|
||||||
if writable {
|
|
||||||
ta_validate(vm, view, true)?;
|
|
||||||
}
|
|
||||||
Ok((payload_ptr, view, offset as usize))
|
Ok((payload_ptr, view, offset as usize))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,18 +163,18 @@ fn arithmetic_write_value<H: VmHost>(
|
|||||||
|
|
||||||
/// `Atomics.load(typedArray, index)`:读指定索引元素。
|
/// `Atomics.load(typedArray, index)`:读指定索引元素。
|
||||||
pub fn atomics_load<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn atomics_load<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
let this_val = arg_at(vm, args, 0);
|
let ta_val = arg_at(vm, args, 1);
|
||||||
let index_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, this_val, false, index_val));
|
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()))
|
NativeResult::Ok(atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(JsValue::undefined()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Atomics.store(typedArray, index, value)`:写值后读回返回(宽度归一,-0 → +0)。
|
/// `Atomics.store(typedArray, index, value)`:写值后读回返回(宽度归一,-0 → +0)。
|
||||||
pub fn atomics_store<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn atomics_store<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
let this_val = arg_at(vm, args, 0);
|
let ta_val = arg_at(vm, args, 1);
|
||||||
let index_val = arg_at(vm, args, 1);
|
let index_val = arg_at(vm, args, 2);
|
||||||
let value_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, this_val, true, index_val));
|
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));
|
let value = native_try!(ta_element_value(vm, view.kind, value_val));
|
||||||
atomic_write(vm, payload_ptr, view.kind, offset, value);
|
atomic_write(vm, payload_ptr, view.kind, offset, value);
|
||||||
NativeResult::Ok(atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(value))
|
NativeResult::Ok(atomic_read(vm, payload_ptr, view.kind, offset).unwrap_or(value))
|
||||||
@@ -180,10 +182,10 @@ pub fn atomics_store<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
|
|
||||||
/// `Atomics.exchange(typedArray, index, value)`:读旧值、写新值、返回旧值。
|
/// `Atomics.exchange(typedArray, index, value)`:读旧值、写新值、返回旧值。
|
||||||
pub fn atomics_exchange<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn atomics_exchange<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
let this_val = arg_at(vm, args, 0);
|
let ta_val = arg_at(vm, args, 1);
|
||||||
let index_val = arg_at(vm, args, 1);
|
let index_val = arg_at(vm, args, 2);
|
||||||
let value_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, this_val, true, index_val));
|
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 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 value = native_try!(ta_element_value(vm, view.kind, value_val));
|
||||||
atomic_write(vm, payload_ptr, view.kind, offset, value);
|
atomic_write(vm, payload_ptr, view.kind, offset, value);
|
||||||
@@ -217,10 +219,10 @@ pub fn atomics_xor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
|
|
||||||
/// add/sub/and/or/xor 共享核:读旧值 → 运算值转换 → 读-改-写 → 返回旧值。
|
/// add/sub/and/or/xor 共享核:读旧值 → 运算值转换 → 读-改-写 → 返回旧值。
|
||||||
fn atomic_arithmetic<H: VmHost>(vm: &mut H, args: &[u8], op: &str) -> NativeResult {
|
fn atomic_arithmetic<H: VmHost>(vm: &mut H, args: &[u8], op: &str) -> NativeResult {
|
||||||
let this_val = arg_at(vm, args, 0);
|
let ta_val = arg_at(vm, args, 1);
|
||||||
let index_val = arg_at(vm, args, 1);
|
let index_val = arg_at(vm, args, 2);
|
||||||
let value_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, this_val, true, index_val));
|
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 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 value = native_try!(ta_element_value(vm, view.kind, value_val));
|
||||||
let result = arithmetic_write_value(vm, view.kind, op, old, value);
|
let result = arithmetic_write_value(vm, view.kind, op, old, value);
|
||||||
@@ -231,11 +233,11 @@ fn atomic_arithmetic<H: VmHost>(vm: &mut H, args: &[u8], op: &str) -> NativeResu
|
|||||||
/// `Atomics.compareExchange(typedArray, index, expected, replacement)`:读旧值;
|
/// `Atomics.compareExchange(typedArray, index, expected, replacement)`:读旧值;
|
||||||
/// 相等(数值宽度 `==`、BigInt 宽度 BigInt 相等)则写 replacement;返回旧值。
|
/// 相等(数值宽度 `==`、BigInt 宽度 BigInt 相等)则写 replacement;返回旧值。
|
||||||
pub fn atomics_compare_exchange<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn atomics_compare_exchange<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
let this_val = arg_at(vm, args, 0);
|
let ta_val = arg_at(vm, args, 1);
|
||||||
let index_val = arg_at(vm, args, 1);
|
let index_val = arg_at(vm, args, 2);
|
||||||
let expected_val = arg_at(vm, args, 2);
|
let expected_val = arg_at(vm, args, 3);
|
||||||
let replacement_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, this_val, true, index_val));
|
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 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 expected = native_try!(ta_element_value(vm, view.kind, expected_val));
|
||||||
let matched = if is_bigint_kind(view.kind) {
|
let matched = if is_bigint_kind(view.kind) {
|
||||||
|
|||||||
@@ -25,7 +25,3 @@ pub fn weakref_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
|||||||
pub fn finalization_registry_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
pub fn finalization_registry_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||||
stub_error(vm, "FinalizationRegistry")
|
stub_error(vm, "FinalizationRegistry")
|
||||||
}
|
}
|
||||||
/// `Atomics` 全局占位:始终抛 TypeError。按设计延后实现。
|
|
||||||
pub fn atomics_stub<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
|
||||||
stub_error(vm, "Atomics")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -725,10 +725,7 @@ mod tests {
|
|||||||
assert!(!dirty.shared_array_buffer);
|
assert!(!dirty.shared_array_buffer);
|
||||||
|
|
||||||
// 脏家族换新对象,未脏家族指针原样保留。
|
// 脏家族换新对象,未脏家族指针原样保留。
|
||||||
assert!(!std::ptr::eq(
|
assert!(!std::ptr::eq(atomics, session.builtin_world.atomics_object.as_ptr() as *mut JsObject));
|
||||||
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));
|
assert!(std::ptr::eq(ab_proto, session.builtin_world.array_buffer_proto.as_ptr() as *mut JsObject));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! test262 harness 内嵌注册表与前缀拼接/缓存:`HarnessSources`(23 处 `include_str!`)、`HARNESS`、黑名单、
|
//! test262 harness 内嵌注册表与前缀拼接/缓存:`HarnessSources`(24 处 `include_str!`)、`HARNESS`、黑名单、
|
||||||
//! 前缀拼接与缓存。全部 `include_str!` 路径相对上 3 级到仓库根,依赖本文件平铺 `src/` 一级,禁子目录。
|
//! 前缀拼接与缓存。全部 `include_str!` 路径相对上 3 级到仓库根,依赖本文件平铺 `src/` 一级,禁子目录。
|
||||||
|
|
||||||
use crate::meta::TestMeta;
|
use crate::meta::TestMeta;
|
||||||
@@ -46,6 +46,7 @@ impl HarnessSources {
|
|||||||
sources.insert("typeCoercion.js", include_str!("../../../tests/test262/harness/typeCoercion.js"));
|
sources.insert("typeCoercion.js", include_str!("../../../tests/test262/harness/typeCoercion.js"));
|
||||||
sources.insert("deepEqual.js", include_str!("../../../tests/test262/harness/deepEqual.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("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("temporalHelpers.js", include_str!("../../../tests/test262/harness/temporalHelpers.js"));
|
||||||
sources.insert("asyncHelpers.js", include_str!("../../../tests/test262/harness/asyncHelpers.js"));
|
sources.insert("asyncHelpers.js", include_str!("../../../tests/test262/harness/asyncHelpers.js"));
|
||||||
sources.insert("doneprintHandle.js", include_str!("../../../tests/test262/harness/doneprintHandle.js"));
|
sources.insert("doneprintHandle.js", include_str!("../../../tests/test262/harness/doneprintHandle.js"));
|
||||||
@@ -74,7 +75,6 @@ fn is_blacklisted_harness(name: &str) -> bool {
|
|||||||
matches!(
|
matches!(
|
||||||
name,
|
name,
|
||||||
"testIntl.js"
|
"testIntl.js"
|
||||||
| "testAtomics.js"
|
|
||||||
| "atomicsHelper.js"
|
| "atomicsHelper.js"
|
||||||
| "proxyTrapsHelper.js"
|
| "proxyTrapsHelper.js"
|
||||||
| "tcoHelper.js"
|
| "tcoHelper.js"
|
||||||
|
|||||||
@@ -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<KernelCore>, 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::<crate::vm::Vm>, 2),
|
||||||
|
("store", oxide_builtins::atomics::atomics_store::<crate::vm::Vm>, 3),
|
||||||
|
("exchange", oxide_builtins::atomics::atomics_exchange::<crate::vm::Vm>, 3),
|
||||||
|
("add", oxide_builtins::atomics::atomics_add::<crate::vm::Vm>, 3),
|
||||||
|
("sub", oxide_builtins::atomics::atomics_sub::<crate::vm::Vm>, 3),
|
||||||
|
("and", oxide_builtins::atomics::atomics_and::<crate::vm::Vm>, 3),
|
||||||
|
("or", oxide_builtins::atomics::atomics_or::<crate::vm::Vm>, 3),
|
||||||
|
("xor", oxide_builtins::atomics::atomics_xor::<crate::vm::Vm>, 3),
|
||||||
|
("compareExchange", oxide_builtins::atomics::atomics_compare_exchange::<crate::vm::Vm>, 4),
|
||||||
|
("isLockFree", oxide_builtins::atomics::atomics_is_lock_free::<crate::vm::Vm>, 1),
|
||||||
|
);
|
||||||
|
|
||||||
|
let a_val = JsValue::from_js_object(atomics_ptr);
|
||||||
|
bind_global_value(core, global, "Atomics", a_val);
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ use oxide_types::value::JsValue;
|
|||||||
|
|
||||||
use crate::bindings::{bind_global_value, configure_native_constructor};
|
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::<crate::vm::Vm> as *const (), 2),
|
("Proxy", oxide_builtins::stubs::proxy_stub::<crate::vm::Vm> as *const (), 2),
|
||||||
("WeakMap", oxide_builtins::stubs::weakmap_stub::<crate::vm::Vm> as *const (), 0),
|
("WeakMap", oxide_builtins::stubs::weakmap_stub::<crate::vm::Vm> as *const (), 0),
|
||||||
("WeakSet", oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (), 0),
|
("WeakSet", oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (), 0),
|
||||||
@@ -18,10 +18,9 @@ const STUBS: [(&str, *const (), u8); 6] = [
|
|||||||
oxide_builtins::stubs::finalization_registry_stub::<crate::vm::Vm> as *const (),
|
oxide_builtins::stubs::finalization_registry_stub::<crate::vm::Vm> as *const (),
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
("Atomics", oxide_builtins::stubs::atomics_stub::<crate::vm::Vm> as *const (), 0),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/// 把未实现内置(Proxy/WeakMap/WeakSet/WeakRef/FinalizationRegistry/Atomics)
|
/// 把未实现内置(Proxy/WeakMap/WeakSet/WeakRef/FinalizationRegistry)
|
||||||
/// 的 stub 构造器绑定到 global,并登记到 `stub_objects` 供快照跟踪。
|
/// 的 stub 构造器绑定到 global,并登记到 `stub_objects` 供快照跟踪。
|
||||||
pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &mut JsObject) {
|
pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &mut JsObject) {
|
||||||
let builtin_world = Arc::get_mut(&mut session.builtin_world)
|
let builtin_world = Arc::get_mut(&mut session.builtin_world)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ pub mod bind_array;
|
|||||||
pub mod bind_array_buffer;
|
pub mod bind_array_buffer;
|
||||||
/// AsyncDisposableStack 构造器与原型的 native 方法绑定(含 dirty reset 同步)。
|
/// AsyncDisposableStack 构造器与原型的 native 方法绑定(含 dirty reset 同步)。
|
||||||
pub mod bind_async_disposable_stack;
|
pub mod bind_async_disposable_stack;
|
||||||
|
/// Atomics 全局纯对象及其 10 个原子方法绑定。
|
||||||
|
pub mod bind_atomics;
|
||||||
/// BigInt 构造器与原型的 native 方法绑定。
|
/// BigInt 构造器与原型的 native 方法绑定。
|
||||||
pub mod bind_bigint;
|
pub mod bind_bigint;
|
||||||
/// Boolean 构造器与原型的 native 方法绑定。
|
/// Boolean 构造器与原型的 native 方法绑定。
|
||||||
@@ -634,7 +636,7 @@ fn install_to_string_tags(core: &Arc<KernelCore>, session: &KernelSession) {
|
|||||||
let sh = core.shape_forge().as_ref();
|
let sh = core.shape_forge().as_ref();
|
||||||
let tag_id = oxide_types::private_key::WELL_KNOWN_SYMBOL_TO_STRING_TAG;
|
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 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.map_proto.as_ptr() as *mut JsObject, "Map"),
|
||||||
(world.array_buffer_proto.as_ptr() as *mut JsObject, "ArrayBuffer"),
|
(world.array_buffer_proto.as_ptr() as *mut JsObject, "ArrayBuffer"),
|
||||||
(world.shared_array_buffer_proto.as_ptr() as *mut JsObject, "SharedArrayBuffer"),
|
(world.shared_array_buffer_proto.as_ptr() as *mut JsObject, "SharedArrayBuffer"),
|
||||||
@@ -648,6 +650,7 @@ fn install_to_string_tags(core: &Arc<KernelCore>, session: &KernelSession) {
|
|||||||
(world.math_object.as_ptr() as *mut JsObject, "Math"),
|
(world.math_object.as_ptr() as *mut JsObject, "Math"),
|
||||||
(world.json_object.as_ptr() as *mut JsObject, "JSON"),
|
(world.json_object.as_ptr() as *mut JsObject, "JSON"),
|
||||||
(world.symbol_proto.as_ptr() as *mut JsObject, "Symbol"),
|
(world.symbol_proto.as_ptr() as *mut JsObject, "Symbol"),
|
||||||
|
(world.atomics_object.as_ptr() as *mut JsObject, "Atomics"),
|
||||||
];
|
];
|
||||||
for (ptr, tag) in cases {
|
for (ptr, tag) in cases {
|
||||||
// SAFETY: ptr 为当前 builtin world 的内置原型,session 独占期间始终有效。
|
// SAFETY: ptr 为当前 builtin world 的内置原型,session 独占期间始终有效。
|
||||||
@@ -887,7 +890,6 @@ fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
|||||||
oxide_builtins::stubs::finalization_registry_stub::<crate::vm::Vm> as *const (),
|
oxide_builtins::stubs::finalization_registry_stub::<crate::vm::Vm> as *const (),
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
("Atomics", oxide_builtins::stubs::atomics_stub::<crate::vm::Vm> as *const (), 0),
|
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@@ -997,6 +999,7 @@ pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession
|
|||||||
("Math", JsValue::from_js_object(world.math_object.as_ptr() as *mut JsObject)),
|
("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)),
|
("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)),
|
("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);
|
bind_existing_global(core, global, name, value);
|
||||||
}
|
}
|
||||||
@@ -1188,6 +1191,9 @@ pub fn rebind_dirty_builtins(core: &Arc<KernelCore>, session: &mut KernelSession
|
|||||||
if dirty.map_or(true, |d| d.math) {
|
if dirty.map_or(true, |d| d.math) {
|
||||||
bind_math::bind_math(core, session, global);
|
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) {
|
if dirty.map_or(true, |d| d.json) {
|
||||||
bind_json::bind_json(core, session, global);
|
bind_json::bind_json(core, session, global);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1303,20 +1303,21 @@ fn sab_full_reset_rebinds() {
|
|||||||
assert_eq!(text, "true|true|true|true");
|
assert_eq!(text, "true|true|true|true");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// STUBS 表收缩钉:剩余 6 枚 stub 全局仍为 function 且 Atomics 调用仍抛
|
/// STUBS 表收缩钉:Atomics 移出 stub 后剩 5 枚 stub 全局仍为 function;
|
||||||
/// TypeError(占位行为未随 SAB 移出而漂移)。
|
/// Atomics 现为纯对象(typeof 'object'),对其调用抛 TypeError(非函数)。
|
||||||
#[test]
|
#[test]
|
||||||
fn sab_stubs_shrunk_six_remain() {
|
fn sab_stubs_shrunk_five_remain() {
|
||||||
let mut vm = Vm::new();
|
let mut vm = Vm::new();
|
||||||
let result = eval(
|
let result = eval(
|
||||||
&mut vm,
|
&mut vm,
|
||||||
"(function () { \
|
"(function () { \
|
||||||
var names = ['Proxy', 'WeakMap', 'WeakSet', 'WeakRef', 'FinalizationRegistry', 'Atomics']; \
|
var names = ['Proxy', 'WeakMap', 'WeakSet', 'WeakRef', 'FinalizationRegistry']; \
|
||||||
for (var i = 0; i < names.length; i++) { \
|
for (var i = 0; i < names.length; i++) { \
|
||||||
if (typeof globalThis[names[i]] !== 'function') { return false; } \
|
if (typeof globalThis[names[i]] !== 'function') { return false; } \
|
||||||
} \
|
} \
|
||||||
|
if (typeof globalThis.Atomics !== 'object') { return false; } \
|
||||||
var threw = 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; })()",
|
return threw; })()",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -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<JsValue, String> {
|
||||||
|
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); }); })()",
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user