"use strict";

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

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

var _get = require("./get.js");

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

var _has = require("./has.js");

var _call = require("./call.js");

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

var _is = require("./is.js");

var _abstract = require("./abstract.js");

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

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

var _invariant2 = _interopRequireDefault(_invariant);

var _babelTypes = require("babel-types");

var t = _interopRequireWildcard(_babelTypes);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

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

function modulo(x, y) {
  return x < 0 ? x % y + y : x % y;
} /**
   * 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.
   */

class ToImplementation {
  constructor() {
    this.ElementConv = {
      Int8: this.ToInt8.bind(this),
      Int16: this.ToInt16.bind(this),
      Int32: this.ToInt32.bind(this),
      Uint8: this.ToUint8.bind(this),
      Uint16: this.ToUint16.bind(this),
      Uint32: this.ToUint32.bind(this),
      Uint8Clamped: this.ToUint8Clamp.bind(this)
    };
  }

  // ECMA262 7.1.5
  ToInt32(realm, argument) {
    // 1. Let number be ? ToNumber(argument).
    let number = this.ToNumber(realm, argument);

    // 2. If number is NaN, +0, -0, +∞, or -∞, return +0.
    if (isNaN(number) || number === 0 || !isFinite(number)) return +0;

    // 3. Let int be the mathematical value that is the same sign as number and whose magnitude is floor(abs(number)).
    let int = number < 0 ? -Math.floor(Math.abs(number)) : Math.floor(Math.abs(number));

    // 4. Let int16bit be int modulo 2^32.
    let int32bit = modulo(int, Math.pow(2, 32));

    // 5. If int32bit ≥ 2^31, return int32bit - 2^32; otherwise return int32bit.
    return int32bit >= Math.pow(2, 31) ? int32bit - Math.pow(2, 32) : int32bit;
  }

  // ECMA262 7.1.6
  ToUint32(realm, argument) {
    // 1. Let number be ? ToNumber(argument).
    let number = this.ToNumber(realm, argument);

    // 2. If number is NaN, +0, -0, +∞, or -∞, return +0.
    if (isNaN(number) || number === 0 || !isFinite(number)) return +0;

    // 3. Let int be the mathematical value that is the same sign as number and whose magnitude is floor(abs(number)).
    let int = number < 0 ? -Math.floor(Math.abs(number)) : Math.floor(Math.abs(number));

    // 4. Let int16bit be int modulo 2^32.
    let int32bit = modulo(int, Math.pow(2, 32));

    // 5. Return int32bit.
    return int32bit;
  }

  // ECMA262 7.1.7
  ToInt16(realm, argument) {
    // 1. Let number be ? ToNumber(argument).
    let number = this.ToNumber(realm, argument);

    // 2. If number is NaN, +0, -0, +∞, or -∞, return +0.
    if (isNaN(number) || number === 0 || !isFinite(number)) return +0;

    // 3. Let int be the mathematical value that is the same sign as number and whose magnitude is floor(abs(number)).
    let int = number < 0 ? -Math.floor(Math.abs(number)) : Math.floor(Math.abs(number));

    // 4. Let int16bit be int modulo 2^16.
    let int16bit = modulo(int, Math.pow(2, 16));

    // 5. If int16bit ≥ 2^15, return int16bit - 2^16; otherwise return int16bit.
    return int16bit >= Math.pow(2, 15) ? int16bit - Math.pow(2, 16) : int16bit;
  }

  // ECMA262 7.1.8
  ToUint16(realm, argument) {
    // 1. Let number be ? ToNumber(argument).
    let number = this.ToNumber(realm, argument);

    // 2. If number is NaN, +0, -0, +∞, or -∞, return +0.
    if (isNaN(number) || number === 0 || !isFinite(number)) return +0;

    // 3. Let int be the mathematical value that is the same sign as number and whose magnitude is floor(abs(number)).
    let int = number < 0 ? -Math.floor(Math.abs(number)) : Math.floor(Math.abs(number));

    // 4. Let int16bit be int modulo 2^16.
    let int16bit = modulo(int, Math.pow(2, 16));

    // 5. Return int16bit.
    return int16bit;
  }

  // ECMA262 7.1.9
  ToInt8(realm, argument) {
    // 1. Let number be ? ToNumber(argument).
    let number = this.ToNumber(realm, argument);

    // 2. If number is NaN, +0, -0, +∞, or -∞, return +0.
    if (isNaN(number) || number === 0 || !isFinite(number)) return +0;

    // 3. Let int be the mathematical value that is the same sign as number and whose magnitude is floor(abs(number)).
    let int = number < 0 ? -Math.floor(Math.abs(number)) : Math.floor(Math.abs(number));

    // 4. Let int8bit be int modulo 2^8.
    let int8bit = modulo(int, Math.pow(2, 8));

    // 5. If int8bit ≥ 2^7, return int8bit - 2^8; otherwise return int8bit.
    return int8bit >= Math.pow(2, 7) ? int8bit - Math.pow(2, 8) : int8bit;
  }

  // ECMA262 7.1.10
  ToUint8(realm, argument) {
    // 1. Let number be ? ToNumber(argument).
    let number = this.ToNumber(realm, argument);

    // 2. If number is NaN, +0, -0, +∞, or -∞, return +0.
    if (isNaN(number) || number === 0 || !isFinite(number)) return +0;

    // 3. Let int be the mathematical value that is the same sign as number and whose magnitude is floor(abs(number)).
    let int = number < 0 ? -Math.floor(Math.abs(number)) : Math.floor(Math.abs(number));

    // 4. Let int8bit be int modulo 2^8.
    let int8bit = modulo(int, Math.pow(2, 8));

    // 5. Return int8bit.
    return int8bit;
  }

  // ECMA262 7.1.11
  ToUint8Clamp(realm, argument) {
    // 1. Let number be ? ToNumber(argument).
    let number = this.ToNumber(realm, argument);

    // 2. If number is NaN, return +0.
    if (isNaN(number)) return +0;

    // 3. If number ≤ 0, return +0.
    if (number <= 0) return +0;

    // 4. If number ≥ 255, return 255.
    if (number >= 255) return 255;

    // 5. Let f be floor(number).
    let f = Math.floor(number);

    // 6. If f + 0.5 < number, return f + 1.
    if (f + 0.5 < number) return f + 1;

    // 7. If number < f + 0.5, return f.
    if (number < f + 0.5) return f;

    // 8. If f is odd, return f + 1.
    if (f % 2 === 1) return f + 1;

    // 9. Return f.
    return f;
  }

  // ECMA262 19.3.3.1
  thisBooleanValue(realm, value) {
    // 1. If Type(value) is Boolean, return value.
    if (value instanceof _index2.BooleanValue) return value;

    // 2. If Type(value) is Object and value has a [[BooleanData]] internal slot, then
    if (value instanceof _index2.ObjectValue && value.$BooleanData) {
      const booleanData = value.$BooleanData.throwIfNotConcreteBoolean();
      // a. Assert: value's [[BooleanData]] internal slot is a Boolean value.
      (0, _invariant2.default)(booleanData instanceof _index2.BooleanValue, "expected boolean data internal slot to be a boolean value");

      // b. Return the value of value's [[BooleanData]] internal slot.
      return booleanData;
    }

    value.throwIfNotConcrete();

    // 3. Throw a TypeError exception.
    throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
  }

  // ECMA262 20.1.3
  thisNumberValue(realm, value) {
    // 1. If Type(value) is Number, return value.
    if (value instanceof _index2.NumberValue) return value;

    // 2. If Type(value) is Object and value has a [[NumberData]] internal slot, then
    if (value instanceof _index2.ObjectValue && value.$NumberData) {
      const numberData = value.$NumberData.throwIfNotConcreteNumber();
      // a. Assert: value's [[NumberData]] internal slot is a Number value.
      (0, _invariant2.default)(numberData instanceof _index2.NumberValue, "expected number data internal slot to be a number value");

      // b. Return the value of value's [[NumberData]] internal slot.
      return numberData;
    }

    value = value.throwIfNotConcrete();

    // 3. Throw a TypeError exception.
    throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
  }

