Explorar o código

LibJS: Stub out Atomics.notify

We don't have the facilities to implement this method fully (namely, a
fully realized SharedArrayBuffer). But we can implement enough to
validate the values passed in by the user.
Timothy Flynn hai 1 ano
pai
achega
026363024f

+ 46 - 0
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<InternalError>(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_)
 {

+ 1 - 0
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_);
 };
 

+ 1 - 0
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)                               \

+ 56 - 0
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);
+    });
+});