Jelajahi Sumber

LibJS: Convert ArrayBuffer construction to ThrowCompletionOr

This also allows us to create TypedArrays with an existing buffer thus
clearing up an additional FIXME in TextEncoder.
davidot 3 tahun lalu
induk
melakukan
de90d54be0

+ 1 - 1
Tests/LibWasm/test-wasm.cpp

@@ -19,7 +19,7 @@ TESTJS_GLOBAL_FUNCTION(read_binary_wasm_file, readBinaryWasmFile)
     if (file.is_error())
         return vm.throw_completion<JS::TypeError>(global_object, strerror(file.error().code()));
     auto contents = file.value()->read_all();
-    auto array = JS::Uint8Array::create(global_object, contents.size());
+    auto* array = TRY(JS::Uint8Array::create(global_object, contents.size()));
     contents.span().copy_to(array->data());
     return JS::Value(array);
 }

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

@@ -10,13 +10,12 @@
 
 namespace JS {
 
-ArrayBuffer* ArrayBuffer::create(GlobalObject& global_object, size_t byte_length)
+ThrowCompletionOr<ArrayBuffer*> ArrayBuffer::create(GlobalObject& global_object, size_t byte_length)
 {
     auto buffer = ByteBuffer::create_zeroed(byte_length);
-    if (buffer.is_error()) {
-        global_object.vm().throw_exception<RangeError>(global_object, ErrorType::NotEnoughMemoryToAllocate, byte_length);
-        return nullptr;
-    }
+    if (buffer.is_error())
+        return global_object.vm().throw_completion<RangeError>(global_object, ErrorType::NotEnoughMemoryToAllocate, byte_length);
+
     return global_object.heap().allocate<ArrayBuffer>(global_object, buffer.release_value(), *global_object.array_buffer_prototype());
 }
 

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

@@ -25,7 +25,7 @@ class ArrayBuffer : public Object {
     JS_OBJECT(ArrayBuffer, Object);
 
 public:
-    static ArrayBuffer* create(GlobalObject&, size_t);
+    static ThrowCompletionOr<ArrayBuffer*> create(GlobalObject&, size_t);
     static ArrayBuffer* create(GlobalObject&, ByteBuffer);
     static ArrayBuffer* create(GlobalObject&, ByteBuffer*);
 

+ 13 - 8
Userland/Libraries/LibJS/Runtime/TypedArray.cpp

@@ -369,16 +369,23 @@ void TypedArrayBase::visit_edges(Visitor& visitor)
     ThrowCompletionOr<ClassName*> ClassName::create(GlobalObject& global_object, u32 length, FunctionObject& new_target)               \
     {                                                                                                                                  \
         auto* prototype = TRY(get_prototype_from_constructor(global_object, new_target, &GlobalObject::snake_name##_prototype));       \
-        return global_object.heap().allocate<ClassName>(global_object, length, *prototype);                                            \
+        auto* array_buffer = TRY(ArrayBuffer::create(global_object, length * sizeof(UnderlyingBufferDataType)));                       \
+        return global_object.heap().allocate<ClassName>(global_object, *prototype, length, *array_buffer);                             \
     }                                                                                                                                  \
                                                                                                                                        \
-    ClassName* ClassName::create(GlobalObject& global_object, u32 length)                                                              \
+    ThrowCompletionOr<ClassName*> ClassName::create(GlobalObject& global_object, u32 length)                                           \
     {                                                                                                                                  \
-        return global_object.heap().allocate<ClassName>(global_object, length, *global_object.snake_name##_prototype());               \
+        auto* array_buffer = TRY(ArrayBuffer::create(global_object, length * sizeof(UnderlyingBufferDataType)));                       \
+        return create(global_object, length, *array_buffer);                                                                           \
     }                                                                                                                                  \
                                                                                                                                        \
-    ClassName::ClassName(u32 length, Object& prototype)                                                                                \
-        : TypedArray(length, prototype)                                                                                                \
+    ClassName* ClassName::create(GlobalObject& global_object, u32 length, ArrayBuffer& array_buffer)                                   \
+    {                                                                                                                                  \
+        return global_object.heap().allocate<ClassName>(global_object, *global_object.snake_name##_prototype(), length, array_buffer); \
+    }                                                                                                                                  \
+                                                                                                                                       \
+    ClassName::ClassName(Object& prototype, u32 length, ArrayBuffer& array_buffer)                                                     \
+        : TypedArray(prototype, length, array_buffer)                                                                                  \
     {                                                                                                                                  \
         if constexpr (StringView { #ClassName }.is_one_of("BigInt64Array", "BigUint64Array"))                                          \
             m_content_type = ContentType::BigInt;                                                                                      \
@@ -450,9 +457,7 @@ void TypedArrayBase::visit_edges(Visitor& visitor)
                 TRY(initialize_typed_array_from_typed_array(global_object(), *typed_array, arg_typed_array));                          \
             } else if (is<ArrayBuffer>(first_argument.as_object())) {                                                                  \
                 auto& array_buffer = static_cast<ArrayBuffer&>(first_argument.as_object());                                            \
-                /* NOTE: I added the padding below to not reindent 150+ lines for a single line change. If you edit this, and the   */ \
-                /*       width happens to change anyway, feel free to remove it.                                                    */ \
-                TRY(initialize_typed_array_from_array_buffer(global_object(), *typed_array, array_buffer, /*                        */ \
+                TRY(initialize_typed_array_from_array_buffer(global_object(), *typed_array, array_buffer,                              \
                     vm.argument(1), vm.argument(2)));                                                                                  \
             } else {                                                                                                                   \
                 auto iterator = TRY(first_argument.get_method(global_object(), *vm.well_known_symbol_iterator()));                     \

+ 5 - 4
Userland/Libraries/LibJS/Runtime/TypedArray.h

@@ -454,11 +454,11 @@ public:
     Value get_modify_set_value_in_buffer(size_t byte_index, Value value, ReadWriteModifyFunction operation, bool is_little_endian = true) override { return viewed_array_buffer()->template get_modify_set_value<T>(byte_index, value, move(operation), is_little_endian); }
 
 protected:
-    TypedArray(u32 array_length, Object& prototype)
+    TypedArray(Object& prototype, u32 array_length, ArrayBuffer& array_buffer)
         : TypedArrayBase(prototype)
     {
         VERIFY(!Checked<u32>::multiplication_would_overflow(array_length, sizeof(UnderlyingBufferDataType)));
-        m_viewed_array_buffer = ArrayBuffer::create(global_object(), array_length * sizeof(UnderlyingBufferDataType));
+        m_viewed_array_buffer = &array_buffer;
         if (array_length)
             VERIFY(!data().is_null());
         m_array_length = array_length;
@@ -479,8 +479,9 @@ ThrowCompletionOr<TypedArrayBase*> typed_array_create(GlobalObject& global_objec
         virtual ~ClassName();                                                               \
         static ThrowCompletionOr<ClassName*> create(                                        \
             GlobalObject&, u32 length, FunctionObject& new_target);                         \
-        static ClassName* create(GlobalObject&, u32 length);                                \
-        ClassName(u32 length, Object& prototype);                                           \
+        static ThrowCompletionOr<ClassName*> create(GlobalObject&, u32 length);             \
+        static ClassName* create(GlobalObject&, u32 length, ArrayBuffer& buffer);           \
+        ClassName(Object& prototype, u32 length, ArrayBuffer& array_buffer);                \
         virtual String element_name() const override;                                       \
     };                                                                                      \
     class PrototypeName final : public Object {                                             \

+ 3 - 5
Userland/Libraries/LibWeb/Encoding/TextEncoder.cpp

@@ -27,11 +27,9 @@ JS::Uint8Array* TextEncoder::encode(String const& input) const
     //     4. If result is finished, then convert output into a byte sequence and return a Uint8Array object wrapping an ArrayBuffer containing output.
 
     auto byte_buffer = input.to_byte_buffer();
-
-    // FIXME: Support `TypedArray::create()` with existing `ArrayBuffer`, so that we don't have to allocate two `ByteBuffer`s.
-    auto* typed_array = JS::Uint8Array::create(global_object, byte_buffer.size());
-    typed_array->viewed_array_buffer()->buffer() = move(byte_buffer);
-    return typed_array;
+    auto array_length = byte_buffer.size();
+    auto* array_buffer = JS::ArrayBuffer::create(global_object, move(byte_buffer));
+    return JS::Uint8Array::create(global_object, array_length, *array_buffer);
 }
 
 // https://encoding.spec.whatwg.org/#dom-textencoder-encoding

+ 3 - 2
Userland/Libraries/LibWeb/HTML/ImageData.cpp

@@ -20,9 +20,10 @@ RefPtr<ImageData> ImageData::create_with_size(JS::GlobalObject& global_object, i
 
     dbgln("Creating ImageData with {}x{}", width, height);
 
-    auto* data = JS::Uint8ClampedArray::create(global_object, width * height * 4);
-    if (!data)
+    auto data_or_error = JS::Uint8ClampedArray::create(global_object, width * height * 4);
+    if (data_or_error.is_error())
         return nullptr;
+    auto* data = data_or_error.release_value();
 
     auto data_handle = JS::make_handle(data);