1 // Copyright 2015 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 var typedArrayConstructors = [ 6 Uint8Array, 7 Int8Array, 8 Uint16Array, 9 Int16Array, 10 Uint32Array, 11 Int32Array, 12 Uint8ClampedArray, 13 Float32Array, 14 Float64Array 15 ]; 16 17 function clone(v) { 18 // Shallow-copies arrays, returns everything else verbatim. 19 if (v instanceof Array) { 20 // Shallow-copy an array. 21 var newArray = new Array(v.length); 22 for (var i in v) { 23 newArray[i] = v[i]; 24 } 25 return newArray; 26 } 27 return v; 28 } 29 30 31 // Creates a callback function for reduce/reduceRight that tests the number 32 // of arguments and otherwise behaves as "func", but which also 33 // records all calls in an array on the function (as arrays of arguments 34 // followed by result). 35 function makeRecorder(func, testName) { 36 var record = []; 37 var f = function recorder(a, b, i, s) { 38 assertEquals(4, arguments.length, 39 testName + "(number of arguments: " + arguments.length + ")"); 40 assertEquals("number", typeof(i), testName + "(index must be number)"); 41 assertEquals(s[i], b, testName + "(current argument is at index)"); 42 if (record.length > 0) { 43 var prevRecord = record[record.length - 1]; 44 var prevResult = prevRecord[prevRecord.length - 1]; 45 assertEquals(prevResult, a, 46 testName + "(prev result -> current input)"); 47 } 48 var args = [clone(a), clone(b), i, clone(s)]; 49 var result = func.apply(this, arguments); 50 args.push(clone(result)); 51 record.push(args); 52 return result; 53 }; 54 f.record = record; 55 return f; 56 } 57 58 59 function testReduce(type, 60 testName, 61 expectedResult, 62 expectedCalls, 63 array, 64 combine, 65 init) { 66 var rec = makeRecorder(combine); 67 var result; 68 var performsCall; 69 if (arguments.length > 6) { 70 result = array[type](rec, init); 71 } else { 72 result = array[type](rec); 73 } 74 var calls = rec.record; 75 assertEquals(expectedCalls.length, calls.length, 76 testName + " (number of calls)"); 77 for (var i = 0; i < expectedCalls.length; i++) { 78 assertEquals(expectedCalls[i], calls[i], 79 testName + " (call " + (i + 1) + ")"); 80 } 81 assertEquals(expectedResult, result, testName + " (result)"); 82 } 83 84 85 function sum(a, b) { return a + b; } 86 function prod(a, b) { return a * b; } 87 function dec(a, b, i, arr) { return a + b * Math.pow(10, arr.length - i - 1); } 88 function accumulate(acc, elem, i) { acc[i] = elem; return acc; } 89 90 for (var constructor of typedArrayConstructors) { 91 // ---- Test Reduce[Left] 92 93 var simpleArray = new constructor([2,4,6]) 94 95 testReduce("reduce", "SimpleReduceSum", 12, 96 [[0, 2, 0, simpleArray, 2], 97 [2, 4, 1, simpleArray, 6], 98 [6, 6, 2, simpleArray, 12]], 99 simpleArray, sum, 0); 100 101 testReduce("reduce", "SimpleReduceProd", 48, 102 [[1, 2, 0, simpleArray, 2], 103 [2, 4, 1, simpleArray, 8], 104 [8, 6, 2, simpleArray, 48]], 105 simpleArray, prod, 1); 106 107 testReduce("reduce", "SimpleReduceDec", 246, 108 [[0, 2, 0, simpleArray, 200], 109 [200, 4, 1, simpleArray, 240], 110 [240, 6, 2, simpleArray, 246]], 111 simpleArray, dec, 0); 112 113 testReduce("reduce", "SimpleReduceAccumulate", [2, 4, 6], 114 [[[], 2, 0, simpleArray, [2]], 115 [[2], 4, 1, simpleArray, [2, 4]], 116 [[2,4], 6, 2, simpleArray, [2, 4, 6]]], 117 simpleArray, accumulate, []); 118 119 120 testReduce("reduce", "EmptyReduceSum", 0, [], new constructor([]), sum, 0); 121 testReduce("reduce", "EmptyReduceProd", 1, [], new constructor([]), prod, 1); 122 testReduce("reduce", "EmptyReduceDec", 0, [], new constructor([]), dec, 0); 123 testReduce("reduce", "EmptyReduceAccumulate", [], [], new constructor([]), accumulate, []); 124 125 testReduce("reduce", "EmptyReduceSumNoInit", 0, [], new constructor([0]), sum); 126 testReduce("reduce", "EmptyReduceProdNoInit", 1, [], new constructor([1]), prod); 127 testReduce("reduce", "EmptyReduceDecNoInit", 0, [], new constructor([0]), dec); 128 129 // ---- Test ReduceRight 130 131 testReduce("reduceRight", "SimpleReduceRightSum", 12, 132 [[0, 6, 2, simpleArray, 6], 133 [6, 4, 1, simpleArray, 10], 134 [10, 2, 0, simpleArray, 12]], 135 simpleArray, sum, 0); 136 137 testReduce("reduceRight", "SimpleReduceRightProd", 48, 138 [[1, 6, 2, simpleArray, 6], 139 [6, 4, 1, simpleArray, 24], 140 [24, 2, 0, simpleArray, 48]], 141 simpleArray, prod, 1); 142 143 testReduce("reduceRight", "SimpleReduceRightDec", 246, 144 [[0, 6, 2, simpleArray, 6], 145 [6, 4, 1, simpleArray, 46], 146 [46, 2, 0, simpleArray, 246]], 147 simpleArray, dec, 0); 148 149 150 testReduce("reduceRight", "EmptyReduceRightSum", 0, [], new constructor([]), sum, 0); 151 testReduce("reduceRight", "EmptyReduceRightProd", 1, [], new constructor([]), prod, 1); 152 testReduce("reduceRight", "EmptyReduceRightDec", 0, [], new constructor([]), dec, 0); 153 testReduce("reduceRight", "EmptyReduceRightAccumulate", [], 154 [], new constructor([]), accumulate, []); 155 156 testReduce("reduceRight", "EmptyReduceRightSumNoInit", 0, [], new constructor([0]), sum); 157 testReduce("reduceRight", "EmptyReduceRightProdNoInit", 1, [], new constructor([1]), prod); 158 testReduce("reduceRight", "EmptyReduceRightDecNoInit", 0, [], new constructor([0]), dec); 159 160 // Ignore non-array properties: 161 162 var arrayPlus = new constructor([1,2,3]); 163 arrayPlus[-1] = NaN; 164 arrayPlus["00"] = NaN; 165 arrayPlus["02"] = NaN; 166 arrayPlus["-0"] = NaN; 167 arrayPlus.x = NaN; 168 169 testReduce("reduce", "ArrayWithNonElementPropertiesReduce", 6, 170 [[0, 1, 0, arrayPlus, 1], 171 [1, 2, 1, arrayPlus, 3], 172 [3, 3, 2, arrayPlus, 6], 173 ], arrayPlus, sum, 0); 174 175 testReduce("reduceRight", "ArrayWithNonElementPropertiesReduceRight", 6, 176 [[0, 3, 2, arrayPlus, 3], 177 [3, 2, 1, arrayPlus, 5], 178 [5, 1, 0, arrayPlus, 6], 179 ], arrayPlus, sum, 0); 180 181 182 // Test error conditions: 183 184 var exception = false; 185 try { 186 new constructor([1]).reduce("not a function"); 187 } catch (e) { 188 exception = true; 189 assertTrue(e instanceof TypeError, 190 "reduce callback not a function not throwing TypeError"); 191 assertTrue(e.message.indexOf(" is not a function") >= 0, 192 "reduce non function TypeError type"); 193 } 194 assertTrue(exception); 195 196 exception = false; 197 try { 198 new constructor([1]).reduceRight("not a function"); 199 } catch (e) { 200 exception = true; 201 assertTrue(e instanceof TypeError, 202 "reduceRight callback not a function not throwing TypeError"); 203 assertTrue(e.message.indexOf(" is not a function") >= 0, 204 "reduceRight non function TypeError type"); 205 } 206 assertTrue(exception); 207 208 exception = false; 209 try { 210 new constructor([]).reduce(sum); 211 } catch (e) { 212 exception = true; 213 assertTrue(e instanceof TypeError, 214 "reduce no initial value not throwing TypeError"); 215 assertEquals("Reduce of empty array with no initial value", e.message, 216 "reduce no initial TypeError type"); 217 } 218 assertTrue(exception); 219 220 exception = false; 221 try { 222 new constructor([]).reduceRight(sum); 223 } catch (e) { 224 exception = true; 225 assertTrue(e instanceof TypeError, 226 "reduceRight no initial value not throwing TypeError"); 227 assertEquals("Reduce of empty array with no initial value", e.message, 228 "reduceRight no initial TypeError type"); 229 } 230 assertTrue(exception); 231 232 // Reduce fails when called on non-TypedArrays 233 assertThrows(function() { 234 constructor.prototype.reduce.call([], function() {}, null); 235 }, TypeError); 236 assertThrows(function() { 237 constructor.prototype.reduceRight.call([], function() {}, null); 238 }, TypeError); 239 240 // Shadowing length doesn't affect every, unlike Array.prototype.every 241 var a = new constructor([1, 2]); 242 Object.defineProperty(a, 'length', {value: 1}); 243 assertEquals(a.reduce(sum, 0), 3); 244 assertEquals(Array.prototype.reduce.call(a, sum, 0), 1); 245 assertEquals(a.reduceRight(sum, 0), 3); 246 assertEquals(Array.prototype.reduceRight.call(a, sum, 0), 1); 247 248 assertEquals(1, constructor.prototype.reduce.length); 249 assertEquals(1, constructor.prototype.reduceRight.length); 250 } 251