Commit fe9bdabe authored by legendecas's avatar legendecas Committed by V8 LUCI CQ

[builtins] implement array grouping

The Array Grouping proposal [1] reached Stage 3 in December 2021 TC39.

[1] https://github.com/tc39/proposal-array-grouping/

Bug: v8:12499
Change-Id: I05b4838d915ab1b0cf8126aa30a3e48f47b9ee59
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3366630Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Commit-Queue: Chengzhong Wu <legendecas@gmail.com>
Cr-Commit-Position: refs/heads/main@{#78794}
parent 1fc5f92a
This diff is collapsed.
......@@ -385,6 +385,9 @@ namespace internal {
/* ES6 #sec-array.prototype.pop */ \
CPP(ArrayPop) \
TFJ(ArrayPrototypePop, kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.groupby */ \
CPP(ArrayPrototypeGroupBy) \
CPP(ArrayPrototypeGroupByToMap) \
/* ES6 #sec-array.prototype.push */ \
CPP(ArrayPush) \
TFJ(ArrayPrototypePush, kDontAdaptArgumentsSentinel) \
......
......@@ -307,7 +307,8 @@ DEFINE_BOOL(harmony_shipping, true, "enable all shipped harmony features")
V(harmony_rab_gsab, \
"harmony ResizableArrayBuffer / GrowableSharedArrayBuffer") \
V(harmony_temporal, "Temporal") \
V(harmony_shadow_realm, "harmony ShadowRealm")
V(harmony_shadow_realm, "harmony ShadowRealm") \
V(harmony_array_grouping, "harmony array grouping")
#ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) HARMONY_INPROGRESS_BASE(V)
......
......@@ -4465,6 +4465,28 @@ void Genesis::InitializeGlobal_harmony_array_find_last() {
}
}
void Genesis::InitializeGlobal_harmony_array_grouping() {
if (!FLAG_harmony_array_grouping) return;
Handle<JSFunction> array_function(native_context()->array_function(),
isolate());
Handle<JSObject> array_prototype(
JSObject::cast(array_function->instance_prototype()), isolate());
SimpleInstallFunction(isolate_, array_prototype, "groupBy",
Builtin::kArrayPrototypeGroupBy, 1, false);
SimpleInstallFunction(isolate_, array_prototype, "groupByToMap",
Builtin::kArrayPrototypeGroupByToMap, 1, false);
Handle<JSObject> unscopables = Handle<JSObject>::cast(
JSObject::GetProperty(isolate(), array_prototype,
isolate()->factory()->unscopables_symbol())
.ToHandleChecked());
InstallTrueValuedProperty(isolate_, unscopables, "groupBy");
InstallTrueValuedProperty(isolate_, unscopables, "groupByToMap");
}
void Genesis::InitializeGlobal_harmony_object_has_own() {
if (!FLAG_harmony_object_has_own) return;
......
......@@ -406,6 +406,13 @@ MaybeHandle<OrderedHashMap> OrderedHashMap::Add(Isolate* isolate,
return table;
}
void OrderedHashMap::SetEntry(InternalIndex entry, Object key, Object value) {
DisallowGarbageCollection no_gc;
int index = EntryToIndex(entry);
this->set(index, key);
this->set(index + kValueOffset, value);
}
template <typename IsolateT>
InternalIndex OrderedNameDictionary::FindEntry(IsolateT* isolate, Object key) {
DisallowGarbageCollection no_gc;
......
......@@ -331,6 +331,9 @@ class V8_EXPORT_PRIVATE OrderedHashMap
int new_capacity);
static MaybeHandle<OrderedHashMap> Rehash(Isolate* isolate,
Handle<OrderedHashMap> table);
void SetEntry(InternalIndex entry, Object key, Object value);
Object ValueAt(InternalIndex entry);
// This takes and returns raw Address values containing tagged Object
......
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-array-grouping
assertEquals(Array.prototype[Symbol.unscopables].groupBy, true);
var array = [-0, 1, 0, 2];
var groupBy = () => {
let result = array.groupBy(v => v > 0);
result = Array.from(Object.entries(result));
return result;
}
// entry order matters
assertEquals(groupBy(), [
['false', [-0, 0]],
['true', [1, 2]],
]);
Object.defineProperty(array, 4, {
enumerable: true,
configurable: true,
writable: true,
value: 3,
});
assertEquals(groupBy(), [
['false', [-0, 0]],
['true', [1, 2, 3]],
]);
Object.defineProperty(array, 5, {
enumerable: true,
configurable: true,
get: () => 4,
});
var result = groupBy();
assertEquals(result, [
['false', [-0, 0]],
['true', [1, 2, 3, 4]],
]);
assertSame(result[0][1][0], -0);
// fairly large result array
var length = 20000;
var array = new Array(length);
for (var idx = 0; idx < length; idx++) {
array[idx] = idx;
}
var groupBy = () => {
let result = array.groupBy(v => v % 2);
result = Array.from(Object.entries(result));
return result;
}
var result = groupBy();
assertEquals(result, [
['0', array.filter(v => v % 2 === 0)],
['1', array.filter(v => v % 2 === 1)],
]);
// check array changed by callbackfn
var array = [-0, 0, 1, 2];
groupBy = () => {
let result = array.groupBy((v, idx) => {
if (idx === 1) {
array[2] = {a: 'b'};
}
return v > 0;
});
result = Array.from(Object.entries(result));
return result;
}
assertEquals(groupBy(), [
['false', [-0, 0, {a: 'b'}]],
['true', [2]],
]);
// check array with holes
var array = [1, , 2, , 3, , 4];
var groupBy = () => {
let result = array.groupBy(v => v % 2 === 0 ? 'even' : 'not_even');
result = Array.from(Object.entries(result));
return result;
};
function checkNoHoles(arr) {
for (let idx = 0; idx < arr.length; idx++) {
assertTrue(Object.getOwnPropertyDescriptor(arr, idx) !== undefined);
}
}
var result = groupBy();
assertEquals(result, [
['not_even', [1, undefined, undefined, 3, undefined]],
['even', [2, 4]],
]);
checkNoHoles(result[0][1]);
checkNoHoles(result[1][1]);
var array = [1, undefined, 2, undefined, 3, undefined, 4];
result = groupBy();
assertEquals(result, [
['not_even', [1, undefined, undefined, 3, undefined]],
['even', [2, 4]],
]);
checkNoHoles(result[0][1]);
checkNoHoles(result[1][1]);
// array like objects
var arrayLikeObjects = [
{
'0': -1,
'1': 1,
'2': 2,
length: 3,
},
(function () { return arguments })(-1, 1, 2),
Int8Array.from([-1, 1, 2]),
Float32Array.from([-1, 1, 2]),
];
var groupBy = () => {
let result = Array.prototype.groupBy.call(array, v => v > 0);
result = Array.from(Object.entries(result));
return result;
};
for (var array of arrayLikeObjects) {
assertEquals(groupBy(), [
['false', [-1]],
['true', [1, 2]],
]);
}
// check proto elements
var array = [,];
var groupBy = () => {
let result = array.groupBy(v => v);
result = Array.from(Object.entries(result));
return result;
}
assertEquals(groupBy(), [
['undefined', [,]],
]);
array.__proto__.push(6);
assertEquals(groupBy(), [
['6', [6]],
]);
// callbackfn throws
var array = [-0, 1, 0, 2];
assertThrows(
() => array.groupBy(() => { throw new Error('foobar'); }),
Error,
'foobar'
);
// ToPropertyKey throws
var array = [-0, 1, 0, 2];
assertThrows(
() => array.groupBy(() => {
return {
toString() {
throw new Error('foobar');
},
};
}),
Error,
'foobar'
);
// callbackfn is not callable
var array = [-0, 1, 0, 2];
assertThrows(
() => array.groupBy('foobar'),
TypeError,
);
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-array-grouping
assertEquals(Array.prototype[Symbol.unscopables].groupByToMap, true);
var array = [-0, 1, 0, 2];
var groupByToMap = () => {
let result = array.groupByToMap(v => v > 0);
result = Array.from(result.entries());
return result;
}
// entry order matters
assertEquals(groupByToMap(), [
[false, [-0, 0]],
[true, [1, 2]],
]);
Object.defineProperty(array, 4, {
enumerable: true,
configurable: true,
writable: true,
value: 3,
});
assertEquals(groupByToMap(), [
[false, [-0, 0]],
[true, [1, 2, 3]],
]);
Object.defineProperty(array, 5, {
enumerable: true,
configurable: true,
get: () => 4,
});
var result = groupByToMap();
assertEquals(result, [
[false, [-0, 0]],
[true, [1, 2, 3, 4]],
]);
assertSame(result[0][1][0], -0);
// fairly large result array
var length = 20000;
var array = new Array(length);
for (var idx = 0; idx < length; idx++) {
array[idx] = idx;
}
var groupByToMap = () => {
let result = array.groupByToMap(v => v % 2);
result = Array.from(result.entries());
return result;
}
var result = groupByToMap();
assertEquals(result, [
[0, array.filter(v => v % 2 === 0)],
[1, array.filter(v => v % 2 === 1)],
]);
// check section groupByToMap 6.d
var array = [-0, 0];
var result = array.groupByToMap(v => v);
assertEquals(result.get(0), [-0, 0]);
// check array changed by callbackfn
var array = [-0, 0, 1, 2];
var groupByToMap = () => {
let result = array.groupByToMap((v, idx) => {
if (idx === 1) {
array[2] = {a: 'b'};
}
return v > 0;
});
result = Array.from(result.entries());
return result;
}
assertEquals(groupByToMap(), [
[false, [-0, 0, {a: 'b'}]],
[true, [2]],
]);
// check array with holes
var array = [1, , 2, , 3, , 4];
var groupByToMap = () => {
let result = array.groupByToMap(v => v % 2 === 0 ? 'even' : 'not_even');
result = Array.from(result.entries());
return result;
};
function checkNoHoles(arr) {
for (let idx = 0; idx < arr.length; idx++) {
assertTrue(Object.getOwnPropertyDescriptor(arr, idx) !== undefined);
}
}
var result = groupByToMap();
assertEquals(result, [
['not_even', [1, undefined, undefined, 3, undefined]],
['even', [2, 4]],
]);
checkNoHoles(result[0][1]);
checkNoHoles(result[1][1]);
var array = [1, undefined, 2, undefined, 3, undefined, 4];
result = groupByToMap();
assertEquals(result, [
['not_even', [1, undefined, undefined, 3, undefined]],
['even', [2, 4]],
]);
checkNoHoles(result[0][1]);
checkNoHoles(result[1][1]);
// array like objects
var arrayLikeObjects = [
{
'0': -1,
'1': 1,
'2': 2,
length: 3,
},
(function () { return arguments })(-1, 1, 2),
Int8Array.from([-1, 1, 2]),
Float32Array.from([-1, 1, 2]),
];
var groupByToMap = () => {
let result = Array.prototype.groupByToMap.call(array, v => v > 0);
result = Array.from(result.entries());
return result;
};
for (var array of arrayLikeObjects) {
assertEquals(groupByToMap(), [
[false, [-1]],
[true, [1, 2]],
]);
}
// check proto elements
var array = [,];
var groupByToMap = () => {
let result = array.groupByToMap(v => v);
result = Array.from(result.entries());
return result;
}
assertEquals(groupByToMap(), [
[undefined, [,]],
]);
array.__proto__.push(6);
assertEquals(groupByToMap(), [
[6, [6]],
]);
// callbackfn throws
var array = [-0, 1, 0, 2];
assertThrows(
() => array.groupByToMap(() => { throw new Error('foobar'); }),
Error,
'foobar'
);
// callbackfn is not callable
var array = [-0, 1, 0, 2];
assertThrows(
() => array.groupByToMap('foobar'),
TypeError,
);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment