"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.GetFunctionRealm = GetFunctionRealm;
exports.OrdinaryGet = OrdinaryGet;
exports.GetGlobalObject = GetGlobalObject;
exports.GetSubstitution = GetSubstitution;
exports.GetMethod = GetMethod;
exports.GetPrototypeFromConstructor = GetPrototypeFromConstructor;
exports.Get = Get;
exports.GetV = GetV;
exports.GetThisValue = GetThisValue;
exports.GetNewTarget = GetNewTarget;
exports.GetTemplateObject = GetTemplateObject;

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

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

var _realm = require("../realm.js");

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

var _environment = require("../environment.js");

var _integrity = require("./integrity.js");

var _index2 = require("./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 }; }

// ECMA262 7.3.22
/**
 * 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 GetFunctionRealm(realm, obj) {
  // 1. Assert: obj is a callable object.
  (0, _invariant2.default)((0, _index2.IsCallable)(realm, obj), "expected callable object");

  // ProxyValue moved to realm before
  // https://github.com/facebook/prepack/pull/1351

  // 4. If obj is a Proxy exotic object, then
  if (obj instanceof _index.ProxyValue) {
    // a. If the value of the [[ProxyHandler]] internal slot of obj is null, throw a TypeError exception.
    if (obj.$ProxyHandler instanceof _index.NullValue) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "proxy handler is null");
    }
    (0, _invariant2.default)(obj.$ProxyTarget instanceof _index.ObjectValue);

    // b. Let proxyTarget be the value of obj's [[ProxyTarget]] internal slot.
    let proxyTarget = obj.$ProxyTarget;

    // c. Return ? GetFunctionRealm(proxyTarget).
    return GetFunctionRealm(realm, proxyTarget);
  }

  // 2. If obj has a [[Realm]] internal slot, then
  if (obj.$Realm) {
    // a. Return obj's [[Realm]] internal slot.
    return obj.$Realm;
  }

  // 3. If obj is a Bound Function exotic object, then
  if (obj instanceof _index.BoundFunctionValue) {
    // a. Let target be obj's [[BoundTargetFunction]] internal slot.
    let target = obj.$BoundTargetFunction;

    // b. Return ? GetFunctionRealm(target).
    return GetFunctionRealm(realm, target);
  }

  // 5. Return the current Realm Record.
  return realm;
}

// ECMA262 9.1.8.1
function OrdinaryGet(realm, O, P, Receiver, dataOnly) {
  // 1. Assert: IsPropertyKey(P) is true.
  (0, _invariant2.default)((0, _index2.IsPropertyKey)(realm, P), "expected property key");

  // 2. Let desc be ? O.[[GetOwnProperty]](P).
  let desc = O.$GetOwnProperty(P);
  if (desc === undefined || desc.joinCondition === undefined) return OrdinaryGetHelper();

  // joined descriptors need special treatment
  let joinCondition = desc.joinCondition;
  let descriptor1 = desc.descriptor1;
  let descriptor2 = desc.descriptor2;
  joinCondition = realm.simplifyAndRefineAbstractCondition(joinCondition);
  if (!joinCondition.mightNotBeTrue()) {
    desc = descriptor1;
    return OrdinaryGetHelper();
  }
  if (!joinCondition.mightNotBeFalse()) {
    desc = desc.descriptor2;
    return OrdinaryGetHelper();
  }
  (0, _invariant2.default)(joinCondition instanceof _index.AbstractValue);
  let compl1, gen1, bindings1, properties1, createdObj1;
  try {
    desc = descriptor1;
    [compl1, gen1, bindings1, properties1, createdObj1] = _singletons.Path.withCondition(joinCondition, () => {
      return desc !== undefined ? realm.evaluateForEffects(() => OrdinaryGetHelper(), undefined, "OrdinaryGet/1") : (0, _realm.construct_empty_effects)(realm);
    });
  } catch (e) {
    if (e instanceof _errors.InfeasiblePathError) {
      // The joinCondition cannot be true in the current path, after all
      desc = descriptor2;
      return OrdinaryGetHelper();
    } else {
      throw e;
    }
  }
  let compl2, gen2, bindings2, properties2, createdObj2;
  try {
    desc = descriptor2;
    [compl2, gen2, bindings2, properties2, createdObj2] = _singletons.Path.withInverseCondition(joinCondition, () => {
      return desc !== undefined ? realm.evaluateForEffects(() => OrdinaryGetHelper(), undefined, "OrdinaryGet/2") : (0, _realm.construct_empty_effects)(realm);
    });
  } catch (e) {
    if (e instanceof _errors.InfeasiblePathError) {
      // The joinCondition cannot be false in the current path, after all
      desc = descriptor1;
      return OrdinaryGetHelper();
    } else {
      throw e;
    }
  }
  // Join the effects, creating an abstract view of what happened, regardless
  // of the actual value of ownDesc.joinCondition.
  let joinedEffects = _singletons.Join.joinEffects(realm, joinCondition, [compl1, gen1, bindings1, properties1, createdObj1], [compl2, gen2, bindings2, properties2, createdObj2]);
  let completion = joinedEffects[0];
  if (completion instanceof _completions.PossiblyNormalCompletion) {
    // in this case one of the branches may complete abruptly, which means that
    // not all control flow branches join into one flow at this point.
    // Consequently we have to continue tracking changes until the point where
    // all the branches come together into one.
    completion = realm.composeWithSavedCompletion(completion);
  }
  // Note that the effects of (non joining) abrupt branches are not included
  // in joinedEffects, but are tracked separately inside completion.
  realm.applyEffects(joinedEffects);

  // return or throw completion
  if (completion instanceof _completions.AbruptCompletion) throw completion;
  (0, _invariant2.default)(completion instanceof _index.Value);
  return completion;

  function OrdinaryGetHelper() {
    let descValue = !desc ? realm.intrinsics.undefined : desc.value === undefined ? realm.intrinsics.undefined : desc.value;
    (0, _invariant2.default)(descValue instanceof _index.Value);

    // 3. If desc is undefined, then
    if (!desc || descValue.mightHaveBeenDeleted()) {
      // a. Let parent be ? O.[[GetPrototypeOf]]().
      let parent = O.$GetPrototypeOf();

      // b. If parent is null, return undefined.
      if (parent instanceof _index.NullValue) {
        // Return the property value since it is now known to be the right value
        // even in the case when it is empty.
        return descValue;
      }

      // c. Return ? parent.[[Get]](P, Receiver).
      if (descValue.mightHaveBeenDeleted() && descValue instanceof _index.AbstractValue) {
        // We don't know for sure that O.P does not exist.
        let parentVal = OrdinaryGet(realm, parent.throwIfNotConcreteObject(), P, descValue, true);
        if (parentVal instanceof _index.UndefinedValue)
          // even O.P returns undefined it is still the right value.
          return descValue;
        // Join with parent value with descValue because the actual value will be
        // descValue unless it is empty.
        // Only get the parent value if it does not involve a getter call.
        // Use a property get for the joined value since it does the check for empty.
        let cond = _index.AbstractValue.createFromBinaryOp(realm, "!==", descValue, realm.intrinsics.empty);
        return _singletons.Join.joinValuesAsConditional(realm, cond, descValue, parentVal);
      }
      (0, _invariant2.default)(!desc || descValue instanceof _index.EmptyValue);
      return parent.$Get(P, Receiver);
    }

    // 4. If IsDataDescriptor(desc) is true, return desc.[[Value]].
    if ((0, _index2.IsDataDescriptor)(realm, desc)) return descValue;
    if (dataOnly) {
      (0, _invariant2.default)(descValue instanceof _index.AbstractValue);
      _index.AbstractValue.reportIntrospectionError(descValue);
      throw new _errors.FatalError();
    }

    // 5. Assert: IsAccessorDescriptor(desc) is true.
    (0, _invariant2.default)((0, _index2.IsAccessorDescriptor)(realm, desc), "expected accessor descriptor");

    // 6. Let getter be desc.[[Get]].
    let getter = desc.get;

    // 7. If getter is undefined, return undefined.
    if (!getter || getter instanceof _index.UndefinedValue) return realm.intrinsics.undefined;

    // 8. Return ? Call(getter, Receiver).
    return (0, _index2.Call)(realm, getter, Receiver);
  }
}

// ECMA262 8.3.6
function GetGlobalObject(realm) {
  // 1. Let ctx be the running execution context.
  let ctx = realm.getRunningContext();

  // 2. Let currentRealm be ctx's Realm.
  let currentRealm = ctx.realm;

  // 3. Return currentRealm.[[GlobalObject]].
  return currentRealm.$GlobalObject;
}

// ECMA262 21.1.3.14.1
function GetSubstitution(realm, matched, str, position, captures, replacement) {
  // 1. Assert: Type(matched) is String.
  (0, _invariant2.default)(typeof matched === "string", "expected matched to be a stirng");

  // 2. Let matchLength be the number of code units in matched.
  let matchLength = matched.length;

  // 3. Assert: Type(str) is String.
  (0, _invariant2.default)(typeof str === "string", "expected matched to be a stirng");

  // 4. Let stringLength be the number of code units in str.
  let stringLength = str.length;

  // 5. Assert: position is a nonnegative integer.
  (0, _invariant2.default)(position >= 0, "expected position to be a nonegative integer");

  // 6. Assert: position ≤ stringLength.
  (0, _invariant2.default)(position <= stringLength, "expected position to be less than string length");

  // 7. Assert: captures is a possibly empty List of Strings.
  (0, _invariant2.default)(Array.isArray(captures), "expected captures to be an array");

  // 8. Assert: Type(replacement) is String.
  (0, _invariant2.default)(typeof replacement === "string", "expected replacement to be a stirng");

  // 9. Let tailPos be position + matchLength.
  let tailPos = position + matchLength;

  // 10. Let m be the number of elements in captures.
  let m = captures.length;

  // 11. Let result be a String value derived from replacement by copying code unit elements
  //     from replacement to result while performing replacements as specified in Table 46.
  //     These $ replacements are done left-to-right, and, once such a replacement is performed,
  //     the new replacement text is not subject to further replacements.
  let result = "";
  for (let i = 0; i < replacement.length; ++i) {
    let ch = replacement.charAt(i);
    if (ch !== "$" || i + 1 >= replacement.length) {
      result += ch;
      continue;
    }
    let peek = replacement.charAt(i + 1);
    if (peek === "&") {
      result += matched;
    } else if (peek === "$") {
      result += "$";
    } else if (peek === "`") {
      result += str.substr(0, position);
    } else if (peek === "'") {
      result += str.substr(tailPos);
    } else if (peek >= "0" && peek <= "9") {
      let idx = peek.charCodeAt(0) - "0".charCodeAt(0);
      if (i + 2 < replacement.length) {
        let peek2 = replacement.charAt(i + 2);
        if (peek2 >= "0" && peek2 <= "9") {
          let newIdx = idx * 10 + (peek2.charCodeAt(0) - "0".charCodeAt(0));
          if (newIdx <= m) {
            idx = newIdx;
            i += 1;
          }
        }
      }
      if (idx > 0 && idx <= m) {
        result += captures[idx - 1] || "";
      } else {
        result += "$" + idx;
      }
    } else {
      result += "$" + peek;
    }
    i += 1;
  }

  // 12. Return result.
  return result;
}

// ECMA262 7.3.9
function GetMethod(realm, V, P) {
  // 1. Assert: IsPropertyKey(P) is true.
  (0, _invariant2.default)((0, _index2.IsPropertyKey)(realm, P), "expected property key");

  // 2. Let func be ? GetV(V, P).
  let func = GetV(realm, V, P);

  // 3. If func is either undefined or null, return undefined.
  if ((0, _index2.HasSomeCompatibleType)(func, _index.NullValue, _index.UndefinedValue)) {
    return realm.intrinsics.undefined;
  }

  // 4. If IsCallable(func) is false, throw a TypeError exception.
  if (!(0, _index2.IsCallable)(realm, func)) {
    throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not callable");
  }

  // 5. Return func.
  return func;
}

// ECMA262 9.1.14
function GetPrototypeFromConstructor(realm, constructor, intrinsicDefaultProto) {
  // 1. Assert: intrinsicDefaultProto is a String value that is this specification's name of an intrinsic
  //   object. The corresponding object must be an intrinsic that is intended to be used as the [[Prototype]]
  //   value of an object.
  (0, _invariant2.default)(realm.intrinsics[intrinsicDefaultProto], "not a valid proto ref");

  // 2. Assert: IsCallable(constructor) is true.
  (0, _invariant2.default)((0, _index2.IsCallable)(realm, constructor) === true, "expected constructor to be callable");

  // 3. Let proto be ? Get(constructor, "prototype").
  let proto = Get(realm, constructor, new _index.StringValue(realm, "prototype"));

  // 4. If Type(proto) is not Object, then
  if (!(proto instanceof _index.ObjectValue) && !(proto instanceof _index.AbstractObjectValue)) {
    // a. Let realm be ? GetFunctionRealm(constructor).
    realm = GetFunctionRealm(realm, constructor);

    // b. Let proto be realm's intrinsic object named intrinsicDefaultProto.
    proto = realm.intrinsics[intrinsicDefaultProto];
  }

  // 5. Return proto.
  return proto;
}

// ECMA262 7.3.1
function Get(realm, O, P) {
  // 1. Assert: Type(O) is Object.
  (0, _invariant2.default)(O instanceof _index.ObjectValue || O instanceof _index.AbstractObjectValue, "Not an object value");

  // 2. Assert: IsPropertyKey(P) is true.
  (0, _invariant2.default)((0, _index2.IsPropertyKey)(realm, P), "Not a valid property key");

  // 3. Return ? O.[[Get]](P, O).
  return O.$Get(P, O);
}

// ECMA262 7.3.2
function GetV(realm, V, P) {
  // 1. Assert: IsPropertyKey(P) is true.
  (0, _invariant2.default)((0, _index2.IsPropertyKey)(realm, P), "Not a valid property key");

  // 2. Let O be ? ToObject(V).
  let O = _singletons.To.ToObjectPartial(realm, V);

  // 3. Return ? O.[[Get]](P, V).
  return O.$Get(P, V);
}

// ECMA262 6.2.3.3
function GetThisValue(realm, V) {
  // 1. Assert: IsPropertyReference(V) is true.
  (0, _invariant2.default)(_singletons.Environment.IsPropertyReference(realm, V), "expected property reference");

  // 2. If IsSuperReference(V) is true, then
  if (_singletons.Environment.IsSuperReference(realm, V)) {
    (0, _invariant2.default)(V.thisValue !== undefined);
    // a. Return the value of the thisValue component of the reference V.
    return V.thisValue;
  }

  // 3. Return GetBase(V).
  let result = _singletons.Environment.GetBase(realm, V);
  (0, _invariant2.default)(result instanceof _index.Value);
  return result;
}

// ECMA262 8.3.5
function GetNewTarget(realm) {
  // 1. Let envRec be GetThisEnvironment( ).
  let envRec = _singletons.Environment.GetThisEnvironment(realm);

  // 2. Assert: envRec has a [[NewTarget]] field.
  if (!("$NewTarget" in envRec)) {
    // In the spec we should not get here because earlier static checks are supposed to prevent it.
    // However, we do not have an appropriate place to do this check earlier.
    throw realm.createErrorThrowCompletion(realm.intrinsics.SyntaxError, "new.target not allowed here");
  }

  // 3. Return envRec.[[NewTarget]].
  return envRec.$NewTarget || realm.intrinsics.undefined;
}

function GetTemplateObject(realm, templateLiteral) {
  // 1. Let rawStrings be TemplateStrings of templateLiteral with argument true.
  let rawStrings = templateLiteral.quasis.map(quasi => quasi.value.raw);

  // 2. Let realm be the current Realm Record.
  realm;

  // 3. Let templateRegistry be realm.[[TemplateMap]].
  let templateRegistry = realm.$TemplateMap;

  // 4. For each element e of templateRegistry, do
  for (let e of templateRegistry) {
    let same;
    if (e.$Strings.length === rawStrings.length) {
      same = true;
      for (let i = 0; i < rawStrings.length; ++i) {
        if (e.$Strings[i] !== rawStrings[i]) {
          same = false;
          break;
        }
      }
    } else {
      same = false;
    }

    // a. If e.[[Strings]] and rawStrings contain the same values in the same order, then
    if (same) {
      // i. Return e.[[Array]].
      return e.$Array;
    }
  }

  // 5. Let cookedStrings be TemplateStrings of templateLiteral with argument false.
  let cookedStrings = templateLiteral.quasis.map(quasi => quasi.value.cooked);

  // 6. Let count be the number of elements in the List cookedStrings.
  let count = cookedStrings.length;

  // 7. Let template be ArrayCreate(count).
  let template = _singletons.Create.ArrayCreate(realm, count);

  // 8. Let rawObj be ArrayCreate(count).
  let rawObj = _singletons.Create.ArrayCreate(realm, count);

  // 9. Let index be 0.
  let index = 0;

  // 10. Repeat while index < count
  while (index < count) {
    // a. Let prop be ! ToString(index).
    let prop = _singletons.To.ToString(realm, new _index.NumberValue(realm, index));

    // b. Let cookedValue be the String value cookedStrings[index].
    let cookedValue = new _index.StringValue(realm, cookedStrings[index]);

    // c. Call template.[[DefineOwnProperty]](prop, PropertyDescriptor{[[Value]]: cookedValue, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false}).
    template.$DefineOwnProperty(prop, {
      value: cookedValue,
      writable: false,
      enumerable: true,
      configurable: false
    });

    // d. Let rawValue be the String value rawStrings[index].
    let rawValue = new _index.StringValue(realm, rawStrings[index]);

    // e. Call rawObj.[[DefineOwnProperty]](prop, PropertyDescriptor{[[Value]]: rawValue, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false}).
    rawObj.$DefineOwnProperty(prop, {
      value: rawValue,
      writable: false,
      enumerable: true,
      configurable: false
    });

    // f. Let index be index+1.
    index = index + 1;
  }

  // 11. Perform SetIntegrityLevel(rawObj, "frozen").
  (0, _integrity.SetIntegrityLevel)(realm, rawObj, "frozen");

  // 12. Call template.[[DefineOwnProperty]]("raw", PropertyDescriptor{[[Value]]: rawObj, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false}).
  template.$DefineOwnProperty("raw", {
    value: rawObj,
    writable: false,
    enumerable: false,
    configurable: false
  });

  // 13. Perform SetIntegrityLevel(template, "frozen").
  (0, _integrity.SetIntegrityLevel)(realm, template, "frozen");

  // 14. Append the Record{[[Strings]]: rawStrings, [[Array]]: template} to templateRegistry.
  templateRegistry.push({ $Strings: rawStrings, $Array: template });

  // 15. Return template.
  return template;
}
//# sourceMappingURL=get.js.map