From 8dc86c6aadb6114f5f32809ed3e3a295648ea549 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Thu, 10 Jun 2021 22:37:56 +0300 Subject: LibJS: Bring ArrayBuffer.prototype.slice closer to spec The exception order was incorrect in the old implementation, and it did not use the Symbol.species constructor as required by the spec. --- Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp | 11 ---- Userland/Libraries/LibJS/Runtime/ArrayBuffer.h | 2 - .../LibJS/Runtime/ArrayBufferPrototype.cpp | 73 +++++++++++++++------- Userland/Libraries/LibJS/Runtime/ErrorTypes.h | 2 + 4 files changed, 51 insertions(+), 37 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp index fc168cabf2..a34955dd9c 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp @@ -14,11 +14,6 @@ ArrayBuffer* ArrayBuffer::create(GlobalObject& global_object, size_t byte_size) return global_object.heap().allocate(global_object, byte_size, *global_object.array_buffer_prototype()); } -ArrayBuffer* ArrayBuffer::create(GlobalObject& global_object, ByteBuffer& buffer) -{ - return global_object.heap().allocate(global_object, buffer, *global_object.array_buffer_prototype()); -} - ArrayBuffer* ArrayBuffer::create(GlobalObject& global_object, ByteBuffer* buffer) { return global_object.heap().allocate(global_object, buffer, *global_object.array_buffer_prototype()); @@ -30,12 +25,6 @@ ArrayBuffer::ArrayBuffer(size_t byte_size, Object& prototype) { } -ArrayBuffer::ArrayBuffer(ByteBuffer& buffer, Object& prototype) - : Object(prototype) - , m_buffer(buffer) -{ -} - ArrayBuffer::ArrayBuffer(ByteBuffer* buffer, Object& prototype) : Object(prototype) , m_buffer(buffer) diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h index d731afc322..db2e13b605 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h +++ b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h @@ -17,11 +17,9 @@ class ArrayBuffer : public Object { public: static ArrayBuffer* create(GlobalObject&, size_t); - static ArrayBuffer* create(GlobalObject&, ByteBuffer&); static ArrayBuffer* create(GlobalObject&, ByteBuffer*); ArrayBuffer(size_t, Object& prototype); - ArrayBuffer(ByteBuffer& buffer, Object& prototype); ArrayBuffer(ByteBuffer* buffer, Object& prototype); virtual ~ArrayBuffer() override; diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp index ebfc37d96a..db345379ed 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -49,9 +50,6 @@ static ArrayBuffer* array_buffer_object_from(VM& vm, GlobalObject& global_object // 25.1.5.3 ArrayBuffer.prototype.slice, https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::slice) { - const auto start = vm.argument(0); - const auto end = vm.argument(1); - auto array_buffer_object = array_buffer_object_from(vm, global_object); if (!array_buffer_object) return {}; @@ -59,38 +57,65 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::slice) // FIXME: Check for shared buffer // FIXME: Check for detached buffer - const auto len = array_buffer_object->byte_length(); + auto length = array_buffer_object->byte_length(); - const auto relative_start = start.is_negative_infinity() - ? 0 - : start.to_integer_or_infinity(global_object); + auto relative_start = vm.argument(0).to_integer_or_infinity(global_object); if (vm.exception()) return {}; - const auto first = relative_start < 0 - ? max(len + relative_start, 0.0) - : min(relative_start, static_cast(len)); + double first; + if (relative_start < 0) + first = max(length + relative_start, 0.0); + else + first = min(relative_start, (double)length); - const auto relative_end = end.is_undefined() - ? len - : end.to_integer_or_infinity(global_object); + auto relative_end = vm.argument(1).is_undefined() ? length : vm.argument(1).to_integer_or_infinity(global_object); if (vm.exception()) return {}; - double final_; - if (end.is_negative_infinity()) - final_ = 0; - else if (relative_end < 0) - final_ = max(len + relative_end, 0.0); + double final; + if (relative_end < 0) + final = max(length + relative_end, 0.0); else - final_ = min(relative_end, static_cast(len)); + final = min(relative_end, (double)length); + + auto new_length = max(final - first, 0.0); + + auto constructor = species_constructor(global_object, *array_buffer_object, *global_object.array_buffer_constructor()); + if (vm.exception()) + return {}; - const auto new_len = max(final_ - first, 0.0); + MarkedValueList arguments(vm.heap()); + arguments.append(Value(new_length)); + auto new_array_buffer = vm.construct(*constructor, *constructor, move(arguments)); + if (vm.exception()) + return {}; + + if (!new_array_buffer.is_object() || !is(new_array_buffer.as_object())) { + vm.throw_exception(global_object, ErrorType::SpeciesConstructorDidNotCreate, "an ArrayBuffer"); + return {}; + } + auto* new_array_buffer_object = static_cast(&new_array_buffer.as_object()); + + // FIXME: Check for shared buffer + // FIXME: Check for detached buffer + if (same_value(new_array_buffer_object, array_buffer_object)) { + vm.throw_exception(global_object, ErrorType::SpeciesConstructorReturned, "same ArrayBuffer instance"); + return {}; + } + if (new_array_buffer_object->byte_length() < new_length) { + vm.throw_exception(global_object, ErrorType::SpeciesConstructorReturned, "an ArrayBuffer smaller than requested"); + return {}; + } + + if (array_buffer_object->is_detached()) { + vm.throw_exception(global_object, ErrorType::DetachedArrayBuffer); + return {}; + } - // FIXME: this is a bit more involved in the specification - auto sliced = array_buffer_object->buffer().slice(first, new_len); - auto buffer = ArrayBuffer::create(global_object, sliced); - return buffer; + // This is ugly, is there a better way to do this? + array_buffer_object->buffer().span().slice(first, new_length).copy_to(new_array_buffer_object->buffer().span()); + return new_array_buffer_object; } JS_DEFINE_NATIVE_GETTER(ArrayBufferPrototype::byte_length_getter) diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h index fb416cdd7c..014436c121 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h @@ -140,6 +140,8 @@ M(RegExpCompileError, "RegExp compile error: {}") \ M(RegExpObjectBadFlag, "Invalid RegExp flag '{}'") \ M(RegExpObjectRepeatedFlag, "Repeated RegExp flag '{}'") \ + M(SpeciesConstructorDidNotCreate, "Species constructor did not create {}") \ + M(SpeciesConstructorReturned, "Species constructor returned {}") \ M(StringRawCannotConvert, "Cannot convert property 'raw' to object from {}") \ M(StringRepeatCountMustBe, "repeat count must be a {} number") \ M(ThisHasNotBeenInitialized, "|this| has not been initialized") \ -- cgit v1.2.3