// 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: --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, [], new constructor([]), sum, 0); testReduce("reduce", "EmptyReduceProd", 1, [], new constructor([]), prod, 1); testReduce("reduce", "EmptyReduceDec", 0, [], new constructor([]), dec, 0); testReduce("reduce", "EmptyReduceAccumulate", [], [], new constructor([]), accumulate, []); testReduce("reduce", "EmptyReduceSumNoInit", 0, [], new constructor([0]), sum); testReduce("reduce", "EmptyReduceProdNoInit", 1, [], new constructor([1]), prod); testReduce("reduce", "EmptyReduceDecNoInit", 0, [], new constructor([0]), dec); // ---- 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, [], new constructor([]), sum, 0); testReduce("reduceRight", "EmptyReduceRightProd", 1, [], new constructor([]), prod, 1); testReduce("reduceRight", "EmptyReduceRightDec", 0, [], new constructor([]), dec, 0); testReduce("reduceRight", "EmptyReduceRightAccumulate", [], [], new constructor([]), accumulate, []); testReduce("reduceRight", "EmptyReduceRightSumNoInit", 0, [], new constructor([0]), sum); testReduce("reduceRight", "EmptyReduceRightProdNoInit", 1, [], new constructor([1]), prod); testReduce("reduceRight", "EmptyReduceRightDecNoInit", 0, [], new constructor([0]), dec); // Ignore non-array properties: var arrayPlus = new constructor([1,2,3]); arrayPlus[-1] = NaN; arrayPlus["00"] = NaN; arrayPlus["02"] = NaN; arrayPlus["-0"] = NaN; arrayPlus.x = 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); // Detached Operation var tmp = { [Symbol.toPrimitive]() { assertUnreachable("Parameter should not be processed when " + "array.[[ViewedArrayBuffer]] is neutered."); return 0; } }; var array = new constructor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); %ArrayBufferNeuter(array.buffer); assertThrows(() => array.reduce(sum, tmp), TypeError); assertThrows(() => array.reduceRight(sum, tmp), TypeError); }