Implement %TypedArray%.prototype.{map,filter,some,reduce,reduceRight}
This patch adds implementations for additional TypedArray methods from the ES6 spec, together with tests adapted from array code. R=arv@chromium.org BUG=v8:3578 LOG=Y Review URL: https://codereview.chromium.org/1139663005 Cr-Commit-Position: refs/heads/master@{#28488}
This commit is contained in:
parent
3199077791
commit
59ef8c5f32
115
src/array.js
115
src/array.js
@ -12,6 +12,11 @@ var $arraySplice;
|
||||
var $arrayUnshift;
|
||||
var $innerArrayForEach;
|
||||
var $innerArrayEvery;
|
||||
var $innerArrayFilter;
|
||||
var $innerArrayMap;
|
||||
var $innerArrayReduce;
|
||||
var $innerArrayReduceRight;
|
||||
var $innerArraySome;
|
||||
|
||||
(function(global, shared, exports) {
|
||||
|
||||
@ -1150,14 +1155,7 @@ function ArraySort(comparefn) {
|
||||
// The following functions cannot be made efficient on sparse arrays while
|
||||
// preserving the semantics, since the calls to the receiver function can add
|
||||
// or delete elements from the array.
|
||||
function ArrayFilter(f, receiver) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter");
|
||||
|
||||
// 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 = $toUint32(array.length);
|
||||
|
||||
function InnerArrayFilter(f, receiver, array, length) {
|
||||
if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
|
||||
var needs_wrapper = false;
|
||||
if (IS_NULL(receiver)) {
|
||||
@ -1186,6 +1184,17 @@ function ArrayFilter(f, receiver) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function ArrayFilter(f, receiver) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter");
|
||||
|
||||
// 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 = $toUint32(array.length);
|
||||
|
||||
return InnerArrayFilter(f, receiver, array, length);
|
||||
}
|
||||
|
||||
function InnerArrayForEach(f, receiver, array, length) {
|
||||
if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
|
||||
var needs_wrapper = false;
|
||||
@ -1219,16 +1228,7 @@ function ArrayForEach(f, receiver) {
|
||||
}
|
||||
|
||||
|
||||
// Executes the function once for each element present in the
|
||||
// array until it finds one where callback returns true.
|
||||
function ArraySome(f, receiver) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.some");
|
||||
|
||||
// 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 InnerArraySome(f, receiver, array, length) {
|
||||
if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
|
||||
var needs_wrapper = false;
|
||||
if (IS_NULL(receiver)) {
|
||||
@ -1252,6 +1252,19 @@ function ArraySome(f, receiver) {
|
||||
}
|
||||
|
||||
|
||||
// Executes the function once for each element present in the
|
||||
// array until it finds one where callback returns true.
|
||||
function ArraySome(f, receiver) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.some");
|
||||
|
||||
// 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 InnerArraySome(f, receiver, array, length);
|
||||
}
|
||||
|
||||
|
||||
function InnerArrayEvery(f, receiver, array, length) {
|
||||
if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
|
||||
var needs_wrapper = false;
|
||||
@ -1286,14 +1299,7 @@ function ArrayEvery(f, receiver) {
|
||||
}
|
||||
|
||||
|
||||
function ArrayMap(f, receiver) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
|
||||
|
||||
// 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 InnerArrayMap(f, receiver, array, length) {
|
||||
if (!IS_SPEC_FUNCTION(f)) throw MakeTypeError(kCalledNonCallable, f);
|
||||
var needs_wrapper = false;
|
||||
if (IS_NULL(receiver)) {
|
||||
@ -1320,6 +1326,17 @@ function ArrayMap(f, receiver) {
|
||||
}
|
||||
|
||||
|
||||
function ArrayMap(f, receiver) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
|
||||
|
||||
// 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 InnerArrayMap(f, receiver, array, length);
|
||||
}
|
||||
|
||||
|
||||
function ArrayIndexOf(element, index) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.indexOf");
|
||||
|
||||
@ -1430,21 +1447,14 @@ function ArrayLastIndexOf(element, index) {
|
||||
}
|
||||
|
||||
|
||||
function ArrayReduce(callback, current) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduce");
|
||||
|
||||
// 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 = $toUint32(array.length);
|
||||
|
||||
function InnerArrayReduce(callback, current, array, length, argumentsLength) {
|
||||
if (!IS_SPEC_FUNCTION(callback)) {
|
||||
throw MakeTypeError(kCalledNonCallable, callback);
|
||||
}
|
||||
|
||||
var is_array = IS_ARRAY(array);
|
||||
var i = 0;
|
||||
find_initial: if (%_ArgumentsLength() < 2) {
|
||||
find_initial: if (argumentsLength < 2) {
|
||||
for (; i < length; i++) {
|
||||
if (HAS_INDEX(array, i, is_array)) {
|
||||
current = array[i++];
|
||||
@ -1467,21 +1477,27 @@ function ArrayReduce(callback, current) {
|
||||
}
|
||||
|
||||
|
||||
function ArrayReduceRight(callback, current) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduceRight");
|
||||
function ArrayReduce(callback, current) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduce");
|
||||
|
||||
// Pull out the length so that side effects are visible before the
|
||||
// callback function is checked.
|
||||
// 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 = $toUint32(array.length);
|
||||
return InnerArrayReduce(callback, current, array, length,
|
||||
%_ArgumentsLength());
|
||||
}
|
||||
|
||||
|
||||
function InnerArrayReduceRight(callback, current, array, length,
|
||||
argumentsLength) {
|
||||
if (!IS_SPEC_FUNCTION(callback)) {
|
||||
throw MakeTypeError(kCalledNonCallable, callback);
|
||||
}
|
||||
|
||||
var is_array = IS_ARRAY(array);
|
||||
var i = length - 1;
|
||||
find_initial: if (%_ArgumentsLength() < 2) {
|
||||
find_initial: if (argumentsLength < 2) {
|
||||
for (; i >= 0; i--) {
|
||||
if (HAS_INDEX(array, i, is_array)) {
|
||||
current = array[i--];
|
||||
@ -1503,6 +1519,18 @@ function ArrayReduceRight(callback, current) {
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
function ArrayReduceRight(callback, current) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduceRight");
|
||||
|
||||
// Pull out the length so that side effects are visible before the
|
||||
// callback function is checked.
|
||||
var array = $toObject(this);
|
||||
var length = $toUint32(array.length);
|
||||
return InnerArrayReduceRight(callback, current, array, length,
|
||||
%_ArgumentsLength());
|
||||
}
|
||||
|
||||
// ES5, 15.4.3.2
|
||||
function ArrayIsArray(obj) {
|
||||
return IS_ARRAY(obj);
|
||||
@ -1607,7 +1635,12 @@ $arraySlice = ArraySlice;
|
||||
$arraySplice = ArraySplice;
|
||||
$arrayUnshift = ArrayUnshift;
|
||||
|
||||
$innerArrayForEach = InnerArrayForEach;
|
||||
$innerArrayEvery = InnerArrayEvery;
|
||||
$innerArrayFilter = InnerArrayFilter;
|
||||
$innerArrayForEach = InnerArrayForEach;
|
||||
$innerArrayMap = InnerArrayMap;
|
||||
$innerArrayReduce = InnerArrayReduce;
|
||||
$innerArrayReduceRight = InnerArrayReduceRight;
|
||||
$innerArraySome = InnerArraySome;
|
||||
|
||||
});
|
||||
|
@ -30,6 +30,28 @@ DECLARE_GLOBALS(Array)
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
function ConstructTypedArray(constructor, array) {
|
||||
// TODO(littledan): This is an approximation of the spec, which requires
|
||||
// that only real TypedArray classes should be accepted (22.2.2.1.1)
|
||||
if (!IS_SPEC_OBJECT(constructor) || IS_UNDEFINED(constructor.prototype) ||
|
||||
!%HasOwnProperty(constructor.prototype, "BYTES_PER_ELEMENT")) {
|
||||
throw MakeTypeError(kNotTypedArray);
|
||||
}
|
||||
|
||||
// TODO(littledan): The spec requires that, rather than directly calling
|
||||
// the constructor, a TypedArray is created with the proper proto and
|
||||
// underlying size and element size, and elements are put in one by one.
|
||||
// By contrast, this would allow subclasses to make a radically different
|
||||
// constructor with different semantics.
|
||||
return new constructor(array);
|
||||
}
|
||||
|
||||
function ConstructTypedArrayLike(typedArray, arrayContents) {
|
||||
// TODO(littledan): The spec requires that we actuallly use
|
||||
// typedArray.constructor[Symbol.species] (bug v8:4093)
|
||||
return new typedArray.constructor(arrayContents);
|
||||
}
|
||||
|
||||
function TypedArrayCopyWithin(target, start, end) {
|
||||
if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
|
||||
|
||||
@ -61,7 +83,7 @@ function TypedArrayForEach(f, receiver) {
|
||||
%FunctionSetLength(TypedArrayForEach, 1);
|
||||
|
||||
// ES6 draft 04-05-14 section 22.2.3.8
|
||||
function TypedArrayFill(value, start , end) {
|
||||
function TypedArrayFill(value, start, end) {
|
||||
if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
|
||||
|
||||
var length = %_TypedArrayGetLength(this);
|
||||
@ -70,6 +92,16 @@ function TypedArrayFill(value, start , end) {
|
||||
}
|
||||
%FunctionSetLength(TypedArrayFill, 1);
|
||||
|
||||
// ES6 draft 07-15-13, section 22.2.3.9
|
||||
function TypedArrayFilter(predicate, thisArg) {
|
||||
if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
|
||||
|
||||
var length = %_TypedArrayGetLength(this);
|
||||
var array = $innerArrayFilter(predicate, thisArg, this, length);
|
||||
return ConstructTypedArrayLike(this, array);
|
||||
}
|
||||
%FunctionSetLength(TypedArrayFilter, 1);
|
||||
|
||||
// ES6 draft 07-15-13, section 22.2.3.10
|
||||
function TypedArrayFind(predicate, thisArg) {
|
||||
if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
|
||||
@ -91,6 +123,52 @@ function TypedArrayFindIndex(predicate, thisArg) {
|
||||
%FunctionSetLength(TypedArrayFindIndex, 1);
|
||||
|
||||
|
||||
// ES6 draft 07-15-13, section 22.2.3.18
|
||||
function TypedArrayMap(predicate, thisArg) {
|
||||
if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
|
||||
|
||||
// TODO(littledan): Preallocate rather than making an intermediate
|
||||
// array, for better performance.
|
||||
var length = %_TypedArrayGetLength(this);
|
||||
var array = $innerArrayMap(predicate, thisArg, this, length);
|
||||
return ConstructTypedArrayLike(this, array);
|
||||
}
|
||||
%FunctionSetLength(TypedArrayMap, 1);
|
||||
|
||||
|
||||
// ES6 draft 07-15-13, section 22.2.3.19
|
||||
function TypedArrayReduce(callback, current) {
|
||||
if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
|
||||
|
||||
var length = %_TypedArrayGetLength(this);
|
||||
return $innerArrayReduce(callback, current, this, length,
|
||||
%_ArgumentsLength());
|
||||
}
|
||||
%FunctionSetLength(TypedArrayReduce, 1);
|
||||
|
||||
|
||||
// ES6 draft 07-15-13, section 22.2.3.19
|
||||
function TypedArrayReduceRight(callback, current) {
|
||||
if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
|
||||
|
||||
var length = %_TypedArrayGetLength(this);
|
||||
return $innerArrayReduceRight(callback, current, this, length,
|
||||
%_ArgumentsLength());
|
||||
}
|
||||
%FunctionSetLength(TypedArrayReduceRight, 1);
|
||||
|
||||
|
||||
// ES6 draft 05-05-15, section 22.2.3.24
|
||||
function TypedArraySome(f, receiver) {
|
||||
if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
|
||||
|
||||
var length = %_TypedArrayGetLength(this);
|
||||
|
||||
return $innerArraySome(f, receiver, this, length);
|
||||
}
|
||||
%FunctionSetLength(TypedArraySome, 1);
|
||||
|
||||
|
||||
// ES6 draft 08-24-14, section 22.2.2.2
|
||||
function TypedArrayOf() {
|
||||
var length = %_ArgumentsLength();
|
||||
@ -137,10 +215,15 @@ macro EXTEND_TYPED_ARRAY(NAME)
|
||||
$installFunctions(GlobalNAME.prototype, DONT_ENUM, [
|
||||
"copyWithin", TypedArrayCopyWithin,
|
||||
"every", TypedArrayEvery,
|
||||
"forEach", TypedArrayForEach,
|
||||
"fill", TypedArrayFill,
|
||||
"filter", TypedArrayFilter,
|
||||
"find", TypedArrayFind,
|
||||
"findIndex", TypedArrayFindIndex,
|
||||
"fill", TypedArrayFill
|
||||
"forEach", TypedArrayForEach,
|
||||
"map", TypedArrayMap,
|
||||
"reduce", TypedArrayReduce,
|
||||
"reduceRight", TypedArrayReduceRight,
|
||||
"some", TypedArraySome
|
||||
]);
|
||||
endmacro
|
||||
|
||||
|
196
test/mjsunit/harmony/typedarray-iteration.js
Normal file
196
test/mjsunit/harmony/typedarray-iteration.js
Normal file
@ -0,0 +1,196 @@
|
||||
// Copyright 2015 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-arrays
|
||||
|
||||
// Tests for standard TypedArray array iteration functions.
|
||||
|
||||
var typedArrayConstructors = [
|
||||
Uint8Array,
|
||||
Int8Array,
|
||||
Uint16Array,
|
||||
Int16Array,
|
||||
Uint32Array,
|
||||
Int32Array,
|
||||
Uint8ClampedArray,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
|
||||
function assertArrayLikeEquals(expected, value, type) {
|
||||
assertEquals(value.__proto__, type.prototype);
|
||||
assertEquals(expected.length, value.length);
|
||||
for (var i = 0; i < value.length; ++i) {
|
||||
assertEquals(expected[i], value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var constructor of typedArrayConstructors) {
|
||||
(function TypedArrayFilterTest() {
|
||||
// Simple use.
|
||||
var a = new constructor([0, 1]);
|
||||
assertArrayLikeEquals([0], a.filter(function(n) { return n == 0; }),
|
||||
constructor);
|
||||
assertArrayLikeEquals([0, 1], a, constructor);
|
||||
|
||||
// Use specified object as this object when calling the function.
|
||||
var o = { value: 42 }
|
||||
a = new constructor([1, 42, 3, 42, 4]);
|
||||
assertArrayLikeEquals([42, 42], a.filter(function(n) {
|
||||
return this.value == n
|
||||
}, o), constructor);
|
||||
|
||||
// Modify original array.
|
||||
a = new constructor([1, 42, 3, 42, 4]);
|
||||
assertArrayLikeEquals([42, 42], a.filter(function(n, index, array) {
|
||||
array[index] = 43; return 42 == n;
|
||||
}), constructor);
|
||||
assertArrayLikeEquals([43, 43, 43, 43, 43], a, constructor);
|
||||
|
||||
// Create a new object in each function call when receiver is a
|
||||
// primitive value. See ECMA-262, Annex C.
|
||||
a = [];
|
||||
new constructor([1, 2]).filter(function() { a.push(this) }, '');
|
||||
assertTrue(a[0] !== a[1]);
|
||||
|
||||
// Do not create a new object otherwise.
|
||||
a = [];
|
||||
new constructor([1, 2]).filter(function() { a.push(this) }, {});
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// In strict mode primitive values should not be coerced to an object.
|
||||
a = [];
|
||||
new constructor([1, 2]).filter(function() {
|
||||
'use strict';
|
||||
a.push(this);
|
||||
}, '');
|
||||
assertEquals('', a[0]);
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// Calling this method on other types is a TypeError
|
||||
assertThrows(function() {
|
||||
constructor.prototype.filter.call([], function() {});
|
||||
}, TypeError);
|
||||
|
||||
// Shadowing the length property doesn't change anything
|
||||
a = new constructor([1, 2]);
|
||||
Object.defineProperty(a, 'length', { value: 1 });
|
||||
assertArrayLikeEquals([2], a.filter(function(elt) {
|
||||
return elt == 2;
|
||||
}), constructor);
|
||||
})();
|
||||
|
||||
(function TypedArrayMapTest() {
|
||||
var a = new constructor([0, 1, 2, 3, 4]);
|
||||
|
||||
// Simple use.
|
||||
var result = [1, 2, 3, 4, 5];
|
||||
assertArrayLikeEquals(result, a.map(function(n) { return n + 1; }),
|
||||
constructor);
|
||||
assertEquals(a, a);
|
||||
|
||||
// Use specified object as this object when calling the function.
|
||||
var o = { delta: 42 }
|
||||
result = [42, 43, 44, 45, 46];
|
||||
assertArrayLikeEquals(result, a.map(function(n) {
|
||||
return this.delta + n;
|
||||
}, o), constructor);
|
||||
|
||||
// Modify original array.
|
||||
a = new constructor([0, 1, 2, 3, 4]);
|
||||
result = [1, 2, 3, 4, 5];
|
||||
assertArrayLikeEquals(result, a.map(function(n, index, array) {
|
||||
array[index] = n + 1;
|
||||
return n + 1;
|
||||
}), constructor);
|
||||
assertArrayLikeEquals(result, a, constructor);
|
||||
|
||||
// Create a new object in each function call when receiver is a
|
||||
// primitive value. See ECMA-262, Annex C.
|
||||
a = [];
|
||||
new constructor([1, 2]).map(function() { a.push(this) }, '');
|
||||
assertTrue(a[0] !== a[1]);
|
||||
|
||||
// Do not create a new object otherwise.
|
||||
a = [];
|
||||
new constructor([1, 2]).map(function() { a.push(this) }, {});
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// In strict mode primitive values should not be coerced to an object.
|
||||
a = [];
|
||||
new constructor([1, 2]).map(function() { 'use strict'; a.push(this); }, '');
|
||||
assertEquals('', a[0]);
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// Test that the result is converted to the right type
|
||||
assertArrayLikeEquals([3, 3], new constructor([1, 2]).map(function() {
|
||||
return "3";
|
||||
}), constructor);
|
||||
if (constructor !== Float32Array && constructor !== Float64Array) {
|
||||
assertArrayLikeEquals([0, 0], new constructor([1, 2]).map(function() {
|
||||
return NaN;
|
||||
}), constructor);
|
||||
}
|
||||
})();
|
||||
|
||||
//
|
||||
// %TypedArray%.prototype.some
|
||||
//
|
||||
(function TypedArraySomeTest() {
|
||||
var a = new constructor([0, 1, 2, 3, 4]);
|
||||
|
||||
// Simple use.
|
||||
assertTrue(a.some(function(n) { return n == 3}));
|
||||
assertFalse(a.some(function(n) { return n == 5}));
|
||||
|
||||
// Use specified object as this object when calling the function.
|
||||
var o = { element: 42 };
|
||||
a = new constructor([1, 42, 3]);
|
||||
assertTrue(a.some(function(n) { return this.element == n; }, o));
|
||||
a = new constructor([1]);
|
||||
assertFalse(a.some(function(n) { return this.element == n; }, o));
|
||||
|
||||
// Modify original array.
|
||||
a = new constructor([0, 1, 2, 3]);
|
||||
assertTrue(a.some(function(n, index, array) {
|
||||
array[index] = n + 1;
|
||||
return n == 2;
|
||||
}));
|
||||
assertArrayLikeEquals([1, 2, 3, 3], a, constructor);
|
||||
|
||||
// Create a new object in each function call when receiver is a
|
||||
// primitive value. See ECMA-262, Annex C.
|
||||
a = [];
|
||||
new constructor([1, 2]).some(function() { a.push(this) }, '');
|
||||
assertTrue(a[0] !== a[1]);
|
||||
|
||||
// Do not create a new object otherwise.
|
||||
a = [];
|
||||
new constructor([1, 2]).some(function() { a.push(this) }, {});
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// In strict mode primitive values should not be coerced to an object.
|
||||
a = [];
|
||||
new constructor([1, 2]).some(function() {
|
||||
'use strict';
|
||||
a.push(this);
|
||||
}, '');
|
||||
assertEquals('', a[0]);
|
||||
assertEquals(a[0], a[1]);
|
||||
|
||||
// Calling this method on other types is a TypeError
|
||||
assertThrows(function() {
|
||||
constructor.prototype.some.call([], function() {});
|
||||
}, TypeError);
|
||||
|
||||
// Shadowing the length property doesn't change anything
|
||||
a = new constructor([1, 2]);
|
||||
Object.defineProperty(a, 'length', { value: 1 });
|
||||
assertEquals(true, a.some(function(elt) { return elt == 2; }));
|
||||
assertEquals(false, Array.prototype.some.call(a, function(elt) {
|
||||
return elt == 2;
|
||||
}));
|
||||
})();
|
||||
|
||||
}
|
256
test/mjsunit/harmony/typedarray-reduce.js
Normal file
256
test/mjsunit/harmony/typedarray-reduce.js
Normal file
@ -0,0 +1,256 @@
|
||||
// Copyright 2015 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-arrays --allow-natives-syntax
|
||||
|
||||
var typedArrayConstructors = [
|
||||
Uint8Array,
|
||||
Int8Array,
|
||||
Uint16Array,
|
||||
Int16Array,
|
||||
Uint32Array,
|
||||
Int32Array,
|
||||
Uint8ClampedArray,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
|
||||
function clone(v) {
|
||||
// Shallow-copies arrays, returns everything else verbatim.
|
||||
if (v instanceof Array) {
|
||||
// Shallow-copy an array.
|
||||
var newArray = new Array(v.length);
|
||||
for (var i in v) {
|
||||
newArray[i] = v[i];
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
// Creates a callback function for reduce/reduceRight that tests the number
|
||||
// of arguments and otherwise behaves as "func", but which also
|
||||
// records all calls in an array on the function (as arrays of arguments
|
||||
// followed by result).
|
||||
function makeRecorder(func, testName) {
|
||||
var record = [];
|
||||
var f = function recorder(a, b, i, s) {
|
||||
assertEquals(4, arguments.length,
|
||||
testName + "(number of arguments: " + arguments.length + ")");
|
||||
assertEquals("number", typeof(i), testName + "(index must be number)");
|
||||
assertEquals(s[i], b, testName + "(current argument is at index)");
|
||||
if (record.length > 0) {
|
||||
var prevRecord = record[record.length - 1];
|
||||
var prevResult = prevRecord[prevRecord.length - 1];
|
||||
assertEquals(prevResult, a,
|
||||
testName + "(prev result -> current input)");
|
||||
}
|
||||
var args = [clone(a), clone(b), i, clone(s)];
|
||||
var result = func.apply(this, arguments);
|
||||
args.push(clone(result));
|
||||
record.push(args);
|
||||
return result;
|
||||
};
|
||||
f.record = record;
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
function testReduce(type,
|
||||
testName,
|
||||
expectedResult,
|
||||
expectedCalls,
|
||||
array,
|
||||
combine,
|
||||
init) {
|
||||
var rec = makeRecorder(combine);
|
||||
var result;
|
||||
var performsCall;
|
||||
if (arguments.length > 6) {
|
||||
result = array[type](rec, init);
|
||||
} else {
|
||||
result = array[type](rec);
|
||||
}
|
||||
var calls = rec.record;
|
||||
assertEquals(expectedCalls.length, calls.length,
|
||||
testName + " (number of calls)");
|
||||
for (var i = 0; i < expectedCalls.length; i++) {
|
||||
assertEquals(expectedCalls[i], calls[i],
|
||||
testName + " (call " + (i + 1) + ")");
|
||||
}
|
||||
assertEquals(expectedResult, result, testName + " (result)");
|
||||
}
|
||||
|
||||
|
||||
function sum(a, b) { return a + b; }
|
||||
function prod(a, b) { return a * b; }
|
||||
function dec(a, b, i, arr) { return a + b * Math.pow(10, arr.length - i - 1); }
|
||||
function accumulate(acc, elem, i) { acc[i] = elem; return acc; }
|
||||
|
||||
for (var constructor of typedArrayConstructors) {
|
||||
// ---- Test Reduce[Left]
|
||||
|
||||
var simpleArray = new constructor([2,4,6])
|
||||
|
||||
testReduce("reduce", "SimpleReduceSum", 12,
|
||||
[[0, 2, 0, simpleArray, 2],
|
||||
[2, 4, 1, simpleArray, 6],
|
||||
[6, 6, 2, simpleArray, 12]],
|
||||
simpleArray, sum, 0);
|
||||
|
||||
testReduce("reduce", "SimpleReduceProd", 48,
|
||||
[[1, 2, 0, simpleArray, 2],
|
||||
[2, 4, 1, simpleArray, 8],
|
||||
[8, 6, 2, simpleArray, 48]],
|
||||
simpleArray, prod, 1);
|
||||
|
||||
testReduce("reduce", "SimpleReduceDec", 246,
|
||||
[[0, 2, 0, simpleArray, 200],
|
||||
[200, 4, 1, simpleArray, 240],
|
||||
[240, 6, 2, simpleArray, 246]],
|
||||
simpleArray, dec, 0);
|
||||
|
||||
testReduce("reduce", "SimpleReduceAccumulate", [2, 4, 6],
|
||||
[[[], 2, 0, simpleArray, [2]],
|
||||
[[2], 4, 1, simpleArray, [2, 4]],
|
||||
[[2,4], 6, 2, simpleArray, [2, 4, 6]]],
|
||||
simpleArray, accumulate, []);
|
||||
|
||||
|
||||
testReduce("reduce", "EmptyReduceSum", 0, [], [], sum, 0);
|
||||
testReduce("reduce", "EmptyReduceProd", 1, [], [], prod, 1);
|
||||
testReduce("reduce", "EmptyReduceDec", 0, [], [], dec, 0);
|
||||
testReduce("reduce", "EmptyReduceAccumulate", [], [], [], accumulate, []);
|
||||
|
||||
testReduce("reduce", "EmptyReduceSumNoInit", 0, [], [0], sum);
|
||||
testReduce("reduce", "EmptyReduceProdNoInit", 1, [], [1], prod);
|
||||
testReduce("reduce", "EmptyReduceDecNoInit", 0, [], [0], dec);
|
||||
testReduce("reduce", "EmptyReduceAccumulateNoInit", [], [], [[]], accumulate);
|
||||
|
||||
// ---- Test ReduceRight
|
||||
|
||||
testReduce("reduceRight", "SimpleReduceRightSum", 12,
|
||||
[[0, 6, 2, simpleArray, 6],
|
||||
[6, 4, 1, simpleArray, 10],
|
||||
[10, 2, 0, simpleArray, 12]],
|
||||
simpleArray, sum, 0);
|
||||
|
||||
testReduce("reduceRight", "SimpleReduceRightProd", 48,
|
||||
[[1, 6, 2, simpleArray, 6],
|
||||
[6, 4, 1, simpleArray, 24],
|
||||
[24, 2, 0, simpleArray, 48]],
|
||||
simpleArray, prod, 1);
|
||||
|
||||
testReduce("reduceRight", "SimpleReduceRightDec", 246,
|
||||
[[0, 6, 2, simpleArray, 6],
|
||||
[6, 4, 1, simpleArray, 46],
|
||||
[46, 2, 0, simpleArray, 246]],
|
||||
simpleArray, dec, 0);
|
||||
|
||||
|
||||
testReduce("reduceRight", "EmptyReduceRightSum", 0, [], [], sum, 0);
|
||||
testReduce("reduceRight", "EmptyReduceRightProd", 1, [], [], prod, 1);
|
||||
testReduce("reduceRight", "EmptyReduceRightDec", 0, [], [], dec, 0);
|
||||
testReduce("reduceRight", "EmptyReduceRightAccumulate", [],
|
||||
[], [], accumulate, []);
|
||||
|
||||
testReduce("reduceRight", "EmptyReduceRightSumNoInit", 0, [], [0], sum);
|
||||
testReduce("reduceRight", "EmptyReduceRightProdNoInit", 1, [], [1], prod);
|
||||
testReduce("reduceRight", "EmptyReduceRightDecNoInit", 0, [], [0], dec);
|
||||
testReduce("reduceRight", "EmptyReduceRightAccumulateNoInit",
|
||||
[], [], [[]], accumulate);
|
||||
|
||||
// Ignore non-array properties:
|
||||
|
||||
var arrayPlus = [1,2,3];
|
||||
arrayPlus[-1] = NaN;
|
||||
arrayPlus[Math.pow(2,32)] = NaN;
|
||||
arrayPlus[NaN] = NaN;
|
||||
arrayPlus["00"] = NaN;
|
||||
arrayPlus["02"] = NaN;
|
||||
arrayPlus["-0"] = NaN;
|
||||
|
||||
testReduce("reduce", "ArrayWithNonElementPropertiesReduce", 6,
|
||||
[[0, 1, 0, arrayPlus, 1],
|
||||
[1, 2, 1, arrayPlus, 3],
|
||||
[3, 3, 2, arrayPlus, 6],
|
||||
], arrayPlus, sum, 0);
|
||||
|
||||
testReduce("reduceRight", "ArrayWithNonElementPropertiesReduceRight", 6,
|
||||
[[0, 3, 2, arrayPlus, 3],
|
||||
[3, 2, 1, arrayPlus, 5],
|
||||
[5, 1, 0, arrayPlus, 6],
|
||||
], arrayPlus, sum, 0);
|
||||
|
||||
|
||||
// Test error conditions:
|
||||
|
||||
var exception = false;
|
||||
try {
|
||||
new constructor([1]).reduce("not a function");
|
||||
} catch (e) {
|
||||
exception = true;
|
||||
assertTrue(e instanceof TypeError,
|
||||
"reduce callback not a function not throwing TypeError");
|
||||
assertTrue(e.message.indexOf(" is not a function") >= 0,
|
||||
"reduce non function TypeError type");
|
||||
}
|
||||
assertTrue(exception);
|
||||
|
||||
exception = false;
|
||||
try {
|
||||
new constructor([1]).reduceRight("not a function");
|
||||
} catch (e) {
|
||||
exception = true;
|
||||
assertTrue(e instanceof TypeError,
|
||||
"reduceRight callback not a function not throwing TypeError");
|
||||
assertTrue(e.message.indexOf(" is not a function") >= 0,
|
||||
"reduceRight non function TypeError type");
|
||||
}
|
||||
assertTrue(exception);
|
||||
|
||||
exception = false;
|
||||
try {
|
||||
new constructor([]).reduce(sum);
|
||||
} catch (e) {
|
||||
exception = true;
|
||||
assertTrue(e instanceof TypeError,
|
||||
"reduce no initial value not throwing TypeError");
|
||||
assertEquals("Reduce of empty array with no initial value", e.message,
|
||||
"reduce no initial TypeError type");
|
||||
}
|
||||
assertTrue(exception);
|
||||
|
||||
exception = false;
|
||||
try {
|
||||
new constructor([]).reduceRight(sum);
|
||||
} catch (e) {
|
||||
exception = true;
|
||||
assertTrue(e instanceof TypeError,
|
||||
"reduceRight no initial value not throwing TypeError");
|
||||
assertEquals("Reduce of empty array with no initial value", e.message,
|
||||
"reduceRight no initial TypeError type");
|
||||
}
|
||||
assertTrue(exception);
|
||||
|
||||
// Reduce fails when called on non-TypedArrays
|
||||
assertThrows(function() {
|
||||
constructor.prototype.reduce.call([], function() {}, null);
|
||||
}, TypeError);
|
||||
assertThrows(function() {
|
||||
constructor.prototype.reduceRight.call([], function() {}, null);
|
||||
}, TypeError);
|
||||
|
||||
// Shadowing length doesn't affect every, unlike Array.prototype.every
|
||||
var a = new constructor([1, 2]);
|
||||
Object.defineProperty(a, 'length', {value: 1});
|
||||
assertEquals(a.reduce(sum, 0), 3);
|
||||
assertEquals(Array.prototype.reduce.call(a, sum, 0), 1);
|
||||
assertEquals(a.reduceRight(sum, 0), 3);
|
||||
assertEquals(Array.prototype.reduceRight.call(a, sum, 0), 1);
|
||||
|
||||
assertEquals(1, constructor.prototype.reduce.length);
|
||||
assertEquals(1, constructor.prototype.reduceRight.length);
|
||||
}
|
Loading…
Reference in New Issue
Block a user