[JSC] Implement Array.prototype.flatMap and Array.prototype.flatten
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 8 Feb 2018 10:43:13 +0000 (10:43 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 8 Feb 2018 10:43:13 +0000 (10:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=182440

Reviewed by Darin Adler.

JSTests:

* stress/array-flatmap.js: Added.
(shouldBe):
(shouldBeArray):
(shouldThrow):
(var):
* stress/array-flatten.js: Added.
(shouldBe):
(shouldBeArray):
* test262.yaml:
* test262/test/built-ins/Array/prototype/flatMap/depth-always-one.js:
(3.flatMap):
Pick test262 82c6148980332febe92a544a1fb653718e9fdb57 change.

Source/JavaScriptCore:

This patch implements Array.prototype.flatMap and Array.prototype.flatten
since they are now stage 3 [1].

[1]: https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray

* builtins/ArrayPrototype.js:
(filter):
(map):
(globalPrivate.concatSlowPath):
(globalPrivate.arraySpeciesCreate):
(globalPrivate.flattenIntoArray):
(flatten):
(globalPrivate.flattenIntoArrayWithCallback):
We separate flattenIntoArray from flattenIntoArrayWithCallback due to performance reason.
We carefully keep both functions small to encourage inlining.

(flatMap):
* runtime/ArrayPrototype.cpp:
(JSC::ArrayPrototype::finishCreation):

LayoutTests:

* inspector/model/remote-object-get-properties-expected.txt:
* js/Object-getOwnPropertyNames-expected.txt:
* js/script-tests/Object-getOwnPropertyNames.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@228266 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
JSTests/ChangeLog
JSTests/stress/array-flatmap.js [new file with mode: 0644]
JSTests/stress/array-flatten.js [new file with mode: 0644]
JSTests/test262.yaml
JSTests/test262/test/built-ins/Array/prototype/flatMap/depth-always-one.js
LayoutTests/ChangeLog
LayoutTests/inspector/model/remote-object-get-properties-expected.txt
LayoutTests/js/Object-getOwnPropertyNames-expected.txt
LayoutTests/js/script-tests/Object-getOwnPropertyNames.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/ArrayPrototype.js
Source/JavaScriptCore/runtime/ArrayPrototype.cpp

index 5317f1b..8d9e247 100644 (file)
@@ -1,3 +1,23 @@
+2018-02-06  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Implement Array.prototype.flatMap and Array.prototype.flatten
+        https://bugs.webkit.org/show_bug.cgi?id=182440
+
+        Reviewed by Darin Adler.
+
+        * stress/array-flatmap.js: Added.
+        (shouldBe):
+        (shouldBeArray):
+        (shouldThrow):
+        (var):
+        * stress/array-flatten.js: Added.
+        (shouldBe):
+        (shouldBeArray):
+        * test262.yaml:
+        * test262/test/built-ins/Array/prototype/flatMap/depth-always-one.js:
+        (3.flatMap):
+        Pick test262 82c6148980332febe92a544a1fb653718e9fdb57 change.
+
 2018-02-06  Keith Miller  <keith_miller@apple.com>
 
         put_to_scope/get_from_scope should not cache lexical scopes when expecting a global object
diff --git a/JSTests/stress/array-flatmap.js b/JSTests/stress/array-flatmap.js
new file mode 100644 (file)
index 0000000..1d1e4d0
--- /dev/null
@@ -0,0 +1,97 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldBeArray(actual, expected) {
+    shouldBe(actual.length, expected.length);
+    for (var i = 0; i < expected.length; ++i) {
+        try {
+            if (Array.isArray(expected[i])) {
+                shouldBe(Array.isArray(actual[i]), true);
+                shouldBeArray(actual[i], expected[i]);
+            } else
+                shouldBe(actual[i], expected[i]);
+        } catch(e) {
+            print(JSON.stringify(actual));
+            throw e;
+        }
+    }
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+shouldThrow(() => {
+    [].flatMap();
+}, `TypeError: Array.prototype.flatMap callback must be a function`);
+
+var array = [42];
+shouldBeArray(array.flatMap(function (v) {
+    "use strict";
+    shouldBe(v, 42);
+    return this;
+}, `Cocoa`), [`Cocoa`]);
+
+shouldBeArray([].flatMap((v) => v), []);
+shouldBeArray([42].flatMap((v) => v), [42]);
+shouldBeArray([42].flatMap((v) => [v]), [42]);
+shouldBeArray([42].flatMap((v) => [[v]]), [[42]]);
+shouldBeArray([42].flatMap((v) => [v, v, v]), [42,42,42]);
+shouldBeArray([42,[43],44].flatMap((v) => [v, v]), [42,42,[43],[43],44,44]);
+shouldBeArray([,,,,,,].flatMap((v) => [v, v]), []);
+shouldBeArray([42,43,44].flatMap((v) => []), []);
+shouldBeArray([42,[43],44].flatMap((v) => v), [42,43,44]);
+
+class DerivedArray extends Array { }
+shouldBe((new DerivedArray).flatMap(() => {}) instanceof DerivedArray, true);
+var flatMap = [].flatMap;
+var realm = createGlobalObject();
+shouldBe(flatMap.call({}, () => {}) instanceof Array, true);
+shouldBe(flatMap.call(new realm.Array, () => {}) instanceof Array, true);
+var array2 = new realm.Array;
+array2.constructor = 0;
+
+shouldThrow(() => {
+    flatMap.call(array2, () => {});
+}, `TypeError: 0 is not a constructor`);
+
+var array2 = new realm.Array;
+array2.constructor = undefined;
+shouldBe(flatMap.call(array2, () => {}) instanceof Array, true);
+
+var array2 = new realm.Array;
+array2.constructor = {
+    get [Symbol.species]() {
+        return null;
+    }
+};
+shouldBe(flatMap.call(array2, () => {}) instanceof Array, true);
+
+var array2 = new realm.Array;
+array2.constructor = {
+    get [Symbol.species]() {
+        return undefined;
+    }
+};
+shouldBe(flatMap.call(array2, () => {}) instanceof Array, true);
+
+var array2 = new realm.Array;
+array2.constructor = {
+    get [Symbol.species]() {
+        return DerivedArray;
+    }
+};
+shouldBe(flatMap.call(array2, () => {}) instanceof DerivedArray, true);
diff --git a/JSTests/stress/array-flatten.js b/JSTests/stress/array-flatten.js
new file mode 100644 (file)
index 0000000..7a6b29d
--- /dev/null
@@ -0,0 +1,108 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldBeArray(actual, expected) {
+    shouldBe(actual.length, expected.length);
+    for (var i = 0; i < expected.length; ++i) {
+        try {
+            if (Array.isArray(expected[i])) {
+                shouldBe(Array.isArray(actual[i]), true);
+                shouldBeArray(actual[i], expected[i]);
+            } else
+                shouldBe(actual[i], expected[i]);
+        } catch(e) {
+            print(JSON.stringify(actual));
+            throw e;
+        }
+    }
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+shouldBe([].flatten.length, 0);
+shouldBe([].flatten.name, `flatten`);
+
+shouldBeArray([].flatten(), []);
+shouldBeArray([0, 1, 2, 3, , 4].flatten(), [0, 1, 2, 3, 4]);
+shouldBeArray([,,,,,].flatten(), []);
+
+shouldBeArray([].flatten(0), []);
+shouldBeArray([0, 1, 2, 3, , 4].flatten(0), [0, 1, 2, 3, 4]);
+shouldBeArray([,,,,,].flatten(0), []);
+
+shouldBeArray([].flatten(-1), []);
+shouldBeArray([0, 1, 2, 3, , 4].flatten(-1), [0, 1, 2, 3, 4]);
+shouldBeArray([,,,,,].flatten(-1), []);
+
+shouldBeArray([[],[]].flatten(), []);
+shouldBeArray([[0],[1]].flatten(), [0,1]);
+shouldBeArray([[0],[],1].flatten(), [0,1]);
+shouldBeArray([[0],[[]],1].flatten(), [0,[],1]);
+shouldBeArray([[0],[[]],1].flatten(1), [0,[],1]);
+shouldBeArray([[0],[[]],1].flatten(2), [0,1]);
+
+shouldBeArray([[],[]].flatten(0), [[],[]]);
+shouldBeArray([[0],[1]].flatten(0), [[0],[1]]);
+shouldBeArray([[0],[],1].flatten(0), [[0],[],1]);
+shouldBeArray([[0],[[]],1].flatten(0), [[0],[[]],1]);
+
+shouldBeArray([[[[[[[[[[[[[[[[[[[[[42]]]]]]]]]]]]]]]]]]]]].flatten(Infinity), [42]);
+
+var array = [];
+shouldBe(array.flatten() !== array, true);
+
+class DerivedArray extends Array { }
+shouldBe((new DerivedArray).flatten() instanceof DerivedArray, true);
+var flatten = [].flatten;
+var realm = createGlobalObject();
+shouldBe(flatten.call({}) instanceof Array, true);
+shouldBe(flatten.call(new realm.Array) instanceof Array, true);
+var array2 = new realm.Array;
+array2.constructor = 0;
+
+shouldThrow(() => {
+    flatten.call(array2);
+}, `TypeError: 0 is not a constructor`);
+
+var array2 = new realm.Array;
+array2.constructor = undefined;
+shouldBe(flatten.call(array2) instanceof Array, true);
+
+var array2 = new realm.Array;
+array2.constructor = {
+    get [Symbol.species]() {
+        return null;
+    }
+};
+shouldBe(flatten.call(array2) instanceof Array, true);
+
+var array2 = new realm.Array;
+array2.constructor = {
+    get [Symbol.species]() {
+        return undefined;
+    }
+};
+shouldBe(flatten.call(array2) instanceof Array, true);
+
+var array2 = new realm.Array;
+array2.constructor = {
+    get [Symbol.species]() {
+        return DerivedArray;
+    }
+};
+shouldBe(flatten.call(array2) instanceof DerivedArray, true);
index 15162b2..8007742 100644 (file)
 - path: test262/test/built-ins/Array/prototype/findIndex/return-negative-one-if-predicate-returns-false-value.js
   cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatMap/array-like-objects.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
 - path: test262/test/built-ins/Array/prototype/flatMap/array-like-objects.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatMap/bound-function-argument.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
 - path: test262/test/built-ins/Array/prototype/flatMap/bound-function-argument.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatMap/depth-always-one.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
 - path: test262/test/built-ins/Array/prototype/flatMap/depth-always-one.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatMap/length.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], []
 - path: test262/test/built-ins/Array/prototype/flatMap/length.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatMap/name.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], []
 - path: test262/test/built-ins/Array/prototype/flatMap/name.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatMap/non-callable-argument-throws.js
   cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], []
 - path: test262/test/built-ins/Array/prototype/flatMap/non-callable-argument-throws.js
 - path: test262/test/built-ins/Array/prototype/flatMap/null-undefined-input-throws.js
   cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatMap/thisArg-argument.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/array-like-objects.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/array-like-objects.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/bound-function-call.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/bound-function-call.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/empty-array-elements.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/empty-array-elements.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/empty-object-elements.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/empty-object-elements.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/length.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/length.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/name.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/name.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/non-numeric-depth-should-not-throw.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/non-numeric-depth-should-not-throw.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/non-object-ctor-throws.js
   cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/non-object-ctor-throws.js
   cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/null-undefined-elements.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/null-undefined-elements.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/null-undefined-input-throws.js
   cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/null-undefined-input-throws.js
   cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/positive-infinity.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/positive-infinity.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/compareArray.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/prop-desc.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/prop-desc.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js", "../../../../../harness/propertyHelper.js"], [:strict]
 - path: test262/test/built-ins/Array/prototype/flatten/symbol-object-create-null-depth-throws.js
   cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], []
 - path: test262/test/built-ins/Array/prototype/flatten/symbol-object-create-null-depth-throws.js