  // ECMA262 21.1.3
  thisStringValue(realm, value) {
    // 1. If Type(value) is String, return value.
    if (value instanceof _index2.StringValue) return value;

    // 2. If Type(value) is Object and value has a [[StringData]] internal slot, then
    if (value instanceof _index2.ObjectValue && value.$StringData) {
      const stringData = value.$StringData.throwIfNotConcreteString();
      // a. Assert: value's [[StringData]] internal slot is a String value.
      (0, _invariant2.default)(stringData instanceof _index2.StringValue, "expected string data internal slot to be a string value");

      // b. Return the value of value's [[StringData]] internal slot.
      return stringData;
    }

    value = value.throwIfNotConcrete();

    // 3. Throw a TypeError exception.
    throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
  }

  // ECMA262 6.2.4.5
  ToPropertyDescriptor(realm, Obj) {
    Obj = Obj.throwIfNotConcrete();

    // 1. If Type(Obj) is not Object, throw a TypeError exception.
    if (!(Obj instanceof _index2.ObjectValue)) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
    }

    // 2. Let desc be a new Property Descriptor that initially has no fields.
    let desc = {};

    // 3. Let hasEnumerable be ? HasProperty(Obj, "enumerable").
    let hasEnumerable = (0, _has.HasProperty)(realm, Obj, "enumerable");

    // 4. If hasEnumerable is true, then
    if (hasEnumerable === true) {
      // a. Let enum be ToBoolean(? Get(Obj, "enumerable")).
      let enu = this.ToBooleanPartial(realm, (0, _get.Get)(realm, Obj, "enumerable"));

      // b. Set the [[Enumerable]] field of desc to enum.
      desc.enumerable = enu === true;
    }

    // 5. Let hasConfigurable be ? HasProperty(Obj, "configurable").
    let hasConfigurable = (0, _has.HasProperty)(realm, Obj, "configurable");

    // 6. If hasConfigurable is true, then
    if (hasConfigurable === true) {
      // a. Let conf be ToBoolean(? Get(Obj, "configurable")).
      let conf = this.ToBooleanPartial(realm, (0, _get.Get)(realm, Obj, "configurable"));

      // b. Set the [[Configurable]] field of desc to conf.
      desc.configurable = conf === true;
    }

    // 7. Let hasValue be ? HasProperty(Obj, "value").
    let hasValue = (0, _has.HasProperty)(realm, Obj, "value");

    // 8. If hasValue is true, then
    if (hasValue === true) {
      // a. Let value be ? Get(Obj, "value").
      let value = (0, _get.Get)(realm, Obj, "value");

      // b. Set the [[Value]] field of desc to value.
      desc.value = value;
    }

    // 9. Let hasWritable be ? HasProperty(Obj, "writable").
    let hasWritable = (0, _has.HasProperty)(realm, Obj, "writable");

    // 10. If hasWritable is true, then
    if (hasWritable === true) {
      // a. Let writable be ToBoolean(? Get(Obj, "writable")).
      let writable = this.ToBooleanPartial(realm, (0, _get.Get)(realm, Obj, "writable"));

      // b. Set the [[Writable]] field of desc to writable.
      desc.writable = writable === true;
    }

    // 11. Let hasGet be ? HasProperty(Obj, "get").
    let hasGet = (0, _has.HasProperty)(realm, Obj, "get");

    // 12. If hasGet is true, then
    if (hasGet === true) {
      // a. Let getter be ? Get(Obj, "get").
      let getter = (0, _get.Get)(realm, Obj, "get");

      // b. If IsCallable(getter) is false and getter is not undefined, throw a TypeError exception.
      if ((0, _is.IsCallable)(realm, getter) === false && !getter.mightBeUndefined()) {
        throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
      }
      getter.throwIfNotConcrete();

      // c. Set the [[Get]] field of desc to getter.
      desc.get = getter;
    }

    // 13. Let hasSet be ? HasProperty(Obj, "set").
    let hasSet = (0, _has.HasProperty)(realm, Obj, "set");

    // 14. If hasSet is true, then
    if (hasSet === true) {
      // a. Let setter be ? Get(Obj, "set").
      let setter = (0, _get.Get)(realm, Obj, "set");

      // b. If IsCallable(setter) is false and setter is not undefined, throw a TypeError exception.
      if ((0, _is.IsCallable)(realm, setter) === false && !setter.mightBeUndefined()) {
        throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
      }
      setter.throwIfNotConcrete();

      // c. Set the [[Set]] field of desc to setter.
      desc.set = setter;
    }

