"use strict";

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

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /**
                                                                                                                                                                                                                                                                   * 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.
                                                                                                                                                                                                                                                                   */

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

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

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

var _utils = require("../react/utils.js");

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

var _builder2 = _interopRequireDefault(_builder);

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

var _singletons = require("../singletons.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 isWidenedValue(v) {
  if (!(v instanceof _index2.AbstractValue)) return false;
  if (v.kind === "widened" || v.kind === "widened property") return true;
  for (let a of v.args) {
    if (isWidenedValue(a)) return true;
  }
  return false;
}

const lengthTemplateSrc = "(A).length";
const lengthTemplate = (0, _builder2.default)(lengthTemplateSrc);

class ObjectValue extends _index2.ConcreteValue {
  constructor(realm, proto, intrinsicName, refuseSerialization = false) {
    super(realm, intrinsicName);
    realm.recordNewObject(this);
    if (realm.useAbstractInterpretation) this.setupBindings(this.getTrackedPropertyNames());
    this.$Prototype = proto || realm.intrinsics.null;
    this.$Extensible = realm.intrinsics.true;
    this._isPartial = realm.intrinsics.false;
    this._isHavoced = realm.intrinsics.false;
    this._isSimple = realm.intrinsics.false;
    this._simplicityIsTransitive = realm.intrinsics.false;
    this._isFinal = realm.intrinsics.false;
    this.properties = new Map();
    this.symbols = new Map();
    this.refuseSerialization = refuseSerialization;
    this.$IsClassPrototype = false;
  }

  getTrackedPropertyNames() {
    return ObjectValue.trackedPropertyNames;
  }

  setupBindings(propertyNames) {
    for (let propName of propertyNames) {
      let desc = { writeable: true, value: undefined };
      this[propName + "_binding"] = {
        descriptor: desc,
        object: this,
        key: propName
      };
    }
  }

  static setupTrackedPropertyAccessors(propertyNames) {
    for (let propName of propertyNames) {
      Object.defineProperty(ObjectValue.prototype, propName, {
        configurable: true,
        get: function () {
          let binding = this[propName + "_binding"];
          return binding.descriptor.value;
        },
        set: function (v) {
          (0, _invariant2.default)(!this.isHavocedObject(), "cannot mutate a havoced object");
          let binding = this[propName + "_binding"];
          this.$Realm.recordModifiedProperty(binding);
          binding.descriptor.value = v;
        }
      });
    }
  } // undefined when the property is "missing"


  // error


  // function


  // promise


  // iterator


  // set


  // react


  // map


  // weak map


  // weak set


  // date
  // of type number

  // array


  // regex


  // string


  // data view


  // array buffer


  // generator


  // typed array


  // backpointer to the constructor if this object was created its prototype object


  // partial objects


  // tainted objects


  // If true, the object has no property getters or setters and it is safe
  // to return AbstractValue for unknown properties.


  // If true, it is not safe to perform any more mutations that would change
  // the object's serialized form.


  // Specifies whether the object is a template that needs to be created in a scope
  // If set, this happened during object initialization and the value is never changed again, so not tracked.


  // If true, then unknown properties should return transitively simple abstract object values


  // The abstract object for which this object is the template.
  // Use this instead of the object itself when deriving temporal values for object properties.


  // An object value with an intrinsic name can either exist from the beginning of time,
  // or it can be associated with a particular point in time by being used as a template
  // when deriving an abstract value via a generator.


  // ReactElement


  // ES2015 classes


  // We track some internal state as properties on the global object, these should
  // never be serialized.


  equals(x) {
    return x instanceof ObjectValue && this.getHash() === x.getHash();
  }

  getHash() {
    if (!this.hashValue) {
      this.hashValue = ++this.$Realm.objectCount;
    }
    return this.hashValue;
  }

  get temporalAlias() {
    return this._temporalAlias;
  }

  set temporalAlias(value) {
    this._temporalAlias = value;
  }

  hasStringOrSymbolProperties() {
    for (let prop of this.properties.values()) {
      if (prop.descriptor === undefined) continue;
      return true;
    }
    for (let prop of this.symbols.values()) {
      if (prop.descriptor === undefined) continue;
      return true;
    }
    return false;
  }

  mightBeFalse() {
    return false;
  }

  mightNotBeObject() {
    return false;
  }

  throwIfNotObject() {
    return this;
  }

  makeNotPartial() {
    this._isPartial = this.$Realm.intrinsics.false;
  }

