diff options
author | tuqqu <artkurbidaev@gmail.com> | 2021-04-06 23:12:38 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-04-06 22:25:05 +0200 |
commit | 7bd0384fa61e5a270a9b000d69c90f737be948f2 (patch) | |
tree | a0fec3ab4836d8c3fbc718d632957e4a3ad7050f | |
parent | bb9cd13a568a49089f781c2268f18cbd4dab32aa (diff) | |
download | serenity-7bd0384fa61e5a270a9b000d69c90f737be948f2.zip |
LibJS: Support mapFn argument of Array.from
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp | 41 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Tests/builtins/Array/Array.from.js | 107 |
2 files changed, 115 insertions, 33 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp b/Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp index 153e2a4fe2..b3c495950e 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp @@ -95,15 +95,37 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from) auto* array = Array::create(global_object); + Function* map_fn = nullptr; + if (!vm.argument(1).is_undefined()) { + auto callback = vm.argument(1); + if (!callback.is_function()) { + vm.throw_exception<TypeError>(global_object, ErrorType::NotAFunction, callback.to_string_without_side_effects()); + return {}; + } + map_fn = &callback.as_function(); + } + // Array.from() lets you create Arrays from: if (auto size = object->indexed_properties().array_like_size()) { // * array-like objects (objects with a length property and indexed elements) MarkedValueList elements(vm.heap()); elements.ensure_capacity(size); for (size_t i = 0; i < size; ++i) { - elements.append(object->get(i)); - if (vm.exception()) - return {}; + if (map_fn) { + auto element = object->get(i); + if (vm.exception()) + return {}; + + auto map_fn_result = vm.call(*map_fn, value, element); + if (vm.exception()) + return {}; + + elements.append(map_fn_result); + } else { + elements.append(object->get(i)); + if (vm.exception()) + return {}; + } } array->set_indexed_property_elements(move(elements)); } else { @@ -111,14 +133,23 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from) get_iterator_values(global_object, value, [&](Value element) { if (vm.exception()) return IterationDecision::Break; - array->indexed_properties().append(element); + + if (map_fn) { + auto map_fn_result = vm.call(*map_fn, value, element); + if (vm.exception()) + return IterationDecision::Break; + + array->indexed_properties().append(map_fn_result); + } else { + array->indexed_properties().append(element); + } + return IterationDecision::Continue; }); if (vm.exception()) return {}; } - // FIXME: if interpreter.argument_count() >= 2: mapFn // FIXME: if interpreter.argument_count() >= 3: thisArg return array; diff --git a/Userland/Libraries/LibJS/Tests/builtins/Array/Array.from.js b/Userland/Libraries/LibJS/Tests/builtins/Array/Array.from.js index 0a015f8141..0c8562ceb5 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Array/Array.from.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Array/Array.from.js @@ -3,20 +3,32 @@ test("length is 1", () => { }); describe("normal behavior", () => { - test("empty array", () => { - var a = Array.from([]); + test("empty array, no mapFn", () => { + const a = Array.from([]); expect(a instanceof Array).toBeTrue(); expect(a).toHaveLength(0); }); - test("empty string", () => { - var a = Array.from(""); + test("empty array, with mapFn", () => { + const a = Array.from([], n => n); expect(a instanceof Array).toBeTrue(); expect(a).toHaveLength(0); }); - test("non-empty array", () => { - var a = Array.from([5, 8, 1]); + test("empty string, no mapFn", () => { + const a = Array.from(""); + expect(a instanceof Array).toBeTrue(); + expect(a).toHaveLength(0); + }); + + test("empty string, with mapFn", () => { + const a = Array.from("", n => n); + expect(a instanceof Array).toBeTrue(); + expect(a).toHaveLength(0); + }); + + test("non-empty array, no mapFn", () => { + const a = Array.from([5, 8, 1]); expect(a instanceof Array).toBeTrue(); expect(a).toHaveLength(3); expect(a[0]).toBe(5); @@ -24,8 +36,17 @@ describe("normal behavior", () => { expect(a[2]).toBe(1); }); - test("non-empty string", () => { - var a = Array.from("what"); + test("non-empty array, with mapFn", () => { + const a = Array.from([5, 8, 1], n => ++n); + expect(a instanceof Array).toBeTrue(); + expect(a).toHaveLength(3); + expect(a[0]).toBe(6); + expect(a[1]).toBe(9); + expect(a[2]).toBe(2); + }); + + test("non-empty string, no mapFn", () => { + const a = Array.from("what"); expect(a instanceof Array).toBeTrue(); expect(a).toHaveLength(4); expect(a[0]).toBe("w"); @@ -34,36 +55,66 @@ describe("normal behavior", () => { expect(a[3]).toBe("t"); }); - test("shallow array copy", () => { - var a = [1, 2, 3]; - var b = Array.from([a]); + test("non-empty string, with mapFn", () => { + const a = Array.from("what", n => n + n); + expect(a instanceof Array).toBeTrue(); + expect(a).toHaveLength(4); + expect(a[0]).toBe("ww"); + expect(a[1]).toBe("hh"); + expect(a[2]).toBe("aa"); + expect(a[3]).toBe("tt"); + }); + + test("shallow array copy, no mapFn", () => { + const a = [1, 2, 3]; + const b = Array.from([a]); expect(b instanceof Array).toBeTrue(); expect(b).toHaveLength(1); b[0][0] = 4; expect(a[0]).toBe(4); }); - test("from iterator", () => { - function rangeIterator(begin, end) { - return { - [Symbol.iterator]() { - let value = begin - 1; - return { - next() { - if (value < end) { - value += 1; - } - return { value: value, done: value >= end }; - }, - }; - }, - }; - } + test("shallow array copy, with mapFn", () => { + const a = [1, 2, 3]; + const b = Array.from([a], n => n.map(n => n + 2)); + expect(b instanceof Array).toBeTrue(); + expect(b).toHaveLength(1); + b[0][0] = 10; + expect(a[0]).toBe(1); + expect(b[0][0]).toBe(10); + expect(b[0][1]).toBe(4); + expect(b[0][2]).toBe(5); + }); - var a = Array.from(rangeIterator(8, 10)); + const rangeIterator = function (begin, end) { + return { + [Symbol.iterator]() { + let value = begin - 1; + return { + next() { + if (value < end) { + value += 1; + } + return { value: value, done: value >= end }; + }, + }; + }, + }; + }; + + test("from iterator, no mapFn", () => { + const a = Array.from(rangeIterator(8, 10)); expect(a instanceof Array).toBeTrue(); expect(a).toHaveLength(2); expect(a[0]).toBe(8); expect(a[1]).toBe(9); }); + + test("from iterator, with mapFn", () => { + const a = Array.from(rangeIterator(8, 10), n => --n); + expect(a instanceof Array).toBeTrue(); + expect(a).toHaveLength(2); + expect(a[0]).toBe(7); + expect(a[1]).toBe(8); + }); }); |