Commit 99b5f699 authored by peterwmwong's avatar peterwmwong Committed by Commit Bot

[builtins] Port Array.p.{find,findIndex} to CSA

- Removes JS implementation and InnerArrayFind/InnerArrayFindIndex
- Adds TFJ, with TFS for slow continuation path

Some quick benchmarks show ~2x improvement for unoptimized code
and up to 16% improvement against optimized code (diminishes with
larger arrays as iterating dominates).

https://github.com/peterwmwong/v8-perf/blob/master/array-find-findIndex/README.md

Bug: chromium:791045, v8:1956, v8:5049, v8:7165
Change-Id: Ie16252ed495bbd91fe548b16d5ef6764de791a50
Reviewed-on: https://chromium-review.googlesource.com/804704Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49851}
parent cc07ac73
......@@ -1679,6 +1679,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
DONT_ENUM);
SimpleInstallFunction(proto, "concat", Builtins::kArrayConcat, 1, false);
SimpleInstallFunction(proto, "find", Builtins::kArrayPrototypeFind, 1,
false);
SimpleInstallFunction(proto, "findIndex",
Builtins::kArrayPrototypeFindIndex, 1, false);
SimpleInstallFunction(proto, "pop", Builtins::kFastArrayPop, 0, false);
SimpleInstallFunction(proto, "push", Builtins::kFastArrayPush, 1, false);
SimpleInstallFunction(proto, "shift", Builtins::kFastArrayShift, 0, false);
......
This diff is collapsed.
......@@ -316,6 +316,15 @@ namespace internal {
TFJ(ArrayReduceRight, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.entries */ \
TFJ(ArrayPrototypeEntries, 0) \
/* ES6 #sec-array.prototype.find */ \
TFS(ArrayFindLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \
kObject, kInitialK, kLength, kTo) \
TFJ(ArrayPrototypeFind, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.findIndex */ \
TFS(ArrayFindIndexLoopContinuation, kReceiver, kCallbackFn, kThisArg, \
kArray, kObject, kInitialK, kLength, kTo) \
TFJ(ArrayPrototypeFindIndex, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.keys */ \
TFJ(ArrayPrototypeKeys, 0) \
/* ES6 #sec-array.prototype.values */ \
......
......@@ -522,6 +522,8 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
case Builtins::kArrayPrototypeValues:
case Builtins::kArrayIncludes:
case Builtins::kArrayPrototypeEntries:
case Builtins::kArrayPrototypeFind:
case Builtins::kArrayPrototypeFindIndex:
case Builtins::kArrayPrototypeKeys:
case Builtins::kArrayForEach:
case Builtins::kArrayEvery:
......
......@@ -1118,64 +1118,6 @@ DEFINE_METHOD_LEN(
);
function InnerArrayFind(predicate, thisArg, array, length) {
if (!IS_CALLABLE(predicate)) {
throw %make_type_error(kCalledNonCallable, predicate);
}
for (var i = 0; i < length; i++) {
var element = array[i];
if (%_Call(predicate, thisArg, element, i, array)) {
return element;
}
}
return;
}
// ES6 draft 07-15-13, section 15.4.3.23
DEFINE_METHOD_LEN(
GlobalArray.prototype,
find(predicate, thisArg) {
var array = TO_OBJECT(this);
var length = TO_INTEGER(array.length);
return InnerArrayFind(predicate, thisArg, array, length);
},
1 /* Set function length */
);
function InnerArrayFindIndex(predicate, thisArg, array, length) {
if (!IS_CALLABLE(predicate)) {
throw %make_type_error(kCalledNonCallable, predicate);
}
for (var i = 0; i < length; i++) {
var element = array[i];
if (%_Call(predicate, thisArg, element, i, array)) {
return i;
}
}
return -1;
}
// ES6 draft 07-15-13, section 15.4.3.24
DEFINE_METHOD_LEN(
GlobalArray.prototype,
findIndex(predicate, thisArg) {
var array = TO_OBJECT(this);
var length = TO_INTEGER(array.length);
return InnerArrayFindIndex(predicate, thisArg, array, length);
},
1 /* Set function length */
);
// ES6, draft 04-05-14, section 22.1.3.6
DEFINE_METHOD_LEN(
GlobalArray.prototype,
......
......@@ -73,6 +73,31 @@
assertEquals(3, count);
for (var i in a) assertEquals(2, a[i]);
// Skip over missing properties.
a = {
"0": 0,
"2": 2,
length: 3
};
var received = [];
assertArrayEquals([2],
Array.prototype.filter.call(a, function(n) {
received.push(n);
return n == 2;
}));
assertArrayEquals([0, 2], received);
// Modify array prototype
a = [0, , 2];
received = [];
assertArrayEquals([2],
Array.prototype.filter.call(a, function(n) {
a.__proto__ = null;
received.push(n);
return n == 2;
}));
assertArrayEquals([0, 2], received);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];
......@@ -131,6 +156,26 @@
a.forEach(function(n) { count++; });
assertEquals(1, count);
// Skip over missing properties.
a = {
"0": 0,
"2": 2,
length: 3
};
var received = [];
Array.prototype.forEach.call(a, function(n) { received.push(n); });
assertArrayEquals([0, 2], received);
// Modify array prototype
a = [0, , 2];
received = [];
Array.prototype.forEach.call(a, function(n) {
a.__proto__ = null;
received.push(n);
return n == 2;
});
assertArrayEquals([0, 2], received);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];
......@@ -194,6 +239,31 @@
assertTrue(a.every(function(n) { count++; return n == 2; }));
assertEquals(2, count);
// Skip over missing properties.
a = {
"0": 2,
"2": 2,
length: 3
};
var received = [];
assertTrue(
Array.prototype.every.call(a, function(n) {
received.push(n);
return n == 2;
}));
assertArrayEquals([2, 2], received);
// Modify array prototype
a = [2, , 2];
received = [];
assertTrue(
Array.prototype.every.call(a, function(n) {
a.__proto__ = null;
received.push(n);
return n == 2;
}));
assertArrayEquals([2, 2], received);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];
......@@ -252,6 +322,31 @@
a = a.map(function(n) { return 2*n; });
for (var i in a) assertEquals(4, a[i]);
// Skip over missing properties.
a = {
"0": 1,
"2": 2,
length: 3
};
var received = [];
assertArrayEquals([2, , 4],
Array.prototype.map.call(a, function(n) {
received.push(n);
return n * 2;
}));
assertArrayEquals([1, 2], received);
// Modify array prototype
a = [1, , 2];
received = [];
assertArrayEquals([2, , 4],
Array.prototype.map.call(a, function(n) {
a.__proto__ = null;
received.push(n);
return n * 2;
}));
assertArrayEquals([1, 2], received);
// Create a new object in each function call when receiver is a
// primitive value. See ECMA-262, Annex C.
a = [];
......
......@@ -233,6 +233,40 @@ assertEquals(22, a.find(function(val) { return 22 === val; }), undefined);
})();
//
// Test predicate is called for missing properties
//
(function() {
const obj = {
"0": 0,
"2": 2,
length: 3
};
const received = [];
const predicate = (v) => { received.push(v); return false; };
const found = Array.prototype.find.call(obj, predicate);
assertEquals(undefined, found);
assertArrayEquals([0, undefined, 2], received);
})();
//
// Test predicate modifying array prototype
//
(function() {
const a = [0, , 2];
const received = [];
const predicate = (v) => {
a.__proto__ = null;
received.push(v);
return false;
};
const found = Array.prototype.find.call(a, predicate);
assertEquals(undefined, found);
assertArrayEquals([0, undefined, 2], received);
})();
//
// Test thisArg
//
......
......@@ -233,6 +233,40 @@ assertEquals(3, a.findIndex(function(val) { return 24 === val; }));
})();
//
// Test predicate is called for missing properties
//
(function() {
const obj = {
"0": 0,
"2": 2,
length: 3
};
const received = [];
const predicate = (v) => { received.push(v); return false; };
const found = Array.prototype.findIndex.call(obj, predicate);
assertEquals(-1, found);
assertArrayEquals([0, undefined, 2], received);
})();
//
// Test predicate modifying array prototype
//
(function() {
const a = [0, , 2];
const received = [];
const predicate = (v) => {
a.__proto__ = null;
received.push(v);
return false;
};
const found = Array.prototype.findIndex.call(a, predicate);
assertEquals(-1, found);
assertArrayEquals([0, undefined, 2], received);
})();
//
// Test thisArg
//
......
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