    // 15. If either desc.[[Get]] or desc.[[Set]] is present, then
    if (desc.get || desc.set) {
      // a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception.
      if ("value" in desc || "writable" in desc) {
        throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
      }
    }

    // 16. Return desc.
    return desc;
  }

  // ECMA262 7.1.13
  ToObject(realm, arg) {
    if (arg instanceof _index2.UndefinedValue) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
    } else if (arg instanceof _index2.NullValue) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
    } else if (arg instanceof _index2.BooleanValue) {
      let obj = new _index2.ObjectValue(realm, realm.intrinsics.BooleanPrototype);
      obj.$BooleanData = arg;
      return obj;
    } else if (arg instanceof _index2.NumberValue) {
      let obj = new _index2.ObjectValue(realm, realm.intrinsics.NumberPrototype);
      obj.$NumberData = arg;
      return obj;
    } else if (arg instanceof _index2.StringValue) {
      let obj = _singletons.Create.StringCreate(realm, arg, realm.intrinsics.StringPrototype);
      return obj;
    } else if (arg instanceof _index2.SymbolValue) {
      let obj = new _index2.ObjectValue(realm, realm.intrinsics.SymbolPrototype);
      obj.$SymbolData = arg;
      return obj;
    } else if (arg instanceof _index2.ObjectValue) {
      return arg;
    }
    (0, _invariant2.default)(false);
  }

  _WrapAbstractInObject(realm, arg) {
    let obj;
    switch (arg.types.getType()) {
      case _index2.IntegralValue:
      case _index2.NumberValue:
        obj = new _index2.ObjectValue(realm, realm.intrinsics.NumberPrototype);
        obj.$NumberData = arg;
        break;

      case _index2.StringValue:
        obj = new _index2.ObjectValue(realm, realm.intrinsics.StringPrototype);
        obj.$StringData = arg;
        break;

      case _index2.BooleanValue:
        obj = new _index2.ObjectValue(realm, realm.intrinsics.BooleanPrototype);
        obj.$BooleanData = arg;
        break;

      case _index2.SymbolValue:
        obj = new _index2.ObjectValue(realm, realm.intrinsics.SymbolPrototype);
        obj.$SymbolData = arg;
        break;

      case _index2.UndefinedValue:
      case _index2.NullValue:
        throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);

      default:
        if (realm.isInPureScope()) {
          // Create a placeholder value to represent the ObjectValue that we would've
          // received, but this object should never leak so as an optimization we will
          // let operations on top of this object force the ToObject operations instead.
          obj = _index2.AbstractValue.createFromType(realm, _index2.ObjectValue, "implicit conversion to object");
          (0, _invariant2.default)(obj instanceof _index2.AbstractObjectValue);
          obj.args = [arg];
        } else {
          obj = arg.throwIfNotConcreteObject();
        }
        break;
    }
    return obj;
  }

  ToObjectPartial(realm, arg) {
    if (arg instanceof _index2.AbstractObjectValue) return arg;
    if (arg instanceof _index2.AbstractValue) {
      return this._WrapAbstractInObject(realm, arg);
    }
    arg = arg.throwIfNotConcrete();
    return this.ToObject(realm, arg);
  }

  // ECMA262 7.1.15
  ToLength(realm, argument) {
    // Let len be ? ToInteger(argument).
    let len = this.ToInteger(realm, argument);

    // If len ≤ +0, return +0.
    if (len <= 0) return +0;

    // If len is +∞, return 2^53-1.
    if (len === +Infinity) return Math.pow(2, 53) - 1;

    // Return min(len, 2^53-1).
    return Math.min(len, Math.pow(2, 53) - 1);
  }

  // ECMA262 7.1.4
  ToInteger(realm, argument) {
    // 1. Let number be ? ToNumber(argument).
    let number = this.ToNumber(realm, argument);

    // 2. If number is NaN, return +0.
    if (isNaN(number)) return +0;

    // 3. If number is +0, -0, +∞, or -∞, return number.
    if (!isFinite(number) || number === 0) return number;

    // 4. Return the number value that is the same sign as number and whose magnitude is floor(abs(number)).
    return number < 0 ? -Math.floor(Math.abs(number)) : Math.floor(Math.abs(number));
  }

  // ECMA262 7.1.17
  ToIndex(realm, value) {
    let index;
    // 1. If value is undefined, then
    if (value instanceof _index2.UndefinedValue) {
      // a. Let index be 0.
      index = 0;
    } else {
      // 2. Else,
      // a. Let integerIndex be ? ToInteger(value).
      let integerIndex = this.ToInteger(realm, value);

      // b. If integerIndex < 0, throw a RangeError exception.
      if (integerIndex < 0) {
        throw realm.createErrorThrowCompletion(realm.intrinsics.RangeError, "integerIndex < 0");
      }

      // c. Let index be ! ToLength(integerIndex).
      index = this.ToLength(realm, integerIndex);

      // d. If SameValueZero(integerIndex, index) is false, throw a RangeError exception.
      if ((0, _abstract.SameValueZero)(realm, new _index2.NumberValue(realm, integerIndex), new _index2.NumberValue(realm, index)) === false) {
        throw realm.createErrorThrowCompletion(realm.intrinsics.RangeError, "integerIndex < 0");
      }
    }
    // 3. Return index.
    return index;
  }

  ToIndexPartial(realm, value) {
    return this.ToIndex(realm, typeof value === "number" ? value : value.throwIfNotConcrete());
  }

  ToNumber(realm, val) {
    const num = this.ToNumberOrAbstract(realm, val);
    if (typeof num !== "number") {
      _index2.AbstractValue.reportIntrospectionError(num);
      throw new _errors.FatalError();
    }
    return num;
  }

  // ECMA262 7.1.3
  ToNumberOrAbstract(realm, val) {
    if (typeof val === "number") {
      return val;
    } else if (val instanceof _index2.AbstractValue) {
      return val;
    } else if (val instanceof _index2.UndefinedValue) {
      return NaN;
    } else if (val instanceof _index2.NullValue) {
      return +0;
    } else if (val instanceof _index2.ObjectValue) {
      let prim = this.ToPrimitiveOrAbstract(realm, val, "number");
      return this.ToNumberOrAbstract(realm, prim);
    } else if (val instanceof _index2.BooleanValue) {
      if (val.value === true) {
        return 1;
      } else {
        // `val.value === false`
        return 0;
      }
    } else if (val instanceof _index2.NumberValue) {
      return val.value;
    } else if (val instanceof _index2.StringValue) {
      return Number(val.value);
    } else if (val instanceof _index2.SymbolValue) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
    } else {
      (0, _invariant2.default)(false, "unexpected type of value");
    }
  }

  IsToNumberPure(realm, val) {
    if (val instanceof _index2.Value) {
      if (this.IsToPrimitivePure(realm, val)) {
        let type = val.getType();
        return type !== _index2.SymbolValue && type !== _index2.PrimitiveValue && type !== _index2.Value;
      }
      return false;
    }
    return true;
  }

  // ECMA262 7.1.1
  ToPrimitive(realm, input, hint) {
    return this.ToPrimitiveOrAbstract(realm, input, hint).throwIfNotConcretePrimitive();
  }

  ToPrimitiveOrAbstract(realm, input, hint) {
    if (input instanceof _index2.PrimitiveValue) {
      return input;
    }

    // When Type(input) is Object, the following steps are taken
    (0, _invariant2.default)(input instanceof _index2.ObjectValue, "expected an object");

    // 1. If PreferredType was not passed, let hint be "default".
    hint = hint || "default";

    // Following two steps are redundant since we just pass string hints.
    // 2. Else if PreferredType is hint String, let hint be "string".
    // 3. Else PreferredType is hint Number, let hint be "number".

    // 4. Let exoticToPrim be ? GetMethod(input, @@toPrimitive).
    let exoticToPrim = (0, _get.GetMethod)(realm, input, realm.intrinsics.SymbolToPrimitive);

    // 5. If exoticToPrim is not undefined, then
    if (!(exoticToPrim instanceof _index2.UndefinedValue)) {
      // a. Let result be ? Call(exoticToPrim, input, « hint »).
      let result = (0, _call.Call)(realm, exoticToPrim, input, [new _index2.StringValue(realm, hint)]);

      // b. If Type(result) is not Object, return result.
      if (!(result instanceof _index2.ObjectValue)) {
        (0, _invariant2.default)(result instanceof _index2.PrimitiveValue);
        return result;
      }

      // c. Throw a TypeError exception.
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
    }

    // 6. If hint is "default", let hint be "number".
    if (hint === "default") hint = "number";

    // 7. Return ? OrdinaryToPrimitive(input, hint).
    return this.OrdinaryToPrimitiveOrAbstract(realm, input, hint);
  }

  // Returns result type of ToPrimitive if it is pure (terminates, does not throw exception, does not read or write heap), otherwise undefined.
  GetToPrimitivePureResultType(realm, input) {
    let type = input.getType();
    if (input instanceof _index2.PrimitiveValue) return type;
    if (input instanceof _index2.AbstractValue && !input.mightBeObject()) return _index2.PrimitiveValue;
    return undefined;
  }

  IsToPrimitivePure(realm, input) {
    return this.GetToPrimitivePureResultType(realm, input) !== undefined;
  }

  // ECMA262 7.1.1
  OrdinaryToPrimitive(realm, input, hint) {
    return this.OrdinaryToPrimitiveOrAbstract(realm, input, hint).throwIfNotConcretePrimitive();
  }

  OrdinaryToPrimitiveOrAbstract(realm, input, hint) {
    let methodNames;

    // 1. Assert: Type(O) is Object.
    (0, _invariant2.default)(input instanceof _index2.ObjectValue, "Expected object");

    // 2. Assert: Type(hint) is String and its value is either "string" or "number".
    (0, _invariant2.default)(hint === "string" || hint === "number", "Expected string or number hint");

    // 3. If hint is "string", then
    if (hint === "string") {
      // a. Let methodNames be « "toString", "valueOf" ».
      methodNames = ["toString", "valueOf"];
    } else {
      // 4. Else,
      // a. Let methodNames be « "valueOf", "toString" ».
      methodNames = ["valueOf", "toString"];
    }

    // 5. For each name in methodNames in List order, do
    for (let name of methodNames) {
      // a. Let method be ? Get(O, name).
      let method = (0, _get.Get)(realm, input, new _index2.StringValue(realm, name));

      // b. If IsCallable(method) is true, then
      if ((0, _is.IsCallable)(realm, method)) {
        // i. Let result be ? Call(method, O).
        let result = (0, _call.Call)(realm, method, input);
        let resultType = result.getType();

        // ii. If Type(result) is not Object, return result.
        if (resultType === _index2.Value) {
          (0, _invariant2.default)(result instanceof _index2.AbstractValue);
          let error = new _errors.CompilerDiagnostic(`${name} might return either an object or primitive`, realm.currentLocation, "PP0028", "RecoverableError");
          realm.handleError(error);
          throw new _errors.FatalError();
        }
        if (_index2.Value.isTypeCompatibleWith(resultType, _index2.PrimitiveValue)) {
          (0, _invariant2.default)(result instanceof _index2.AbstractValue || result instanceof _index2.PrimitiveValue);
          return result;
        }
      }
    }

    // 6. Throw a TypeError exception.
    throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "can't turn to primitive");
  }

  // ECMA262 7.1.12
  ToString(realm, val) {
    if (typeof val === "string") {
      return val;
    } else if (val instanceof _index2.StringValue) {
      return val.value;
    } else if (val instanceof _index2.NumberValue) {
      return val.value + "";
    } else if (val instanceof _index2.UndefinedValue) {
      return "undefined";
    } else if (val instanceof _index2.NullValue) {
      return "null";
    } else if (val instanceof _index2.SymbolValue) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
    } else if (val instanceof _index2.BooleanValue) {
      return val.value ? "true" : "false";
    } else if (val instanceof _index2.ObjectValue) {
      let primValue = this.ToPrimitive(realm, val, "string");
      return this.ToString(realm, primValue);
    } else {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "unknown value type, can't coerce to string");
    }
  }

  ToStringPartial(realm, val) {
    return this.ToString(realm, typeof val === "string" ? val : val.throwIfNotConcrete());
  }

  ToStringValue(realm, val) {
    if (val.getType() === _index2.StringValue) return val;
    let str;
    if (typeof val === "string") {
      str = val;
    } else if (val instanceof _index2.NumberValue) {
      str = val.value + "";
    } else if (val instanceof _index2.UndefinedValue) {
      str = "undefined";
    } else if (val instanceof _index2.NullValue) {
      str = "null";
    } else if (val instanceof _index2.SymbolValue) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
    } else if (val instanceof _index2.BooleanValue) {
      str = val.value ? "true" : "false";
    } else if (val instanceof _index2.ObjectValue) {
      let primValue = this.ToPrimitiveOrAbstract(realm, val, "string");
      if (primValue.getType() === _index2.StringValue) return primValue;
      str = this.ToStringPartial(realm, primValue);
    } else {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "unknown value type, can't coerce to string");
    }
    return new _index2.StringValue(realm, str);
  }

  ToStringAbstract(realm, value) {
    if (value.mightNotBeString()) {
      // If the property is not a string we need to coerce it.
      let coerceToString = ([p]) => t.binaryExpression("+", t.stringLiteral(""), p);
      let result;
      if (value.mightNotBeNumber() && !value.isSimpleObject()) {
        // If this might be a non-simple object, we need to coerce this at a
        // temporal point since it can have side-effects.
        // We can't rely on comparison to do it later, even if
        // it is non-strict comparison since we'll do multiple
        // comparisons. So we have to be explicit about when this
        // happens.
        result = realm.evaluateWithPossibleThrowCompletion(() => _index2.AbstractValue.createTemporalFromBuildFunction(realm, _index2.StringValue, [value], coerceToString), _index.TypesDomain.topVal, _index.ValuesDomain.topVal);
      } else {
        result = _index2.AbstractValue.createFromBuildFunction(realm, _index2.StringValue, [value], coerceToString);
      }
      (0, _invariant2.default)(result instanceof _index2.AbstractValue);
      return result;
    }
    return value;
  }

  // ECMA262 7.1.2
  ToBoolean(realm, val) {
    if (val instanceof _index2.BooleanValue) {
      return val.value;
    } else if (val instanceof _index2.UndefinedValue) {
      return false;
    } else if (val instanceof _index2.NullValue) {
      return false;
    } else if (val instanceof _index2.NumberValue) {
      return val.value !== 0 && !isNaN(val.value);
    } else if (val instanceof _index2.StringValue) {
      return val.value.length > 0;
    } else if (val instanceof _index2.ObjectValue) {
      return true;
    } else if (val instanceof _index2.SymbolValue) {
      return true;
    } else {
      (0, _invariant2.default)(!(val instanceof _index2.AbstractValue));
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "unknown value type, can't coerce to a boolean");
    }
  }

  ToBooleanPartial(realm, val) {
    if (!val.mightNotBeObject()) return true;
    return this.ToBoolean(realm, val.throwIfNotConcrete());
  }

  // ECMA262 7.1.14
  ToPropertyKey(realm, arg) /* but not StringValue */{
    // 1. Let key be ? ToPrimitive(argument, hint String).
    let key = this.ToPrimitive(realm, arg, "string");

    // 2. If Type(key) is Symbol, then
    if (key instanceof _index2.SymbolValue) {
      // a. Return key.
      return key;
    }

    // 3. Return ! ToString(key).
    return this.ToString(realm, key);
  }

  ToPropertyKeyPartial(realm, arg) /* but not StringValue */{
    if (arg instanceof _index2.ConcreteValue) return this.ToPropertyKey(realm, arg);
    // if we are in pure scope, we can assume that ToPropertyKey
    // won't cause side-effects even if it's not simple
    if (arg.mightNotBeString() && arg.mightNotBeNumber() && !arg.isSimpleObject() && !realm.isInPureScope()) {
      arg.throwIfNotConcrete();
    }
    (0, _invariant2.default)(arg instanceof _index2.AbstractValue);
    return arg;
  }

  // ECMA262 7.1.16
  CanonicalNumericIndexString(realm, argument) {
    // 1. Assert: Type(argument) is String.
    (0, _invariant2.default)(argument instanceof _index2.StringValue);

    // 2. If argument is "-0", return −0.
    if (argument.value === "-0") return -0;

    // 3. Let n be ToNumber(argument).
    let n = this.ToNumber(realm, argument);

    // 4. If SameValue(ToString(n), argument) is false, return undefined.
    if ((0, _abstract.SameValue)(realm, new _index2.StringValue(realm, this.ToString(realm, new _index2.NumberValue(realm, n))), argument) === false) return undefined;

    // 5. Return n.
    return n;
  }
}
exports.ToImplementation = ToImplementation;
//# sourceMappingURL=to.js.map