"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Reference = exports.LexicalEnvironment = exports.GlobalEnvironmentRecord = exports.FunctionEnvironmentRecord = exports.ObjectEnvironmentRecord = exports.DeclarativeEnvironmentRecord = exports.EnvironmentRecord = undefined;
exports.havocBinding = havocBinding;
exports.mightBecomeAnObject = mightBecomeAnObject;

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

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

var _options = require("./options");

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

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

var _babelGenerator = require("babel-generator");

var _babelGenerator2 = _interopRequireDefault(_babelGenerator);

var _parse = require("./utils/parse.js");

var _parse2 = _interopRequireDefault(_parse);

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

var _invariant2 = _interopRequireDefault(_invariant);

var _traverseFast = require("./utils/traverse-fast.js");

var _traverseFast2 = _interopRequireDefault(_traverseFast);

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

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

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

var t = _interopRequireWildcard(_babelTypes);

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

var _PrimitiveValue = require("./values/PrimitiveValue");

var _PrimitiveValue2 = _interopRequireDefault(_PrimitiveValue);

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 }; }

const sourceMap = require("source-map"); /**
                                          * 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 deriveGetBinding(realm, binding) {
  let types = _index3.TypesDomain.topVal;
  let values = _index3.ValuesDomain.topVal;
  (0, _invariant2.default)(realm.generator !== undefined);
  return realm.generator.derive(types, values, [], (_, context) => context.serializeBinding(binding));
}

function havocBinding(binding) {
  let realm = binding.environment.realm;
  let value = binding.value;
  if (!binding.hasLeaked && value instanceof _index.ObjectValue && !value.isFinalObject()) {
    realm.recordModifiedBinding(binding).hasLeaked = true;
  }
}

// ECMA262 8.1.1
class EnvironmentRecord {

  constructor(realm) {
    (0, _invariant2.default)(realm, "expected realm");
    this.realm = realm;
    this.isReadOnly = false;
    this.id = EnvironmentRecord.nextId++;
  }

}

exports.EnvironmentRecord = EnvironmentRecord;
EnvironmentRecord.nextId = 0;


// ECMA262 8.1.1.1
class DeclarativeEnvironmentRecord extends EnvironmentRecord {
  constructor(realm) {
    super(realm);
    this.bindings = Object.create(null);
    this.frozen = false;
  }
  // Frozen Records cannot have bindings created or deleted but can have bindings updated


  // ECMA262 8.1.1.1.1
  HasBinding(N) {
    // 1. Let envRec be the declarative Environment Record for which the method was invoked.
    let envRec = this;

    // 2. If envRec has a binding for the name that is the value of N, return true.
    if (envRec.bindings[N]) return true;

    // 3. Return false.
    return false;
  }

  // ECMA262 8.1.1.1.2
  CreateMutableBinding(N, D, isGlobal = false) {
    (0, _invariant2.default)(!this.frozen);
    let realm = this.realm;

    // 1. Let envRec be the declarative Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Assert: envRec does not already have a binding for N.
    (0, _invariant2.default)(!envRec.bindings[N], `shouldn't have the binding ${N}`);

    // 3. Create a mutable binding in envRec for N and record that it is uninitialized. If D is true, record that the newly created binding may be deleted by a subsequent DeleteBinding call.
    this.bindings[N] = realm.recordModifiedBinding({
      initialized: false,
      mutable: true,
      deletable: D,
      environment: envRec,
      name: N,
      isGlobal: isGlobal,
      hasLeaked: false
    });

    // 4. Return NormalCompletion(empty).
    return realm.intrinsics.undefined;
  }

  // ECMA262 8.1.1.1.3
  CreateImmutableBinding(N, S, isGlobal = false, skipRecord = false) {
    (0, _invariant2.default)(!this.frozen);
    let realm = this.realm;

    // 1. Let envRec be the declarative Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Assert: envRec does not already have a binding for N.
    (0, _invariant2.default)(!envRec.bindings[N], `shouldn't have the binding ${N}`);

    // 3. Create an immutable binding in envRec for N and record that it is uninitialized. If S is true, record that the newly created binding is a strict binding.
    let binding = {
      initialized: false,
      strict: S,
      deletable: false,
      environment: envRec,
      name: N,
      isGlobal: isGlobal,
      hasLeaked: false
    };
    this.bindings[N] = skipRecord ? binding : realm.recordModifiedBinding(binding);

    // 4. Return NormalCompletion(empty).
    return realm.intrinsics.undefined;
  }

  // ECMA262 8.1.1.1.4
  InitializeBinding(N, V, skipRecord = false) {
    // 1. Let envRec be the declarative Environment Record for which the method was invoked.
    let envRec = this;

    let binding = envRec.bindings[N];

    // 2. Assert: envRec must have an uninitialized binding for N.
    (0, _invariant2.default)(binding && !binding.initialized, `shouldn't have the binding ${N}`);

    // 3. Set the bound value for N in envRec to V.
    if (!skipRecord) this.realm.recordModifiedBinding(binding).value = V;else binding.value = V;

    // 4. Record that the binding for N in envRec has been initialized.
    binding.initialized = true;

    // 5. Return NormalCompletion(empty).
    return this.realm.intrinsics.empty;
  }

  // ECMA262 8.1.1.1.5
  SetMutableBinding(N, V, S) {
    // We can mutate frozen bindings because of captured bindings.
    let realm = this.realm;

    // 1. Let envRec be the declarative Environment Record for which the method was invoked.
    let envRec = this;

    let binding = envRec.bindings[N];

    // 2. If envRec does not have a binding for N, then
    if (!binding) {
      // a. If S is true, throw a ReferenceError exception.
      if (S) {
        throw realm.createErrorThrowCompletion(realm.intrinsics.ReferenceError, `${N} not found`);
      }

      // b. Perform envRec.CreateMutableBinding(N, true).
      envRec.CreateMutableBinding(N, true);

      // c. Perform envRec.InitializeBinding(N, V).
      envRec.InitializeBinding(N, V);

      // d. Return NormalCompletion(empty).
      return this.realm.intrinsics.empty;
    }

    // 3. If the binding for N in envRec is a strict binding, let S be true.
    if (binding.strict) S = true;

    // 4. If the binding for N in envRec has not yet been initialized, throw a ReferenceError exception.
    if (!binding.initialized) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.ReferenceError, `${N} has not yet been initialized`);
    } else if (binding.mutable) {
      // 5. Else if the binding for N in envRec is a mutable binding, change its bound value to V.
      if (binding.hasLeaked) {
        _singletons.Havoc.value(realm, V);
        (0, _invariant2.default)(realm.generator);
        realm.generator.emitBindingAssignment(binding, V);
      } else {
        realm.recordModifiedBinding(binding).value = V;
      }
    } else {
      // 6. Else,
      // a. Assert: This is an attempt to change the value of an immutable binding.

      // b. If S is true, throw a TypeError exception.
      if (S) {
        throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "attempt to change immutable binding");
      }
    }

    // 7. Return NormalCompletion(empty).
    return this.realm.intrinsics.empty;
  }

  // ECMA262 8.1.1.1.6
  GetBindingValue(N, S) {
    let realm = this.realm;

    // 1. Let envRec be the declarative Environment Record for which the method was invoked.
    let envRec = this;

    let binding = envRec.bindings[N];

    // 2. Assert: envRec has a binding for N.
    (0, _invariant2.default)(binding, "expected binding");

    // 3. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception.
    if (!binding.initialized) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.ReferenceError);
    }

    // 4. Return the value currently bound to N in envRec.
    if (binding.hasLeaked) {
      return deriveGetBinding(realm, binding);
    }
    (0, _invariant2.default)(binding.value);
    return binding.value;
  }

  // ECMA262 8.1.1.1.7
  DeleteBinding(N) {
    (0, _invariant2.default)(!this.frozen);
    // 1. Let envRec be the declarative Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Assert: envRec has a binding for the name that is the value of N.
    (0, _invariant2.default)(envRec.bindings[N], "expected binding to exist");

    // 3. If the binding for N in envRec cannot be deleted, return false.
    if (!envRec.bindings[N].deletable) return false;

    // 4. Remove the binding for N from envRec.
    this.realm.recordModifiedBinding(envRec.bindings[N]).value = undefined;
    delete envRec.bindings[N];

    // 5. Return true.
    return true;
  }

  // ECMA262 8.1.1.1.8
  HasThisBinding() {
    // 1. Return false.
    return false;
  }

  // ECMA262 8.1.1.1.9
  HasSuperBinding() {
    // 1. Return false.
    return false;
  }

  // ECMA262 8.1.1.1.10
  WithBaseObject() {
    // 1. Return undefined.
    return this.realm.intrinsics.undefined;
  }
}

exports.DeclarativeEnvironmentRecord = DeclarativeEnvironmentRecord; // ECMA262 8.1.1.2

class ObjectEnvironmentRecord extends EnvironmentRecord {

  constructor(realm, obj) {
    super(realm);
    this.object = obj;
  }

  // ECMA262 8.1.1.2.1
  HasBinding(N) {
    let realm = this.realm;

    // 1. Let envRec be the object Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let bindings be the binding object for envRec.
    let bindings = this.object;

    // 3. Let foundBinding be ? HasProperty(bindings, N).
    let foundBinding = (0, _index2.HasProperty)(realm, bindings, N);

    // 4. If foundBinding is false, return false.
    if (!foundBinding) return false;

    // 5. If the withEnvironment flag of envRec is false, return true.
    if (!envRec.withEnvironment) return true;

    // 6. Let unscopables be ? Get(bindings, @@unscopables).
    let unscopables = (0, _index2.Get)(realm, bindings, realm.intrinsics.SymbolUnscopables);

    // 7. If Type(unscopables) is Object, then
    if (unscopables instanceof _index.ObjectValue || unscopables instanceof _index.AbstractObjectValue) {
      // a. Let blocked be ToBoolean(? Get(unscopables, N)).
      let blocked = _singletons.To.ToBooleanPartial(realm, (0, _index2.Get)(realm, unscopables, N));

      // b. If blocked is true, return false.
      if (blocked) return false;
    }
    unscopables.throwIfNotConcrete();

    // 8. Return true.
    return true;
  }

  // ECMA262 8.1.1.2.2
  CreateMutableBinding(N, D) {
    let realm = this.realm;

    // 1. Let envRec be the object Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let bindings be the binding object for envRec.
    let bindings = envRec.object;

    // 3. If D is true, let configValue be true; otherwise let configValue be false.
    let configValue = D ? true : false;

    // 4. Return ? DefinePropertyOrThrow(bindings, N, PropertyDescriptor{[[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: configValue}).
    return new _index.BooleanValue(realm, _singletons.Properties.DefinePropertyOrThrow(realm, bindings, N, {
      value: realm.intrinsics.undefined,
      writable: true,
      enumerable: true,
      configurable: configValue
    }));
  }

  // ECMA262 8.1.1.2.3
  CreateImmutableBinding(N, S) {
    // The concrete Environment Record method CreateImmutableBinding is never used within this specification in association with object Environment Records.
    (0, _invariant2.default)(false);
  }

  // ECMA262 8.1.1.2.4
  InitializeBinding(N, V) {
    // 1. Let envRec be the object Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Assert: envRec must have an uninitialized binding for N.
    // 3. Record that the binding for N in envRec has been initialized.

    // 4. Return ? envRec.SetMutableBinding(N, V, false).
    return envRec.SetMutableBinding(N, V, false);
  }

  // ECMA262 8.1.1.2.5
  SetMutableBinding(N, V, S) {
    let realm = this.realm;

    // 1. Let envRec be the object Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let bindings be the binding object for envRec.
    let bindings = envRec.object;

    // 3. Return ? Set(bindings, N, V, S).
    return new _index.BooleanValue(realm, _singletons.Properties.Set(realm, bindings, N, V, S));
  }

  // ECMA262 8.1.1.2.6
  GetBindingValue(N, S) {
    let realm = this.realm;

    // 1. Let envRec be the object Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let bindings be the binding object for envRec.
    let bindings = envRec.object;

    // 3. Let value be ? HasProperty(bindings, N).
    let value = (0, _index2.HasProperty)(realm, bindings, N);

    // 4. If value is false, then
    if (!value) {
      // a. If S is false, return the value undefined; otherwise throw a ReferenceError exception.
      if (!S) {
        return realm.intrinsics.undefined;
      } else {
        throw realm.createErrorThrowCompletion(realm.intrinsics.ReferenceError);
      }
    }

    // 5. Return ? Get(bindings, N).
    return (0, _index2.Get)(realm, bindings, N);
  }

  // ECMA262 8.1.1.2.7
  DeleteBinding(N) {
    // 1. Let envRec be the object Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let bindings be the binding object for envRec.
    let bindings = envRec.object;

    // 3. Return ? bindings.[[Delete]](N).
    return bindings.$Delete(N);
  }

  // ECMA262 8.1.1.2.8
  HasThisBinding() {
    // 1. Return false.
    return false;
  }

  // ECMA262 8.1.1.2.9
  HasSuperBinding() {
    // 1. Return false.
    return false;
  }

  // ECMA262 8.1.1.2.10
  WithBaseObject() {
    // 1. Let envRec be the object Environment Record for which the method was invoked.
    let envRec = this;

    // 2. If the withEnvironment flag of envRec is true, return the binding object for envRec.
    if (envRec.withEnvironment) return envRec.object;

    // 3. Otherwise, return undefined.
    return this.realm.intrinsics.undefined;
  }
}

exports.ObjectEnvironmentRecord = ObjectEnvironmentRecord; // ECMA262 8.1.1.3

class FunctionEnvironmentRecord extends DeclarativeEnvironmentRecord {

  // ECMA262 8.1.1.3.1
  BindThisValue(V) {
    let realm = this.realm;

    // 1. Let envRec be the function Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Assert: envRec.[[ThisBindingStatus]] is not "lexical".
    (0, _invariant2.default)(envRec.$ThisBindingStatus !== "lexical", "this binding status shouldn't be lexical");

    // 3. If envRec.[[ThisBindingStatus]] is "initialized", throw a ReferenceError exception.
    if (envRec.$ThisBindingStatus === "initialized") {
      throw realm.createErrorThrowCompletion(realm.intrinsics.ReferenceError);
    }

    // 4. Set envRec.[[ThisValue]] to V.
    envRec.$ThisValue = V;

    // 5. Set envRec.[[ThisBindingStatus]] to "initialized".
    envRec.$ThisBindingStatus = "initialized";

    // 6. Return V.
    return V;
  }

  // ECMA262 8.1.1.3.2
  HasThisBinding() {
    // 1. Let envRec be the function Environment Record for which the method was invoked.
    let envRec = this;

    // 2. If envRec.[[ThisBindingStatus]] is "lexical", return false; otherwise, return true.
    return envRec.$ThisBindingStatus === "lexical" ? false : true;
  }

  // ECMA262 8.1.1.3.3
  HasSuperBinding() {
    // 1. Let envRec be the function Environment Record for which the method was invoked.
    let envRec = this;

    // 2. If envRec.[[ThisBindingStatus]] is "lexical", return false.
    if (envRec.$ThisBindingStatus === "lexical") return false;

    // 3. If envRec.[[HomeObject]] has the value undefined, return false; otherwise, return true.
    if (envRec.$HomeObject === undefined) {
      return false;
    } else {
      return true;
    }
  }

  // ECMA262 8.1.1.3.4
  GetThisBinding() {
    let realm = this.realm;

    // 1. Let envRec be the function Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Assert: envRec.[[ThisBindingStatus]] is not "lexical".
    (0, _invariant2.default)(envRec.$ThisBindingStatus !== "lexical", "this binding status shouldn't be lexical");

    // 3. If envRec.[[ThisBindingStatus]] is "uninitialized", throw a ReferenceError exception.
    if (envRec.$ThisBindingStatus === "uninitialized") {
      throw realm.createErrorThrowCompletion(realm.intrinsics.ReferenceError);
    }

    // 4. Return envRec.[[ThisValue]].
    return envRec.$ThisValue;
  }

  // ECMA262 8.1.1.3.5
  GetSuperBase() {
    // 1. Let envRec be the function Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let home be the value of envRec.[[HomeObject]].
    let home = envRec.$HomeObject;

    // 3. If home has the value undefined, return undefined.
    if (home === undefined) return this.realm.intrinsics.undefined;

    // 4. Assert: Type(home) is Object.
    (0, _invariant2.default)(home instanceof _index.ObjectValue, "expected object value");

    // 5. Return ? home.[[GetPrototypeOf]]().
    return home.$GetPrototypeOf();
  }
}

exports.FunctionEnvironmentRecord = FunctionEnvironmentRecord; // ECMA262 8.1.1.4

class GlobalEnvironmentRecord extends EnvironmentRecord {

  // ECMA262 8.1.1.4.1
  HasBinding(N) {
    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let DclRec be envRec.[[DeclarativeRecord]].
    let DclRec = envRec.$DeclarativeRecord;

    // 3. If DclRec.HasBinding(N) is true, return true.
    if (DclRec.HasBinding(N)) return true;

    // 4. Let ObjRec be envRec.[[ObjectRecord]].
    let ObjRec = envRec.$ObjectRecord;

    // 5. Return ? ObjRec.HasBinding(N).
    return ObjRec.HasBinding(N);
  }

  // ECMA262 8.1.1.4.2
  CreateMutableBinding(N, D) {
    let realm = this.realm;

    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let DclRec be envRec.[[DeclarativeRecord]].
    let DclRec = envRec.$DeclarativeRecord;

    // 3. If DclRec.HasBinding(N) is true, throw a TypeError exception.
    if (DclRec.HasBinding(N)) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
    }

    // 4. Return DclRec.CreateMutableBinding(N, D).
    return DclRec.CreateMutableBinding(N, D, true);
  }

  // ECMA262 8.1.1.4.3
  CreateImmutableBinding(N, S) {
    let realm = this.realm;

    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let DclRec be envRec.[[DeclarativeRecord]].
    let DclRec = envRec.$DeclarativeRecord;

    // 3. If DclRec.HasBinding(N) is true, throw a TypeError exception.
    if (DclRec.HasBinding(N)) {
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
    }

    // 4. Return DclRec.CreateImmutableBinding(N, S).
    return DclRec.CreateImmutableBinding(N, S, true);
  }

  // ECMA262 8.1.1.4.4
  InitializeBinding(N, V) {
    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let DclRec be envRec.[[DeclarativeRecord]].
    let DclRec = envRec.$DeclarativeRecord;

    // 3. If DclRec.HasBinding(N) is true, then
    if (DclRec.HasBinding(N)) {
      // a. Return DclRec.InitializeBinding(N, V).
      return DclRec.InitializeBinding(N, V);
    }

    // 4. Assert: If the binding exists, it must be in the object Environment Record.

    // 5. Let ObjRec be envRec.[[ObjectRecord]].
    let ObjRec = envRec.$ObjectRecord;

    // 6. Return ? ObjRec.InitializeBinding(N, V).
    return ObjRec.InitializeBinding(N, V);
  }

  // ECMA262 8.1.1.4.5
  SetMutableBinding(N, V, S) {
    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let DclRec be envRec.[[DeclarativeRecord]].
    let DclRec = envRec.$DeclarativeRecord;

    // 3. If DclRec.HasBinding(N) is true, then
    if (DclRec.HasBinding(N)) {
      // a. Return DclRec.SetMutableBinding(N, V, S).
      return DclRec.SetMutableBinding(N, V, S);
    }

    // 4. Let ObjRec be envRec.[[ObjectRecord]].
    let ObjRec = envRec.$ObjectRecord;

    // 5. Return ? ObjRec.SetMutableBinding(N, V, S).
    return ObjRec.SetMutableBinding(N, V, S);
  }

  // ECMA262 8.1.1.4.6
  GetBindingValue(N, S) {
    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let DclRec be envRec.[[DeclarativeRecord]].
    let DclRec = envRec.$DeclarativeRecord;

    // 3. If DclRec.HasBinding(N) is true, then
    if (DclRec.HasBinding(N)) {
      // a. Return DclRec.GetBindingValue(N, S).
      return DclRec.GetBindingValue(N, S);
    }

    // 4. Let ObjRec be envRec.[[ObjectRecord]].
    let ObjRec = envRec.$ObjectRecord;

    // 5. Return ? ObjRec.GetBindingValue(N, S).
    return ObjRec.GetBindingValue(N, S);
  }

  // ECMA262 8.1.1.4.7
  DeleteBinding(N) {
    let realm = this.realm;

    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let DclRec be envRec.[[DeclarativeRecord]].
    let DclRec = envRec.$DeclarativeRecord;

    // 3. If DclRec.HasBinding(N) is true, then
    if (DclRec.HasBinding(N)) {
      // a. Return DclRec.DeleteBinding(N).
      return DclRec.DeleteBinding(N);
    }

    // 4. Let ObjRec be envRec.[[ObjectRecord]].
    let ObjRec = envRec.$ObjectRecord;

    // 5. Let globalObject be the binding object for ObjRec.
    let globalObject = ObjRec.object;

    // 6. Let existingProp be ? HasOwnProperty(globalObject, N).
    let existingProp = (0, _index2.HasOwnProperty)(realm, globalObject, N);

    // 7. If existingProp is true, then
    if (existingProp) {
      // a. Let status be ? ObjRec.DeleteBinding(N).
      let status = ObjRec.DeleteBinding(N);

      // b. If status is true, then
      if (status) {
        // i. Let varNames be envRec.[[VarNames]].
        let varNames = envRec.$VarNames;

        // ii. If N is an element of varNames, remove that element from the varNames.
        if (varNames.indexOf(N) >= 0) {
          varNames.splice(varNames.indexOf(N), 1);
        }
      }

      // c. Return status.
      return status;
    }

    // 8. Return true.
    return true;
  }

  // ECMA262 8.1.1.4.8
  HasThisBinding() {
    // 1. Return true.
    return true;
  }

  // ECMA262 8.1.1.4.9
  HasSuperBinding() {
    // 1. Return true.
    return true;
  }

  // ECMA262 8.1.1.4.10
  WithBaseObject() {
    // 1. Return undefined.
    return this.realm.intrinsics.undefined;
  }

  // ECMA262 8.1.1.4.11
  GetThisBinding() {
    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    (0, _invariant2.default)(envRec.$GlobalThisValue);
    // 2. Return envRec.[[GlobalThisValue]].
    return envRec.$GlobalThisValue;
  }

  // ECMA262 8.1.1.4.12
  HasVarDeclaration(N) {
    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let varDeclaredNames be envRec.[[VarNames]].
    let varDeclaredNames = envRec.$VarNames;

    // 3. If varDeclaredNames contains the value of N, return true.
    if (varDeclaredNames.indexOf(N) >= 0) return true;

    // 4. Return false.
    return false;
  }

  // ECMA262 8.1.1.4.13
  HasLexicalDeclaration(N) {
    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let DclRec be envRec.[[DeclarativeRecord]].
    let DclRec = envRec.$DeclarativeRecord;

    // 3. Return DclRec.HasBinding(N).
    return DclRec.HasBinding(N);
  }

  // ECMA262 8.1.1.4.14
  HasRestrictedGlobalProperty(N) {
    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let ObjRec be envRec.[[ObjectRecord]].
    let ObjRec = envRec.$ObjectRecord;

    // 3. Let globalObject be the binding object for ObjRec.
    let globalObject = ObjRec.object;

    // 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
    let existingProp = globalObject.$GetOwnProperty(N);

    // 5. If existingProp is undefined, return false.
    if (!existingProp) return false;
    _singletons.Properties.ThrowIfMightHaveBeenDeleted(existingProp.value);

    // 6. If existingProp.[[Configurable]] is true, return false.
    if (existingProp.configurable) return false;

    // 7. Return true.
    return true;
  }

  // ECMA262 8.1.1.4.15
  CanDeclareGlobalVar(N) {
    let realm = this.realm;

    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let ObjRec be envRec.[[ObjectRecord]].
    let ObjRec = envRec.$ObjectRecord;

    // 3. Let globalObject be the binding object for ObjRec.
    let globalObject = ObjRec.object;

    // 4. Let hasProperty be ? HasOwnProperty(globalObject, N).
    let hasProperty = (0, _index2.HasOwnProperty)(realm, globalObject, N);

    // 5. If hasProperty is true, return true.
    if (hasProperty) return true;

    // 6. Return ? IsExtensible(globalObject).
    return (0, _index2.IsExtensible)(realm, globalObject);
  }

  // ECMA262 8.1.1.4.16
  CanDeclareGlobalFunction(N) {
    let realm = this.realm;

    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let ObjRec be envRec.[[ObjectRecord]].
    let ObjRec = envRec.$ObjectRecord;

    // 3. Let globalObject be the binding object for ObjRec.
    let globalObject = ObjRec.object;

    // 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
    let existingProp = globalObject.$GetOwnProperty(N);

    // 5. If existingProp is undefined, return ? IsExtensible(globalObject).
    if (!existingProp) return (0, _index2.IsExtensible)(realm, globalObject);
    _singletons.Properties.ThrowIfMightHaveBeenDeleted(existingProp.value);

    // 6. If existingProp.[[Configurable]] is true, return true.
    if (existingProp.configurable) return true;

    // 7. If IsDataDescriptor(existingProp) is true and existingProp has attribute values {[[Writable]]: true, [[Enumerable]]: true}, return true.
    if ((0, _index2.IsDataDescriptor)(realm, existingProp) && existingProp.writable && existingProp.enumerable) {
      return true;
    }

    // 8. Return false.
    return false;
  }

  // ECMA262 8.1.1.4.17
  CreateGlobalVarBinding(N, D) {
    let realm = this.realm;

    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let ObjRec be envRec.[[ObjectRecord]].
    let ObjRec = envRec.$ObjectRecord;

    // 3. Let globalObject be the binding object for ObjRec.
    let globalObject = ObjRec.object;

    // 4. Let hasProperty be ? HasOwnProperty(globalObject, N).
    let hasProperty = (0, _index2.HasOwnProperty)(realm, globalObject, N);

    // 5. Let extensible be ? IsExtensible(globalObject).
    let extensible = (0, _index2.IsExtensible)(realm, globalObject);

    // 6. If hasProperty is false and extensible is true, then
    if (!hasProperty && extensible) {
      // a. Perform ? ObjRec.CreateMutableBinding(N, D).
      ObjRec.CreateMutableBinding(N, D);

      // b. Perform ? ObjRec.InitializeBinding(N, undefined).
      ObjRec.InitializeBinding(N, this.realm.intrinsics.undefined);
    }

    // 7. Let varDeclaredNames be envRec.[[VarNames]].
    let varDeclaredNames = envRec.$VarNames;

    // 8. If varDeclaredNames does not contain the value of N, then
    if (varDeclaredNames.indexOf(N) < 0) {
      // a. Append N to varDeclaredNames.
      varDeclaredNames.push(N);
    }

    // 9. Return NormalCompletion(empty).
  }

  // ECMA262 8.1.1.4.18
  CreateGlobalFunctionBinding(N, V, D) {
    // 1. Let envRec be the global Environment Record for which the method was invoked.
    let envRec = this;

    // 2. Let ObjRec be envRec.[[ObjectRecord]].
    let ObjRec = envRec.$ObjectRecord;

    // 3. Let globalObject be the binding object for ObjRec.
    let globalObject = ObjRec.object;

    // 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
    let existingProp = globalObject.$GetOwnProperty(N);

    // 5. If existingProp is undefined or existingProp.[[Configurable]] is true, then
    let desc;
    if (!existingProp || existingProp.configurable) {
      // a. Let desc be the PropertyDescriptor{[[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D}.
      desc = { value: V, writable: true, enumerable: true, configurable: D };
    } else {
      // 6. Else,
      _singletons.Properties.ThrowIfMightHaveBeenDeleted(existingProp.value);
      // a. Let desc be the PropertyDescriptor{[[Value]]: V }.
      desc = { value: V };
    }

    // 7. Perform ? DefinePropertyOrThrow(globalObject, N, desc).
    _singletons.Properties.DefinePropertyOrThrow(this.realm, globalObject, N, desc);

    // 8. Record that the binding for N in ObjRec has been initialized.

    // 9. Perform ? Set(globalObject, N, V, false).
    _singletons.Properties.Set(this.realm, globalObject, N, V, false);

    // 10. Let varDeclaredNames be envRec.[[VarNames]].
    let varDeclaredNames = envRec.$VarNames;

    // 11. If varDeclaredNames does not contain the value of N, then
    if (varDeclaredNames.indexOf(N) < 0) {
      // a. Append N to varDeclaredNames.
      varDeclaredNames.push(N);
    }

    // 12. Return NormalCompletion(empty).
  }
}

exports.GlobalEnvironmentRecord = GlobalEnvironmentRecord; // ECMA262 8.1

let uid = 0;
class LexicalEnvironment {
  constructor(realm) {
    (0, _invariant2.default)(realm, "expected realm");
    this.realm = realm;
    this.destroyed = false;
    this._uid = uid++;
  }

  // For debugging it is convenient to have an ID for each of these.


  destroy() {
    this.destroyed = true;
    // Once the containing environment is destroyed, we can no longer add or remove entries from the environmentRecord
    // (but we can update existing values).
    if (this.environmentRecord instanceof DeclarativeEnvironmentRecord) {
      this.environmentRecord.frozen = true;
    }
  }

  assignToGlobal(globalAst, rvalue) {
    let globalValue = this.evaluate(globalAst, false);
    _singletons.Properties.PutValue(this.realm, globalValue, rvalue);
  }

  partiallyEvaluateCompletionDeref(ast, strictCode, metadata) {
    let [result, partial_ast, partial_io] = this.partiallyEvaluateCompletion(ast, strictCode, metadata);
    if (result instanceof Reference) {
      result = _singletons.Environment.GetValue(this.realm, result);
    }
    return [result, partial_ast, partial_io];
  }

  partiallyEvaluateCompletion(ast, strictCode, metadata) {
    try {
      return this.partiallyEvaluate(ast, strictCode, metadata);
    } catch (err) {
      if (err instanceof _completions.Completion) return [err, ast, []];
      if (err instanceof Error)
        // rethrowing Error should preserve stack trace
        throw err;
      // let's wrap into a proper Error to create stack trace
      throw new _errors.FatalError(err);
    }
  }

  evaluateCompletionDeref(ast, strictCode, metadata) {
    let result = this.evaluateCompletion(ast, strictCode, metadata);
    if (result instanceof Reference) result = _singletons.Environment.GetValue(this.realm, result);
    return result;
  }

  evaluateCompletion(ast, strictCode, metadata) {
    try {
      return this.evaluate(ast, strictCode, metadata);
    } catch (err) {
      if ((err instanceof _completions.JoinedAbruptCompletions || err instanceof _completions.PossiblyNormalCompletion) && err.containsBreakOrContinue()) {
        _index.AbstractValue.reportIntrospectionError(err.joinCondition);
        throw new _errors.FatalError();
      }
      if (err instanceof _completions.AbruptCompletion) return err;
      if (err instanceof Error)
        // rethrowing Error should preserve stack trace
        throw err;
      // let's wrap into a proper Error to create stack trace
      throw new _errors.FatalError(err);
    }
  }

  evaluateAbstractCompletion(ast, strictCode, metadata) {
    try {
      return this.evaluateAbstract(ast, strictCode, metadata);
    } catch (err) {
      if (err instanceof _completions.Completion) return err;
      if (err instanceof Error)
        // rethrowing Error should preserve stack trace
        throw err;
      // let's wrap into a proper Error to create stack trace
      if (err instanceof Object) throw new _errors.FatalError(err.constructor.name + ": " + err);
      throw new _errors.FatalError(err);
    }
  }

  concatenateAndParse(sources, sourceType = "script") {
    let asts = [];
    let code = {};
    let directives = [];
    for (let source of sources) {
      try {
        let node = (0, _parse2.default)(this.realm, source.fileContents, source.filePath, sourceType);
        if (source.sourceMapContents && source.sourceMapContents.length > 0) this.fixup_source_locations(node, source.sourceMapContents);
        this.fixup_filenames(node);
        asts = asts.concat(node.program.body);
        code[source.filePath] = source.fileContents;
        directives = directives.concat(node.program.directives);
      } catch (e) {
        if (e instanceof _completions.ThrowCompletion) {
          let error = e.value;
          if (error instanceof _index.ObjectValue) {
            let message = error.$Get("message", error);
            message.value = `Syntax error: ${message.value}`;
            e.location.source = source.filePath;
            // the position was not located properly on the
            // syntax errors happen on one given position, so start position = end position
            e.location.start = { line: e.location.line, column: e.location.column };
            e.location.end = { line: e.location.line, column: e.location.column };
            let diagnostic = new _errors.CompilerDiagnostic(message.value, e.location, "PP1004", "FatalError");
            this.realm.handleError(diagnostic);
            throw new _errors.FatalError(message.value);
          }
        }
        throw e;
      }
    }
    return [t.file(t.program(asts, directives)), code];
  }

  executeSources(sources, sourceType = "script", onParse = undefined) {
    let context = new _realm.ExecutionContext();
    context.lexicalEnvironment = this;
    context.variableEnvironment = this;
    context.realm = this.realm;
    this.realm.pushContext(context);
    let res, code;
    try {
      let ast;
      [ast, code] = this.concatenateAndParse(sources, sourceType);
      if (onParse) onParse(ast);
      res = this.evaluateCompletion(ast, false);
    } finally {
      this.realm.popContext(context);
      this.realm.onDestroyScope(context.lexicalEnvironment);
      if (!this.destroyed) this.realm.onDestroyScope(this);
      (0, _invariant2.default)(this.realm.activeLexicalEnvironments.size === 0);
    }
    if (res instanceof _completions.AbruptCompletion) return [res, code];

    return [_singletons.Environment.GetValue(this.realm, res), code];
  }

  executePartialEvaluator(sources, options = _options.defaultOptions, sourceType = "script") {
    let [ast, code] = this.concatenateAndParse(sources, sourceType);
    let context = new _realm.ExecutionContext();
    context.lexicalEnvironment = this;
    context.variableEnvironment = this;
    context.realm = this.realm;
    this.realm.pushContext(context);
    let partialAST;
    try {
      [, partialAST] = this.partiallyEvaluateCompletionDeref(ast, false);
    } finally {
      this.realm.popContext(context);
      this.realm.onDestroyScope(context.lexicalEnvironment);
      if (!this.destroyed) this.realm.onDestroyScope(this);
      (0, _invariant2.default)(this.realm.activeLexicalEnvironments.size === 0);
    }
    (0, _invariant2.default)(partialAST.type === "File");
    let fileAst = partialAST;
    let prog = t.program(fileAst.program.body, ast.program.directives);
    this.fixup_filenames(prog);
    // The type signature for generate is not complete, hence the any
    return (0, _babelGenerator2.default)(prog, { sourceMaps: options.sourceMaps }, code);
  }

  execute(code, filename, map = "", sourceType = "script", onParse = undefined) {
    let context = new _realm.ExecutionContext();
    context.lexicalEnvironment = this;
    context.variableEnvironment = this;
    context.realm = this.realm;

    this.realm.pushContext(context);

    let ast, res;
    try {
      try {
        ast = (0, _parse2.default)(this.realm, code, filename, sourceType);
      } catch (e) {
        if (e instanceof _completions.ThrowCompletion) return e;
        throw e;
      }
      if (onParse) onParse(ast);
      if (map.length > 0) this.fixup_source_locations(ast, map);
      this.fixup_filenames(ast);
      res = this.evaluateCompletion(ast, false);
    } finally {
      this.realm.popContext(context);
      // Avoid destroying "this" scope as execute may be called many times.
      if (context.lexicalEnvironment !== this) this.realm.onDestroyScope(context.lexicalEnvironment);
      (0, _invariant2.default)(this.realm.activeLexicalEnvironments.size === 1);
    }
    if (res instanceof _completions.AbruptCompletion) return res;

    return _singletons.Environment.GetValue(this.realm, res);
  }

  fixup_source_locations(ast, map) {
    const smc = new sourceMap.SourceMapConsumer(map);
    (0, _traverseFast2.default)(ast, node => {
      let loc = node.loc;
      if (!loc) return false;
      fixup(loc, loc.start);
      fixup(loc, loc.end);
      fixup_comments(node.leadingComments);
      fixup_comments(node.innerComments);
      fixup_comments(node.trailingComments);
      return false;

      function fixup(new_loc, new_pos) {
        let old_pos = smc.originalPositionFor({ line: new_pos.line, column: new_pos.column });
        if (old_pos.source === null) return;
        new_pos.line = old_pos.line;
        new_pos.column = old_pos.column;
        new_loc.source = old_pos.source;
      }

      function fixup_comments(comments) {
        if (!comments) return;
        for (let c of comments) {
          let cloc = c.loc;
          if (!cloc) continue;
          fixup(cloc, cloc.start);
          fixup(cloc, cloc.end);
        }
      }
    });
  }

  fixup_filenames(ast) {
    (0, _traverseFast2.default)(ast, node => {
      let loc = node.loc;
      if (!loc || !loc.source) {
        node.leadingComments = null;
        node.innerComments = null;
        node.trailingComments = null;
        node.loc = null;
      } else {
        let filename = loc.source;
        loc.filename = filename;
        fixup_comments(node.leadingComments, filename);
        fixup_comments(node.innerComments, filename);
        fixup_comments(node.trailingComments, filename);
      }
      return false;

      function fixup_comments(comments, filename) {
        if (!comments) return;
        for (let c of comments) {
          if (c.loc) {
            c.loc.filename = filename;
            c.loc.source = filename;
          }
        }
      }
    });
  }

  evaluate(ast, strictCode, metadata) {
    if (this.realm.debuggerInstance) {
      this.realm.debuggerInstance.checkForActions(ast);
    }
    let res = this.evaluateAbstract(ast, strictCode, metadata);
    (0, _invariant2.default)(res instanceof _index.Value || res instanceof Reference, ast.type);
    return res;
  }

  evaluateAbstract(ast, strictCode, metadata) {
    this.realm.currentLocation = ast.loc;
    this.realm.testTimeout();

    let evaluator = this.realm.evaluators[ast.type];
    if (evaluator) {
      let result = evaluator(ast, strictCode, this, this.realm, metadata);
      return result;
    }

    throw new TypeError(`Unsupported node type ${ast.type}`);
  }

  partiallyEvaluate(ast, strictCode, metadata) {
    let partialEvaluator = this.realm.partialEvaluators[ast.type];
    if (partialEvaluator) {
      return partialEvaluator(ast, strictCode, this, this.realm, metadata);
    }

    let err = new TypeError(`Unsupported node type ${ast.type}`);
    throw err;
  }
}

exports.LexicalEnvironment = LexicalEnvironment; // ECMA262 6.2.3
// A Reference is a resolved name or property binding. A Reference consists of three components, the base value,
// the referenced name and the Boolean valued strict reference flag. The base value is either undefined, an Object,
// a Boolean, a String, a Symbol, a Number, or an Environment Record. A base value of undefined indicates that the
// Reference could not be resolved to a binding. The referenced name is a String or Symbol value.

function mightBecomeAnObject(base) {
  let type = base.getType();
  // The top Value type might be able to become an object. We let it
  // pass and error later if it can't.
  return type === _index.Value || type === _PrimitiveValue2.default || type === _index.BooleanValue || type === _index.StringValue || type === _index.SymbolValue || type === _index.NumberValue || type === _index.IntegralValue;
}

class Reference {

  constructor(base, refName, strict, thisValue) {
    (0, _invariant2.default)(base instanceof _index.AbstractObjectValue || base === undefined || base instanceof _index.ObjectValue || base instanceof EnvironmentRecord || mightBecomeAnObject(base));
    this.base = base;
    this.referencedName = refName;
    (0, _invariant2.default)(!(refName instanceof _index.AbstractValue) || !(refName.mightNotBeString() && refName.mightNotBeNumber() && !refName.isSimpleObject()) || refName.$Realm.isInPureScope());
    this.strict = strict;
    this.thisValue = thisValue;
    (0, _invariant2.default)(thisValue === undefined || !(base instanceof EnvironmentRecord));
  }
}
exports.Reference = Reference;
//# sourceMappingURL=environment.js.map