From 01187e58f2012632d783af29c7b862509320a5d6 Mon Sep 17 00:00:00 2001 From: Jamie Mansfield Date: Fri, 2 Apr 2021 21:31:23 +0100 Subject: [PATCH] LibJS: ArrayBuffer.prototype.slice Implements the aforementioned native Javascript function, following the specification's [1] implementation. [1] https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice --- .../Libraries/LibJS/Runtime/ArrayBuffer.cpp | 11 +++++ .../Libraries/LibJS/Runtime/ArrayBuffer.h | 2 + .../LibJS/Runtime/ArrayBufferPrototype.cpp | 41 ++++++++++++++++++- .../ArrayBuffer.prototype.slice.js | 29 +++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 Userland/Libraries/LibJS/Tests/builtins/ArrayBuffer/ArrayBuffer.prototype.slice.js diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp index 007e61c0fa..1c506f0b79 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp @@ -34,12 +34,23 @@ 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(size_t byte_size, Object& prototype) : Object(prototype) , m_buffer(ByteBuffer::create_zeroed(byte_size)) { } +ArrayBuffer::ArrayBuffer(ByteBuffer& buffer, Object& prototype) + : Object(prototype) + , m_buffer(buffer) +{ +} + ArrayBuffer::~ArrayBuffer() { } diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h index d2724cd96e..adf52a5249 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h +++ b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h @@ -36,8 +36,10 @@ class ArrayBuffer : public Object { public: static ArrayBuffer* create(GlobalObject&, size_t); + static ArrayBuffer* create(GlobalObject&, ByteBuffer&); ArrayBuffer(size_t, Object& prototype); + ArrayBuffer(ByteBuffer& buffer, Object& prototype); virtual ~ArrayBuffer() override; size_t byte_length() const { return m_buffer.size(); } diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp index 846acbd562..5bcd583379 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp @@ -66,12 +66,51 @@ static ArrayBuffer* array_buffer_object_from(VM& vm, GlobalObject& global_object return static_cast(&this_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 {}; - TODO(); + + // FIXME: Check for shared buffer + // FIXME: Check for detached buffer + + const auto len = array_buffer_object->byte_length(); + + const auto relative_start = start.is_negative_infinity() + ? 0 + : start.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)); + + const auto relative_end = end.is_undefined() + ? len + : end.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); + else + final_ = min(relative_end, static_cast(len)); + + const auto new_len = max(final_ - first, 0.0); + + // 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; } JS_DEFINE_NATIVE_GETTER(ArrayBufferPrototype::byte_length_getter) diff --git a/Userland/Libraries/LibJS/Tests/builtins/ArrayBuffer/ArrayBuffer.prototype.slice.js b/Userland/Libraries/LibJS/Tests/builtins/ArrayBuffer/ArrayBuffer.prototype.slice.js new file mode 100644 index 0000000000..a8dd527d9b --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/ArrayBuffer/ArrayBuffer.prototype.slice.js @@ -0,0 +1,29 @@ +test("single parameter", () => { + const buffer = new ArrayBuffer(16); + const fullView = new Int32Array(buffer); + + // modify some value that we can check in the sliced buffer + fullView[3] = 7; + + // slice the buffer and use a new int32 view to perform basic checks + const slicedBuffer = buffer.slice(12); + const slicedView = new Int32Array(slicedBuffer); + + expect(slicedView).toHaveLength(1); + expect(slicedView[0]).toBe(7); +}); + +test("both parameters", () => { + const buffer = new ArrayBuffer(16); + const fullView = new Int32Array(buffer); + + // modify some value that we can check in the sliced buffer + fullView[1] = 12; + + // slice the buffer and use a new int32 view to perform basic checks + const slicedBuffer = buffer.slice(4, 8); + const slicedView = new Int32Array(slicedBuffer); + + expect(slicedView).toHaveLength(1); + expect(slicedView[0]).toBe(12); +});