| // Copyright 2019 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 --opt --no-always-opt |
| |
| /** |
| * @fileoverview Test reduce and reduceRight |
| */ |
| |
| 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; |
| 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 Number(a) + Number(b); } |
| function prod(a, b) { return Number(a) * Number(b); } |
| function dec(a, b, i, arr) { return Number(a) + Number(b) * Math.pow(10, arr.length - i - 1); } |
| function accumulate(acc, elem, i) { acc[i] = elem; return acc; } |
| |
| // ---- Test Reduce[Left] |
| |
| var simpleArray = ['2',4,6]; |
| Object.seal(simpleArray); |
| |
| 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", simpleArray, |
| [[[], '2', 0, simpleArray, ['2']], |
| [['2'], 4, 1, simpleArray, ['2', 4]], |
| [['2', 4], 6, 2, simpleArray, simpleArray]], |
| simpleArray, accumulate, []); |
| |
| var emptyArray = []; |
| Object.seal(emptyArray); |
| |
| testReduce("reduce", "EmptyReduceSum", 0, [], emptyArray, sum, 0); |
| testReduce("reduce", "EmptyReduceProd", 1, [], emptyArray, prod, 1); |
| testReduce("reduce", "EmptyReduceDec", 0, [], emptyArray, dec, 0); |
| testReduce("reduce", "EmptyReduceAccumulate", [], [], emptyArray, accumulate, []); |
| |
| testReduce("reduce", "EmptyReduceSumNoInit", 0, emptyArray, [0], sum); |
| testReduce("reduce", "EmptyReduceProdNoInit", 1, emptyArray, [1], prod); |
| testReduce("reduce", "EmptyReduceDecNoInit", 0, emptyArray, [0], dec); |
| testReduce("reduce", "EmptyReduceAccumulateNoInit", [], emptyArray, [[]], accumulate); |
| |
| |
| var simpleSparseArray = [,,,'2',,4,,6,,]; |
| Object.seal(simpleSparseArray); |
| |
| testReduce("reduce", "SimpleSparseReduceSum", 12, |
| [[0, '2', 3, simpleSparseArray, 2], |
| [2, 4, 5, simpleSparseArray, 6], |
| [6, 6, 7, simpleSparseArray, 12]], |
| simpleSparseArray, sum, 0); |
| |
| testReduce("reduce", "SimpleSparseReduceProd", 48, |
| [[1, '2', 3, simpleSparseArray, 2], |
| [2, 4, 5, simpleSparseArray, 8], |
| [8, 6, 7, simpleSparseArray, 48]], |
| simpleSparseArray, prod, 1); |
| |
| testReduce("reduce", "SimpleSparseReduceDec", 204060, |
| [[0, '2', 3, simpleSparseArray, 200000], |
| [200000, 4, 5, simpleSparseArray, 204000], |
| [204000, 6, 7, simpleSparseArray, 204060]], |
| simpleSparseArray, dec, 0); |
| |
| testReduce("reduce", "SimpleSparseReduceAccumulate", [,,,'2',,4,,6], |
| [[[], '2', 3, simpleSparseArray, [,,,'2']], |
| [[,,,'2'], 4, 5, simpleSparseArray, [,,,'2',,4]], |
| [[,,,'2',,4], 6, 7, simpleSparseArray, [,,,'2',,4,,6]]], |
| simpleSparseArray, accumulate, []); |
| |
| |
| testReduce("reduce", "EmptySparseReduceSumNoInit", 0, [], [,,0,,], sum); |
| testReduce("reduce", "EmptySparseReduceProdNoInit", 1, [], [,,1,,], prod); |
| testReduce("reduce", "EmptySparseReduceDecNoInit", 0, [], [,,0,,], dec); |
| testReduce("reduce", "EmptySparseReduceAccumulateNoInit", |
| [], [], [,,[],,], accumulate); |
| |
| |
| var verySparseArray = []; |
| verySparseArray.length = 10000; |
| verySparseArray[2000] = '2'; |
| verySparseArray[5000] = 4; |
| verySparseArray[9000] = 6; |
| var verySparseSlice2 = verySparseArray.slice(0, 2001); |
| var verySparseSlice4 = verySparseArray.slice(0, 5001); |
| var verySparseSlice6 = verySparseArray.slice(0, 9001); |
| Object.seal(verySparseArray); |
| |
| testReduce("reduce", "VerySparseReduceSum", 12, |
| [[0, '2', 2000, verySparseArray, 2], |
| [2, 4, 5000, verySparseArray, 6], |
| [6, 6, 9000, verySparseArray, 12]], |
| verySparseArray, sum, 0); |
| |
| testReduce("reduce", "VerySparseReduceProd", 48, |
| [[1, '2', 2000, verySparseArray, 2], |
| [2, 4, 5000, verySparseArray, 8], |
| [8, 6, 9000, verySparseArray, 48]], |
| verySparseArray, prod, 1); |
| |
| testReduce("reduce", "VerySparseReduceDec", Infinity, |
| [[0, '2', 2000, verySparseArray, Infinity], |
| [Infinity, 4, 5000, verySparseArray, Infinity], |
| [Infinity, 6, 9000, verySparseArray, Infinity]], |
| verySparseArray, dec, 0); |
| |
| testReduce("reduce", "VerySparseReduceAccumulate", |
| verySparseSlice6, |
| [[[], '2', 2000, verySparseArray, verySparseSlice2], |
| [verySparseSlice2, 4, 5000, verySparseArray, verySparseSlice4], |
| [verySparseSlice4, 6, 9000, verySparseArray, verySparseSlice6]], |
| verySparseArray, accumulate, []); |
| |
| |
| testReduce("reduce", "VerySparseReduceSumNoInit", 12, |
| [['2', 4, 5000, verySparseArray, 6], |
| [6, 6, 9000, verySparseArray, 12]], |
| verySparseArray, sum); |
| |
| testReduce("reduce", "VerySparseReduceProdNoInit", 48, |
| [['2', 4, 5000, verySparseArray, 8], |
| [8, 6, 9000, verySparseArray, 48]], |
| verySparseArray, prod); |
| |
| testReduce("reduce", "VerySparseReduceDecNoInit", Infinity, |
| [['2', 4, 5000, verySparseArray, Infinity], |
| [Infinity, 6, 9000, verySparseArray, Infinity]], |
| verySparseArray, dec); |
| |
| testReduce("reduce", "SimpleSparseReduceAccumulateNoInit", |
| '2', |
| [['2', 4, 5000, verySparseArray, '2'], |
| ['2', 6, 9000, verySparseArray, '2']], |
| verySparseArray, 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", "SimpleReduceRightAccumulate", simpleArray, |
| [[[], 6, 2, simpleArray, [,,6]], |
| [[,,6], 4, 1, simpleArray, [,4,6]], |
| [[,4,6], '2', 0, simpleArray, simpleArray]], |
| simpleArray, accumulate, []); |
| |
| |
| 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); |
| |
| |
| testReduce("reduceRight", "SimpleSparseReduceRightSum", 12, |
| [[0, 6, 7, simpleSparseArray, 6], |
| [6, 4, 5, simpleSparseArray, 10], |
| [10, '2', 3, simpleSparseArray, 12]], |
| simpleSparseArray, sum, 0); |
| |
| testReduce("reduceRight", "SimpleSparseReduceRightProd", 48, |
| [[1, 6, 7, simpleSparseArray, 6], |
| [6, 4, 5, simpleSparseArray, 24], |
| [24, '2', 3, simpleSparseArray, 48]], |
| simpleSparseArray, prod, 1); |
| |
| testReduce("reduceRight", "SimpleSparseReduceRightDec", 204060, |
| [[0, 6, 7, simpleSparseArray, 60], |
| [60, 4, 5, simpleSparseArray, 4060], |
| [4060, '2', 3, simpleSparseArray, 204060]], |
| simpleSparseArray, dec, 0); |
| |
| testReduce("reduceRight", "SimpleSparseReduceRightAccumulate", [,,,'2',,4,,6], |
| [[[], 6, 7, simpleSparseArray, [,,,,,,,6]], |
| [[,,,,,,,6], 4, 5, simpleSparseArray, [,,,,,4,,6]], |
| [[,,,,,4,,6], '2', 3, simpleSparseArray, [,,,'2',,4,,6]]], |
| simpleSparseArray, accumulate, []); |
| |
| |
| testReduce("reduceRight", "EmptySparseReduceRightSumNoInit", |
| 0, [], [,,0,,], sum); |
| testReduce("reduceRight", "EmptySparseReduceRightProdNoInit", |
| 1, [], [,,1,,], prod); |
| testReduce("reduceRight", "EmptySparseReduceRightDecNoInit", |
| 0, [], [,,0,,], dec); |
| testReduce("reduceRight", "EmptySparseReduceRightAccumulateNoInit", |
| [], [], [,,[],,], accumulate); |
| |
| |
| var verySparseSuffix6 = []; |
| verySparseSuffix6[9000] = 6; |
| var verySparseSuffix4 = []; |
| verySparseSuffix4[5000] = 4; |
| verySparseSuffix4[9000] = 6; |
| var verySparseSuffix2 = verySparseSlice6; |
| |
| |
| testReduce("reduceRight", "VerySparseReduceRightSum", 12, |
| [[0, 6, 9000, verySparseArray, 6], |
| [6, 4, 5000, verySparseArray, 10], |
| [10, '2', 2000, verySparseArray, 12]], |
| verySparseArray, sum, 0); |
| |
| testReduce("reduceRight", "VerySparseReduceRightProd", 48, |
| [[1, 6, 9000, verySparseArray, 6], |
| [6, 4, 5000, verySparseArray, 24], |
| [24, '2', 2000, verySparseArray, 48]], |
| verySparseArray, prod, 1); |
| |
| testReduce("reduceRight", "VerySparseReduceRightDec", Infinity, |
| [[0, 6, 9000, verySparseArray, Infinity], |
| [Infinity, 4, 5000, verySparseArray, Infinity], |
| [Infinity, '2', 2000, verySparseArray, Infinity]], |
| verySparseArray, dec, 0); |
| |
| testReduce("reduceRight", "VerySparseReduceRightAccumulate", |
| verySparseSuffix2, |
| [[[], 6, 9000, verySparseArray, verySparseSuffix6], |
| [verySparseSuffix6, 4, 5000, verySparseArray, verySparseSuffix4], |
| [verySparseSuffix4, '2', 2000, verySparseArray, verySparseSuffix2]], |
| verySparseArray, accumulate, []); |
| |
| |
| testReduce("reduceRight", "VerySparseReduceRightSumNoInit", 12, |
| [[6, 4, 5000, verySparseArray, 10], |
| [10, '2', 2000, verySparseArray, 12]], |
| verySparseArray, sum); |
| |
| testReduce("reduceRight", "VerySparseReduceRightProdNoInit", 48, |
| [[6, 4, 5000, verySparseArray, 24], |
| [24, '2', 2000, verySparseArray, 48]], |
| verySparseArray, prod); |
| |
| testReduce("reduceRight", "VerySparseReduceRightDecNoInit", Infinity, |
| [[6, 4, 5000, verySparseArray, Infinity], |
| [Infinity, '2', 2000, verySparseArray, Infinity]], |
| verySparseArray, dec); |
| |
| testReduce("reduceRight", "SimpleSparseReduceRightAccumulateNoInit", |
| 6, |
| [[6, 4, 5000, verySparseArray, 6], |
| [6, '2', 2000, verySparseArray, 6]], |
| verySparseArray, accumulate); |
| |
| |
| // undefined is an element |
| var undefArray = [,,undefined,,undefined,,]; |
| Object.seal(undefArray); |
| |
| testReduce("reduce", "SparseUndefinedReduceAdd", NaN, |
| [[0, undefined, 2, undefArray, NaN], |
| [NaN, undefined, 4, undefArray, NaN], |
| ], |
| undefArray, sum, 0); |
| |
| testReduce("reduceRight", "SparseUndefinedReduceRightAdd", NaN, |
| [[0, undefined, 4, undefArray, NaN], |
| [NaN, undefined, 2, undefArray, NaN], |
| ], undefArray, sum, 0); |
| |
| testReduce("reduce", "SparseUndefinedReduceAddNoInit", NaN, |
| [[undefined, undefined, 4, undefArray, NaN], |
| ], undefArray, sum); |
| |
| testReduce("reduceRight", "SparseUndefinedReduceRightAddNoInit", NaN, |
| [[undefined, undefined, 2, undefArray, NaN], |
| ], undefArray, sum); |
| |
| |
| // 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; |
| Object.seal(arrayPlus); |
| |
| testReduce("reduce", "ArrayWithNonElementPropertiesReduce", 6, |
| [[0, 1, 0, arrayPlus, 1], |
| [1, '2', 1, arrayPlus, 3], |
| [3, 3, 3, arrayPlus, 6], |
| ], arrayPlus, sum, 0); |
| |
| testReduce("reduceRight", "ArrayWithNonElementPropertiesReduceRight", 6, |
| [[0, 3, 3, arrayPlus, 3], |
| [3, '2', 1, arrayPlus, 5], |
| [5, 1, 0, arrayPlus, 6], |
| ], arrayPlus, sum, 0); |
| |
| // Test passing undefined as initial value (to test missing parameter |
| // detection). |
| Object.seal(['1']).reduce((a, b) => { assertEquals(a, undefined); assertEquals(b, '1') }, |
| undefined); |
| Object.seal(['1', 2]).reduce((a, b) => { assertEquals(a, '1'); assertEquals(b, 2); }); |
| Object.seal(['1']).reduce((a, b) => { assertTrue(false); }); |
| |
| // Test error conditions: |
| |
| var exception = false; |
| try { |
| Object.seal(['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 { |
| Object.seal(['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 { |
| Object.seal([]).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 { |
| Object.seal([]).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); |
| |
| exception = false; |
| try { |
| Object.seal([,,,]).reduce(sum); |
| } catch (e) { |
| exception = true; |
| assertTrue(e instanceof TypeError, |
| "reduce sparse 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 { |
| Object.seal([,,,]).reduceRight(sum); |
| } catch (e) { |
| exception = true; |
| assertTrue(e instanceof TypeError, |
| "reduceRight sparse no initial value not throwing TypeError"); |
| assertEquals("Reduce of empty array with no initial value", e.message, |
| "reduceRight no initial TypeError type"); |
| } |
| assertTrue(exception); |
| |
| |
| // Array changing length |
| |
| function extender(a, b, i, s) { |
| s[s.length] = s.length; |
| return Number(a) + Number(b); |
| } |
| |
| var arr = [1, '2', 3, 4]; |
| Object.seal(arr); |
| testReduce("reduce", "ArrayManipulationExtender", 10, |
| [[0, 1, 0, [1, '2', 3, 4], 1], |
| [1, '2', 1, [1, '2', 3, 4], 3], |
| [3, 3, 2, [1, '2', 3, 4], 6], |
| [6, 4, 3, [1, '2', 3, 4], 10], |
| ], arr, extender, 0); |
| |
| var arr = []; |
| Object.defineProperty(arr, "0", { get: function() { delete this[0] }, |
| configurable: true }); |
| assertEquals(undefined, Object.seal(arr).reduce(function(val) { return val })); |
| |
| var arr = []; |
| Object.defineProperty(arr, "0", { get: function() { delete this[0] }, |
| configurable: true}); |
| assertEquals(undefined, Object.seal(arr).reduceRight(function(val) { return val })); |
| |
| |
| (function ReduceRightMaxIndex() { |
| const kMaxIndex = 0xffffffff-1; |
| let array = []; |
| array[kMaxIndex-2] = 'value-2'; |
| array[kMaxIndex-1] = 'value-1'; |
| // Use the maximum array index possible. |
| array[kMaxIndex] = 'value'; |
| // Add the next index which is a normal property and thus will not show up. |
| array[kMaxIndex+1] = 'normal property'; |
| assertThrowsEquals( () => { |
| Object.seal(array).reduceRight((sum, value) => { |
| assertEquals('initial', sum); |
| assertEquals('value', value); |
| // Throw at this point as we would very slowly loop down from kMaxIndex. |
| throw 'do not continue'; |
| }, 'initial') |
| }, 'do not continue'); |
| })(); |
| |
| (function OptimizedReduce() { |
| let f = (a,current) => a + Number(current); |
| let g = function(a) { |
| return a.reduce(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| let a = [1,'2',3,4,5,6,7,8,9,10]; |
| Object.seal(a); |
| g(a); g(a); |
| let total = g(a); |
| %OptimizeFunctionOnNextCall(g); |
| assertEquals(total, g(a)); |
| assertOptimized(g); |
| })(); |
| |
| (function OptimizedReduceEmpty() { |
| let f = (a,current) => a + Number(current); |
| let g = function(a) { |
| return a.reduce(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| let a = [1,'2',3,4,5,6,7,8,9,10]; |
| Object.seal(a); |
| g(a); g(a); g(a); |
| %OptimizeFunctionOnNextCall(g); |
| g(a); |
| assertOptimized(g); |
| assertThrows(() => g([])); |
| assertUnoptimized(g); |
| })(); |
| |
| (function OptimizedReduceLazyDeopt() { |
| let deopt = false; |
| let f = (a,current) => { if (deopt) %DeoptimizeNow(); return a + Number(current); }; |
| let g = function(a) { |
| return a.reduce(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| let a = [1,'2',3,4,5,6,7,8,9,10]; |
| Object.seal(a); |
| g(a); g(a); |
| let total = g(a); |
| %OptimizeFunctionOnNextCall(g); |
| g(a); |
| assertOptimized(g); |
| deopt = true; |
| assertEquals(total, g(a)); |
| assertOptimized(g); |
| })(); |
| |
| (function OptimizedReduceLazyDeoptMiddleOfIteration() { |
| let deopt = false; |
| let f = (a,current) => { |
| if (current == 6 && deopt) %DeoptimizeNow(); |
| return a + Number(current); |
| }; |
| let g = function(a) { |
| return a.reduce(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| let a = [11,'22',33,45,56,6,77,84,93,101]; |
| Object.seal(a); |
| g(a); g(a); |
| let total = g(a); |
| %OptimizeFunctionOnNextCall(g); |
| g(a); |
| assertOptimized(g); |
| deopt = true; |
| assertEquals(total, g(a)); |
| assertOptimized(g); |
| })(); |
| |
| (function OptimizedReduceEagerDeoptMiddleOfIteration() { |
| let deopt = false; |
| let array = [11,'22',33,45,56,6,77,84,93,101]; |
| Object.seal(array); |
| let f = (a,current) => { |
| if (current == 6 && deopt) {array[0] = 1.5; } |
| return a + Number(current); |
| }; |
| let g = function() { |
| return array.reduce(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertOptimized(g); |
| deopt = true; |
| g(); |
| assertOptimized(g); |
| %PrepareFunctionForOptimization(g); |
| deopt = false; |
| array = [11,'22',33,45,56,6,77,84,93,101]; |
| Object.seal(array); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertOptimized(g); |
| deopt = true; |
| assertEquals(total, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function OptimizedReduceEagerDeoptMiddleOfIterationHoley() { |
| let deopt = false; |
| let array = [, ,11,'22',,33,45,56,,6,77,84,93,101,]; |
| Object.seal(array); |
| let f = (a,current) => { |
| if (current == 6 && deopt) {array[0] = 1.5; } |
| return a + Number(current); |
| }; |
| let g = function() { |
| return array.reduce(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertOptimized(g); |
| deopt = true; |
| g(); |
| assertOptimized(g); |
| %PrepareFunctionForOptimization(g); |
| deopt = false; |
| array = [11,'22',33,45,56,6,77,84,93,101]; |
| Object.seal(array); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertUnoptimized(g); |
| deopt = true; |
| assertEquals(total, g()); |
| assertUnoptimized(g); |
| })(); |
| |
| (function TriggerReduceRightPreLoopDeopt() { |
| function f(a) { |
| a.reduceRight((x) => { return Number(x) + 1 }); |
| }; |
| %PrepareFunctionForOptimization(f); |
| var arr = Object.seal([1, '2', ]); |
| f(arr); |
| f(arr); |
| %OptimizeFunctionOnNextCall(f); |
| assertThrows(() => f([]), TypeError); |
| assertUnoptimized(f); |
| })(); |
| |
| (function OptimizedReduceRightEagerDeoptMiddleOfIterationHoley() { |
| let deopt = false; |
| let array = [, ,11,'22',,33,45,56,,6,77,84,93,101,]; |
| Object.seal(array); |
| let f = (a,current) => { |
| if (current == 6 && deopt) {array[array.length-1] = 1.5; } |
| return a + Number(current); |
| }; |
| let g = function() { |
| return array.reduceRight(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertOptimized(g); |
| deopt = true; |
| g(); |
| assertOptimized(g); |
| %PrepareFunctionForOptimization(g); |
| deopt = false; |
| array = [11,'22',33,45,56,6,77,84,93,101]; |
| Object.seal(array); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertUnoptimized(g); |
| deopt = true; |
| assertEquals(total, g()); |
| assertUnoptimized(g); |
| })(); |
| |
| (function ReduceCatch() { |
| let f = (a,current) => { |
| return a + current; |
| }; |
| let g = function() { |
| try { |
| return Object.seal(array).reduce(f); |
| } catch (e) { |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| g(); |
| assertEquals(total, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceThrow() { |
| let done = false; |
| let f = (a, current) => { |
| if (done) throw "x"; |
| return a + Number(current); |
| }; |
| let array = [1,'2',3]; |
| Object.seal(array); |
| let g = function() { |
| try { |
| return array.reduce(f); |
| } catch (e) { |
| return null; |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| %PrepareFunctionForOptimization(g); |
| %OptimizeFunctionOnNextCall(g); |
| done = false; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceThrow() { |
| let done = false; |
| let f = (a, current) => { |
| if (done) throw "x"; |
| return a + Number(current); |
| }; |
| %NeverOptimizeFunction(f); |
| let array = [1,'2',3]; |
| Object.seal(array); |
| let g = function() { |
| try { |
| return array.reduce(f); |
| } catch (e) { |
| return null; |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| %PrepareFunctionForOptimization(g); |
| done = false; |
| %OptimizeFunctionOnNextCall(g); |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceFinally() { |
| let done = false; |
| let f = (a, current) => { |
| if (done) throw "x"; |
| return a + Number(current); |
| }; |
| let array = [1,'2',3]; |
| Object.seal(array); |
| let g = function() { |
| try { |
| return array.reduce(f); |
| } catch (e) { |
| } finally { |
| if (done) return null; |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| done = false; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceFinallyNoInline() { |
| let done = false; |
| let f = (a, current) => { |
| if (done) throw "x"; |
| return a + Number(current); |
| }; |
| %NeverOptimizeFunction(f); |
| let array = [1, '2', 3]; |
| Object.seal(array); |
| let g = function() { |
| try { |
| return array.reduce(f); |
| } catch (e) { |
| } finally { |
| if (done) return null; |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| done = false; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceNonCallableOpt() { |
| let done = false; |
| let f = (a, current) => { |
| return a + Number(current); |
| }; |
| let array = [1,'2',3]; |
| Object.seal(array); |
| let g = function() { |
| return array.reduce(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); g(); |
| assertEquals(6, g()); |
| assertOptimized(g); |
| f = null; |
| assertThrows(() => g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceCatchInlineDeopt() { |
| let done = false; |
| let f = (a, current) => { |
| if (done) { |
| %DeoptimizeNow(); |
| throw "x"; |
| } |
| return a + Number(current); |
| }; |
| let array = [1,2,3]; |
| Object.seal(array); |
| let g = function() { |
| try { |
| return array.reduce(f); |
| } catch (e) { |
| if (done) return null; |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| done = false; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceFinallyInlineDeopt() { |
| let done = false; |
| let f = (a, current) => { |
| if (done) { |
| %DeoptimizeNow(); |
| throw "x"; |
| } |
| return a + Number(current); |
| }; |
| let array = [1,'2',3]; |
| Object.seal(array); |
| let g = function() { |
| try { |
| return array.reduce(f); |
| } catch (e) { |
| } finally { |
| if (done) return null; |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| done = false; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function OptimizedReduceRight() { |
| let count = 0; |
| let f = (a,current,i) => a + Number(current) * ++count; |
| let g = function(a) { |
| count = 0; |
| return a.reduceRight(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| let a = [1,'2',3,4,5,6,7,8,9,10]; |
| Object.seal(a); |
| g(a); g(a); |
| let total = g(a); |
| %OptimizeFunctionOnNextCall(g); |
| assertEquals(total, g(a)); |
| assertOptimized(g); |
| })(); |
| |
| (function OptimizedReduceEmpty() { |
| let count = 0; |
| let f = (a,current,i) => a + Number(current) * ++count; |
| let g = function(a) { |
| count = 0; |
| return a.reduceRight(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| let a = [1,'2',3,4,5,6,7,8,9,10]; |
| Object.seal(a); |
| g(a); g(a); g(a); |
| %OptimizeFunctionOnNextCall(g); |
| g(a); |
| assertOptimized(g); |
| assertThrows(() => g([])); |
| assertUnoptimized(g); |
| })(); |
| |
| (function OptimizedReduceLazyDeopt() { |
| let deopt = false; |
| let f = (a,current) => { if (deopt) %DeoptimizeNow(); return a + Number(current); }; |
| let g = function(a) { |
| return a.reduceRight(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| let a = [1,'2',3,4,5,6,7,8,9,10]; |
| Object.seal(a); |
| g(a); g(a); |
| let total = g(a); |
| %OptimizeFunctionOnNextCall(g); |
| g(a); |
| deopt = true; |
| assertEquals(total, g(a)); |
| assertOptimized(g); |
| })(); |
| |
| (function OptimizedReduceLazyDeoptMiddleOfIteration() { |
| let deopt = false; |
| let f = (a,current) => { |
| if (current == 6 && deopt) %DeoptimizeNow(); |
| return a + Number(current); |
| }; |
| let g = function(a) { |
| return a.reduceRight(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| let a = [11,'22',33,45,56,6,77,84,93,101]; |
| Object.seal(a); |
| g(a); g(a); |
| let total = g(a); |
| %OptimizeFunctionOnNextCall(g); |
| g(a); |
| deopt = true; |
| assertEquals(total, g(a)); |
| assertOptimized(g); |
| })(); |
| |
| (function OptimizedReduceEagerDeoptMiddleOfIteration() { |
| let deopt = false; |
| let array = [11,'22',33,45,56,6,77,84,93,101]; |
| Object.seal(array); |
| let f = (a,current) => { |
| if (current == 6 && deopt) {array[9] = 1.5; } |
| return a + Number(current); |
| }; |
| let g = function() { |
| return array.reduceRight(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertOptimized(g); |
| deopt = true; |
| %PrepareFunctionForOptimization(g); |
| g(); |
| deopt = false; |
| array = [11,'22',33,45,56,6,77,84,93,101]; |
| Object.seal(array); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| deopt = true; |
| assertEquals(total, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceCatch() { |
| let f = (a,current) => { |
| return a + Number(current); |
| }; |
| let g = function() { |
| try { |
| return Object.seal(array).reduceRight(f); |
| } catch (e) { |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| g(); |
| assertEquals(total, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceThrow() { |
| let done = false; |
| let f = (a, current) => { |
| if (done) throw "x"; |
| return a + Number(current); |
| }; |
| let array = [1,'2',3]; |
| Object.seal(array); |
| let g = function() { |
| try { |
| return array.reduceRight(f); |
| } catch (e) { |
| return null; |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| assertOptimized(g); |
| done = true; |
| assertEquals(null, g()); |
| done = false; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceThrow() { |
| let done = false; |
| let f = (a, current) => { |
| if (done) throw "x"; |
| return a + Number(current); |
| }; |
| %NeverOptimizeFunction(f); |
| let array = [1,'2',3]; |
| Object.seal(array); |
| let g = function() { |
| try { |
| return array.reduceRight(f); |
| } catch (e) { |
| return null; |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| done = false; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceFinally() { |
| let done = false; |
| let f = (a, current) => { |
| if (done) throw "x"; |
| return a + Number(current); |
| }; |
| let array = [1, '2', 3]; |
| Object.seal(array); |
| let g = function() { |
| try { |
| return array.reduceRight(f); |
| } catch (e) { |
| } finally { |
| if (done) return null; |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| done = false; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceFinallyNoInline() { |
| let done = false; |
| let f = (a, current) => { |
| if (done) throw "x"; |
| return a + Number(current); |
| }; |
| %NeverOptimizeFunction(f); |
| let array = [1,'2',3]; |
| Object.seal(array); |
| let g = function() { |
| try { |
| return array.reduceRight(f); |
| } catch (e) { |
| } finally { |
| if (done) return null; |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| assertOptimized(g); |
| done = true; |
| assertEquals(null, g()); |
| done = false; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceNonCallableOpt() { |
| let done = false; |
| let f = (a, current) => { |
| return a + Number(current); |
| }; |
| let array = [1,'2',3]; |
| Object.seal(array); |
| let g = function() { |
| return array.reduceRight(f); |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); g(); |
| assertEquals(6, g()); |
| f = null; |
| assertThrows(() => g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceCatchInlineDeopt() { |
| let done = false; |
| let f = (a, current) => { |
| if (done) { |
| %DeoptimizeNow(); |
| throw "x"; |
| } |
| return a + Number(current); |
| }; |
| let array = [1,'2',3]; |
| Object.seal(array); |
| let g = function() { |
| try { |
| return array.reduceRight(f); |
| } catch (e) { |
| if (done) return null; |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| done = false; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceFinallyInlineDeopt() { |
| let done = false; |
| let f = (a, current) => { |
| if (done) { |
| %DeoptimizeNow(); |
| throw "x"; |
| } |
| return a + Number(current); |
| }; |
| let array = [1,'2',3]; |
| Object.seal(array); |
| let g = function() { |
| try { |
| return array.reduceRight(f); |
| } catch (e) { |
| } finally { |
| if (done) return null; |
| } |
| }; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| let total = g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| done = false; |
| %PrepareFunctionForOptimization(g); |
| g(); g(); |
| %OptimizeFunctionOnNextCall(g); |
| g(); |
| assertEquals(6, g()); |
| done = true; |
| assertEquals(null, g()); |
| assertOptimized(g); |
| })(); |
| |
| (function ReduceHoleyArrayWithDefaultAccumulator() { |
| var holey = new Array(10); |
| Object.seal(holey); |
| function reduce(a) { |
| let callback = function(accumulator, currentValue) { |
| return currentValue; |
| }; |
| return a.reduce(callback, 13); |
| }; |
| %PrepareFunctionForOptimization(reduce); |
| assertEquals(13, reduce(holey)); |
| assertEquals(13, reduce(holey)); |
| assertEquals(13, reduce(holey)); |
| %OptimizeFunctionOnNextCall(reduce); |
| assertEquals(13, reduce(holey)); |
| assertOptimized(reduce); |
| })(); |
| |
| (function ReduceRightHoleyArrayWithDefaultAccumulator() { |
| var holey = new Array(10); |
| Object.seal(holey); |
| function reduce(a) { |
| let callback = function(accumulator, currentValue) { |
| return currentValue; |
| }; |
| return a.reduceRight(callback, 13); |
| }; |
| %PrepareFunctionForOptimization(reduce); |
| assertEquals(13, reduce(holey)); |
| assertEquals(13, reduce(holey)); |
| assertEquals(13, reduce(holey)); |
| %OptimizeFunctionOnNextCall(reduce); |
| assertEquals(13, reduce(holey)); |
| assertOptimized(reduce); |
| })(); |
| |
| (function ReduceHoleyArrayOneElementWithDefaultAccumulator() { |
| var holey = new Array(10); |
| holey[1] = '5'; |
| Object.seal(holey); |
| function reduce(a) { |
| let callback = function(accumulator, currentValue) { |
| return Number(currentValue) + accumulator; |
| }; |
| return a.reduce(callback, 13); |
| }; |
| %PrepareFunctionForOptimization(reduce); |
| assertEquals(18, reduce(holey)); |
| assertEquals(18, reduce(holey)); |
| assertEquals(18, reduce(holey)); |
| %OptimizeFunctionOnNextCall(reduce); |
| assertEquals(18, reduce(holey)); |
| assertOptimized(reduce); |
| })(); |
| |
| (function ReduceRightHoleyArrayOneElementWithDefaultAccumulator() { |
| var holey = new Array(10); |
| holey[1] = '5'; |
| Object.seal(holey); |
| function reduce(a) { |
| let callback = function(accumulator, currentValue) { |
| return Number(currentValue) + accumulator; |
| }; |
| return a.reduceRight(callback, 13); |
| }; |
| %PrepareFunctionForOptimization(reduce); |
| assertEquals(18, reduce(holey)); |
| assertEquals(18, reduce(holey)); |
| assertEquals(18, reduce(holey)); |
| %OptimizeFunctionOnNextCall(reduce); |
| assertEquals(18, reduce(holey)); |
| assertOptimized(reduce); |
| })(); |
| |
| (function ReduceMixedHoleyArrays() { |
| function r(a) { |
| return a.reduce((acc, i) => {acc[0]}); |
| }; |
| |
| // Hold on to the objects, otherwise their maps might be garbage |
| // collected and {r} will get deoptmized before the {assertOptimized}. |
| const object1 = Object.seal([[0]]); |
| const object2 = Object.seal([0,,]); |
| const object3 = Object.seal([,0,0]); |
| |
| %PrepareFunctionForOptimization(r); |
| assertEquals(r(object1), [0]); |
| assertEquals(r(object1), [0]); |
| assertEquals(r(object2), 0); |
| %OptimizeFunctionOnNextCall(r); |
| assertEquals(r(object3), undefined); |
| assertOptimized(r); |
| })(); |