LibJS: Add Array.prototype.splice

Adds splice to Array.prototype according to the specification:
https://tc39.es/ecma262/#sec-array.prototype.splice
This commit is contained in:
Luke 2020-05-25 18:58:49 +01:00 committed by Andreas Kling
parent 6137475c39
commit f9f7cb4583
Notes: sideshowbarker 2024-07-19 06:07:51 +09:00
4 changed files with 201 additions and 0 deletions

View file

@ -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;
}
}

View file

@ -60,6 +60,7 @@ private:
static Value find_index(Interpreter&);
static Value some(Interpreter&);
static Value every(Interpreter&);
static Value splice(Interpreter&);
};
}

View file

@ -27,6 +27,18 @@ try {
assert(o.length === 0);
});
{
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" }) === "");

View file

@ -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);
}