  makePartial() {
    this._isPartial = this.$Realm.intrinsics.true;
  }

  makeSimple(option) {
    this._isSimple = this.$Realm.intrinsics.true;
    this._simplicityIsTransitive = new _index2.BooleanValue(this.$Realm, option === "transitive" || option instanceof _index2.StringValue && option.value === "transitive");
  }

  makeFinal() {
    this._isFinal = this.$Realm.intrinsics.true;
  }

  isPartialObject() {
    return !!this._isPartial && this._isPartial.mightBeTrue();
  }

  isFinalObject() {
    return !!this._isFinal && this._isFinal.mightBeTrue();
  }

  havoc() {
    this._isHavoced = this.$Realm.intrinsics.true;
  }

  isHavocedObject() {
    return !!this._isHavoced && this._isHavoced.mightBeTrue();
  }

  isSimpleObject() {
    if (this._isSimple && !this._isSimple.mightNotBeTrue()) return true;
    if (this.isPartialObject()) return false;
    if (this.symbols.size > 0) return false;
    for (let propertyBinding of this.properties.values()) {
      let desc = propertyBinding.descriptor;
      if (desc === undefined) continue; // deleted
      if (!(0, _index3.IsDataDescriptor)(this.$Realm, desc)) return false;
      if (!desc.writable) return false;
    }
    if (this.$Prototype instanceof _index2.NullValue) return true;
    if (this.$Prototype === this.$Realm.intrinsics.ObjectPrototype) return true;
    (0, _invariant2.default)(this.$Prototype);
    return this.$Prototype.isSimpleObject();
  }

  isTransitivelySimple() {
    return !!this._simplicityIsTransitive && !this._simplicityIsTransitive.mightNotBeTrue();
  }

  getExtensible() {
    return this.$Extensible.throwIfNotConcreteBoolean().value;
  }

  setExtensible(v) {
    this.$Extensible = v ? this.$Realm.intrinsics.true : this.$Realm.intrinsics.false;
  }

  getKind() {
    // we can deduce the natural prototype by checking whether the following internal slots are present
    if (this.$SymbolData !== undefined) return "Symbol";
    if (this.$StringData !== undefined) return "String";
    if (this.$NumberData !== undefined) return "Number";
    if (this.$BooleanData !== undefined) return "Boolean";
    if (this.$DateValue !== undefined) return "Date";
    if (this.$RegExpMatcher !== undefined) return "RegExp";
    if (this.$SetData !== undefined) return "Set";
    if (this.$MapData !== undefined) return "Map";
    if (this.$DataView !== undefined) return "DataView";
    if (this.$ArrayBufferData !== undefined) return "ArrayBuffer";
    if (this.$WeakMapData !== undefined) return "WeakMap";
    if (this.$WeakSetData !== undefined) return "WeakSet";
    if ((0, _utils.isReactElement)(this) && this.$Realm.react.enabled) return "ReactElement";
    if (this.$TypedArrayName !== undefined) return this.$TypedArrayName;
    // TODO #26 #712: Promises. All kinds of iterators. Generators.
    return "Object";
  }

  defineNativeMethod(name, length, callback, desc = {}) {
    let intrinsicName;
    if (typeof name === "string") {
      if (this.intrinsicName) intrinsicName = `${this.intrinsicName}.${name}`;
    } else if (name instanceof _index2.SymbolValue) {
      if (this.intrinsicName && name.intrinsicName) intrinsicName = `${this.intrinsicName}[${name.intrinsicName}]`;
    } else {
      (0, _invariant2.default)(false);
    }
    let fnValue = new _index2.NativeFunctionValue(this.$Realm, intrinsicName, name, length, callback, false);
    this.defineNativeProperty(name, fnValue, desc);
    return fnValue;
  }

  defineNativeProperty(name, value, desc = {}) {
    (0, _invariant2.default)(!value || value instanceof _index2.Value);
    this.$DefineOwnProperty(name, _extends({
      value,
      writable: true,
      enumerable: false,
      configurable: true
    }, desc));
  }

