"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.parseTypeNameOrTemplate = parseTypeNameOrTemplate;
exports.createAbstractArgument = createAbstractArgument;
exports.createAbstract = createAbstract;

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

var _builder = require("../../utils/builder.js");

var _builder2 = _interopRequireDefault(_builder);

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

var _Error = require("../ecma262/Error.js");

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

var _AbstractObjectValue = require("../../values/AbstractObjectValue");

var _AbstractObjectValue2 = _interopRequireDefault(_AbstractObjectValue);

var _errors = require("../../errors.js");

var _singletons2 = require("../../singletons");

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

var _invariant2 = _interopRequireDefault(_invariant);

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

/**
 * 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.
 */

const throwTemplateSrc = "(function(){throw new global.Error('abstract value defined at ' + A);})()";
const throwTemplate = (0, _builder2.default)(throwTemplateSrc);

function parseTypeNameOrTemplate(realm, typeNameOrTemplate) {
  if (typeNameOrTemplate === undefined || typeNameOrTemplate instanceof _index.UndefinedValue) {
    return { type: _index.Value, template: undefined };
  } else if (typeof typeNameOrTemplate === "string") {
    let type = _singletons2.Utils.getTypeFromName(typeNameOrTemplate);
    if (type === undefined) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "unknown typeNameOrTemplate");
    }
    return { type, template: undefined };
  } else if (typeNameOrTemplate instanceof _index.StringValue) {
    let typeNameString = _singletons.To.ToStringPartial(realm, typeNameOrTemplate);
    let hasFunctionResultType = typeNameString.startsWith(":");
    if (hasFunctionResultType) typeNameString = typeNameString.substring(1);
    let type = _singletons2.Utils.getTypeFromName(typeNameString);
    if (type === undefined) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "unknown typeNameOrTemplate");
    }
    return hasFunctionResultType ? { type: _index.FunctionValue, template: undefined, functionResultType: type } : { type, template: undefined };
  } else if (typeNameOrTemplate instanceof _index.FunctionValue) {
    return { type: _index.FunctionValue, template: typeNameOrTemplate };
  } else if (typeNameOrTemplate instanceof _index.ObjectValue) {
    return { type: _index.ObjectValue, template: typeNameOrTemplate };
  } else {
    throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "typeNameOrTemplate has unsupported type");
  }
}

function createAbstractArgument(realm, name, location, type = _index.Value) {
  if (!realm.useAbstractInterpretation) {
    throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "realm is not partial");
  }

  let locString;
  if (location) locString = (0, _Error.describeLocation)(realm, undefined, undefined, location);
  let locVal = new _index.StringValue(realm, locString || "(unknown location)");
  let kind = _index.AbstractValue.makeKind("abstractCounted", (realm.objectCount++).toString()); // need not be an object, but must be unique
  let result = _index.AbstractValue.createFromTemplate(realm, (0, _builder2.default)(name), type, [locVal], kind);
  result.intrinsicName = name;

  return result;
}

function createAbstract(realm, typeNameOrTemplate, name, ...additionalValues) {
  if (!realm.useAbstractInterpretation) {
    throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "realm is not partial");
  }

  let { type, template, functionResultType } = parseTypeNameOrTemplate(realm, typeNameOrTemplate);

  let result;
  let locString,
      loc = null;
  for (let executionContext of realm.contextStack.slice().reverse()) {
    let caller = executionContext.caller;
    loc = executionContext.loc;
    locString = (0, _Error.describeLocation)(realm, caller ? caller.function : undefined, caller ? caller.lexicalEnvironment : undefined, loc);
    if (locString !== undefined) break;
  }
  if (!name) {
    let locVal = new _index.StringValue(realm, locString || "(unknown location)");
    let kind = _index.AbstractValue.makeKind("abstractCounted", (realm.objectCount++).toString()); // need not be an object, but must be unique
    result = _index.AbstractValue.createFromTemplate(realm, throwTemplate, type, [locVal], kind);
  } else {
    let kind = _index.AbstractValue.makeKind("abstract", name);
    if (!realm.isNameStringUnique(name)) {
      let error = new _errors.CompilerDiagnostic("An abstract value with the same name exists", loc, "PP0019", "FatalError");
      realm.handleError(error);
      throw new _errors.FatalError();
    } else {
      realm.saveNameString(name);
    }
    result = _index.AbstractValue.createFromTemplate(realm, (0, _builder2.default)(name), type, [], kind);
    result.intrinsicName = name;
  }

  if (template) result.values = new _index2.ValuesDomain(new Set([template]));
  if (template && !(template instanceof _index.FunctionValue)) {
    // why exclude functions?
    template.makePartial();
    if (name) realm.rebuildNestedProperties(result, name);
  }
  if (functionResultType) {
    (0, _invariant2.default)(result instanceof _AbstractObjectValue2.default);
    result.functionResultType = functionResultType;
  }

  if (additionalValues.length > 0) result = _index.AbstractValue.createAbstractConcreteUnion(realm, result, ...additionalValues);
  return result;
}
//# sourceMappingURL=utils.js.map