Index: test/mjsunit/optimized-foreach.js |
diff --git a/test/mjsunit/optimized-foreach.js b/test/mjsunit/optimized-foreach.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a6e434142042216b2859c81fea4173554c76dc41 |
--- /dev/null |
+++ b/test/mjsunit/optimized-foreach.js |
@@ -0,0 +1,313 @@ |
+// Copyright 2017 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 --expose-gc --turbo-inline-array-builtins |
+ |
+var a = [0, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,0,0]; |
+var b = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]; |
+var c = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]; |
+ |
+// Unknown field access leads to soft-deopt unrelated to forEach, should still |
+// lead to correct result. |
+(function() { |
+ var result = 0; |
+ var eagerDeoptInCalled = function(deopt) { |
+ var sum = function(v,i,o) { |
+ result += v; |
+ if (i == 13 && deopt) { |
+ a.abc = 25; |
+ } |
+ } |
+ a.forEach(sum); |
+ } |
+ eagerDeoptInCalled(); |
+ eagerDeoptInCalled(); |
+ %OptimizeFunctionOnNextCall(eagerDeoptInCalled); |
+ eagerDeoptInCalled(); |
+ eagerDeoptInCalled(true); |
+ eagerDeoptInCalled(); |
+ assertEquals(1500, result); |
+})(); |
+ |
+// Length change detected during loop, must cause properly handled eager deopt. |
+(function() { |
+ var result = 0; |
+ var eagerDeoptInCalled = function(deopt) { |
+ var sum = function(v,i,o) { |
+ result += v; |
+ a.length = (i == 13 && deopt) ? 25 : 27; |
+ } |
+ a.forEach(sum); |
+ } |
+ eagerDeoptInCalled(); |
+ eagerDeoptInCalled(); |
+ %OptimizeFunctionOnNextCall(eagerDeoptInCalled); |
+ eagerDeoptInCalled(); |
+ eagerDeoptInCalled(true); |
+ eagerDeoptInCalled(); |
+ assertEquals(1500, result); |
+})(); |
+ |
+// Escape analyzed array |
+(function() { |
+ var result = 0; |
+ var eagerDeoptInCalled = function(deopt) { |
+ var a_noescape = [0,1,2,3,4,5]; |
+ var sum = function(v,i,o) { |
+ result += v; |
+ if (i == 13 && deopt) { |
+ a_noescape.length = 25; |
+ } |
+ } |
+ a_noescape.forEach(sum); |
+ } |
+ eagerDeoptInCalled(); |
+ eagerDeoptInCalled(); |
+ %OptimizeFunctionOnNextCall(eagerDeoptInCalled); |
+ eagerDeoptInCalled(); |
+ eagerDeoptInCalled(true); |
+ eagerDeoptInCalled(); |
+ assertEquals(75, result); |
+})(); |
+ |
+// Escape analyzed array where sum function isn't inlined, forcing a lazy deopt |
+// with GC that relies on the stashed-away return result fro the lazy deopt |
+// being properly stored in a place on the stack that gets GC'ed. |
+(function() { |
+ var result = 0; |
+ var lazyDeopt = function(deopt) { |
+ var b = [1,2,3]; |
+ var sum = function(v,i,o) { |
+ result += i; |
+ if (i == 1 && deopt) { |
+ %DeoptimizeFunction(lazyDeopt); |
+ } |
+ gc(); gc(); |
+ }; |
+ %NeverOptimizeFunction(sum); |
+ b.forEach(sum); |
+ } |
+ lazyDeopt(); |
+ lazyDeopt(); |
+ %OptimizeFunctionOnNextCall(lazyDeopt); |
+ lazyDeopt(); |
+ lazyDeopt(true); |
+ lazyDeopt(); |
+})(); |
+ |
+// Lazy deopt from runtime call from inlined callback function. |
+(function() { |
+ var result = 0; |
+ var lazyDeopt = function(deopt) { |
+ var sum = function(v,i,o) { |
+ result += i; |
+ if (i == 13 && deopt) { |
+ %DeoptimizeNow(); |
+ } |
+ } |
+ b.forEach(sum); |
+ } |
+ lazyDeopt(); |
+ lazyDeopt(); |
+ %OptimizeFunctionOnNextCall(lazyDeopt); |
+ lazyDeopt(); |
+ lazyDeopt(true); |
+ lazyDeopt(); |
+ assertEquals(1500, result); |
+})(); |
+ |
+// Lazy deopt from runtime call from non-inline callback function. |
+(function() { |
+ var result = 0; |
+ var lazyDeopt = function(deopt) { |
+ var sum = function(v,i,o) { |
+ result += i; |
+ if (i == 13 && deopt) { |
+ %DeoptimizeNow(); |
+ } |
+ }; |
+ %NeverOptimizeFunction(sum); |
+ b.forEach(sum); |
+ } |
+ lazyDeopt(); |
+ lazyDeopt(); |
+ %OptimizeFunctionOnNextCall(lazyDeopt); |
+ lazyDeopt(); |
+ lazyDeopt(true); |
+ lazyDeopt(); |
+ assertEquals(1500, result); |
+})(); |
+ |
+(function() { |
+ var result = 0; |
+ var lazyDeopt = function(deopt) { |
+ var sum = function(v,i,o) { |
+ result += i; |
+ if (i == 13 && deopt) { |
+ %DeoptimizeNow(); |
+ gc(); |
+ gc(); |
+ gc(); |
+ } |
+ } |
+ c.forEach(sum); |
+ } |
+ lazyDeopt(); |
+ lazyDeopt(); |
+ %OptimizeFunctionOnNextCall(lazyDeopt); |
+ lazyDeopt(); |
+ lazyDeopt(true); |
+ lazyDeopt(); |
+ assertEquals(1500, result); |
+})(); |
+ |
+// Call to a.forEach is done inside a try-catch block and the callback function |
+// being called actually throws. |
+(function() { |
+ var caught = false; |
+ var result = 0; |
+ var lazyDeopt = function(deopt) { |
+ var sum = function(v,i,o) { |
+ result += i; |
+ if (i == 1 && deopt) { |
+ throw("a"); |
+ } |
+ } |
+ try { |
+ c.forEach(sum); |
+ } catch (e) { |
+ caught = true; |
+ } |
+ } |
+ lazyDeopt(); |
+ lazyDeopt(); |
+ %OptimizeFunctionOnNextCall(lazyDeopt); |
+ lazyDeopt(); |
+ assertDoesNotThrow(lazyDeopt.bind(this, true)); |
+ assertTrue(caught); |
+ lazyDeopt(); |
+})(); |
+ |
+// Call to a.forEach is done inside a try-catch block and the callback function |
+// being called actually throws, but the callback is not inlined. |
+(function() { |
+ var caught = false; |
+ var result = 0; |
+ var lazyDeopt = function(deopt) { |
+ var sum = function(v,i,o) { |
+ result += i; |
+ if (i == 1 && deopt) { |
+ throw("a"); |
+ } |
+ }; |
+ %NeverOptimizeFunction(sum); |
+ try { |
+ c.forEach(sum); |
+ } catch (e) { |
+ caught = true; |
+ } |
+ } |
+ lazyDeopt(); |
+ lazyDeopt(); |
+ %OptimizeFunctionOnNextCall(lazyDeopt); |
+ lazyDeopt(); |
+ assertDoesNotThrow(lazyDeopt.bind(this, true)); |
+ assertTrue(caught); |
+ lazyDeopt(); |
+})(); |
+ |
+(function() { |
+ var re = /Array\.forEach/; |
+ var lazyDeopt = function(deopt) { |
+ var b = [1,2,3]; |
+ var result = 0; |
+ var sum = function(v,i,o) { |
+ result += v; |
+ if (i == 1) { |
+ var e = new Error(); |
+ assertTrue(re.exec(e.stack) !== null); |
+ } |
+ }; |
+ var o = [1,2,3]; |
+ b.forEach(sum); |
+ } |
+ lazyDeopt(); |
+ lazyDeopt(); |
+ %OptimizeFunctionOnNextCall(lazyDeopt); |
+ lazyDeopt(); |
+})(); |
+ |
+(function() { |
+ var re = /Array\.forEach/; |
+ var lazyDeopt = function(deopt) { |
+ var b = [1,2,3]; |
+ var result = 0; |
+ var sum = function(v,i,o) { |
+ result += v; |
+ if (i == 1) { |
+ var e = new Error(); |
+ assertTrue(re.exec(e.stack) !== null); |
+ } |
+ }; |
+ %NeverOptimizeFunction(sum); |
+ var o = [1,2,3]; |
+ b.forEach(sum); |
+ } |
+ lazyDeopt(); |
+ lazyDeopt(); |
+ %OptimizeFunctionOnNextCall(lazyDeopt); |
+ lazyDeopt(); |
+})(); |
+ |
+(function() { |
+ var re = /Array\.forEach/; |
+ var lazyDeopt = function(deopt) { |
+ var b = [1,2,3]; |
+ var result = 0; |
+ var sum = function(v,i,o) { |
+ result += v; |
+ if (i == 1) { |
+ %DeoptimizeNow(); |
+ } else if (i == 2) { |
+ var e = new Error(); |
+ assertTrue(re.exec(e.stack) !== null); |
+ } |
+ }; |
+ var o = [1,2,3]; |
+ b.forEach(sum); |
+ } |
+ lazyDeopt(); |
+ lazyDeopt(); |
+ %OptimizeFunctionOnNextCall(lazyDeopt); |
+ lazyDeopt(); |
+})(); |
+ |
+(function() { |
+ var re = /Array\.forEach/; |
+ var a = [1,2,3]; |
+ var result = 0; |
+ var lazyDeopt = function() { |
+ var sum = function(v,i,o) { |
+ result += i; |
+ if (i == 1) { |
+ %DeoptimizeFunction(lazyDeopt); |
+ throw new Error(); |
+ } |
+ }; |
+ a.forEach(sum); |
+ } |
+ assertThrows(() => lazyDeopt()); |
+ assertThrows(() => lazyDeopt()); |
+ try { |
+ lazyDeopt(); |
+ } catch (e) { |
+ assertTrue(re.exec(e.stack) !== null); |
+ } |
+ %OptimizeFunctionOnNextCall(lazyDeopt); |
+ try { |
+ lazyDeopt(); |
+ } catch (e) { |
+ assertTrue(re.exec(e.stack) !== null); |
+ } |
+})(); |