summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Libraries/LibJS/Runtime/ArrayPrototype.cpp101
-rw-r--r--Libraries/LibJS/Runtime/ArrayPrototype.h1
-rw-r--r--Libraries/LibJS/Tests/Array.prototype-generic-functions.js12
-rw-r--r--Libraries/LibJS/Tests/Array.prototype.splice.js87
4 files changed, 201 insertions, 0 deletions
diff --git a/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Libraries/LibJS/Runtime/ArrayPrototype.cpp
index 24be708df4..297f791afc 100644
--- a/Libraries/LibJS/Runtime/ArrayPrototype.cpp
+++ b/Libraries/LibJS/Runtime/ArrayPrototype.cpp
@@ -67,6 +67,7 @@ ArrayPrototype::ArrayPrototype()
put_native_function("findIndex", find_index, 1, attr);
put_native_function("some", some, 1, attr);
put_native_function("every", every, 1, attr);
+ put_native_function("splice", splice, 2, attr);
put("length", Value(0), Attribute::Configurable);
}
@@ -642,4 +643,104 @@ Value ArrayPrototype::every(Interpreter& interpreter)
return Value(result);
}
+Value ArrayPrototype::splice(Interpreter& interpreter)
+{
+ auto* this_object = interpreter.this_value().to_object(interpreter);
+ if (!this_object)
+ return {};
+
+ auto initial_length = get_length(interpreter, *this_object);
+ if (interpreter.exception())
+ return {};
+
+ auto relative_start = interpreter.argument(0).to_i32(interpreter);
+ if (interpreter.exception())
+ return {};
+
+ size_t actual_start;
+
+ if (relative_start < 0)
+ actual_start = max((ssize_t)initial_length + relative_start, (ssize_t)0);
+ else
+ actual_start = min((size_t)relative_start, initial_length);
+
+ size_t insert_count = 0;
+ size_t actual_delete_count = 0;
+
+ if (interpreter.argument_count() == 1) {
+ actual_delete_count = initial_length - actual_start;
+ } else if (interpreter.argument_count() >= 2) {
+ insert_count = interpreter.argument_count() - 2;
+ i32 delete_count = interpreter.argument(1).to_i32(interpreter);
+ if (interpreter.exception())
+ return {};
+
+ actual_delete_count = min((size_t)max(delete_count, 0), initial_length - actual_start);
+ }
+
+ size_t new_length = initial_length + insert_count - actual_delete_count;
+
+ if (new_length > MAX_ARRAY_LIKE_INDEX)
+ return interpreter.throw_exception<TypeError>("Maximum array size exceeded");
+
+ auto removed_elements = Array::create(interpreter.global_object());
+
+ for (size_t i = 0; i < actual_delete_count; ++i) {
+ auto value = this_object->get_by_index(actual_start + i);
+ if (interpreter.exception())
+ return {};
+
+ removed_elements->elements().append(value);
+ }
+
+ if (insert_count < actual_delete_count) {
+ for (size_t i = actual_start; i < initial_length - actual_delete_count; ++i) {
+ auto from = this_object->get_by_index(i + actual_delete_count);
+ if (interpreter.exception())
+ return {};
+
+ auto to = i + insert_count;
+
+ if (!from.is_empty()) {
+ this_object->put_by_index(to, from);
+ if (interpreter.exception())
+ return {};
+ }
+ else
+ this_object->delete_property(PropertyName(to));
+ }
+
+ for (size_t i = initial_length; i > new_length; --i)
+ this_object->delete_property(PropertyName(i - 1));
+ } else if (insert_count > actual_delete_count) {
+ for (size_t i = initial_length - actual_delete_count; i > actual_start; --i) {
+ auto from = this_object->get_by_index(i + actual_delete_count - 1);
+ if (interpreter.exception())
+ return {};
+
+ auto to = i + insert_count - 1;
+
+ if (!from.is_empty()) {
+ this_object->put_by_index(to, from);
+ if (interpreter.exception())
+ return {};
+ }
+ else
+ this_object->delete_property(PropertyName(to));
+ }
+ }
+
+ for (size_t i = 0; i < insert_count; ++i) {
+ this_object->put_by_index(actual_start + i, interpreter.argument(i + 2));
+ if (interpreter.exception())
+ return {};
+ }
+
+ this_object->put("length", Value((i32)new_length));
+ if (interpreter.exception())
+ return {};
+
+ return removed_elements;
+}
+
}
diff --git a/Libraries/LibJS/Runtime/ArrayPrototype.h b/Libraries/LibJS/Runtime/ArrayPrototype.h
index d22b412534..854788ed54 100644
--- a/Libraries/LibJS/Runtime/ArrayPrototype.h
+++ b/Libraries/LibJS/Runtime/ArrayPrototype.h
@@ -60,6 +60,7 @@ private:
static Value find_index(Interpreter&);
static Value some(Interpreter&);
static Value every(Interpreter&);
+ static Value splice(Interpreter&);
};
}
diff --git a/Libraries/LibJS/Tests/Array.prototype-generic-functions.js b/Libraries/LibJS/Tests/Array.prototype-generic-functions.js
index 6a981b0985..7f00edcae7 100644
--- a/Libraries/LibJS/Tests/Array.prototype-generic-functions.js
+++ b/Libraries/LibJS/Tests/Array.prototype-generic-functions.js
@@ -28,6 +28,18 @@ try {
});
{
+ const o = { length: 3, 0: "hello", 2: "serenity" };
+ const removed = Array.prototype.splice.call(o, 0, 2, "hello", "friends");
+ assert(o.length === 3);
+ assert(o[0] === "hello");
+ assert(o[1] === "friends");
+ assert(o[2] === "serenity");
+ assert(removed.length === 2);
+ assert(removed[0] === "hello");
+ assert(removed[1] === undefined);
+ }
+
+ {
assert(Array.prototype.join.call({}) === "");
assert(Array.prototype.join.call({ length: "foo" }) === "");
assert(Array.prototype.join.call({ length: 3 }) === ",,");
diff --git a/Libraries/LibJS/Tests/Array.prototype.splice.js b/Libraries/LibJS/Tests/Array.prototype.splice.js
new file mode 100644
index 0000000000..e140631430
--- /dev/null
+++ b/Libraries/LibJS/Tests/Array.prototype.splice.js
@@ -0,0 +1,87 @@
+load("test-common.js");
+
+try {
+ assert(Array.prototype.splice.length === 2);
+
+ var array = ["hello", "friends", "serenity", 1, 2];
+ var removed = array.splice(3);
+ assert(array.length === 3);
+ assert(array[0] === "hello");
+ assert(array[1] === "friends");
+ assert(array[2] === "serenity");
+ assert(removed.length === 2);
+ assert(removed[0] === 1);
+ assert(removed[1] === 2);
+
+ array = ["hello", "friends", "serenity", 1, 2];
+ removed = array.splice(-2);
+ assert(array.length === 3);
+ assert(array[0] === "hello");
+ assert(array[1] === "friends");
+ assert(array[2] === "serenity");
+ assert(removed.length === 2);
+ assert(removed[0] === 1);
+ assert(removed[1] === 2);
+
+ array = ["hello", "friends", "serenity", 1, 2];
+ removed = array.splice(-2, 1);
+ assert(array.length === 4);
+ assert(array[0] === "hello");
+ assert(array[1] === "friends");
+ assert(array[2] === "serenity");
+ assert(array[3] === 2);
+ assert(removed.length === 1);
+ assert(removed[0] === 1);
+
+ array = ["serenity"];
+ removed = array.splice(0, 0, "hello", "friends");
+ assert(array.length === 3);
+ assert(array[0] === "hello");
+ assert(array[1] === "friends");
+ assert(array[2] === "serenity");
+ assert(removed.length === 0);
+
+ array = ["goodbye", "friends", "serenity"];
+ removed = array.splice(0, 1, "hello");
+ assert(array.length === 3);
+ assert(array[0] === "hello");
+ assert(array[1] === "friends");
+ assert(array[2] === "serenity");
+ assert(removed.length === 1);
+ assert(removed[0] === "goodbye");
+
+ array = ["foo", "bar", "baz"];
+ removed = array.splice();
+ assert(array.length === 3);
+ assert(array[0] === "foo");
+ assert(array[1] === "bar");
+ assert(array[2] === "baz");
+ assert(removed.length === 0);
+
+ removed = array.splice(0, 123);
+ assert(array.length === 0);
+ assert(removed.length === 3);
+ assert(removed[0] === "foo");
+ assert(removed[1] === "bar");
+ assert(removed[2] === "baz");
+
+ array = ["foo", "bar", "baz"];
+ removed = array.splice(123, 123);
+ assert(array.length === 3);
+ assert(array[0] === "foo");
+ assert(array[1] === "bar");
+ assert(array[2] === "baz");
+ assert(removed.length === 0);
+
+ array = ["foo", "bar", "baz"];
+ removed = array.splice(-123, 123);
+ assert(array.length === 0);
+ assert(removed.length === 3);
+ assert(removed[0] === "foo");
+ assert(removed[1] === "bar");
+ assert(removed[2] === "baz");
+
+ console.log("PASS");
+} catch (e) {
+ console.log("FAIL: " + e);
+}