Browse Source

LibJS: Accept ArrayBuffer constructor options argument

Test262 seems to test the changes in the "Resizable ArrayBuffer and
growable SharedArrayBuffer" proposal. Begin implementing this proposal
by accepting the new options object argument to the ArrayBuffer
constructor.

https://tc39.es/proposal-resizablearraybuffer
https://github.com/tc39/test262/blob/main/test/built-ins/ArrayBuffer/options-maxbytelength-diminuitive.js
ForLoveOfCats 3 years ago
parent
commit
e8416b603f

+ 4 - 1
Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
  * Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
+ * Copyright (c) 2022, the SerenityOS developers.
  *
  *
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
@@ -54,8 +55,10 @@ void ArrayBuffer::visit_edges(Cell::Visitor& visitor)
 }
 }
 
 
 // 25.1.2.1 AllocateArrayBuffer ( constructor, byteLength ), https://tc39.es/ecma262/#sec-allocatearraybuffer
 // 25.1.2.1 AllocateArrayBuffer ( constructor, byteLength ), https://tc39.es/ecma262/#sec-allocatearraybuffer
-ThrowCompletionOr<ArrayBuffer*> allocate_array_buffer(GlobalObject& global_object, FunctionObject& constructor, size_t byte_length)
+// 1.1.2 AllocateArrayBuffer ( constructor, byteLength [, maxByteLength ] ), https://tc39.es/proposal-resizablearraybuffer/#sec-allocatearraybuffer
+ThrowCompletionOr<ArrayBuffer*> allocate_array_buffer(GlobalObject& global_object, FunctionObject& constructor, size_t byte_length, Optional<size_t> max_byte_length)
 {
 {
+    (void)max_byte_length;
     // 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] »).
     // 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] »).
     auto* obj = TRY(ordinary_create_from_constructor<ArrayBuffer>(global_object, constructor, &GlobalObject::array_buffer_prototype, nullptr));
     auto* obj = TRY(ordinary_create_from_constructor<ArrayBuffer>(global_object, constructor, &GlobalObject::array_buffer_prototype, nullptr));
 
 

+ 1 - 1
Userland/Libraries/LibJS/Runtime/ArrayBuffer.h

@@ -75,7 +75,7 @@ private:
     Value m_detach_key;
     Value m_detach_key;
 };
 };
 
 
-ThrowCompletionOr<ArrayBuffer*> allocate_array_buffer(GlobalObject&, FunctionObject& constructor, size_t byte_length);
+ThrowCompletionOr<ArrayBuffer*> allocate_array_buffer(GlobalObject&, FunctionObject& constructor, size_t byte_length, Optional<size_t> max_byte_length = {});
 ThrowCompletionOr<ArrayBuffer*> clone_array_buffer(GlobalObject&, ArrayBuffer& source_buffer, size_t source_byte_offset, size_t source_length, FunctionObject& clone_constructor);
 ThrowCompletionOr<ArrayBuffer*> clone_array_buffer(GlobalObject&, ArrayBuffer& source_buffer, size_t source_byte_offset, size_t source_length, FunctionObject& clone_constructor);
 
 
 // 25.1.2.9 RawBytesToNumeric ( type, rawBytes, isLittleEndian ), https://tc39.es/ecma262/#sec-rawbytestonumeric
 // 25.1.2.9 RawBytesToNumeric ( type, rawBytes, isLittleEndian ), https://tc39.es/ecma262/#sec-rawbytestonumeric

+ 47 - 3
Userland/Libraries/LibJS/Runtime/ArrayBufferConstructor.cpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
  * Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
+ * Copyright (c) 2022, the SerenityOS developers.
  *
  *
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
@@ -46,20 +47,63 @@ ThrowCompletionOr<Value> ArrayBufferConstructor::call()
     return vm.throw_completion<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, vm.names.ArrayBuffer);
     return vm.throw_completion<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, vm.names.ArrayBuffer);
 }
 }
 
 
+// 1.1.6 GetArrayBufferMaxByteLengthOption ( options ), https://tc39.es/proposal-resizablearraybuffer/#sec-getarraybuffermaxbytelengthoption
+static ThrowCompletionOr<Optional<size_t>> get_array_buffer_max_byte_length_option(VM& vm, GlobalObject& global_object, Value options)
+{
+    // 1. If Type(options) is not Object, return empty.
+    if (!options.is_object())
+        return Optional<size_t>();
+
+    // 2. Let maxByteLength be ? Get(options, "maxByteLength").
+    auto max_byte_length = TRY(options.get(global_object, vm.names.maxByteLength));
+
+    // 3. If maxByteLength is undefined, return empty.
+    if (max_byte_length.is_undefined())
+        return Optional<size_t>();
+
+    // 4. Return ? ToIndex(maxByteLength).
+    return TRY(max_byte_length.to_index(global_object));
+}
+
 // 25.1.3.1 ArrayBuffer ( length ), https://tc39.es/ecma262/#sec-arraybuffer-length
 // 25.1.3.1 ArrayBuffer ( length ), https://tc39.es/ecma262/#sec-arraybuffer-length
