// 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. // The GetIterator bytecode is used to implement a part of the iterator // protocol (https://tc39.es/ecma262/#sec-getiterator). Here, the // bytecode performs multiple operations including some that have side-effects // and may deoptimize eagerly or lazily. // This test ensures the lazy deoptimization is handled correctly. // Flags: --allow-natives-syntax --no-always-opt var triggerLazyDeopt = false var iteratorCount = 0; var iteratorAfterLazyDeoptCount = 0; var getIteratorCount = 0; function foo(obj) { // The following for-of loop uses the iterator protocol to iterate // over the 'obj'. // The GetIterator bytecode invovlves 2 steps: // 1. method = GetMethod(obj, @@iterator) // 2. iterator = Call(method, obj). for(var x of obj){} } // This iterator gets inlined when the 'foo' function is JIT compiled for // the first time. var iterator = function() { iteratorCount++; return { next: function() { return { done: true }; } } } iteratorAfterLazyDeopt = function() { iteratorAfterLazyDeoptCount++; return { next: function() { return { done: true }; } } } // Here, retrieval of function at @@iterator has side effect (increments the // 'getIteratorCount').The lazy deoptimization is triggerred by setting the // 'triggerLazyDeopt' to true after the count is incremented. Now the deopt // cannot resume at the beginning of the bytecode because it would end up in // incrementing the count again. let y = { get [Symbol.iterator] () { getIteratorCount++; if(triggerLazyDeopt) { %DeoptimizeFunction(foo); iterator = iteratorAfterLazyDeopt } return iterator; } }; %PrepareFunctionForOptimization(foo); foo(y); foo(y); %OptimizeFunctionOnNextCall(foo); triggerLazyDeopt = true; foo(y); assertUnoptimized(foo); assertEquals(getIteratorCount, 3); assertEquals(iteratorCount, 2); assertEquals(iteratorAfterLazyDeoptCount, 1);