"use strict";

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

exports.default = function (realm) {
  return build("Error", realm, false);
};

exports.describeLocation = describeLocation;
exports.build = build;

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

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

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

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

var _invariant2 = _interopRequireDefault(_invariant);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function describeLocation(realm, callerFn, env, loc) {
  let locString = "";
  let displayName = "";

  // check if we've already encountered the callFn and if so
  // re-use that described location. plus we may get stuck trying
  // to get the location by recursively checking the same fun
  // so this also prevents a stack overflow
  if (callerFn) {
    if (realm.alreadyDescribedLocations.has(callerFn)) {
      return realm.alreadyDescribedLocations.get(callerFn);
    }
    realm.alreadyDescribedLocations.set(callerFn, undefined);

    if (callerFn instanceof _index.NativeFunctionValue) {
      locString = "native";
    }

    let name = callerFn.$Get("name", callerFn);
    if (!name.mightBeUndefined()) displayName = _singletons.To.ToStringPartial(realm, name);else name.throwIfNotConcrete();

    if (env && env.environmentRecord.$NewTarget) displayName = `new ${displayName}`;
  }

  if (!locString) {
    if (loc) {
      locString = `${loc.start.line}:${loc.start.column + 1}`;
      if (loc.source) locString = `${loc.source}:${locString}`;
    } else {
      locString = (loc ? loc.source : undefined) || "unknown";
      if (!displayName) return undefined;
    }
  }

  let location;
  if (displayName) {
    location = `at ${displayName} (${locString})`;
  } else {
    location = `at ${locString}`;
  }
  if (callerFn) {
    realm.alreadyDescribedLocations.set(callerFn, location);
  }
  return location;
} /**
   * Copyright (c) 2017-present, Facebook, Inc.
   * All rights reserved.
   *
   * This source code is licensed under the BSD-style license found in the
   * LICENSE file in the root directory of this source tree. An additional grant
   * of patent rights can be found in the PATENTS file in the same directory.
   */

function buildStack(realm, context) {
  (0, _invariant2.default)(context.$ErrorData);

  let stack = context.$ErrorData.contextStack;
  if (!stack) return realm.intrinsics.undefined;

  let lines = [];
  let header = "";

  header += _singletons.To.ToStringPartial(realm, (0, _index2.Get)(realm, context, "name"));

  let msg = (0, _index2.Get)(realm, context, "message");
  if (!msg.mightBeUndefined()) {
    msg = _singletons.To.ToStringPartial(realm, msg);
    if (msg) header += `: ${msg}`;
  } else {
    msg.throwIfNotConcrete();
  }

  for (let executionContext of stack.reverse()) {
    let caller = executionContext.caller;
    let locString = describeLocation(realm, caller ? caller.function : undefined, caller ? caller.lexicalEnvironment : undefined, executionContext.loc);
    if (locString !== undefined) lines.push(locString);
  }

  return new _index.StringValue(realm, `${header}\n    ${lines.join("\n    ")}`);
}

function build(name, realm, inheritError = true) {
  let func = new _index.NativeFunctionValue(realm, name, name, 1, (context, [message], argLength, NewTarget) => {
    // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
    let newTarget = NewTarget || func;

    // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%ErrorPrototype%", « [[ErrorData]] »).
    let O = _singletons.Create.OrdinaryCreateFromConstructor(realm, newTarget, `${name}Prototype`, { $ErrorData: undefined });
    O.$ErrorData = {
      contextStack: realm.contextStack.slice(1),
      locationData: undefined
    };

    // Build a text description of the stack.
    let stackDesc = {
      value: buildStack(realm, O),
      enumerable: false,
      configurable: true,
      writable: true
    };
    _singletons.Properties.DefinePropertyOrThrow(realm, O, "stack", stackDesc);

    // 3. If message is not undefined, then
    if (!message.mightBeUndefined()) {
      // a. Let msg be ? ToString(message).
      let msg = message.getType() === _index.StringValue ? message : _singletons.To.ToStringValue(realm, message);

      // b. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}.
      let msgDesc = {
        value: msg,
        writable: true,
        enumerable: false,
        configurable: true
      };

      // c. Perform ! DefinePropertyOrThrow(O, "message", msgDesc).
      _singletons.Properties.DefinePropertyOrThrow(realm, O, "message", msgDesc);
    } else {
      message.throwIfNotConcrete();
    }

    // 4. Return O.
    return O;
  });

  if (inheritError) {
    func.$Prototype = realm.intrinsics.Error;
  }

  return func;
}
//# sourceMappingURL=Error.js.map