// Copyright 2016 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

(function LazyDeoptFromTryBlock() {
  function g(dummy) {
    %DeoptimizeFunction(f);
    throw 42;
  }

  function f() {
    var a = 1;
    try {
      var dummy = 2;  // perturb the stack height.
      g(dummy);
    } catch (e) {
      return e + a;
    }
  }

  %PrepareFunctionForOptimization(f);
  assertEquals(43, f());
  assertEquals(43, f());
  %NeverOptimizeFunction(g);
  %OptimizeFunctionOnNextCall(f);
  assertEquals(43, f());
})();


(function LazyDeoptDoublyNestedTryBlock() {
  function g(dummy) {
    %DeoptimizeFunction(f);
    throw 42;
  }

  function f() {
    var b;
    try {
      var a = 1;
      try {
        var dummy = 2;  // perturb the stack height.
        g(dummy);
      } catch (e) {
        b = e + a;
      }
    } catch (e) {
      return 0;
    }
    return b;
  }

  %PrepareFunctionForOptimization(f);
  assertEquals(43, f());
  assertEquals(43, f());
  %NeverOptimizeFunction(g);
  %OptimizeFunctionOnNextCall(f);
  assertEquals(43, f());
})();

(function LazyDeoptInlinedTry() {
  function g(dummy) {
    %DeoptimizeFunction(f);
    %DeoptimizeFunction(h);
    throw 42;
  }

  function h() {
    var a = 1;
    try {
      var dummy = 2;  // perturb the stack height.
      g(dummy);
    } catch (e) {
      b = e + a;
    }
    return b;
  }

  function f() {
    var c = 1;
    return h() + 1;
  }

  %PrepareFunctionForOptimization(f);
  assertEquals(44, f());
  assertEquals(44, f());
  %NeverOptimizeFunction(g);
  %OptimizeFunctionOnNextCall(f);
  assertEquals(44, f());
})();

(function LazyDeoptInlinedIntoTry() {
  function g(c) {
    %DeoptimizeFunction(f);
    %DeoptimizeFunction(h);
    throw c;
  }

  function h(c) {
    return g(c);
  }

  function f() {
    var a = 1;
    try {
      var c = 42;  // perturb the stack height.
      h(c);
    } catch (e) {
      a += e;
    }
    return a;
  }

  %PrepareFunctionForOptimization(f);
  assertEquals(43, f());
  assertEquals(43, f());
  %NeverOptimizeFunction(g);
  %OptimizeFunctionOnNextCall(f);
  assertEquals(43, f());
})();

(function LazyDeoptTryBlockContextCatch() {
  var global = 0;

  function g() {
    %DeoptimizeFunction(f);
    throw "boom!";
  }

  function f(a) {
    var x = a + 23
    try {
      let y = a + 42;
      function capture() { return x + y }
      g();
    } catch(e) {
      global = x;
    }
    return x;
  }
  %PrepareFunctionForOptimization(f);
  assertEquals(23, f(0));
  assertEquals(24, f(1));
  %OptimizeFunctionOnNextCall(f);
  assertEquals(25, f(2));
  assertEquals(25, global);
})();

(function LazyDeoptTryBlockFinally() {
  var global = 0;

  function g() {
    %DeoptimizeFunction(f);
    throw "boom!";
  }

  function f(a) {
    var x = a + 23
    try {
      let y = a + 42;
      function capture() { return x + y }
      g();
    } finally {
      global = x;
    }
    return x;
  }
  %PrepareFunctionForOptimization(f);
  assertThrows(function() { f(0) });
  assertThrows(function() { f(1) });
  %OptimizeFunctionOnNextCall(f);
  assertThrowsEquals(function() { f(2) }, "boom!");
  assertEquals(25, global);
})();

(function LazyDeoptTryCatchContextCatch() {
  var global = 0;

  function g() {
    %DeoptimizeFunction(f);
    throw 5;
  }

  function f(a) {
    var x = a + 23
    try {
      try {
        throw 1;
      } catch(e2) {
        function capture() { return x + y }
        g();
      }
    } catch(e) {
      global = x + e;
    }
    return x;
  }
  %PrepareFunctionForOptimization(f);
  assertEquals(23, f(0));
  assertEquals(24, f(1));
  %OptimizeFunctionOnNextCall(f);
  assertEquals(25, f(2));
  assertEquals(30, global);
})();

(function LazyDeoptTryWithContextCatch() {
  var global = 0;

  function g() {
    %DeoptimizeFunction(f);
    throw 5;
  }

  function f(a) {
    var x = a + 23
    try {
      with ({ y : a + 42 }) {
        function capture() { return x + y }
        g();
      }
    } catch(e) {
      global = x + e;
    }
    return x;
  }
  %PrepareFunctionForOptimization(f);
  assertEquals(23, f(0));
  assertEquals(24, f(1));
  %OptimizeFunctionOnNextCall(f);
  assertEquals(25, f(2));
  assertEquals(30, global);
})();