1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 22:07:34 +00:00

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.
This commit is contained in:
davidot 2022-02-07 13:34:32 +01:00 committed by Linus Groh
parent 4136cbdb09
commit de90d54be0
7 changed files with 30 additions and 26 deletions

View file

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

View file

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

View file

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

View file

@ -369,16 +369,23 @@ void TypedArrayBase::visit_edges(Visitor& visitor)
ThrowCompletionOr<ClassName*> ClassName::create(GlobalObject& global_object, u32 length, FunctionObject& new_target) \ 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)); \ 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) \ ClassName* ClassName::create(GlobalObject& global_object, u32 length, ArrayBuffer& array_buffer) \
: TypedArray(length, prototype) \ { \
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")) \ if constexpr (StringView { #ClassName }.is_one_of("BigInt64Array", "BigUint64Array")) \
m_content_type = ContentType::BigInt; \ 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)); \ TRY(initialize_typed_array_from_typed_array(global_object(), *typed_array, arg_typed_array)); \
} else if (is<ArrayBuffer>(first_argument.as_object())) { \ } else if (is<ArrayBuffer>(first_argument.as_object())) { \
auto& array_buffer = static_cast<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 */ \ TRY(initialize_typed_array_from_array_buffer(global_object(), *typed_array, array_buffer, \
/* width happens to change anyway, feel free to remove it. */ \
TRY(initialize_typed_array_from_array_buffer(global_object(), *typed_array, array_buffer, /* */ \
vm.argument(1), vm.argument(2))); \ vm.argument(1), vm.argument(2))); \
} else { \ } else { \
auto iterator = TRY(first_argument.get_method(global_object(), *vm.well_known_symbol_iterator())); \ auto iterator = TRY(first_argument.get_method(global_object(), *vm.well_known_symbol_iterator())); \

View file

@ -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); } 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: protected:
TypedArray(u32 array_length, Object& prototype) TypedArray(Object& prototype, u32 array_length, ArrayBuffer& array_buffer)
: TypedArrayBase(prototype) : TypedArrayBase(prototype)
{ {
VERIFY(!Checked<u32>::multiplication_would_overflow(array_length, sizeof(UnderlyingBufferDataType))); 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) if (array_length)
VERIFY(!data().is_null()); VERIFY(!data().is_null());
m_array_length = array_length; m_array_length = array_length;
@ -479,8 +479,9 @@ ThrowCompletionOr<TypedArrayBase*> typed_array_create(GlobalObject& global_objec
virtual ~ClassName(); \ virtual ~ClassName(); \
static ThrowCompletionOr<ClassName*> create( \ static ThrowCompletionOr<ClassName*> create( \
GlobalObject&, u32 length, FunctionObject& new_target); \ GlobalObject&, u32 length, FunctionObject& new_target); \
static ClassName* create(GlobalObject&, u32 length); \ static ThrowCompletionOr<ClassName*> create(GlobalObject&, u32 length); \
ClassName(u32 length, Object& prototype); \ static ClassName* create(GlobalObject&, u32 length, ArrayBuffer& buffer); \
ClassName(Object& prototype, u32 length, ArrayBuffer& array_buffer); \
virtual String element_name() const override; \ virtual String element_name() const override; \
}; \ }; \
class PrototypeName final : public Object { \ class PrototypeName final : public Object { \

View file

@ -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. // 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(); auto byte_buffer = input.to_byte_buffer();
auto array_length = byte_buffer.size();
// FIXME: Support `TypedArray::create()` with existing `ArrayBuffer`, so that we don't have to allocate two `ByteBuffer`s. auto* array_buffer = JS::ArrayBuffer::create(global_object, move(byte_buffer));
auto* typed_array = JS::Uint8Array::create(global_object, byte_buffer.size()); return JS::Uint8Array::create(global_object, array_length, *array_buffer);
typed_array->viewed_array_buffer()->buffer() = move(byte_buffer);
return typed_array;
} }
// https://encoding.spec.whatwg.org/#dom-textencoder-encoding // https://encoding.spec.whatwg.org/#dom-textencoder-encoding

View file

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