From 7c1e2adf8a1fe2667378f734f0cf22ec625ab55c Mon Sep 17 00:00:00 2001 From: davidot Date: Sat, 12 Jun 2021 19:23:33 +0200 Subject: [PATCH] LibJS: Make Array.prototype.shift generic --- .../LibJS/Runtime/ArrayPrototype.cpp | 47 ++++++++++++++++--- .../Array.prototype-generic-functions.js | 10 ++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp index fd6d271c46..64187d3af6 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp @@ -246,15 +246,50 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::pop) // 23.1.3.24 Array.prototype.shift ( ), https://tc39.es/ecma262/#sec-array.prototype.shift JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::shift) { - auto* array = Array::typed_this(vm, global_object); - if (!array) + auto* this_object = vm.this_value(global_object).to_object(global_object); + if (!this_object) return {}; - if (array->indexed_properties().is_empty()) - return js_undefined(); - auto result = array->indexed_properties().take_first(array); + auto length = length_of_array_like(global_object, *this_object); if (vm.exception()) return {}; - return result.value.value_or(js_undefined()); + if (length == 0) { + this_object->put(vm.names.length, Value(0)); + if (vm.exception()) + return {}; + return js_undefined(); + } + auto first = this_object->get(0).value_or(js_undefined()); + if (vm.exception()) + return {}; + + for (size_t k = 1; k < length; ++k) { + size_t from = k; + size_t to = k - 1; + bool from_present = this_object->has_property(from); + if (vm.exception()) + return {}; + if (from_present) { + auto from_value = this_object->get(from).value_or(js_undefined()); + if (vm.exception()) + return {}; + this_object->put(to, from_value); + if (vm.exception()) + return {}; + } else { + this_object->delete_property(to); + if (vm.exception()) + return {}; + } + } + + this_object->delete_property(length - 1); + if (vm.exception()) + return {}; + + this_object->put(vm.names.length, Value(length - 1)); + if (vm.exception()) + return {}; + return first; } // 23.1.3.30 Array.prototype.toString ( ), https://tc39.es/ecma262/#sec-array.prototype.tostring diff --git a/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype-generic-functions.js b/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype-generic-functions.js index 85ac3bf9e5..b86cc0d4c5 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype-generic-functions.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype-generic-functions.js @@ -100,6 +100,16 @@ describe("ability to work with generic non-array objects", () => { expect(Array.prototype.includes.call({ length: 5, 2: "foo" }, "foo")).toBeTrue(); }); + test("shift", () => { + expect(Array.prototype.shift.call({})).toBeUndefined(); + expect(Array.prototype.shift.call({ length: 0 })).toBeUndefined(); + + const o = { length: 5, 0: "a", 1: "b", 3: "c" }; + const front = Array.prototype.shift.call(o); + expect(front).toEqual("a"); + expect(o).toEqual({ length: 4, 0: "b", 2: "c" }); + }); + const o = { length: 5, 0: "foo", 1: "bar", 3: "baz" }; test("every", () => {