diff options
author | Luke <luke.wilde@live.co.uk> | 2020-05-25 18:58:49 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-05-26 12:47:11 +0200 |
commit | f9f7cb45834bbb353a095ccb371487bdc8b2e9e3 (patch) | |
tree | cdac6e21210ed9acee72a19e632af29a5755a7c0 /Libraries | |
parent | 6137475c39d36cbe5471996f65ebfd42f4764983 (diff) | |
download | serenity-f9f7cb45834bbb353a095ccb371487bdc8b2e9e3.zip |
LibJS: Add Array.prototype.splice
Adds splice to Array.prototype according to the specification:
https://tc39.es/ecma262/#sec-array.prototype.splice
Diffstat (limited to 'Libraries')
-rw-r--r-- | Libraries/LibJS/Runtime/ArrayPrototype.cpp | 101 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/ArrayPrototype.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Tests/Array.prototype-generic-functions.js | 12 | ||||
-rw-r--r-- | Libraries/LibJS/Tests/Array.prototype.splice.js | 87 |
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); +} |