  defineNativeGetter(name, callback, desc = {}) {
    let intrinsicName, funcName;
    if (typeof name === "string") {
      funcName = `get ${name}`;
      if (this.intrinsicName) intrinsicName = `${this.intrinsicName}.${name}`;
    } else if (name instanceof _index2.SymbolValue) {
      funcName = name.$Description instanceof _index2.Value ? `get [${name.$Description.throwIfNotConcreteString().value}]` : `get [${"?"}]`;
      if (this.intrinsicName && name.intrinsicName) intrinsicName = `${this.intrinsicName}[${name.intrinsicName}]`;
    } else {
      (0, _invariant2.default)(false);
    }

    let func = new _index2.NativeFunctionValue(this.$Realm, intrinsicName, funcName, 0, callback);
    this.$DefineOwnProperty(name, _extends({
      get: func,
      set: this.$Realm.intrinsics.undefined,
      enumerable: false,
      configurable: true
    }, desc));
  }

  defineNativeConstant(name, value, desc = {}) {
    (0, _invariant2.default)(!value || value instanceof _index2.Value);
    this.$DefineOwnProperty(name, _extends({
      value,
      writable: false,
      enumerable: false,
      configurable: false
    }, desc));
  }

  getOwnPropertyKeysArray() {
    if (this.isPartialObject() || this.isHavocedObject() || this.unknownProperty !== undefined) {
      _index2.AbstractValue.reportIntrospectionError(this);
      throw new _errors.FatalError();
    }

    let keyArray = Array.from(this.properties.keys());
    keyArray = keyArray.filter(x => {
      let pb = this.properties.get(x);
      if (!pb || pb.descriptor === undefined) return false;
      let pv = pb.descriptor.value;
      if (pv === undefined) return true;
      (0, _invariant2.default)(pv instanceof _index2.Value);
      if (!pv.mightHaveBeenDeleted()) return true;
      // The property may or may not be there at runtime.
      // We can at best return an abstract keys array.
      // For now just terminate.
      (0, _invariant2.default)(pv instanceof _index2.AbstractValue);
      _index2.AbstractValue.reportIntrospectionError(pv);
      throw new _errors.FatalError();
    });
    this.$Realm.callReportObjectGetOwnProperties(this);
    return keyArray;
  }

  // Note that internal properties will not be copied to the snapshot, nor will they be removed.
  getSnapshot(options) {
    try {
      if (this.temporalAlias !== undefined) return this.temporalAlias;
      (0, _invariant2.default)(!this.isPartialObject());
      let template = new ObjectValue(this.$Realm, this.$Realm.intrinsics.ObjectPrototype);
      this.copyKeys(this.$OwnPropertyKeys(), this, template);
      let result = _index2.AbstractValue.createTemporalFromBuildFunction(this.$Realm, ObjectValue, [template], ([x]) => x, {
        skipInvariant: true
      });
      (0, _invariant2.default)(result instanceof _index2.AbstractObjectValue);
      result.values = new _index.ValuesDomain(template);
      return result;
    } finally {
      if (options && options.removeProperties) {
        this.properties = new Map();
        this.symbols = new Map();
        this.unknownProperty = undefined;
      }
    }
  }

  copyKeys(keys, from, to) {
    // c. Repeat for each element nextKey of keys in List order,
    for (let nextKey of keys) {
      // i. Let desc be ? from.[[GetOwnProperty]](nextKey).
      let desc = from.$GetOwnProperty(nextKey);

      // ii. If desc is not undefined and desc.[[Enumerable]] is true, then
      if (desc && desc.enumerable) {
        _singletons.Properties.ThrowIfMightHaveBeenDeleted(desc.value);

        // 1. Let propValue be ? Get(from, nextKey).
        let propValue = (0, _index3.Get)(this.$Realm, from, nextKey);

        // 2. Perform ? Set(to, nextKey, propValue, true).
        _singletons.Properties.Set(this.$Realm, to, nextKey, propValue, true);
      }
    }
  }

  _serialize(set, stack) {
    let obj = set({});

    for (let [key, propertyBinding] of this.properties) {
      let desc = propertyBinding.descriptor;
      if (desc === undefined) continue; // deleted
      _singletons.Properties.ThrowIfMightHaveBeenDeleted(desc.value);
      let serializedDesc = { enumerable: desc.enumerable, configurable: desc.configurable };
      if (desc.value) {
        serializedDesc.writable = desc.writable;
        (0, _invariant2.default)(desc.value instanceof _index2.Value);
        serializedDesc.value = desc.value.serialize(stack);
      } else {
        (0, _invariant2.default)(desc.get !== undefined);
        serializedDesc.get = desc.get.serialize(stack);
        (0, _invariant2.default)(desc.set !== undefined);
        serializedDesc.set = desc.set.serialize(stack);
      }
      Object.defineProperty(obj, key, serializedDesc);
    }
    return obj;
  }

