Prechádzať zdrojové kódy

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.
Idan Horowitz 4 rokov pred
rodič
commit
8dc86c6aad

+ 0 - 11
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<ArrayBuffer>(global_object, byte_size, *global_object.array_buffer_prototype());
 }
 
-ArrayBuffer* ArrayBuffer::create(GlobalObject& global_object, ByteBuffer& buffer)
-{
-    return global_object.heap().allocate<ArrayBuffer>(global_object, buffer, *global_object.array_buffer_prototype());
-}
-
 ArrayBuffer* ArrayBuffer::create(GlobalObject& global_object, ByteBuffer* buffer)
 {
     return global_object.heap().allocate<ArrayBuffer>(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)

+ 0 - 2
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;
 

+ 49 - 24
Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp

@@ -6,6 +6,7 @@
 
 #include <AK/Function.h>
 #include <LibJS/Runtime/ArrayBuffer.h>
+#include <LibJS/Runtime/ArrayBufferConstructor.h>
 #include <LibJS/Runtime/ArrayBufferPrototype.h>
 #include <LibJS/Runtime/GlobalObject.h>
 
@@ -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<double>(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<double>(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<ArrayBuffer>(new_array_buffer.as_object())) {
+        vm.throw_exception<TypeError>(global_object, ErrorType::SpeciesConstructorDidNotCreate, "an ArrayBuffer");
+        return {};
+    }
+    auto* new_array_buffer_object = static_cast<ArrayBuffer*>(&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<TypeError>(global_object, ErrorType::SpeciesConstructorReturned, "same ArrayBuffer instance");
+        return {};
+    }
+    if (new_array_buffer_object->byte_length() < new_length) {
+        vm.throw_exception<TypeError>(global_object, ErrorType::SpeciesConstructorReturned, "an ArrayBuffer smaller than requested");
+        return {};
+    }
+
+    if (array_buffer_object->is_detached()) {
+        vm.throw_exception<TypeError>(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)

+ 2 - 0
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")                                                                     \