+// 1.2.1 ArrayBuffer ( length [, options ] ), https://tc39.es/proposal-resizablearraybuffer/#sec-arraybuffer-constructor
 ThrowCompletionOr<Object*> ArrayBufferConstructor::construct(FunctionObject& new_target)
 ThrowCompletionOr<Object*> ArrayBufferConstructor::construct(FunctionObject& new_target)
 {
 {
+    auto& global_object = new_target.realm()->global_object();
     auto& vm = this->vm();
     auto& vm = this->vm();
-    auto byte_length_or_error = vm.argument(0).to_index(global_object());
+
+    // 1. If NewTarget is undefined, throw a TypeError exception.
+    // NOTE: See `ArrayBufferConstructor::call()`
+
+    // 2. Let byteLength be ? ToIndex(length).
+    auto byte_length_or_error = vm.argument(0).to_index(global_object);
     if (byte_length_or_error.is_error()) {
     if (byte_length_or_error.is_error()) {
         auto error = byte_length_or_error.release_error();
         auto error = byte_length_or_error.release_error();
         if (error.value()->is_object() && is<RangeError>(error.value()->as_object())) {
         if (error.value()->is_object() && is<RangeError>(error.value()->as_object())) {
             // Re-throw more specific RangeError
             // Re-throw more specific RangeError
-            return vm.throw_completion<RangeError>(global_object(), ErrorType::InvalidLength, "array buffer");
+            return vm.throw_completion<RangeError>(global_object, ErrorType::InvalidLength, "array buffer");
         }
         }
         return error;
         return error;
     }
     }
-    return TRY(allocate_array_buffer(global_object(), new_target, byte_length_or_error.release_value()));
+    auto byte_length = byte_length_or_error.release_value();
+
+    // 3. Let requestedMaxByteLength be ? GetArrayBufferMaxByteLengthOption(options).
+    auto options = vm.argument(1);
+    auto requested_max_byte_length_value = TRY(get_array_buffer_max_byte_length_option(vm, global_object, options));
+
+    // 4. If requestedMaxByteLength is empty, then
+    if (!requested_max_byte_length_value.has_value()) {
+        // a. Return ? AllocateArrayBuffer(NewTarget, byteLength).
+        return TRY(allocate_array_buffer(global_object, new_target, byte_length));
+    }
+    auto requested_max_byte_length = requested_max_byte_length_value.release_value();
+
+    // 5. If byteLength > requestedMaxByteLength, throw a RangeError exception.
+    if (byte_length > requested_max_byte_length)
+        return vm.throw_completion<RangeError>(global_object, ErrorType::ByteLengthBeyondRequestedMax);
+
+    // 6. Return ? AllocateArrayBuffer(NewTarget, byteLength, requestedMaxByteLength).
+    return TRY(allocate_array_buffer(global_object, new_target, byte_length, requested_max_byte_length));
 }
 }
 
 
 // 25.1.4.1 ArrayBuffer.isView ( arg ), https://tc39.es/ecma262/#sec-arraybuffer.isview
 // 25.1.4.1 ArrayBuffer.isView ( arg ), https://tc39.es/ecma262/#sec-arraybuffer.isview

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

@@ -314,6 +314,7 @@ namespace JS {
     P(log10)                                 \
     P(log10)                                 \
     P(map)                                   \
     P(map)                                   \
     P(max)                                   \
     P(max)                                   \
+    P(maxByteLength)                         \
     P(maximize)                              \
     P(maximize)                              \
     P(mergeFields)                           \
     P(mergeFields)                           \
     P(message)                               \
     P(message)                               \

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

@@ -15,6 +15,7 @@
     M(BigIntFromNonIntegral, "Cannot convert non-integral number to BigInt")                                                            \
     M(BigIntFromNonIntegral, "Cannot convert non-integral number to BigInt")                                                            \
     M(BigIntInvalidValue, "Invalid value for BigInt: {}")                                                                               \
     M(BigIntInvalidValue, "Invalid value for BigInt: {}")                                                                               \
     M(BindingNotInitialized, "Binding {} is not initialized")                                                                           \
     M(BindingNotInitialized, "Binding {} is not initialized")                                                                           \
+    M(ByteLengthBeyondRequestedMax, "Byte length exceeds maxByteLength option")                                                         \
     M(CallStackSizeExceeded, "Call stack size limit exceeded")                                                                          \
     M(CallStackSizeExceeded, "Call stack size limit exceeded")                                                                          \
     M(CannotDeclareGlobalFunction, "Cannot declare global function of name '{}'")                                                       \
     M(CannotDeclareGlobalFunction, "Cannot declare global function of name '{}'")                                                       \
     M(CannotDeclareGlobalVariable, "Cannot declare global variable of name '{}'")                                                       \
     M(CannotDeclareGlobalVariable, "Cannot declare global variable of name '{}'")                                                       \