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:
parent
6137475c39
commit
f9f7cb4583
Notes:
sideshowbarker
2024-07-19 06:07:51 +09:00
Author: https://github.com/Lubrsi Commit: https://github.com/SerenityOS/serenity/commit/f9f7cb45834 Pull-request: https://github.com/SerenityOS/serenity/pull/2379 Reviewed-by: https://github.com/linusg ✅
4 changed files with 201 additions and 0 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ private:
|
|||
static Value find_index(Interpreter&);
|
||||
static Value some(Interpreter&);
|
||||
static Value every(Interpreter&);
|
||||
static Value splice(Interpreter&);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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" }) === "");
|
||||
|
|
87
Libraries/LibJS/Tests/Array.prototype.splice.js
Normal file
87
Libraries/LibJS/Tests/Array.prototype.splice.js
Normal 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);
|
||||
}
|
Loading…
Add table
Reference in a new issue