"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

exports.default = function (ast, strictCode, env, realm) {
  let wasInPureTryStatement = realm.isInPureTryStatement;
  if (realm.isInPureScope()) {
    // TODO(1264): This is used to issue a warning if we have abstract function calls in here.
    // We might not need it once we have full support for handling potential errors. Even
    // then we might need it to know whether we should bother tracking error handling.
    realm.isInPureTryStatement = true;
  }
  let blockRes;
  try {
    blockRes = env.evaluateCompletionDeref(ast.block, strictCode);
  } finally {
    realm.isInPureTryStatement = wasInPureTryStatement;
  }

  let handlerRes = blockRes;
  let handler = ast.handler;
  if (handler) {
    // The start of the catch handler is a join point where all throw completions come together
    blockRes = _singletons.Functions.incorporateSavedCompletion(realm, blockRes);
    if (blockRes instanceof _completions.ThrowCompletion) {
      handlerRes = env.evaluateCompletionDeref(handler, strictCode, blockRes);
      // Note: The handler may have introduced new forks
    } else if (blockRes instanceof _completions.JoinedAbruptCompletions || blockRes instanceof _completions.PossiblyNormalCompletion) {
      if (blockRes instanceof _completions.PossiblyNormalCompletion) {
        // Nothing has been joined and we are going to keep it that way.
        // The current state may have advanced since the time control forked into the various paths recorded in blockRes.
        // Update the normal path and restore the global state to what it was at the time of the fork.
        let subsequentEffects = realm.getCapturedEffects(blockRes, blockRes.value);
        (0, _invariant2.default)(subsequentEffects !== undefined);
        realm.stopEffectCaptureAndUndoEffects(blockRes);
        _singletons.Join.updatePossiblyNormalCompletionWithSubsequentEffects(realm, blockRes, subsequentEffects);
      }
      // All of the forked threads of control are now joined together and the global state reflects their joint effects
      let handlerEffects = composeNestedThrowEffectsWithHandler(blockRes);
      handlerRes = handlerEffects[0];
      if (handlerRes instanceof _index2.Value) {
        // This can happen if all of the abrupt completions in blockRes were throw completions
        // and if the handler does not introduce any abrupt completions of its own.
        realm.applyEffects(handlerEffects);
        // The global state is now all joined up
      } else {
          // more than thread of control leaves the handler
          // The effects of each thread is tracked in handlerRes
        }
    } else {
        // The handler is not invoked, so just carry on.
      }
  }

  let finalizerRes = handlerRes;
  if (ast.finalizer) {
    // The start of the finalizer is a join point where all threads of control come together.
    // However, we choose to keep the threads unjoined and to apply the finalizer separately to each thread.
    if (blockRes instanceof _completions.PossiblyNormalCompletion || blockRes instanceof _completions.JoinedAbruptCompletions) {
      // The current global state is a the point of the fork that led to blockRes
      // All subsequent effects are kept inside the branches of blockRes.
      let finalizerEffects = composeNestedEffectsWithFinalizer(blockRes);
      finalizerRes = finalizerEffects[0];
      // The result may become abrupt because of the finalizer, but it cannot become normal.
      (0, _invariant2.default)(!(finalizerRes instanceof _index2.Value));
    } else {
      // A single thread of control has produced a normal blockRes and the global state is up to date.
      finalizerRes = env.evaluateCompletion(ast.finalizer, strictCode);
    }
  }

  if (finalizerRes instanceof _completions.AbruptCompletion) throw finalizerRes;
  if (finalizerRes instanceof _completions.PossiblyNormalCompletion) realm.composeWithSavedCompletion(finalizerRes);
  if (handlerRes instanceof _completions.PossiblyNormalCompletion) handlerRes = handlerRes.value;
  if (handlerRes instanceof _index2.Value) return (0, _index.UpdateEmpty)(realm, handlerRes, realm.intrinsics.undefined);
  throw handlerRes;

  // The handler is a potential join point for all throw completions, but is easier to not do the join here because
  // it is tricky to join the joined and composed result of the throw completions with the non exceptional completions.
  // Unfortunately, things are still complicated because the handler may turn abrupt completions into normal
  // completions and the other way around. When this happens the container has to change its type.
  // We do this by call joinEffects to create a new container at every level of the recursion.
  function composeNestedThrowEffectsWithHandler(c, priorEffects = []) {
    let consequent = c.consequent;
    let consequentEffects = c.consequentEffects;
    priorEffects.push(consequentEffects);
    if (consequent instanceof _completions.JoinedAbruptCompletions || consequent instanceof _completions.PossiblyNormalCompletion) {
      consequentEffects = composeNestedThrowEffectsWithHandler(consequent, priorEffects);
    } else if (consequent instanceof _completions.ThrowCompletion) {
      consequentEffects = realm.evaluateForEffects(() => {
        for (let priorEffect of priorEffects) realm.applyEffects(priorEffect);
        (0, _invariant2.default)(ast.handler);
        return env.evaluateCompletionDeref(ast.handler, strictCode, consequent);
      }, undefined, "composeNestedThrowEffectsWithHandler/1");
    }
    priorEffects.pop();
    let alternate = c.alternate;
    let alternateEffects = c.alternateEffects;
    priorEffects.push(alternateEffects);
    if (alternate instanceof _completions.PossiblyNormalCompletion || alternate instanceof _completions.JoinedAbruptCompletions) {
      alternateEffects = composeNestedThrowEffectsWithHandler(alternate, priorEffects);
    } else if (alternate instanceof _completions.ThrowCompletion) {
      alternateEffects = realm.evaluateForEffects(() => {
        for (let priorEffect of priorEffects) realm.applyEffects(priorEffect);
        (0, _invariant2.default)(ast.handler);
        return env.evaluateCompletionDeref(ast.handler, strictCode, alternate);
      }, undefined, "composeNestedThrowEffectsWithHandler/2");
    }
    priorEffects.pop();
    return _singletons.Join.joinEffects(realm, c.joinCondition, consequentEffects, alternateEffects);
  }

  // The finalizer is not a join point, so update each path in the completion separately.
  // Things are complicated because the finalizer may turn normal completions into abrupt completions.
  // When this happens the container has to change its type.
  // We do this by call joinEffects to create a new container at every level of the recursion.
  function composeNestedEffectsWithFinalizer(c, priorEffects = []) {
    let consequent = c.consequent;
    let consequentEffects = c.consequentEffects;
    priorEffects.push(consequentEffects);
    if (consequent instanceof _completions.JoinedAbruptCompletions || consequent instanceof _completions.PossiblyNormalCompletion) {
      consequentEffects = composeNestedThrowEffectsWithHandler(consequent, priorEffects);
    } else {
      consequentEffects = realm.evaluateForEffects(() => {
        for (let priorEffect of priorEffects) realm.applyEffects(priorEffect);
        (0, _invariant2.default)(ast.finalizer);
        return env.evaluateCompletionDeref(ast.finalizer, strictCode);
      }, undefined, "composeNestedEffectsWithFinalizer/1");
      if (!(consequentEffects[0] instanceof _completions.AbruptCompletion)) consequentEffects[0] = consequent;
    }
    priorEffects.pop();
    let alternate = c.alternate;
    let alternateEffects = c.alternateEffects;
    priorEffects.push(alternateEffects);
    if (alternate instanceof _completions.PossiblyNormalCompletion || alternate instanceof _completions.JoinedAbruptCompletions) {
      alternateEffects = composeNestedThrowEffectsWithHandler(alternate, priorEffects);
    } else {
      alternateEffects = realm.evaluateForEffects(() => {
        for (let priorEffect of priorEffects) realm.applyEffects(priorEffect);
        (0, _invariant2.default)(ast.finalizer);
        return env.evaluateCompletionDeref(ast.finalizer, strictCode);
      }, undefined, "composeNestedThrowEffectsWithHandler");
      if (!(alternateEffects[0] instanceof _completions.AbruptCompletion)) alternateEffects[0] = alternate;
    }
    priorEffects.pop();
    return _singletons.Join.joinEffects(realm, c.joinCondition, consequentEffects, alternateEffects);
  }
};

require("../environment.js");

var _completions = require("../completions.js");

var _index = require("../methods/index.js");

var _singletons = require("../singletons.js");

var _index2 = require("../values/index.js");

var _invariant = require("../invariant.js");

var _invariant2 = _interopRequireDefault(_invariant);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
//# sourceMappingURL=TryStatement.js.map