mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-22 04:25:13 +00:00
LibJS: add Array.prototype.reduceRight()
This patch adds `Array.prototype.reduceRight()` method to LibJS Runtime. The implementation is (to my best knowledge) conformant to https://tc39.es/ecma262/#sec-array.prototype.reduceright. Short test in `LibJS/Tests/Array.prototype-generic-functions.js` demonstrates that the function can be applied to other objects besides `Array`.
This commit is contained in:
parent
9c8d390682
commit
99991761fd
Notes:
sideshowbarker
2024-07-19 06:11:00 +09:00
Author: https://github.com/nooga Commit: https://github.com/SerenityOS/serenity/commit/99991761fd3 Pull-request: https://github.com/SerenityOS/serenity/pull/2356 Reviewed-by: https://github.com/linusg ✅
4 changed files with 216 additions and 7 deletions
|
@ -59,6 +59,7 @@ ArrayPrototype::ArrayPrototype()
|
|||
put_native_function("slice", slice, 2, attr);
|
||||
put_native_function("indexOf", index_of, 1, attr);
|
||||
put_native_function("reduce", reduce, 1, attr);
|
||||
put_native_function("reduceRight", reduce_right, 1, attr);
|
||||
put_native_function("reverse", reverse, 0, attr);
|
||||
put_native_function("lastIndexOf", last_index_of, 1, attr);
|
||||
put_native_function("includes", includes, 1, attr);
|
||||
|
@ -398,6 +399,10 @@ Value ArrayPrototype::reduce(Interpreter& interpreter)
|
|||
if (interpreter.exception())
|
||||
return {};
|
||||
|
||||
auto* callback_function = callback_from_args(interpreter, "reduce");
|
||||
if (!callback_function)
|
||||
return {};
|
||||
|
||||
size_t start = 0;
|
||||
|
||||
auto accumulator = js_undefined();
|
||||
|
@ -420,10 +425,6 @@ Value ArrayPrototype::reduce(Interpreter& interpreter)
|
|||
}
|
||||
}
|
||||
|
||||
auto* callback_function = callback_from_args(interpreter, "reduce");
|
||||
if (!callback_function)
|
||||
return {};
|
||||
|
||||
auto this_value = js_undefined();
|
||||
|
||||
for (size_t i = start; i < initial_length; ++i) {
|
||||
|
@ -447,6 +448,65 @@ Value ArrayPrototype::reduce(Interpreter& interpreter)
|
|||
return accumulator;
|
||||
}
|
||||
|
||||
Value ArrayPrototype::reduce_right(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* callback_function = callback_from_args(interpreter, "reduceRight");
|
||||
if (!callback_function)
|
||||
return {};
|
||||
|
||||
int start = initial_length - 1;
|
||||
|
||||
auto accumulator = js_undefined();
|
||||
if (interpreter.argument_count() > 1) {
|
||||
accumulator = interpreter.argument(1);
|
||||
} else {
|
||||
bool start_found = false;
|
||||
while (!start_found && start >= 0) {
|
||||
auto value = this_object->get_by_index(start);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
start_found = !value.is_empty();
|
||||
if (start_found)
|
||||
accumulator = value;
|
||||
start -= 1;
|
||||
}
|
||||
if (!start_found) {
|
||||
interpreter.throw_exception<TypeError>("Reduce of empty array with no initial value");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
auto this_value = js_undefined();
|
||||
|
||||
for (int i = start; i >= 0; --i) {
|
||||
auto value = this_object->get_by_index(i);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
if (value.is_empty())
|
||||
continue;
|
||||
|
||||
MarkedValueList arguments(interpreter.heap());
|
||||
arguments.append(accumulator);
|
||||
arguments.append(value);
|
||||
arguments.append(Value(i));
|
||||
arguments.append(this_object);
|
||||
|
||||
accumulator = interpreter.call(*callback_function, this_value, move(arguments));
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
Value ArrayPrototype::reverse(Interpreter& interpreter)
|
||||
{
|
||||
auto* array = array_from(interpreter);
|
||||
|
|
|
@ -52,6 +52,7 @@ private:
|
|||
static Value slice(Interpreter&);
|
||||
static Value index_of(Interpreter&);
|
||||
static Value reduce(Interpreter&);
|
||||
static Value reduce_right(Interpreter&);
|
||||
static Value reverse(Interpreter&);
|
||||
static Value last_index_of(Interpreter&);
|
||||
static Value includes(Interpreter&);
|
||||
|
|
|
@ -83,17 +83,31 @@ try {
|
|||
assert(visited[2] === "baz");
|
||||
});
|
||||
|
||||
["reduce"].forEach(name => {
|
||||
{
|
||||
const visited = [];
|
||||
Array.prototype[name].call(o, function (_, value) {
|
||||
Array.prototype.reduce.call(o, function (_, value) {
|
||||
visited.push(value);
|
||||
return false;
|
||||
}, "initial");
|
||||
|
||||
assert(visited.length === 3);
|
||||
assert(visited[0] === "foo");
|
||||
assert(visited[1] === "bar");
|
||||
assert(visited[2] === "baz");
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const visited = [];
|
||||
Array.prototype.reduceRight.call(o, function (_, value) {
|
||||
visited.push(value);
|
||||
return false;
|
||||
}, "initial");
|
||||
|
||||
assert(visited.length === 3);
|
||||
assert(visited[2] === "foo");
|
||||
assert(visited[1] === "bar");
|
||||
assert(visited[0] === "baz");
|
||||
}
|
||||
|
||||
console.log("PASS");
|
||||
} catch (e) {
|
||||
|
|
134
Libraries/LibJS/Tests/Array.prototype.reduceRight.js
Normal file
134
Libraries/LibJS/Tests/Array.prototype.reduceRight.js
Normal file
|
@ -0,0 +1,134 @@
|
|||
load("test-common.js");
|
||||
|
||||
try {
|
||||
assert(Array.prototype.reduceRight.length === 1);
|
||||
|
||||
assertThrowsError(
|
||||
() => {
|
||||
[1].reduceRight();
|
||||
},
|
||||
{
|
||||
error: TypeError,
|
||||
message: "Array.prototype.reduceRight() requires at least one argument",
|
||||
}
|
||||
);
|
||||
|
||||
assertThrowsError(
|
||||
() => {
|
||||
[1].reduceRight(undefined);
|
||||
},
|
||||
{
|
||||
error: TypeError,
|
||||
message: "undefined is not a function",
|
||||
}
|
||||
);
|
||||
|
||||
assertThrowsError(
|
||||
() => {
|
||||
[].reduceRight((a, x) => x);
|
||||
},
|
||||
{
|
||||
error: TypeError,
|
||||
message: "Reduce of empty array with no initial value",
|
||||
}
|
||||
);
|
||||
|
||||
assertThrowsError(
|
||||
() => {
|
||||
[, ,].reduceRight((a, x) => x);
|
||||
},
|
||||
{
|
||||
error: TypeError,
|
||||
message: "Reduce of empty array with no initial value",
|
||||
}
|
||||
);
|
||||
|
||||
[1, 2].reduceRight(() => {
|
||||
assert(this === undefined);
|
||||
});
|
||||
|
||||
var callbackCalled = 0;
|
||||
var callback = () => {
|
||||
callbackCalled++;
|
||||
return true;
|
||||
};
|
||||
|
||||
assert([1].reduceRight(callback) === 1);
|
||||
assert(callbackCalled === 0);
|
||||
|
||||
assert([1].reduceRight(callback) === 1);
|
||||
assert(callbackCalled === 0);
|
||||
|
||||
callbackCalled = 0;
|
||||
assert([1, 2, 3].reduceRight(callback) === true);
|
||||
assert(callbackCalled === 2);
|
||||
|
||||
callbackCalled = 0;
|
||||
assert([1, 2, 3, ,].reduceRight(callback) === true);
|
||||
assert(callbackCalled === 2);
|
||||
|
||||
callbackCalled = 0;
|
||||
assert([, , , 1, , , 10, , 100, , ,].reduceRight(callback) === true);
|
||||
assert(callbackCalled === 2);
|
||||
|
||||
var constantlySad = () => ":^(";
|
||||
var result = [].reduceRight(constantlySad, ":^)");
|
||||
assert(result === ":^)");
|
||||
|
||||
result = [":^0"].reduceRight(constantlySad, ":^)");
|
||||
assert(result === ":^(");
|
||||
|
||||
result = [":^0"].reduceRight(constantlySad);
|
||||
assert(result === ":^0");
|
||||
|
||||
result = [5, 4, 3, 2, 1].reduceRight((accum, elem) => "" + accum + elem);
|
||||
assert(result === "12345");
|
||||
|
||||
result = [1, 2, 3, 4, 5, 6].reduceRight((accum, elem) => {
|
||||
return "" + accum + elem;
|
||||
}, 100);
|
||||
assert(result === "100654321");
|
||||
|
||||
result = [6, 5, 4, 3, 2, 1].reduceRight((accum, elem) => {
|
||||
return "" + accum + elem;
|
||||
}, 100);
|
||||
assert(result === "100123456");
|
||||
|
||||
var indexes = [];
|
||||
result = ["foo", 1, true].reduceRight((a, v, i) => {
|
||||
indexes.push(i);
|
||||
});
|
||||
assert(result === undefined);
|
||||
assert(indexes.length === 2);
|
||||
assert(indexes[0] === 1);
|
||||
assert(indexes[1] === 0);
|
||||
|
||||
indexes = [];
|
||||
result = ["foo", 1, true].reduceRight((a, v, i) => {
|
||||
indexes.push(i);
|
||||
}, "foo");
|
||||
assert(result === undefined);
|
||||
assert(indexes.length === 3);
|
||||
assert(indexes[0] === 2);
|
||||
assert(indexes[1] === 1);
|
||||
assert(indexes[2] === 0);
|
||||
|
||||
var mutable = { prop: 0 };
|
||||
result = ["foo", 1, true].reduceRight((a, v) => {
|
||||
a.prop = v;
|
||||
return a;
|
||||
}, mutable);
|
||||
assert(result === mutable);
|
||||
assert(result.prop === "foo");
|
||||
|
||||
var a1 = [1, 2];
|
||||
var a2 = null;
|
||||
a1.reduceRight((a, v, i, t) => {
|
||||
a2 = t;
|
||||
});
|
||||
assert(a1 === a2);
|
||||
|
||||
console.log("PASS");
|
||||
} catch (e) {
|
||||
console.log("FAIL: " + e);
|
||||
}
|
Loading…
Add table
Reference in a new issue