Factor out core of Array.forEach and .every, for use in TypedArrays

The idea is to make this the model for future TypedArray methods.
A possible downside could be lower array method performance
if everything gets polymorhpic (but if enough inlining happens, it
should still be fast), but on the upside, this change means that
the TypedArray methods won't create as much code size bloat.

BUG=v8:3578
LOG=Y
R=adamk@chromium.org
CC=arv@chromium.org, caitpotter88@gmail.com

Review URL: https://codereview.chromium.org/1133503003

Cr-Commit-Position: refs/heads/master@{#28351}
This commit is contained in:
dehrenberg 2015-05-11 18:32:26 -07:00 committed by Commit bot
parent c50d9819c9
commit 1ebbaaa036
4 changed files with 45 additions and 72 deletions

View File

@ -10,6 +10,8 @@ var $arrayShift;
var $arraySlice;
var $arraySplice;
var $arrayUnshift;
var $innerArrayForEach;
var $innerArrayEvery;
(function(global, shared, exports) {
@ -1179,15 +1181,7 @@ function ArrayFilter(f, receiver) {
return result;
}
function ArrayForEach(f, receiver) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.forEach");
// Pull out the length so that modifications to the length in the
// loop will not affect the looping and side effects are visible.
var array = $toObject(this);
var length = TO_UINT32(array.length);
function InnerArrayForEach(f, receiver, array, length) {
if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
var needs_wrapper = false;
if (IS_NULL(receiver)) {
@ -1209,6 +1203,16 @@ function ArrayForEach(f, receiver) {
}
}
function ArrayForEach(f, receiver) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.forEach");
// Pull out the length so that modifications to the length in the
// loop will not affect the looping and side effects are visible.
var array = $toObject(this);
var length = TO_UINT32(array.length);
InnerArrayForEach(f, receiver, array, length);
}
// Executes the function once for each element present in the
// array until it finds one where callback returns true.
@ -1243,14 +1247,7 @@ function ArraySome(f, receiver) {
}
function ArrayEvery(f, receiver) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.every");
// Pull out the length so that modifications to the length in the
// loop will not affect the looping and side effects are visible.
var array = $toObject(this);
var length = TO_UINT32(array.length);
function InnerArrayEvery(f, receiver, array, length) {
if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
var needs_wrapper = false;
if (IS_NULL(receiver)) {
@ -1273,6 +1270,16 @@ function ArrayEvery(f, receiver) {
return true;
}
function ArrayEvery(f, receiver) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.every");
// Pull out the length so that modifications to the length in the
// loop will not affect the looping and side effects are visible.
var array = $toObject(this);
var length = TO_UINT32(array.length);
return InnerArrayEvery(f, receiver, array, length);
}
function ArrayMap(f, receiver) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
@ -1595,4 +1602,7 @@ $arraySlice = ArraySlice;
$arraySplice = ArraySplice;
$arrayUnshift = ArrayUnshift;
})
$innerArrayForEach = InnerArrayForEach;
$innerArrayEvery = InnerArrayEvery;
});

View File

@ -30,70 +30,27 @@ TYPED_ARRAYS(DECLARE_GLOBALS)
// -------------------------------------------------------------------
// ES6 draft 05-05-15, section 22.2.3.7
function TypedArrayEvery(f /* thisArg */) { // length == 1
if (!%IsTypedArray(this)) {
throw MakeTypeError('not_typed_array', []);
}
if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
function TypedArrayEvery(f, receiver) {
if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
var length = %_TypedArrayGetLength(this);
var receiver;
if (%_ArgumentsLength() > 1) {
receiver = %_Arguments(1);
}
var needs_wrapper = false;
if (IS_NULL(receiver)) {
if (%IsSloppyModeFunction(mapfn)) receiver = UNDEFINED;
} else if (!IS_UNDEFINED(receiver)) {
needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
}
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
for (var i = 0; i < length; i++) {
var element = this[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(f);
var new_receiver = needs_wrapper ? $toObject(receiver) : receiver;
if (!%_CallFunction(new_receiver, TO_OBJECT_INLINE(element), i, this, f)) {
return false;
}
}
return true;
return $innerArrayEvery(f, receiver, this, length);
}
%FunctionSetLength(TypedArrayEvery, 1);
// ES6 draft 08-24-14, section 22.2.3.12
function TypedArrayForEach(f /* thisArg */) { // length == 1
function TypedArrayForEach(f, receiver) {
if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
var length = %_TypedArrayGetLength(this);
var receiver;
if (%_ArgumentsLength() > 1) {
receiver = %_Arguments(1);
}
var needs_wrapper = false;
if (IS_NULL(receiver)) {
if (%IsSloppyModeFunction(mapfn)) receiver = UNDEFINED;
} else if (!IS_UNDEFINED(receiver)) {
needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
}
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
for (var i = 0; i < length; i++) {
var element = this[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(f);
var new_receiver = needs_wrapper ? $toObject(receiver) : receiver;
%_CallFunction(new_receiver, TO_OBJECT_INLINE(element), i, this, f);
}
$innerArrayForEach(f, receiver, this, length);
}
%FunctionSetLength(TypedArrayForEach, 1);
// ES6 draft 08-24-14, section 22.2.2.2
function TypedArrayOf() { // length == 0
function TypedArrayOf() {
var length = %_ArgumentsLength();
var array = new this(length);
for (var i = 0; i < length; i++) {

View File

@ -85,8 +85,9 @@ function TestTypedArrayForEach(constructor) {
// still make .forEach() finish, and the array should keep being
// empty after neutering it.
count = 0;
a = new constructor(2);
a = new constructor(3);
result = a.every(function (n, index, array) {
assertFalse(array[index] === undefined); // don't get here if neutered
if (count > 0) %ArrayBufferNeuter(array.buffer);
array[index] = n + 1;
count++;

View File

@ -85,8 +85,13 @@ function TestTypedArrayForEach(constructor) {
assertEquals(42, a[1]);
// Neutering the buffer backing the typed array mid-way should
// still make .forEach() finish, and the array should keep being
// empty after neutering it.
// still make .forEach() finish, but exiting early due to the missing
// elements, and the array should keep being empty after detaching it.
// TODO(dehrenberg): According to the ES6 spec, accessing or testing
// for members on a detached TypedArray should throw, so really this
// should throw in the third iteration. However, this behavior matches
// the Khronos spec.
a = new constructor(3);
count = 0;
a.forEach(function (n, index, array) {
if (count > 0) %ArrayBufferNeuter(array.buffer);