  // ECMA262 9.1.1
  $GetPrototypeOf() {
    return this.$Prototype;
  }

  // ECMA262 9.1.2
  $SetPrototypeOf(V) {
    // 1. Return ! OrdinarySetPrototypeOf(O, V).
    return _singletons.Properties.OrdinarySetPrototypeOf(this.$Realm, this, V);
  }

  // ECMA262 9.1.3
  $IsExtensible() {
    // 1. Return ! OrdinaryIsExtensible(O).
    return (0, _index3.OrdinaryIsExtensible)(this.$Realm, this);
  }

  // ECMA262 9.1.4
  $PreventExtensions() {
    // 1. Return ! OrdinaryPreventExtensions(O).
    return (0, _index3.OrdinaryPreventExtensions)(this.$Realm, this);
  }

  // ECMA262 9.1.5
  $GetOwnProperty(P) {
    // 1. Return ! OrdinaryGetOwnProperty(O, P).
    return _singletons.Properties.OrdinaryGetOwnProperty(this.$Realm, this, P);
  }

  // ECMA262 9.1.6
  $DefineOwnProperty(P, Desc) {
    // 1. Return ? OrdinaryDefineOwnProperty(O, P, Desc).
    return _singletons.Properties.OrdinaryDefineOwnProperty(this.$Realm, this, P, Desc);
  }

  // ECMA262 9.1.7
  $HasProperty(P) {
    if (this.unknownProperty !== undefined && this.$GetOwnProperty(P) === undefined) {
      _index2.AbstractValue.reportIntrospectionError(this, P);
      throw new _errors.FatalError();
    }

    return (0, _index3.OrdinaryHasProperty)(this.$Realm, this, P);
  }

  // ECMA262 9.1.8
  $Get(P, Receiver) {
    let prop = this.unknownProperty;
    if (prop !== undefined && prop.descriptor !== undefined && this.$GetOwnProperty(P) === undefined) {
      let desc = prop.descriptor;
      (0, _invariant2.default)(desc !== undefined);
      let val = desc.value;
      (0, _invariant2.default)(val instanceof _index2.AbstractValue);
      let propName;
      if (P instanceof _index2.StringValue) {
        propName = P;
      } else if (typeof P === "string") {
        propName = new _index2.StringValue(this.$Realm, P);
      } else {
        _index2.AbstractValue.reportIntrospectionError(val, "abstract computed property name");
        throw new _errors.FatalError();
      }
      return this.specializeJoin(val, propName);
    }

    // 1. Return ? OrdinaryGet(O, P, Receiver).
    return (0, _index3.OrdinaryGet)(this.$Realm, this, P, Receiver);
  }

