// 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: --expose-debug-as debug --allow-natives-syntax

// Test debug event catch prediction for thrown exceptions. We distinguish
// between "caught" and "uncaught" based on the following assumptions:
//  1) try-catch   : Will always catch the exception.
//  2) try-finally : Will always re-throw the exception.

Debug = debug.Debug;

var log = [];

function listener(event, exec_state, event_data, data) {
  try {
    if (event == Debug.DebugEvent.Exception) {
      log.push([event_data.exception(), event_data.uncaught()]);
    }
  } catch (e) {
    %AbortJS(e + "\n" + e.stack);
  }
}

Debug.setBreakOnException();
Debug.setListener(listener);

(function TryCatch() {
  log = [];  // Clear log.
  function f(a) {
    try {
      throw "boom" + a;
    } catch(e) {
      return e;
    }
  }
  assertEquals("boom1", f(1));
  assertEquals("boom2", f(2));
  %OptimizeFunctionOnNextCall(f);
  assertEquals("boom3", f(3));
  print("Collect log:", log);
  assertEquals([["boom1",false], ["boom2",false], ["boom3",false]], log);
})();

(function TryFinally() {
  log = [];  // Clear log.
  function f(a) {
    try {
      throw "baem" + a;
    } finally {
      return a + 10;
    }
  }
  assertEquals(11, f(1));
  assertEquals(12, f(2));
  %OptimizeFunctionOnNextCall(f);
  assertEquals(13, f(3));
  print("Collect log:", log);
  assertEquals([["baem1",true], ["baem2",true], ["baem3",true]], log);
})();

(function TryCatchFinally() {
  log = [];  // Clear log.
  function f(a) {
    try {
      throw "wosh" + a;
    } catch(e) {
      return e + a;
    } finally {
      // Nothing.
    }
  }
  assertEquals("wosh11", f(1));
  assertEquals("wosh22", f(2));
  %OptimizeFunctionOnNextCall(f);
  assertEquals("wosh33", f(3));
  print("Collect log:", log);
  assertEquals([["wosh1",false], ["wosh2",false], ["wosh3",false]], log);
})();

(function TryCatchNestedFinally() {
  log = [];  // Clear log.
  function f(a) {
    try {
      try {
        throw "bang" + a;
      } finally {
        // Nothing.
      }
    } catch(e) {
      return e + a;
    }
  }
  assertEquals("bang11", f(1));
  assertEquals("bang22", f(2));
  %OptimizeFunctionOnNextCall(f);
  assertEquals("bang33", f(3));
  print("Collect log:", log);
  assertEquals([["bang1",false], ["bang2",false], ["bang3",false]], log);
})();

(function TryFinallyNestedCatch() {
  log = [];  // Clear log.
  function f(a) {
    try {
      try {
        throw "peng" + a;
      } catch(e) {
        return e
      }
    } finally {
      return a + 10;
    }
  }
  assertEquals(11, f(1));
  assertEquals(12, f(2));
  %OptimizeFunctionOnNextCall(f);
  assertEquals(13, f(3));
  print("Collect log:", log);
  assertEquals([["peng1",false], ["peng2",false], ["peng3",false]], log);
})();

(function TryFinallyNestedFinally() {
  log = [];  // Clear log.
  function f(a) {
    try {
      try {
        throw "oops" + a;
      } finally {
        // Nothing.
      }
    } finally {
      return a + 10;
    }
  }
  assertEquals(11, f(1));
  assertEquals(12, f(2));
  %OptimizeFunctionOnNextCall(f);
  assertEquals(13, f(3));
  print("Collect log:", log);
  assertEquals([["oops1",true], ["oops2",true], ["oops3",true]], log);
})();
