Diff
Modified: trunk/JSTests/ChangeLog (287135 => 287136)
--- trunk/JSTests/ChangeLog 2021-12-16 15:49:37 UTC (rev 287135)
+++ trunk/JSTests/ChangeLog 2021-12-16 16:10:09 UTC (rev 287136)
@@ -1,3 +1,26 @@
+2021-12-16 Devin Rousso <[email protected]>
+
+ Implement Array.prototype.groupBy and Array.prototype.groupByToMap
+ https://bugs.webkit.org/show_bug.cgi?id=234327
+
+ Reviewed by Yusuke Suzuki.
+
+ * stress/array-groupBy.js: Added.
+ (shouldBe):
+ (shouldBeObject):
+ (shouldBeObject.replacer):
+ (notReached):
+ (toObject):
+ (reverseInsertionOrder):
+ * stress/array-groupByToMap.js: Added.
+ (shouldBe):
+ (shouldBeObject):
+ (shouldBeObject.replacer):
+ (shouldBeMap):
+ (notReached):
+ (toObject):
+ (reverseInsertionOrder):
+
2021-12-15 Joseph Griego <[email protected]>
[Shadow Realms] Wrapped functions must only throw TypeError from calling realm
Added: trunk/JSTests/stress/array-groupBy.js (0 => 287136)
--- trunk/JSTests/stress/array-groupBy.js (rev 0)
+++ trunk/JSTests/stress/array-groupBy.js 2021-12-16 16:10:09 UTC (rev 287136)
@@ -0,0 +1,162 @@
+//@ requireOptions("--useArrayGroupByMethod=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`FAIL: expected '${expected}' actual '${actual}'`);
+}
+
+function shouldBeObject(actual, expected) {
+ function replacer(key, value) {
+ if (value === undefined)
+ return "%undefined%";
+ return value;
+ }
+
+ let actualJSON = JSON.stringify(actual, replacer).replaceAll("\"%undefined%\"", "undefined");
+ let expectedJSON = JSON.stringify(expected, replacer).replaceAll("\"%undefined%\"", "undefined");
+ shouldBe(actualJSON, expectedJSON);
+}
+
+function notReached() {
+ throw new Error("should not reach here");
+}
+
+
+// Helper variables
+
+Array.prototype.__test = 42;
+
+let symbol = Symbol("symbol");
+
+let sparseArrayLength = 6;
+let mixPartialAndFast = new Array(sparseArrayLength);
+mixPartialAndFast[sparseArrayLength - 1] = sparseArrayLength - 1;
+for(let i = 0; i < 3; ++i)
+ mixPartialAndFast[i] = i;
+
+function toObject(array) {
+ let result = {};
+ result.length = array.length;
+ for (let i in array)
+ result[i] = array[i];
+ result.groupBy = Array.prototype.groupBy;
+ return result;
+}
+
+function reverseInsertionOrder(array) {
+ let obj = toObject(array);
+ let props = [];
+ for (let i in obj)
+ props.push(i);
+ let result = {};
+ for (let i = props.length - 1; i >= 0; i--)
+ result[props[i]] = obj[props[i]];
+ result.groupBy = Array.prototype.groupBy;
+ return result;
+}
+
+let objectWithToStringThatThrows = {
+ toString: notReached,
+};
+
+let objectWithValueOfThatThrows = {
+ valueOf: notReached,
+};
+
+
+// Basic
+
+shouldBe(Array.prototype.groupBy.length, 1);
+shouldBe(Array.prototype.groupBy.name, "groupBy");
+
+shouldBeObject([undefined].groupBy((x) => x === undefined ? "a" : "b"), {"a": [undefined]});
+shouldBeObject([undefined].groupBy((x) => x === undefined), {"true": [undefined]});
+
+shouldBeObject((new Array(4)).groupBy((x) => x === undefined ? "a" : "b"), {"a": [undefined, undefined, undefined, undefined]});
+shouldBeObject((new Array(4)).groupBy((x) => x === undefined), {"true": [undefined, undefined, undefined, undefined]});
+
+shouldBeObject([0, 1, 2, 3].groupBy((x) => !(x & 1) ? "a" : "b"), {"a": [0, 2], "b": [1, 3]});
+shouldBeObject([0, 1, 2, 3].groupBy((x) => !(x & 1)), {"true": [0, 2], "false": [1, 3]});
+
+shouldBeObject([0, 1, 2, 3].groupBy((x, i) => i >= 2 ? "a" : "b"), {"b": [0, 1], "a": [2, 3]});
+shouldBeObject([0, 1, 2, 3].groupBy((x, i) => i >= 2), {"false": [0, 1], "true": [2, 3]});
+
+shouldBeObject(mixPartialAndFast.groupBy((x, i) => i >= 2 ? "a" : "b"), {"b": [0, 1], "a": [2, undefined, undefined, sparseArrayLength - 1]});
+shouldBeObject(mixPartialAndFast.groupBy((x, i) => i >= 2), {"false": [0, 1], "true": [2, undefined, undefined, sparseArrayLength - 1]});
+
+
+// Generic Object
+
+shouldBeObject(toObject([undefined]).groupBy((x) => x === undefined ? "a" : "b"), {"a": [undefined]});
+shouldBeObject(toObject([undefined]).groupBy((x) => x === undefined), {"true": [undefined]});
+
+shouldBeObject(toObject(new Array(4)).groupBy((x) => x === undefined ? "a" : "b"), {"a": [undefined, undefined, undefined, undefined]});
+shouldBeObject(toObject(new Array(4)).groupBy((x) => x === undefined), {"true": [undefined, undefined, undefined, undefined]});
+
+shouldBeObject(toObject([0, 1, 2, 3]).groupBy((x) => !(x & 1) ? "a" : "b"), {"a": [0, 2], "b": [1, 3]});
+shouldBeObject(toObject([0, 1, 2, 3]).groupBy((x) => !(x & 1)), {"true": [0, 2], "false": [1, 3]});
+
+shouldBeObject(toObject([0, 1, 2, 3]).groupBy((x, i) => i >= 2 ? "a" : "b"), {"b": [0, 1], "a": [2, 3]});
+shouldBeObject(toObject([0, 1, 2, 3]).groupBy((x, i) => i >= 2), {"false": [0, 1], "true": [2, 3]});
+
+shouldBeObject(toObject(mixPartialAndFast).groupBy((x, i) => i >= 2 ? "a" : "b"), {"b": [0, 1], "a": [2, undefined, undefined, sparseArrayLength - 1]});
+shouldBeObject(toObject(mixPartialAndFast).groupBy((x, i) => i >= 2), {"false": [0, 1], "true": [2, undefined, undefined, sparseArrayLength - 1]});
+
+
+// Array-like object with invalid lengths
+
+shouldBeObject(Array.prototype.groupBy.call({ 0: 0, 1: 1, 2: 2, 3: 3, length: 0 }, notReached), {});
+shouldBeObject(Array.prototype.groupBy.call({ 0: 0, 1: 1, 2: 2, 3: 3, length: -0 }, notReached), {});
+shouldBeObject(Array.prototype.groupBy.call({ 0: 0, 1: 1, 2: 2, 3: 3, length: -4 }, notReached), {});
+
+
+// Reversed generic Object
+
+shouldBeObject(reverseInsertionOrder([undefined]).groupBy((x) => x === undefined ? "a" : "b"), {"a": [undefined]});
+shouldBeObject(reverseInsertionOrder([undefined]).groupBy((x) => x === undefined), {"true": [undefined]});
+
+shouldBeObject(reverseInsertionOrder(new Array(4)).groupBy((x) => x === undefined ? "a" : "b"), {"a": [undefined, undefined, undefined, undefined]});
+shouldBeObject(reverseInsertionOrder(new Array(4)).groupBy((x) => x === undefined), {"true": [undefined, undefined, undefined, undefined]});
+
+shouldBeObject(reverseInsertionOrder([0, 1, 2, 3]).groupBy((x) => !(x & 1) ? "a" : "b"), {"a": [0, 2], "b": [1, 3]});
+shouldBeObject(reverseInsertionOrder([0, 1, 2, 3]).groupBy((x) => !(x & 1)), {"true": [0, 2], "false": [1, 3]});
+
+shouldBeObject(reverseInsertionOrder([0, 1, 2, 3]).groupBy((x, i) => i >= 2 ? "a" : "b"), {"b": [0, 1], "a": [2, 3]});
+shouldBeObject(reverseInsertionOrder([0, 1, 2, 3]).groupBy((x, i) => i >= 2), {"false": [0, 1], "true": [2, 3]});
+
+shouldBeObject(reverseInsertionOrder(mixPartialAndFast).groupBy((x, i) => i >= 2 ? "a" : "b"), {"b": [0, 1], "a": [2, undefined, undefined, sparseArrayLength - 1]});
+shouldBeObject(reverseInsertionOrder(mixPartialAndFast).groupBy((x, i) => i >= 2), {"false": [0, 1], "true": [2, undefined, undefined, sparseArrayLength - 1]});
+
+
+// Extra callback parameters
+
+shouldBeObject([0, 1, 2, 3].groupBy((i, j, k, l, m) => m = !m), {"true": [0, 1, 2, 3]});
+
+
+// Special keys
+
+shouldBeObject([0, 1, 2, 3].groupBy((x) => "constructor").constructor, [0, 1, 2, 3]);
+
+shouldBeObject([0, 1, 2, 3].groupBy((x) => "prototype").prototype, [0, 1, 2, 3]);
+shouldBeObject([0, 1, 2, 3].groupBy((x) => "__proto__").__proto__, [0, 1, 2, 3]);
+
+shouldBeObject([0, 1, 2, 3].groupBy((x) => -0)[0], [0, 1, 2, 3]);
+shouldBeObject([0, 1, 2, 3].groupBy((x) => 0)[0], [0, 1, 2, 3]);
+
+let objectWithToStringCounter = {
+ counter: 0,
+ toString() { return this.counter++; },
+};
+shouldBeObject([0, 1, 2, 3].groupBy((x) => objectWithToStringCounter), {"0": [0], "1": [1], "2": [2], "3": [3]});
+shouldBe(objectWithToStringCounter.counter, 4);
+
+try {
+ shouldBeObject([0, 1, 2, 3].groupBy((x) => objectWithToStringThatThrows), {});
+ notReached();
+} catch (e) {
+ shouldBe(e.message, "should not reach here");
+}
+
+shouldBeObject([0, 1, 2, 3].groupBy((x) => objectWithValueOfThatThrows)["[object Object]"], [0, 1, 2, 3]);
+
+shouldBeObject([0, 1, 2, 3].groupBy((x) => symbol)[symbol], [0, 1, 2, 3]);
Added: trunk/JSTests/stress/array-groupByToMap.js (0 => 287136)
--- trunk/JSTests/stress/array-groupByToMap.js (rev 0)
+++ trunk/JSTests/stress/array-groupByToMap.js 2021-12-16 16:10:09 UTC (rev 287136)
@@ -0,0 +1,161 @@
+//@ requireOptions("--useArrayGroupByMethod=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`FAIL: expected '${expected}' actual '${actual}'`);
+}
+
+function shouldBeObject(actual, expected) {
+ function replacer(key, value) {
+ if (value === undefined)
+ return "%undefined%";
+ return value;
+ }
+
+ let actualJSON = JSON.stringify(actual, replacer).replaceAll("\"%undefined%\"", "undefined");
+ let expectedJSON = JSON.stringify(expected, replacer).replaceAll("\"%undefined%\"", "undefined");
+ shouldBe(actualJSON, expectedJSON);
+}
+
+function shouldBeMap(a, b) {
+ shouldBeObject(Array.from(a), b);
+}
+
+function notReached() {
+ throw new Error("should not reach here");
+}
+
+
+// Helper variables
+
+Array.prototype.__test = 42;
+
+let symbol = Symbol("symbol");
+
+let sparseArrayLength = 6;
+let mixPartialAndFast = new Array(sparseArrayLength);
+mixPartialAndFast[sparseArrayLength - 1] = sparseArrayLength - 1;
+for(let i = 0; i < 3; ++i)
+ mixPartialAndFast[i] = i;
+
+function toObject(array) {
+ let result = {};
+ result.length = array.length;
+ for (let i in array)
+ result[i] = array[i];
+ result.groupByToMap = Array.prototype.groupByToMap;
+ return result;
+}
+
+function reverseInsertionOrder(array) {
+ let obj = toObject(array);
+ let props = [];
+ for (let i in obj)
+ props.push(i);
+ let result = {};
+ for (let i = props.length - 1; i >= 0; i--)
+ result[props[i]] = obj[props[i]];
+ result.groupByToMap = Array.prototype.groupByToMap;
+ return result;
+}
+
+let objectWithToStringThatThrows = {
+ toString: notReached,
+};
+
+let objectWithValueOfThatThrows = {
+ valueOf: notReached,
+};
+
+
+// Basic
+
+shouldBe(Array.prototype.groupByToMap.length, 1);
+shouldBe(Array.prototype.groupByToMap.name, "groupByToMap");
+
+shouldBeMap([undefined].groupByToMap((x) => x === undefined ? "a" : "b"), [["a", [undefined]]]);
+shouldBeMap([undefined].groupByToMap((x) => x === undefined), [[true, [undefined]]]);
+
+shouldBeMap((new Array(4)).groupByToMap((x) => x === undefined ? "a" : "b"), [["a", [undefined, undefined, undefined, undefined]]]);
+shouldBeMap((new Array(4)).groupByToMap((x) => x === undefined), [[true, [undefined, undefined, undefined, undefined]]]);
+
+shouldBeMap([0, 1, 2, 3].groupByToMap((x) => !(x & 1) ? "a" : "b"), [["a", [0, 2]], ["b", [1, 3]]]);
+shouldBeMap([0, 1, 2, 3].groupByToMap((x) => !(x & 1)), [[true, [0, 2]], [false, [1, 3]]]);
+
+shouldBeMap([0, 1, 2, 3].groupByToMap((x, i) => i >= 2 ? "a" : "b"), [["b", [0, 1]], ["a", [2, 3]]]);
+shouldBeMap([0, 1, 2, 3].groupByToMap((x, i) => i >= 2), [[false, [0, 1]], [true, [2, 3]]]);
+
+shouldBeMap(mixPartialAndFast.groupByToMap((x, i) => i >= 2 ? "a" : "b"), [["b", [0, 1]], ["a", [2, undefined, undefined, sparseArrayLength - 1]]]);
+shouldBeMap(mixPartialAndFast.groupByToMap((x, i) => i >= 2), [[false, [0, 1]], [true, [2, undefined, undefined, sparseArrayLength - 1]]]);
+
+
+// Generic Object
+
+shouldBeMap(toObject([undefined]).groupByToMap((x) => x === undefined ? "a" : "b"), [["a", [undefined]]]);
+shouldBeMap(toObject([undefined]).groupByToMap((x) => x === undefined), [[true, [undefined]]]);
+
+shouldBeMap(toObject(new Array(4)).groupByToMap((x) => x === undefined ? "a" : "b"), [["a", [undefined, undefined, undefined, undefined]]]);
+shouldBeMap(toObject(new Array(4)).groupByToMap((x) => x === undefined), [[true, [undefined, undefined, undefined, undefined]]]);
+
+shouldBeMap(toObject([0, 1, 2, 3]).groupByToMap((x) => !(x & 1) ? "a" : "b"), [["a", [0, 2]], ["b", [1, 3]]]);
+shouldBeMap(toObject([0, 1, 2, 3]).groupByToMap((x) => !(x & 1)), [[true, [0, 2]], [false, [1, 3]]]);
+
+shouldBeMap(toObject([0, 1, 2, 3]).groupByToMap((x, i) => i >= 2 ? "a" : "b"), [["b", [0, 1]], ["a", [2, 3]]]);
+shouldBeMap(toObject([0, 1, 2, 3]).groupByToMap((x, i) => i >= 2), [[false, [0, 1]], [true, [2, 3]]]);
+
+shouldBeMap(toObject(mixPartialAndFast).groupByToMap((x, i) => i >= 2 ? "a" : "b"), [["b", [0, 1]], ["a", [2, undefined, undefined, sparseArrayLength - 1]]]);
+shouldBeMap(toObject(mixPartialAndFast).groupByToMap((x, i) => i >= 2), [[false, [0, 1]], [true, [2, undefined, undefined, sparseArrayLength - 1]]]);
+
+
+// Array-like object with invalid lengths
+
+shouldBeMap(Array.prototype.groupByToMap.call({ 0: 0, 1: 1, 2: 2, 3: 3, length: 0 }, notReached), []);
+shouldBeMap(Array.prototype.groupByToMap.call({ 0: 0, 1: 1, 2: 2, 3: 3, length: -0 }, notReached), []);
+shouldBeMap(Array.prototype.groupByToMap.call({ 0: 0, 1: 1, 2: 2, 3: 3, length: -4 }, notReached), []);
+
+
+// Reversed generic Object
+
+shouldBeMap(reverseInsertionOrder([undefined]).groupByToMap((x) => x === undefined ? "a" : "b"), [["a", [undefined]]]);
+shouldBeMap(reverseInsertionOrder([undefined]).groupByToMap((x) => x === undefined), [[true, [undefined]]]);
+
+shouldBeMap(reverseInsertionOrder(new Array(4)).groupByToMap((x) => x === undefined ? "a" : "b"), [["a", [undefined, undefined, undefined, undefined]]]);
+shouldBeMap(reverseInsertionOrder(new Array(4)).groupByToMap((x) => x === undefined), [[true, [undefined, undefined, undefined, undefined]]]);
+
+shouldBeMap(reverseInsertionOrder([0, 1, 2, 3]).groupByToMap((x) => !(x & 1) ? "a" : "b"), [["a", [0, 2]], ["b", [1, 3]]]);
+shouldBeMap(reverseInsertionOrder([0, 1, 2, 3]).groupByToMap((x) => !(x & 1)), [[true, [0, 2]], [false, [1, 3]]]);
+
+shouldBeMap(reverseInsertionOrder([0, 1, 2, 3]).groupByToMap((x, i) => i >= 2 ? "a" : "b"), [["b", [0, 1]], ["a", [2, 3]]]);
+shouldBeMap(reverseInsertionOrder([0, 1, 2, 3]).groupByToMap((x, i) => i >= 2), [[false, [0, 1]], [true, [2, 3]]]);
+
+shouldBeMap(reverseInsertionOrder(mixPartialAndFast).groupByToMap((x, i) => i >= 2 ? "a" : "b"), [["b", [0, 1]], ["a", [2, undefined, undefined, sparseArrayLength - 1]]]);
+shouldBeMap(reverseInsertionOrder(mixPartialAndFast).groupByToMap((x, i) => i >= 2), [[false, [0, 1]], [true, [2, undefined, undefined, sparseArrayLength - 1]]]);
+
+
+// Extra callback parameters
+
+shouldBeMap([0, 1, 2, 3].groupByToMap((i, j, k, l, m) => m = !m), [[true, [0, 1, 2, 3]]]);
+
+
+// Special keys
+
+shouldBeObject([0, 1, 2, 3].groupByToMap((x) => "constructor").get("constructor"), [0, 1, 2, 3]);
+
+shouldBeObject([0, 1, 2, 3].groupByToMap((x) => "prototype").get("prototype"), [0, 1, 2, 3]);
+shouldBeObject([0, 1, 2, 3].groupByToMap((x) => "__proto__").get("__proto__"), [0, 1, 2, 3]);
+
+shouldBeObject([0, 1, 2, 3].groupByToMap((x) => -0).get(0), [0, 1, 2, 3]);
+shouldBeObject([0, 1, 2, 3].groupByToMap((x) => 0).get(0), [0, 1, 2, 3]);
+
+let objectWithToStringCounter = {
+ counter: 0,
+ toString() { return this.counter++; },
+};
+shouldBeObject([0, 1, 2, 3].groupByToMap((x) => objectWithToStringCounter).get(objectWithToStringCounter), [0, 1, 2, 3]);
+shouldBe(objectWithToStringCounter.counter, 0);
+
+shouldBeObject([0, 1, 2, 3].groupByToMap((x) => objectWithToStringThatThrows).get(objectWithToStringThatThrows), [0, 1, 2, 3]);
+
+shouldBeObject([0, 1, 2, 3].groupByToMap((x) => objectWithValueOfThatThrows).get(objectWithValueOfThatThrows), [0, 1, 2, 3]);
+
+shouldBeObject([0, 1, 2, 3].groupByToMap((x) => symbol).get(symbol), [0, 1, 2, 3]);
Modified: trunk/Source/_javascript_Core/ChangeLog (287135 => 287136)
--- trunk/Source/_javascript_Core/ChangeLog 2021-12-16 15:49:37 UTC (rev 287135)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-12-16 16:10:09 UTC (rev 287136)
@@ -1,3 +1,45 @@
+2021-12-16 Devin Rousso <[email protected]>
+
+ Implement Array.prototype.groupBy and Array.prototype.groupByToMap
+ https://bugs.webkit.org/show_bug.cgi?id=234327
+
+ Reviewed by Yusuke Suzuki.
+
+ Implement new Array Grouping proposal <https://tc39.es/proposal-array-grouping/>, which just
+ reached Stage 3.
+
+ `Array.prototype.groupBy`/`Array.prototype.groupByToMap` will return a `{}`/`Map` where each
+ value in the array is put into a "bucket" keyed by the return value of the provoded callback.
+
+ ```js
+ const array = [1, 2, 3, 4];
+
+ array.groupBy(n => n % 2 ? "odd" : "even") // { odd: [1, 3], even: [2, 4] }
+ array.groupByToMap(n => n % 2 ? "odd" : "even") // new Map([["odd", [1, 3]], ["even", [2, 4]])
+ ```
+
+ * builtins/ArrayPrototype.js:
+ (groupBy): Added.
+ (groupByToMap): Added.
+ * runtime/ArrayPrototype.cpp:
+ (JSC::ArrayPrototype::finishCreation):
+
+ * bytecode/BytecodeIntrinsicRegistry.h:
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::BytecodeIntrinsicNode::emit_intrinsic_toPropertyKey): Added.
+ Allow `@toPropertyKey` to be used in builtins to convert a value to a property key. This is
+ used to avoid converting the return value of the callback given to `groupBy` more than once.
+
+ * builtins/BuiltinNames.h:
+ * bytecode/LinkTimeConstant.h:
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::init):
+ Allow `@Map` to be used in builtins to create a primordial `Map` instance. This is used to
+ avoid side effects when creating and populating the `Map` returned by `groupByToMap`.
+
+ * runtime/OptionsList.h:
+ Add `useArrayGroupByMethod` option.
+
2021-12-15 Yusuke Suzuki <[email protected]>
Rename Wasm::CodeBlock to Wasm::CalleeGroup
Modified: trunk/Source/_javascript_Core/builtins/ArrayPrototype.js (287135 => 287136)
--- trunk/Source/_javascript_Core/builtins/ArrayPrototype.js 2021-12-16 15:49:37 UTC (rev 287135)
+++ trunk/Source/_javascript_Core/builtins/ArrayPrototype.js 2021-12-16 16:10:09 UTC (rev 287136)
@@ -156,6 +156,54 @@
return result;
}
+function groupBy(callback /*, thisArg */)
+{
+ var array = @toObject(this, "Array.prototype.groupBy requires that |this| not be null or undefined");
+ var length = @toLength(array.length);
+
+ if (!@isCallable(callback))
+ @throwTypeError("Array.prototype.groupBy callback must be a function");
+
+ var thisArg = @argument(1);
+
+ var groups = @Object.@create(null);
+ for (var i = 0; i < length; ++i) {
+ var value = array[i];
+ var key = @toPropertyKey(callback.@call(thisArg, value, i, array));
+ var group = groups[key];
+ if (!group) {
+ group = [];
+ @putByValDirect(groups, key, group);
+ }
+ @putByValDirect(group, group.length, value);
+ }
+ return groups;
+}
+
+function groupByToMap(callback /*, thisArg */)
+{
+ var array = @toObject(this, "Array.prototype.groupByToMap requires that |this| not be null or undefined");
+ var length = @toLength(array.length);
+
+ if (!@isCallable(callback))
+ @throwTypeError("Array.prototype.groupByToMap callback must be a function");
+
+ var thisArg = @argument(1);
+
+ var groups = new @Map;
+ for (var i = 0; i < length; ++i) {
+ var value = array[i];
+ var key = callback.@call(thisArg, value, i, array);
+ var group = groups.@get(key);
+ if (!group) {
+ group = [];
+ groups.@set(key, group);
+ }
+ @putByValDirect(group, group.length, value);
+ }
+ return groups;
+}
+
function map(callback /*, thisArg */)
{
"use strict";
Modified: trunk/Source/_javascript_Core/builtins/BuiltinNames.h (287135 => 287136)
--- trunk/Source/_javascript_Core/builtins/BuiltinNames.h 2021-12-16 15:49:37 UTC (rev 287135)
+++ trunk/Source/_javascript_Core/builtins/BuiltinNames.h 2021-12-16 16:10:09 UTC (rev 287136)
@@ -70,6 +70,7 @@
macro(defineProperty) \
macro(defaultPromiseThen) \
macro(Set) \
+ macro(Map) \
macro(throwTypeErrorFunction) \
macro(typedArrayLength) \
macro(typedArrayContentType) \
Modified: trunk/Source/_javascript_Core/bytecode/BytecodeIntrinsicRegistry.h (287135 => 287136)
--- trunk/Source/_javascript_Core/bytecode/BytecodeIntrinsicRegistry.h 2021-12-16 15:49:37 UTC (rev 287135)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeIntrinsicRegistry.h 2021-12-16 16:10:09 UTC (rev 287136)
@@ -90,6 +90,7 @@
macro(putSetIteratorInternalField) \
macro(toNumber) \
macro(toString) \
+ macro(toPropertyKey) \
macro(toObject) \
macro(newArrayWithSize) \
macro(newPromise) \
Modified: trunk/Source/_javascript_Core/bytecode/LinkTimeConstant.h (287135 => 287136)
--- trunk/Source/_javascript_Core/bytecode/LinkTimeConstant.h 2021-12-16 15:49:37 UTC (rev 287135)
+++ trunk/Source/_javascript_Core/bytecode/LinkTimeConstant.h 2021-12-16 16:10:09 UTC (rev 287136)
@@ -77,6 +77,7 @@
v(appendMemcpy, nullptr) \
v(hostPromiseRejectionTracker, nullptr) \
v(Set, nullptr) \
+ v(Map, nullptr) \
v(thisTimeValue, nullptr) \
v(importInRealm, nullptr) \
v(evalInRealm, nullptr) \
Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (287135 => 287136)
--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2021-12-16 15:49:37 UTC (rev 287135)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2021-12-16 16:10:09 UTC (rev 287136)
@@ -1841,6 +1841,15 @@
return generator.move(dst, generator.emitToString(generator.tempDestination(dst), src.get()));
}
+RegisterID* BytecodeIntrinsicNode::emit_intrinsic_toPropertyKey(BytecodeGenerator& generator, RegisterID* dst)
+{
+ ArgumentListNode* node = m_args->m_listNode;
+ RefPtr<RegisterID> src = ""
+ ASSERT(!node->m_next);
+
+ return generator.move(dst, generator.emitToPropertyKey(generator.tempDestination(dst), src.get()));
+}
+
RegisterID* BytecodeIntrinsicNode::emit_intrinsic_toObject(BytecodeGenerator& generator, RegisterID* dst)
{
ArgumentListNode* node = m_args->m_listNode;
Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (287135 => 287136)
--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2021-12-16 15:49:37 UTC (rev 287135)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2021-12-16 16:10:09 UTC (rev 287136)
@@ -100,6 +100,10 @@
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(vm.propertyNames->builtinNames().filterPublicName(), arrayPrototypeFilterCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+ if (Options::useArrayGroupByMethod()) {
+ JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().groupByPublicName(), arrayPrototypeGroupByCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+ JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().groupByToMapPublicName(), arrayPrototypeGroupByToMapCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+ }
JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().flatPublicName(), arrayPrototypeFlatCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().flatMapPublicName(), arrayPrototypeFlatMapCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().reducePublicName(), arrayPrototypeReduceCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
@@ -137,6 +141,8 @@
Options::useArrayFindLastMethod() ? &vm.propertyNames->builtinNames().findLastIndexPublicName() : nullptr,
&vm.propertyNames->builtinNames().flatPublicName(),
&vm.propertyNames->builtinNames().flatMapPublicName(),
+ Options::useArrayGroupByMethod() ? &vm.propertyNames->builtinNames().groupByPublicName() : nullptr,
+ Options::useArrayGroupByMethod() ? &vm.propertyNames->builtinNames().groupByToMapPublicName() : nullptr,
&vm.propertyNames->builtinNames().includesPublicName(),
&vm.propertyNames->builtinNames().keysPublicName(),
&vm.propertyNames->builtinNames().valuesPublicName()
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (287135 => 287136)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2021-12-16 15:49:37 UTC (rev 287135)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2021-12-16 16:10:09 UTC (rev 287136)
@@ -1350,6 +1350,9 @@
m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::Set)].initLater([] (const Initializer<JSCell>& init) {
init.set(jsCast<JSGlobalObject*>(init.owner)->setConstructor());
});
+ m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::Map)].initLater([] (const Initializer<JSCell>& init) {
+ init.set(jsCast<JSGlobalObject*>(init.owner)->mapConstructor());
+ });
m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::mapBucketHead)].initLater([] (const Initializer<JSCell>& init) {
init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 0, String(), mapPrivateFuncMapBucketHead, JSMapBucketHeadIntrinsic));
});
Modified: trunk/Source/_javascript_Core/runtime/OptionsList.h (287135 => 287136)
--- trunk/Source/_javascript_Core/runtime/OptionsList.h 2021-12-16 15:49:37 UTC (rev 287135)
+++ trunk/Source/_javascript_Core/runtime/OptionsList.h 2021-12-16 16:10:09 UTC (rev 287136)
@@ -538,6 +538,7 @@
/* Feature Flags */\
\
v(Bool, useArrayFindLastMethod, true, Normal, "Expose the findLast() and findLastIndex() methods on Array and %TypedArray%.") \
+ v(Bool, useArrayGroupByMethod, false, Normal, "Expose the groupBy() and groupByToMap() methods on Array.") \
v(Bool, useAtMethod, true, Normal, "Expose the at() method on Array, %TypedArray%, and String.") \
v(Bool, useHasOwn, true, Normal, "Expose the Object.hasOwn method") \
v(Bool, useIntlEnumeration, true, Normal, "Expose the Intl enumeration APIs.") \
Modified: trunk/Source/WebInspectorUI/ChangeLog (287135 => 287136)
--- trunk/Source/WebInspectorUI/ChangeLog 2021-12-16 15:49:37 UTC (rev 287135)
+++ trunk/Source/WebInspectorUI/ChangeLog 2021-12-16 16:10:09 UTC (rev 287136)
@@ -1,3 +1,12 @@
+2021-12-16 Devin Rousso <[email protected]>
+
+ Implement Array.prototype.groupBy and Array.prototype.groupByToMap
+ https://bugs.webkit.org/show_bug.cgi?id=234327
+
+ Reviewed by Yusuke Suzuki.
+
+ * UserInterface/Models/NativeFunctionParameters.js:
+
2021-12-14 Razvan Caliman <[email protected]>
Web Inspector: Computed Panel: Adjust color of CSS Variables grouping mode control
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/NativeFunctionParameters.js (287135 => 287136)
--- trunk/Source/WebInspectorUI/UserInterface/Models/NativeFunctionParameters.js 2021-12-16 15:49:37 UTC (rev 287135)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/NativeFunctionParameters.js 2021-12-16 16:10:09 UTC (rev 287136)
@@ -252,6 +252,8 @@
findLast: "callback, [thisArg]",
findLastIndex: "callback, [thisArg]",
forEach: "callback, [thisArg]",
+ groupBy: "callback, [thisArg]",
+ groupByToMap: "callback, [thisArg]",
includes: "searchValue, [startIndex=0]",
indexOf: "searchValue, [startIndex=0]",
join: "[separator=\",\"]",