Преглед изворни кода

LibJS: Implement and test getters added by resizable ArrayBuffer

ForLoveOfCats пре 3 година
родитељ
комит
b29e19c52a

+ 10 - 0
Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp

@@ -48,6 +48,16 @@ ArrayBuffer::~ArrayBuffer()
 {
 }
 
+// 1.1.5 IsResizableArrayBuffer ( arrayBuffer ), https://tc39.es/proposal-resizablearraybuffer/#sec-isresizablearraybuffer
+bool ArrayBuffer::is_resizable_array_buffer() const
+{
+    // 1. Assert: Type(arrayBuffer) is Object and arrayBuffer has an [[ArrayBufferData]] internal slot.
+
+    // 2. If buffer has an [[ArrayBufferMaxByteLength]] internal slot, return true.
+    // 3. Return false.
+    return m_max_byte_length.has_value();
+}
+
 void ArrayBuffer::visit_edges(Cell::Visitor& visitor)
 {
     Base::visit_edges(visitor);

+ 3 - 0
Userland/Libraries/LibJS/Runtime/ArrayBuffer.h

@@ -34,6 +34,7 @@ public:
     virtual ~ArrayBuffer() override;
 
     size_t byte_length() const { return buffer_impl().size(); }
+    size_t max_byte_length() const { return m_max_byte_length.value(); } // Will VERIFY() that it has value
     ByteBuffer& buffer() { return buffer_impl(); }
     const ByteBuffer& buffer() const { return buffer_impl(); }
 
@@ -47,6 +48,8 @@ public:
     void detach_buffer() { m_buffer = Empty {}; }
     bool is_detached() const { return m_buffer.has<Empty>(); }
 
+    bool is_resizable_array_buffer() const;
+
     enum Order {
         SeqCst,
         Unordered

+ 43 - 0
Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp

@@ -2,6 +2,7 @@
  * Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
  * Copyright (c) 2021-2022, Jamie Mansfield <jmansfield@cadixdev.org>
  * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ * Copyright (c) 2022, the SerenityOS developers.
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -26,6 +27,8 @@ void ArrayBufferPrototype::initialize(GlobalObject& global_object)
     u8 attr = Attribute::Writable | Attribute::Configurable;
     define_native_function(vm.names.slice, slice, 2, attr);
     define_native_accessor(vm.names.byteLength, byte_length_getter, {}, Attribute::Configurable);
+    define_native_accessor(vm.names.maxByteLength, max_byte_length_getter, {}, Attribute::Configurable);
+    define_native_accessor(vm.names.resizable, resizable_getter, {}, Attribute::Configurable);
 
     // 25.1.5.4 ArrayBuffer.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-arraybuffer.prototype-@@tostringtag
     define_direct_property(*vm.well_known_symbol_to_string_tag(), js_string(vm, vm.names.ArrayBuffer.as_string()), Attribute::Configurable);
@@ -145,4 +148,44 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::byte_length_getter)
     return Value(length);
 }
 
+// 1.3.2 get ArrayBuffer.prototype.maxByteLength, https://tc39.es/proposal-resizablearraybuffer/#sec-get-arraybuffer.prototype.maxbytelength
+JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::max_byte_length_getter)
+{
+    // 1. Let O be the this value.
+    // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
+    auto* array_buffer_object = TRY(typed_this_value(global_object));
+
+    // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
+    // FIXME: Check for shared buffer
+
+    // 4. If IsDetachedBuffer(O) is true, return +0𝔽.
+    if (array_buffer_object->is_detached())
+        return Value(0);
+
+    // 5. If IsResizableArrayBuffer(O) is true, then
+    if (array_buffer_object->is_resizable_array_buffer()) {
+        // a. Let length be O.[[ArrayBufferMaxByteLength]].
+        return array_buffer_object->max_byte_length();
+    }
+
+    // 6. Else
+    //     a. Let length be O.[[ArrayBufferByteLength]].
+    // 7. Return 𝔽(length).
+    return array_buffer_object->byte_length();
+}
+
+// 1.3.3 get ArrayBuffer.prototype.resizable, https://tc39.es/proposal-resizablearraybuffer/#sec-get-arraybuffer.prototype.resizable
+JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::resizable_getter)
+{
+    // 1. Let O be the this value.
+    // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
+    auto* array_buffer_object = TRY(typed_this_value(global_object));
+
+    // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
+    // FIXME: Check for shared buffer
+
+    // 4. Return IsResizableArrayBuffer(O).
+    return array_buffer_object->is_resizable_array_buffer();
+}
+
 }

+ 2 - 0
Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.h

@@ -22,6 +22,8 @@ public:
 private:
     JS_DECLARE_NATIVE_FUNCTION(slice);
     JS_DECLARE_NATIVE_FUNCTION(byte_length_getter);
+    JS_DECLARE_NATIVE_FUNCTION(max_byte_length_getter);
+    JS_DECLARE_NATIVE_FUNCTION(resizable_getter);
 };
 
 }

+ 1 - 0
Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h

@@ -380,6 +380,7 @@ namespace JS {
     P(reject)                                \
     P(relativeTo)                            \
     P(repeat)                                \
+    P(resizable)                             \
     P(resolve)                               \
     P(resolvedOptions)                       \
     P(reverse)                               \

+ 54 - 0
Userland/Libraries/LibJS/Tests/builtins/ArrayBuffer/ArrayBuffer.js

@@ -11,3 +11,57 @@ test("ArrayBuffer constructor must be invoked with 'new'", () => {
         ArrayBuffer();
     }).toThrowWithMessage(TypeError, "ArrayBuffer constructor must be called with 'new'");
 });
+
+describe("resizable array buffer", () => {
+    test("construct with options", () => {
+        expect(new ArrayBuffer(5, { maxByteLength: 5 })).toBeInstanceOf(ArrayBuffer);
+    });
+
+    test("resizable when provided max byte length", () => {
+        expect(new ArrayBuffer(1).resizable).toEqual(false);
+        expect(new ArrayBuffer(1, {}).resizable).toEqual(false);
+        expect(new ArrayBuffer(1, { maxByteLength: undefined }).resizable).toEqual(false);
+        expect(new ArrayBuffer(1, { maxByteLength: 1 }).resizable).toEqual(true);
+    });
+
+    test("byte length must be shorter than max byte length", () => {
+        expect(() => {
+            new ArrayBuffer(1, { maxByteLength: 0 });
+        }).toThrowWithMessage(RangeError, "Byte length exceeds maxByteLength option");
+    });
+
+    test("max byte length cannot be too large", () => {
+        expect(() => {
+            new ArrayBuffer(0, { maxByteLength: 9007199254740992 });
+        }).toThrowWithMessage(RangeError, "Index must be a positive integer");
+    });
+
+    test("max byte length cannot be negative", () => {
+        expect(() => {
+            new ArrayBuffer(0, { maxByteLength: -1 });
+        }).toThrowWithMessage(RangeError, "Index must be a positive integer");
+    });
+
+    test("invalid max byte length object", () => {
+        expect(() => {
+            new ArrayBuffer(0, {
+                maxByteLength: {
+                    toString: function () {
+                        return {};
+                    },
+                    valueOf: function () {
+                        return {};
+                    },
+                },
+            });
+        }).toThrowWithMessage(TypeError, "Cannot convert object to number");
+
+        expect(() => {
+            new ArrayBuffer(0, {
+                get maxByteLength() {
+                    throw "Exception";
+                },
+            });
+        }).toThrow();
+    });
+});