index 83b7b5f..851a9a7 100644 (file)
@@ -14,7 +14,7 @@ assert.compareArray([1, 2].flatMap(function(e) {
 
 var result = [1, 2, 3].flatMap(function(ele) {
   return [[ele * 2]];
-};
+});
 assert.sameValue(result.length, 3, 'array depth is more than 1 - length');
 assert.compareArray(result[0], [2], 'array depth is more than 1 - 1st element');
 assert.compareArray(result[1], [4], 'array depth is more than 1 - 2nd element');
index 31806d7..41270ae 100644 (file)
@@ -1,3 +1,14 @@
+2018-02-06  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Implement Array.prototype.flatMap and Array.prototype.flatten
+        https://bugs.webkit.org/show_bug.cgi?id=182440
+
+        Reviewed by Darin Adler.
+
+        * inspector/model/remote-object-get-properties-expected.txt:
+        * js/Object-getOwnPropertyNames-expected.txt:
+        * js/script-tests/Object-getOwnPropertyNames.js:
+
 2018-01-13  Darin Adler  <darin@apple.com>
 
         Event improvements
index 8ee1a90..682352d 100644 (file)
@@ -74,6 +74,8 @@ ALL PROPERTIES:
     indexOf
     lastIndexOf
     filter
+    flatMap
+    flatten
     reduce
     reduceRight
     map
@@ -125,6 +127,8 @@ OWN PROPERTIES:
     indexOf
     lastIndexOf
     filter
+    flatMap
+    flatten
     reduce
     reduceRight
     map
@@ -161,6 +165,8 @@ DISPLAYABLE PROPERTIES:
     indexOf
     lastIndexOf
     filter
+    flatMap
+    flatten
     reduce
     reduceRight
     map
@@ -197,6 +203,8 @@ ALL PROPERTIES:
     indexOf
     lastIndexOf
     filter
+    flatMap
+    flatten
     reduce
     reduceRight
     map
index e8e753e..71f8a66 100644 (file)
@@ -47,7 +47,7 @@ PASS getSortedOwnPropertyNames(Object.prototype) is ['__defineGetter__', '__defi
 PASS getSortedOwnPropertyNames(Function) is ['length', 'name', 'prototype']
 PASS getSortedOwnPropertyNames(Function.prototype) is ['apply', 'arguments', 'bind', 'call', 'caller', 'constructor', 'length', 'name', 'toString']
 PASS getSortedOwnPropertyNames(Array) is ['from', 'isArray', 'length', 'name', 'of', 'prototype']
-PASS getSortedOwnPropertyNames(Array.prototype) is ['concat', 'constructor', 'copyWithin', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'length', 'map', 'pop', 'push', 'reduce', 'reduceRight', 'reverse', 'shift', 'slice', 'some', 'sort', 'splice', 'toLocaleString', 'toString', 'unshift', 'values']
+PASS getSortedOwnPropertyNames(Array.prototype) is ['concat', 'constructor', 'copyWithin', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flatMap', 'flatten', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'length', 'map', 'pop', 'push', 'reduce', 'reduceRight', 'reverse', 'shift', 'slice', 'some', 'sort', 'splice', 'toLocaleString', 'toString', 'unshift', 'values']
 PASS getSortedOwnPropertyNames(String) is ['fromCharCode', 'fromCodePoint', 'length', 'name', 'prototype', 'raw']
 PASS getSortedOwnPropertyNames(String.prototype) is ['anchor', 'big', 'blink', 'bold', 'charAt', 'charCodeAt', 'codePointAt', 'concat', 'constructor', 'endsWith', 'fixed', 'fontcolor', 'fontsize', 'includes', 'indexOf', 'italics', 'lastIndexOf', 'length', 'link', 'localeCompare', 'match', 'normalize', 'padEnd', 'padStart', 'repeat', 'replace', 'search', 'slice', 'small', 'split', 'startsWith', 'strike', 'sub', 'substr', 'substring', 'sup', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toString', 'toUpperCase', 'trim', 'trimEnd', 'trimLeft', 'trimRight', 'trimStart', 'valueOf']
 PASS getSortedOwnPropertyNames(Boolean) is ['length', 'name', 'prototype']
index a9c0726..bbae583 100644 (file)
@@ -56,7 +56,7 @@ var expectedPropertyNamesSet = {
     "Function": "['length', 'name', 'prototype']",
     "Function.prototype": "['apply', 'arguments', 'bind', 'call', 'caller', 'constructor', 'length', 'name', 'toString']",
     "Array": "['from', 'isArray', 'length', 'name', 'of', 'prototype']",
-    "Array.prototype": "['concat', 'constructor', 'copyWithin', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'length', 'map', 'pop', 'push', 'reduce', 'reduceRight', 'reverse', 'shift', 'slice', 'some', 'sort', 'splice', 'toLocaleString', 'toString', 'unshift', 'values']",
+    "Array.prototype": "['concat', 'constructor', 'copyWithin', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flatMap', 'flatten', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'length', 'map', 'pop', 'push', 'reduce', 'reduceRight', 'reverse', 'shift', 'slice', 'some', 'sort', 'splice', 'toLocaleString', 'toString', 'unshift', 'values']",
     "String": "['fromCharCode', 'fromCodePoint', 'length', 'name', 'prototype', 'raw']",
     "String.prototype": "['anchor', 'big', 'blink', 'bold', 'charAt', 'charCodeAt', 'codePointAt', 'concat', 'constructor', 'endsWith', 'fixed', 'fontcolor', 'fontsize', 'includes', 'indexOf', 'italics', 'lastIndexOf', 'length', 'link', 'localeCompare', 'match', 'normalize', 'padEnd', 'padStart', 'repeat', 'replace', 'search', 'slice', 'small', 'split', 'startsWith', 'strike', 'sub', 'substr', 'substring', 'sup', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toString', 'toUpperCase', 'trim', 'trimEnd', 'trimLeft', 'trimRight', 'trimStart', 'valueOf']",
     "Boolean": "['length', 'name', 'prototype']",
index 883e457..76c7556 100644 (file)
@@ -1,3 +1,30 @@
+2018-02-06  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Implement Array.prototype.flatMap and Array.prototype.flatten
+        https://bugs.webkit.org/show_bug.cgi?id=182440
+
+        Reviewed by Darin Adler.
+
+        This patch implements Array.prototype.flatMap and Array.prototype.flatten
+        since they are now stage 3 [1].
+
+        [1]: https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
+
+        * builtins/ArrayPrototype.js:
+        (filter):
+        (map):
+        (globalPrivate.concatSlowPath):
+        (globalPrivate.arraySpeciesCreate):
+        (globalPrivate.flattenIntoArray):
+        (flatten):
+        (globalPrivate.flattenIntoArrayWithCallback):
+        We separate flattenIntoArray from flattenIntoArrayWithCallback due to performance reason.
+        We carefully keep both functions small to encourage inlining.
+
+        (flatMap):
+        * runtime/ArrayPrototype.cpp:
+        (JSC::ArrayPrototype::finishCreation):
+
 2018-01-13  Darin Adler  <darin@apple.com>
 
         Event improvements
index 5e557b8..b1718a4 100644 (file)
@@ -184,7 +184,7 @@ function filter(callback /*, thisArg */)
         // We have this check so that if some array from a different global object
         // calls this map they don't get an array with the Array.prototype of the
         // other global object.
-        if (@isArrayConstructor(constructor) && @Array !== constructor)
+        if (@Array !== constructor && @isArrayConstructor(constructor))
             constructor = @undefined;
         if (@isObject(constructor)) {
             constructor = constructor.@speciesSymbol;
@@ -230,7 +230,7 @@ function map(callback /*, thisArg */)
         // We have this check so that if some array from a different global object
         // calls this map they don't get an array with the Array.prototype of the
         // other global object.
-        if (@isArrayConstructor(constructor) && @Array !== constructor)
+        if (@Array !== constructor && @isArrayConstructor(constructor))
             constructor = @undefined;
         if (@isObject(constructor)) {
             constructor = constructor.@speciesSymbol;
@@ -623,7 +623,7 @@ function concatSlowPath()
         // We have this check so that if some array from a different global object
         // calls this map they don't get an array with the Array.prototype of the
         // other global object.
-        if (@isArrayConstructor(constructor) && @Array !== constructor)
+        if (@Array !== constructor && @isArrayConstructor(constructor))
             constructor = @undefined;
         else if (@isObject(constructor)) {
             constructor = constructor.@speciesSymbol;
@@ -738,3 +738,107 @@ function copyWithin(target, start /*, end */)
 
     return array;
 }
+
+@globalPrivate
+function arraySpeciesCreate(array, length)
+{
+    "use strict";
+
+    if (!@isArray(array))
+        return @newArrayWithSize(length);
+
+    var constructor = array.constructor;
+    var arrayConstructorInRealm = @Array;
+    // We have this check so that if some array from a different global object
+    // calls this map they don't get an array with the Array.prototype of the
+    // other global object.
+    if (arrayConstructorInRealm !== constructor && @isArrayConstructor(constructor))
+        return @newArrayWithSize(length);
+
+    if (@isObject(constructor)) {
+        constructor = constructor.@speciesSymbol;
+        if (constructor == null)
+            return @newArrayWithSize(length);
+    }
+
+    if (constructor === arrayConstructorInRealm || constructor === @undefined)
+        return @newArrayWithSize(length);
+    return new constructor(length);
+}
+
+@globalPrivate
+function flattenIntoArray(target, source, sourceLength, targetIndex, depth)
+{
+    "use strict";
+
+    for (var sourceIndex = 0; sourceIndex < sourceLength; ++sourceIndex) {
+        if (sourceIndex in source) {
+            var element = source[sourceIndex];
+            if (depth > 0 && @isArray(element))
+                targetIndex = @flattenIntoArray(target, element, @toLength(element.length), targetIndex, depth - 1);
+            else {
+                if (targetIndex >= @MAX_SAFE_INTEGER)
+                    @throwTypeError("flatten array exceeds 2**53 - 1");
+                @putByValDirect(target, targetIndex, element);
+                ++targetIndex;
+            }
+        }
+    }
+    return targetIndex;
+}
+
+function flatten()
+{
+    "use strict";
+
+    var array = @toObject(this, "Array.prototype.flatten requires that |this| not be null or undefined");
+    var length = @toLength(array.length);
+
+    var depthNum = 1;
+    var depth = @argument(0);
+    if (depth !== @undefined)
+        depthNum = @toInteger(depth);
+
+    var result = @arraySpeciesCreate(array, 0);
+
+    @flattenIntoArray(result, array, length, 0, depthNum);
+    return result;
+}
+
+@globalPrivate
+function flattenIntoArrayWithCallback(target, source, sourceLength, targetIndex, callback, thisArg)
+{
+    "use strict";
+
+    for (var sourceIndex = 0; sourceIndex < sourceLength; ++sourceIndex) {
+        if (sourceIndex in source) {
+            var element = callback.@call(thisArg, source[sourceIndex], sourceIndex, source);
+            if (@isArray(element))
+                targetIndex = @flattenIntoArray(target, element, @toLength(element.length), targetIndex, 0);
+            else {
+                if (targetIndex >= @MAX_SAFE_INTEGER)
+                    @throwTypeError("flatten array exceeds 2**53 - 1");
+                @putByValDirect(target, targetIndex, element);
+                ++targetIndex;
+            }
+        }
+    }
+    return target;
+}
+
+function flatMap(callback)
+{
+    "use strict";
+
+    var array = @toObject(this, "Array.prototype.flatMap requires that |this| not be null or undefined");
+    var length = @toLength(array.length);
+
+    if (typeof callback !== "function")
+        @throwTypeError("Array.prototype.flatMap callback must be a function");
+
+    var thisArg = @argument(1);
+
+    var result = @arraySpeciesCreate(array, 0);
+
+    return @flattenIntoArrayWithCallback(result, array, length, 0, callback, thisArg);
+}
index 9c31d7a..efbaf32 100644 (file)
@@ -107,6 +107,8 @@ void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("indexOf", arrayProtoFuncIndexOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1, ArrayIndexOfIntrinsic);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", arrayProtoFuncLastIndexOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("filter", arrayPrototypeFilterCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("flatMap", arrayPrototypeFlatMapCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("flatten", arrayPrototypeFlattenCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduce", arrayPrototypeReduceCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduceRight", arrayPrototypeReduceRightCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("map", arrayPrototypeMapCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));