diff --git a/Userland/Libraries/LibJS/Runtime/AtomicsObject.cpp b/Userland/Libraries/LibJS/Runtime/AtomicsObject.cpp index 4e5031df67..50a211d973 100644 --- a/Userland/Libraries/LibJS/Runtime/AtomicsObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/AtomicsObject.cpp @@ -213,6 +213,7 @@ void AtomicsObject::initialize(Realm& realm) define_native_function(realm, vm.names.sub, sub, 3, attr); define_native_function(realm, vm.names.wait, wait, 4, attr); define_native_function(realm, vm.names.waitAsync, wait_async, 4, attr); + define_native_function(realm, vm.names.notify, notify, 3, attr); define_native_function(realm, vm.names.xor_, xor_, 3, attr); // 25.4.15 Atomics [ @@toStringTag ], https://tc39.es/ecma262/#sec-atomics-@@tostringtag @@ -486,6 +487,51 @@ JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::wait_async) return TRY(do_wait(vm, WaitMode::Async, *typed_array, index, value, timeout)); } +// 25.4.15 Atomics.notify ( typedArray, index, count ), https://tc39.es/ecma262/#sec-atomics.notify +JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::notify) +{ + auto* typed_array = TRY(typed_array_from(vm, vm.argument(0))); + auto index = vm.argument(1); + auto count_value = vm.argument(2); + + // 1. Let byteIndexInBuffer be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index, true). + // FIXME: ValidateAtomicAccessOnIntegerTypedArray is a new AO from the resizable array buffer proposal. Use it when the proposal is implemented. + TRY(validate_integer_typed_array(vm, *typed_array, true)); + auto byte_index_in_buffer = TRY(validate_atomic_access(vm, *typed_array, index)); + + // 2. If count is undefined, then + double count = 0.0; + if (count_value.is_undefined()) { + // a. Let c be +∞. + count = js_infinity().as_double(); + } + // 3. Else, + else { + // a. Let intCount be ? ToIntegerOrInfinity(count). + auto int_count = TRY(count_value.to_integer_or_infinity(vm)); + + // b. Let c be max(intCount, 0). + count = max(int_count, 0.0); + } + + // 4. Let buffer be typedArray.[[ViewedArrayBuffer]]. + auto* buffer = typed_array->viewed_array_buffer(); + + // 5. Let block be buffer.[[ArrayBufferData]]. + auto& block = buffer->buffer(); + + // 6. If IsSharedArrayBuffer(buffer) is false, return +0𝔽. + if (!buffer->is_shared_array_buffer()) + return Value { 0 }; + + // FIXME: Implement the remaining steps when we support SharedArrayBuffer. + (void)byte_index_in_buffer; + (void)count; + (void)block; + + return vm.throw_completion(ErrorType::NotImplemented, "SharedArrayBuffer"sv); +} + // 25.4.14 Atomics.xor ( typedArray, index, value ), https://tc39.es/ecma262/#sec-atomics.xor JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::xor_) { diff --git a/Userland/Libraries/LibJS/Runtime/AtomicsObject.h b/Userland/Libraries/LibJS/Runtime/AtomicsObject.h index 004263718a..bf2404b689 100644 --- a/Userland/Libraries/LibJS/Runtime/AtomicsObject.h +++ b/Userland/Libraries/LibJS/Runtime/AtomicsObject.h @@ -32,6 +32,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(sub); JS_DECLARE_NATIVE_FUNCTION(wait); JS_DECLARE_NATIVE_FUNCTION(wait_async); + JS_DECLARE_NATIVE_FUNCTION(notify); JS_DECLARE_NATIVE_FUNCTION(xor_); }; diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index 2a8b24c8cb..280affeb91 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -387,6 +387,7 @@ namespace JS { P(next) \ P(normalize) \ P(notation) \ + P(notify) \ P(now) \ P(numberingSystem) \ P(numeric) \ diff --git a/Userland/Libraries/LibJS/Tests/builtins/Atomics/Atomics.notify.js b/Userland/Libraries/LibJS/Tests/builtins/Atomics/Atomics.notify.js new file mode 100644 index 0000000000..75ecbaba70 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Atomics/Atomics.notify.js @@ -0,0 +1,56 @@ +describe("errors", () => { + test("called on non-TypedArray", () => { + expect(() => { + Atomics.notify(Symbol.hasInstance, 0, 0); + }).toThrowWithMessage(TypeError, "Not an object of type TypedArray"); + }); + + test("detached buffer", () => { + expect(() => { + const typedArray = new Int32Array(4); + detachArrayBuffer(typedArray.buffer); + + Atomics.notify(typedArray, 0, 0); + }).toThrowWithMessage(TypeError, "ArrayBuffer is detached"); + }); + + test("invalid TypedArray type", () => { + expect(() => { + const typedArray = new Float32Array(4); + Atomics.notify(typedArray, 0, 0); + }).toThrowWithMessage( + TypeError, + "Typed array Float32Array element type is not Int32 or BigInt64" + ); + }); + + test("invalid index", () => { + expect(() => { + const buffer = new SharedArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT); + const typedArray = new Int32Array(buffer); + + Atomics.notify(typedArray, 4, 0); + }).toThrowWithMessage(RangeError, "Index 4 is out of range of array length 4"); + }); + + test("invalid count", () => { + expect(() => { + const buffer = new SharedArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT); + const typedArray = new Int32Array(buffer); + + Atomics.notify(typedArray, 0, Symbol.hasInstance); + }).toThrowWithMessage(TypeError, "Cannot convert symbol to number"); + }); +}); + +test("basic functionality", () => { + test("invariants", () => { + expect(Atomics.notify).toHaveLength(3); + }); + + test("non-shared ArrayBuffer", () => { + const typedArray = new Int32Array(4); + const waiters = Atomics.notify(typedArray, 0, 0); + expect(waiters).toBe(0); + }); +});