summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp50
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype-generic-functions.js20
2 files changed, 65 insertions, 5 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp
index 64187d3af6..0c5382b42f 100644
--- a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp
@@ -203,12 +203,52 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::push)
// 23.1.3.31 Array.prototype.unshift ( ...items ), https://tc39.es/ecma262/#sec-array.prototype.unshift
JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::unshift)
{
- 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 {};
+ auto length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+ auto arg_count = vm.argument_count();
+ size_t new_length = length + arg_count;
+ if (arg_count > 0) {
+ if (new_length > MAX_ARRAY_LIKE_INDEX) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ArrayMaxSize);
+ return {};
+ }
+
+ for (size_t k = length; k > 0; --k) {
+ auto from = k - 1;
+ auto to = k + arg_count - 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 {};
+ }
+ }
+
+ for (size_t j = 0; j < arg_count; j++) {
+ this_object->put(j, vm.argument(j));
+ if (vm.exception())
+ return {};
+ }
+ }
+
+ this_object->put(vm.names.length, Value(new_length));
+ if (vm.exception())
return {};
- for (size_t i = 0; i < vm.argument_count(); ++i)
- array->indexed_properties().insert(i, vm.argument(i));
- return Value(static_cast<i32>(array->indexed_properties().array_like_size()));
+ return Value(new_length);
}
// 23.1.3.19 Array.prototype.pop ( ), https://tc39.es/ecma262/#sec-array.prototype.pop
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 b86cc0d4c5..c35a17719e 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
@@ -110,6 +110,26 @@ describe("ability to work with generic non-array objects", () => {
expect(o).toEqual({ length: 4, 0: "b", 2: "c" });
});
+ test("unshift", () => {
+ {
+ const o = { length: 5, 0: "a", 1: "b", 3: "c" };
+ const front = "z";
+ Array.prototype.unshift.call(o, front);
+ expect(o[0]).toEqual(front);
+ expect(o[1]).toEqual("a");
+ expect(o.length).toEqual(6);
+ }
+ {
+ const o = { length: 5, 0: "a", 1: "b", 3: "c" };
+ const front = "z";
+ Array.prototype.unshift.call(o, front, front);
+ expect(o[0]).toEqual(front);
+ expect(o[1]).toEqual(front);
+ expect(o[2]).toEqual("a");
+ expect(o.length).toEqual(7);
+ }
+ });
+
const o = { length: 5, 0: "foo", 1: "bar", 3: "baz" };
test("every", () => {