  $GetPartial(P, Receiver) {
    if (Receiver instanceof _index2.AbstractValue && Receiver.getType() === _index2.StringValue && P === "length") {
      return _index2.AbstractValue.createFromTemplate(this.$Realm, lengthTemplate, _index2.NumberValue, [Receiver], lengthTemplateSrc);
    }

    if (!(P instanceof _index2.AbstractValue)) return this.$Get(P, Receiver);

    // A string coercion might have side-effects.
    // TODO #1682: We assume that simple objects mean that they don't have a
    // side-effectful valueOf and toString but that's not enforced.
    if (P.mightNotBeString() && P.mightNotBeNumber() && !P.isSimpleObject()) {
      if (this.$Realm.isInPureScope()) {
        // If we're in pure scope, we can havoc the key and keep going.
        // Coercion can only have effects on anything reachable from the key.
        _singletons.Havoc.value(this.$Realm, P);
      } else {
        let error = new _errors.CompilerDiagnostic("property key might not have a well behaved toString or be a symbol", this.$Realm.currentLocation, "PP0002", "RecoverableError");
        if (this.$Realm.handleError(error) !== "Recover") {
          throw new _errors.FatalError();
        }
      }
    }

    // We assume that simple objects have no getter/setter properties.
    if (!this.isSimpleObject()) {
      if (this.$Realm.isInPureScope()) {
        // If we're in pure scope, we can havoc the object. Coercion
        // can only have effects on anything reachable from this object.
        // We assume that if the receiver is different than this object,
        // then we only got here because there were no other keys with
        // this name on other parts of the prototype chain.
        // TODO #1675: A fix to 1675 needs to take this into account.
        _singletons.Havoc.value(this.$Realm, Receiver);
        return _index2.AbstractValue.createTemporalFromBuildFunction(this.$Realm, _index2.Value, [Receiver, P], ([o, p]) => t.memberExpression(o, p, true));
      } else {
        let error = new _errors.CompilerDiagnostic("unknown property access might need to invoke a getter", this.$Realm.currentLocation, "PP0030", "RecoverableError");
        if (this.$Realm.handleError(error) !== "Recover") {
          throw new _errors.FatalError();
        }
      }
    }

    P = _singletons.To.ToStringAbstract(this.$Realm, P);

    // If all else fails, use this expression
    // TODO #1675: Check the prototype chain for known properties too.
    let result;
    if (this.isPartialObject()) {
      if (isWidenedValue(P)) {
        // TODO #1678: Use a snapshot or havoc this object.
        return _index2.AbstractValue.createTemporalFromBuildFunction(this.$Realm, _index2.Value, [this, P], ([o, p]) => t.memberExpression(o, p, true));
      }
      result = _index2.AbstractValue.createFromType(this.$Realm, _index2.Value, "sentinel member expression");
      result.args = [this, P];
    } else {
      result = this.$Realm.intrinsics.undefined;
    }
    // Get a specialization of the join of all values written to the object
    // with abstract property names.
    let prop = this.unknownProperty;
    if (prop !== undefined) {
      let desc = prop.descriptor;
      if (desc !== undefined) {
        let val = desc.value;
        (0, _invariant2.default)(val instanceof _index2.AbstractValue);
        result = this.specializeJoin(val, P);
      }
    }
    // Join in all of the other values that were written to the object with
    // concrete property names.
    for (let [key, propertyBinding] of this.properties) {
      let desc = propertyBinding.descriptor;
      if (desc === undefined) continue; // deleted
      (0, _invariant2.default)(desc.value !== undefined); // otherwise this is not simple
      let val = desc.value;
      (0, _invariant2.default)(val instanceof _index2.Value);
      let cond = _index2.AbstractValue.createFromBinaryOp(this.$Realm, "===", P, new _index2.StringValue(this.$Realm, key), undefined, "check for known property");
      result = _singletons.Join.joinValuesAsConditional(this.$Realm, cond, val, result);
    }
    return result;
  }

  specializeJoin(absVal, propName) {
    if (absVal.kind === "widened property") {
      let ob = absVal.args[0];
      if (propName instanceof _index2.StringValue) {
        let pName = propName.value;
        let pNumber = +pName;
        if (pName === pNumber + "") propName = new _index2.NumberValue(this.$Realm, pNumber);
      }
      return _index2.AbstractValue.createTemporalFromBuildFunction(this.$Realm, absVal.getType(), [ob, propName], ([o, p]) => {
        return t.memberExpression(o, p, true);
      });
    }
    (0, _invariant2.default)(absVal.args.length === 3 && absVal.kind === "conditional");
    let generic_cond = absVal.args[0];
    (0, _invariant2.default)(generic_cond instanceof _index2.AbstractValue);
    let cond = this.specializeCond(generic_cond, propName);
    let arg1 = absVal.args[1];
    if (arg1 instanceof _index2.AbstractValue && arg1.args.length === 3) arg1 = this.specializeJoin(arg1, propName);
    let arg2 = absVal.args[2];
    if (arg2 instanceof _index2.AbstractValue && arg2.args.length === 3) arg2 = this.specializeJoin(arg2, propName);
    return _index2.AbstractValue.createFromConditionalOp(this.$Realm, cond, arg1, arg2, absVal.expressionLocation);
  }

  specializeCond(absVal, propName) {
    if (absVal.kind === "template for property name condition") return _index2.AbstractValue.createFromBinaryOp(this.$Realm, "===", absVal.args[0], propName);
    return absVal;
  }

  // ECMA262 9.1.9
  $Set(P, V, Receiver) {
    // 1. Return ? OrdinarySet(O, P, V, Receiver).
    return _singletons.Properties.OrdinarySet(this.$Realm, this, P, V, Receiver);
  }

  $SetPartial(P, V, Receiver) {
    if (!(P instanceof _index2.AbstractValue)) return this.$Set(P, V, Receiver);
    let pIsLoopVar = isWidenedValue(P);
    let pIsNumeric = _index2.Value.isTypeCompatibleWith(P.getType(), _index2.NumberValue);

    function createTemplate(realm, propName) {
      return _index2.AbstractValue.createFromBinaryOp(realm, "===", propName, new _index2.StringValue(realm, ""), undefined, "template for property name condition");
    }

    // We assume that simple objects have no getter/setter properties and
    // that all properties are writable.
    if (this !== Receiver || !this.isSimpleObject() || P.mightNotBeString() && P.mightNotBeNumber() && !P.isSimpleObject()) {
      _index2.AbstractValue.reportIntrospectionError(P, "TODO #1021");
      throw new _errors.FatalError();
    }

    P = _singletons.To.ToStringAbstract(this.$Realm, P);

    let prop;
    if (this.unknownProperty === undefined) {
      prop = {
        descriptor: undefined,
        object: this,
        key: P
      };
      this.unknownProperty = prop;
    } else {
      prop = this.unknownProperty;
    }
    this.$Realm.recordModifiedProperty(prop);
    let desc = prop.descriptor;
    if (desc === undefined) {
      let newVal = V;
      if (!(V instanceof _index2.UndefinedValue) && !isWidenedValue(P)) {
        // join V with undefined, using a property name test as the condition
        let cond = createTemplate(this.$Realm, P);
        newVal = _singletons.Join.joinValuesAsConditional(this.$Realm, cond, V, this.$Realm.intrinsics.undefined);
      }
      prop.descriptor = {
        writable: true,
        enumerable: true,
        configurable: true,
        value: newVal
      };
    } else {
      // join V with current value of this.unknownProperty. I.e. weak update.
      let oldVal = desc.value;
      (0, _invariant2.default)(oldVal instanceof _index2.Value);
      let newVal = oldVal;
      if (!(V instanceof _index2.UndefinedValue)) {
        if (isWidenedValue(P)) {
          newVal = V; // It will be widened later on
        } else {
          let cond = createTemplate(this.$Realm, P);
          newVal = _singletons.Join.joinValuesAsConditional(this.$Realm, cond, V, oldVal);
        }
      }
      desc.value = newVal;
    }

    // Since we don't know the name of the property we are writing to, we also need
    // to perform weak updates of all of the known properties.
    // First clear out this.unknownProperty so that helper routines know its OK to update the properties
    let savedUnknownProperty = this.unknownProperty;
    this.unknownProperty = undefined;
    for (let [key, propertyBinding] of this.properties) {
      if (pIsLoopVar && pIsNumeric) {
        // Delete numeric properties and don't do weak updates on other properties.
        if (key !== +key + "") continue;
        this.properties.delete(key);
        continue;
      }
      let oldVal = this.$Realm.intrinsics.empty;
      if (propertyBinding.descriptor && propertyBinding.descriptor.value) {
        oldVal = propertyBinding.descriptor.value;
        (0, _invariant2.default)(oldVal instanceof _index2.Value); // otherwise this is not simple
      }
      let cond = _index2.AbstractValue.createFromBinaryOp(this.$Realm, "===", P, new _index2.StringValue(this.$Realm, key));
      let newVal = _singletons.Join.joinValuesAsConditional(this.$Realm, cond, V, oldVal);
      _singletons.Properties.OrdinarySet(this.$Realm, this, key, newVal, Receiver);
    }
    this.unknownProperty = savedUnknownProperty;

    return true;
  }

  // ECMA262 9.1.10
  $Delete(P) {
    if (this.unknownProperty !== undefined) {
      // TODO #946: generate a delete from the object
      _index2.AbstractValue.reportIntrospectionError(this, P);
      throw new _errors.FatalError();
    }

    // 1. Return ? OrdinaryDelete(O, P).
    return _singletons.Properties.OrdinaryDelete(this.$Realm, this, P);
  }

  // ECMA262 9.1.11
  $OwnPropertyKeys() {
    return (0, _index3.OrdinaryOwnPropertyKeys)(this.$Realm, this);
  }
}
exports.default = ObjectValue;
ObjectValue.trackedPropertyNames = ["_isPartial", "_isHavoced", "_isSimple", "_isFinal", "_simplicityIsTransitive", "_temporalAlias", "$ArrayIteratorNextIndex", "$DateValue", "$Extensible", "$IteratedList", "$IteratedObject", "$IteratedSet", "$IteratedString", "$Map", "$MapData", "$MapNextIndex", "$Prototype", "$SetData", "$SetNextIndex", "$StringIteratorNextIndex", "$WeakMapData", "$WeakSetData"];
//# sourceMappingURL=ObjectValue.js.map