prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
1,069 lines (805 loc) • 39.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _errors = require("../errors.js");
var _index = require("./index.js");
var _index2 = require("../domains/index.js");
var _index3 = require("../methods/index.js");
var _singletons = require("../singletons.js");
var _invariant = _interopRequireDefault(require("../invariant.js"));
var _generator = require("../utils/generator.js");
var _realm = require("../realm.js");
var _completions = require("../completions.js");
var _descriptors = require("../descriptors.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
class AbstractObjectValue extends _index.AbstractValue {
constructor(realm, types, values, hashValue, args, operationDescriptor, optionalArgs) {
super(realm, types, values, hashValue, args, operationDescriptor, optionalArgs);
if (!values.isTop()) {
for (let element of this.values.getElements()) (0, _invariant.default)(element instanceof _index.ObjectValue);
}
}
getTemplate() {
for (let element of this.values.getElements()) {
(0, _invariant.default)(element instanceof _index.ObjectValue);
if (element.isPartialObject()) {
return element;
} else {
break;
}
}
_index.AbstractValue.reportIntrospectionError(this);
throw new _errors.FatalError();
}
set temporalAlias(temporalValue) {
if (this.values.isTop()) {
_index.AbstractValue.reportIntrospectionError(this);
throw new _errors.FatalError();
}
for (let element of this.values.getElements()) {
(0, _invariant.default)(element instanceof _index.ObjectValue);
element.temporalAlias = temporalValue;
}
}
hasStringOrSymbolProperties() {
if (this.values.isTop()) return false;
for (let element of this.values.getElements()) {
(0, _invariant.default)(element instanceof _index.ObjectValue);
if (element.hasStringOrSymbolProperties()) return true;
}
return false;
}
isPartialObject() {
// At the very least, the identity of the object is unknown
return true;
}
isSimpleObject() {
if (this.cachedIsSimpleObject === undefined) this.cachedIsSimpleObject = this._elementsAreSimpleObjects();
return this.cachedIsSimpleObject;
}
_elementsAreSimpleObjects() {
if (this.values.isTop()) return false;
let result;
for (let element of this.values.getElements()) {
(0, _invariant.default)(element instanceof _index.ObjectValue);
if (result === undefined) {
result = element.isSimpleObject();
} else if (result !== element.isSimpleObject()) {
_index.AbstractValue.reportIntrospectionError(this);
throw new _errors.FatalError();
}
}
if (result === undefined) {
_index.AbstractValue.reportIntrospectionError(this);
throw new _errors.FatalError();
}
return result;
}
mightBeFinalObject() {
// modeled objects are always read-only
if (this.shape) return true;
if (this.values.isTop()) return false;
for (let element of this.values.getElements()) {
(0, _invariant.default)(element instanceof _index.ObjectValue);
if (element.mightBeFinalObject()) return true;
}
return false;
}
mightNotBeFinalObject() {
// modeled objects are always read-only
if (this.shape) return false;
if (this.values.isTop()) return false;
for (let element of this.values.getElements()) {
(0, _invariant.default)(element instanceof _index.ObjectValue);
if (element.mightNotBeFinalObject()) return true;
}
return false;
}
mightBeFalse() {
return false;
}
mightNotBeFalse() {
return true;
}
makePartial() {
if (this.values.isTop()) {
_index.AbstractValue.reportIntrospectionError(this);
throw new _errors.FatalError();
}
for (let element of this.values.getElements()) {
(0, _invariant.default)(element instanceof _index.ObjectValue);
element.makePartial();
}
}
makeSimple(option) {
if (this.values.isTop() && this.getType() === _index.ObjectValue) {
let obj = new _index.ObjectValue(this.$Realm, this.$Realm.intrinsics.ObjectPrototype);
obj.intrinsicName = this.intrinsicName;
obj.intrinsicNameGenerated = true;
obj.makePartial();
obj._templateFor = this;
this.values = new _index2.ValuesDomain(obj);
}
if (!this.values.isTop()) {
for (let element of this.values.getElements()) {
(0, _invariant.default)(element instanceof _index.ObjectValue);
element.makeSimple(option);
}
}
this.cachedIsSimpleObject = true;
} // Use this only if it is known that only the string properties of the snapshot will be accessed.
getSnapshot(options) {
if (this.isIntrinsic()) return this; // already temporal
if (this.values.isTop()) return this; // always the same
if (this.kind === "conditional") {
let [c, l, r] = this.args;
(0, _invariant.default)(l instanceof _index.ObjectValue || l instanceof AbstractObjectValue);
let ls = l.getSnapshot(options);
(0, _invariant.default)(r instanceof _index.ObjectValue || r instanceof AbstractObjectValue);
let rs = r.getSnapshot(options);
(0, _invariant.default)(c instanceof _index.AbstractValue);
let absVal = _index.AbstractValue.createFromConditionalOp(this.$Realm, c, ls, rs, this.expressionLocation);
(0, _invariant.default)(absVal instanceof AbstractObjectValue);
return absVal;
} // If this is some other kind of abstract object we don't know how to make a copy, so just make this final
this.makeFinal();
return this;
}
makeFinal() {
if (this.shape) return;
if (this.values.isTop()) {
_index.AbstractValue.reportIntrospectionError(this);
throw new _errors.FatalError();
}
for (let element of this.values.getElements()) {
(0, _invariant.default)(element instanceof _index.ObjectValue);
element.makeFinal();
}
}
throwIfNotObject() {
return this;
}
usesOrdinaryObjectInternalPrototypeMethods() {
return true;
} // ECMA262 9.1.1
$GetPrototypeOf() {
let realm = this.$Realm;
if (this.values.isTop()) {
let error = new _errors.CompilerDiagnostic("prototype access on unknown object", this.$Realm.currentLocation, "PP0032", "FatalError");
this.$Realm.handleError(error);
throw new _errors.FatalError();
}
(0, _invariant.default)(this.kind !== "widened", "widening currently always leads to top values");
let elements = this.values.getElements();
if (elements.size === 1) {
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
return cv.$GetPrototypeOf();
}
(0, _invariant.default)(false);
} else if (this.kind === "conditional") {
// this is the join of two concrete/abstract objects
let [cond, ob1, ob2] = this.args;
(0, _invariant.default)(cond instanceof _index.AbstractValue);
(0, _invariant.default)(ob1 instanceof _index.ObjectValue || ob1 instanceof AbstractObjectValue);
(0, _invariant.default)(ob2 instanceof _index.ObjectValue || ob2 instanceof AbstractObjectValue);
let p1 = ob1.$GetPrototypeOf();
let p2 = ob2.$GetPrototypeOf();
let joinedObject = _index.AbstractValue.createFromConditionalOp(realm, cond, p1, p2);
(0, _invariant.default)(joinedObject instanceof AbstractObjectValue || joinedObject instanceof _index.ObjectValue || joinedObject instanceof _index.NullValue);
return joinedObject;
} else if (this.kind === "explicit conversion to object") {
let primitiveValue = this.args[0];
(0, _invariant.default)(!_index.Value.isTypeCompatibleWith(primitiveValue.getType(), _index.PrimitiveValue));
let result = _index.AbstractValue.createFromBuildFunction(realm, _index.ObjectValue, [primitiveValue], (0, _generator.createOperationDescriptor)("ABSTRACT_OBJECT_GET_PROTO_OF"));
(0, _invariant.default)(result instanceof AbstractObjectValue);
return result;
} else {
let joinedObject;
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
let p = cv.$GetPrototypeOf();
if (joinedObject === undefined) {
joinedObject = p;
} else {
let cond = _index.AbstractValue.createFromBinaryOp(realm, "===", this, cv, this.expressionLocation);
joinedObject = _index.AbstractValue.createFromConditionalOp(realm, cond, p, joinedObject);
}
}
(0, _invariant.default)(joinedObject instanceof AbstractObjectValue || joinedObject instanceof _index.ObjectValue || joinedObject instanceof _index.NullValue);
return joinedObject;
}
} // ECMA262 9.1.3
$IsExtensible() {
return false;
} // ECMA262 9.1.5
$GetOwnProperty(_P) {
let P = _P;
if (P instanceof _index.StringValue) P = P.value;
if (this.values.isTop()) {
let error = new _errors.CompilerDiagnostic("property access on unknown object", this.$Realm.currentLocation, "PP0031", "FatalError");
this.$Realm.handleError(error);
throw new _errors.FatalError();
}
let elements = this.values.getElements();
if (elements.size === 1) {
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
return cv.$GetOwnProperty(P);
}
(0, _invariant.default)(false);
} else if (this.kind === "conditional") {
// this is the join of two concrete/abstract objects
let [cond, ob1, ob2] = this.args;
(0, _invariant.default)(cond instanceof _index.AbstractValue);
(0, _invariant.default)(ob1 instanceof _index.ObjectValue || ob1 instanceof AbstractObjectValue);
(0, _invariant.default)(ob2 instanceof _index.ObjectValue || ob2 instanceof AbstractObjectValue);
let d1 = ob1.$GetOwnProperty(P);
let d2 = ob2.$GetOwnProperty(P);
return _singletons.Join.joinDescriptors(this.$Realm, cond, d1, d2);
} else if (this.kind === "widened") {
// This abstract object was created by repeated assignments of freshly allocated objects to the same binding inside a loop
let [ob1, ob2] = this.args; // ob1: summary of iterations 1...n, ob2: summary of iteration n+1
(0, _invariant.default)(ob1 instanceof _index.ObjectValue);
(0, _invariant.default)(ob2 instanceof _index.ObjectValue);
let d1 = ob1.$GetOwnProperty(P);
let d2 = ob2.$GetOwnProperty(P);
if (d1 === undefined || d2 === undefined) {
// We do not handle the case where different loop iterations result in different kinds of propperties
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
d1 = d1.throwIfNotConcrete(this.$Realm);
d2 = d2.throwIfNotConcrete(this.$Realm);
if (!(0, _descriptors.equalDescriptors)(d1, d2)) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
let desc = (0, _descriptors.cloneDescriptor)(d1);
(0, _invariant.default)(desc !== undefined);
if ((0, _index3.IsDataDescriptor)(this.$Realm, desc)) {
// Values may be different, i.e. values may be loop variant, so the widened value summarizes the entire loop
// equalDescriptors guarantees that both have value props and if you have a value prop is value is defined.
let d1Value = d1.value;
(0, _invariant.default)(d1Value instanceof _index.Value);
let d2Value = d2.value;
(0, _invariant.default)(d2Value instanceof _index.Value);
let dValue = _singletons.Widen.widenValues(this.$Realm, d1Value, d2Value);
(0, _invariant.default)(dValue instanceof _index.Value);
desc.value = dValue;
} else {// In this case equalDescriptors guarantees exact equality betwee d1 and d2.
// Inlining the accessors will eventually bring in data properties if the accessors have loop variant behavior
}
return desc;
} else {
let first = true;
let joinedDescriptor;
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
let desc = cv.$GetOwnProperty(P);
if (first) {
first = false;
joinedDescriptor = desc;
} else {
let cond = _index.AbstractValue.createFromBinaryOp(this.$Realm, "===", this, cv, this.expressionLocation);
(0, _invariant.default)(cond instanceof _index.AbstractValue);
joinedDescriptor = _singletons.Join.joinDescriptors(this.$Realm, cond, desc, joinedDescriptor);
}
}
return joinedDescriptor;
}
} // ECMA262 9.1.6
$DefineOwnProperty(_P, _Desc) {
let P = _P;
if (P instanceof _index.StringValue) P = P.value;
if (this.values.isTop()) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
let elements = this.values.getElements();
if (elements.size === 1) {
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
return cv.$DefineOwnProperty(P, _Desc);
}
(0, _invariant.default)(false);
} else {
let Desc = _Desc.throwIfNotConcrete(this.$Realm);
if (!(0, _index3.IsDataDescriptor)(this.$Realm, Desc)) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
} // Extract the first existing descriptor to get its existing attributes as defaults.
let firstExistingDesc;
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
firstExistingDesc = cv.$GetOwnProperty(P);
if (firstExistingDesc) {
break;
}
}
if (firstExistingDesc) {
firstExistingDesc = firstExistingDesc.throwIfNotConcrete(this.$Realm);
}
let desc = new _descriptors.PropertyDescriptor({
value: Desc.value !== undefined ? Desc.value : this.$Realm.intrinsics.undefined,
writable: Desc.writable !== undefined ? Desc.writable : firstExistingDesc ? firstExistingDesc.writable : false,
enumerable: Desc.enumerable !== undefined ? Desc.enumerable : firstExistingDesc ? firstExistingDesc.enumerable : false,
configurable: Desc.configurable !== undefined ? Desc.configurable : firstExistingDesc ? firstExistingDesc.configurable : false
});
let newVal = desc.value;
if (this.kind === "conditional") {
// this is the join of two concrete/abstract objects
let [cond, ob1, ob2] = this.args;
(0, _invariant.default)(cond instanceof _index.AbstractValue);
(0, _invariant.default)(ob1 instanceof _index.ObjectValue || ob1 instanceof AbstractObjectValue);
(0, _invariant.default)(ob2 instanceof _index.ObjectValue || ob2 instanceof AbstractObjectValue);
let d1 = ob1.$GetOwnProperty(P);
let d2 = ob2.$GetOwnProperty(P);
if (d1 !== undefined) {
d1 = d1.throwIfNotConcrete(this.$Realm);
if (!(0, _descriptors.equalDescriptors)(d1, desc)) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
}
if (d2 !== undefined) {
d2 = d2.throwIfNotConcrete(this.$Realm);
if (!(0, _descriptors.equalDescriptors)(d2, desc)) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
}
let oldVal1 = d1 === undefined || d1.value === undefined ? this.$Realm.intrinsics.empty : d1.value;
let oldVal2 = d2 === undefined || d2.value === undefined ? this.$Realm.intrinsics.empty : d2.value;
(0, _invariant.default)(oldVal1 instanceof _index.Value);
(0, _invariant.default)(oldVal2 instanceof _index.Value);
let newVal1 = _index.AbstractValue.createFromConditionalOp(this.$Realm, cond, newVal, oldVal1);
let newVal2 = _index.AbstractValue.createFromConditionalOp(this.$Realm, cond, oldVal2, newVal);
desc.value = newVal1;
let result1 = ob1.$DefineOwnProperty(P, desc);
desc.value = newVal2;
let result2 = ob2.$DefineOwnProperty(P, desc);
if (result1 !== result2) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
return result1;
} else {
(0, _invariant.default)(newVal instanceof _index.Value);
let sawTrue = false;
let sawFalse = false;
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
let d = cv.$GetOwnProperty(P);
if (d !== undefined) {
d = d.throwIfNotConcrete(this.$Realm);
if (!(0, _descriptors.equalDescriptors)(d, desc)) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
}
let dval = d === undefined || d.value === undefined ? this.$Realm.intrinsics.empty : d.value;
(0, _invariant.default)(dval instanceof _index.Value);
let cond = _index.AbstractValue.createFromBinaryOp(this.$Realm, "===", this, cv, this.expressionLocation);
desc.value = _index.AbstractValue.createFromConditionalOp(this.$Realm, cond, newVal, dval);
if (cv.$DefineOwnProperty(P, desc)) {
sawTrue = true;
} else sawFalse = true;
}
if (sawTrue && sawFalse) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
return sawTrue;
}
}
} // ECMA262 9.1.7
$HasProperty(_P) {
let P = _P;
if (P instanceof _index.StringValue) P = P.value;
if (this.values.isTop()) {
let error = new _errors.CompilerDiagnostic("property access on unknown object", this.$Realm.currentLocation, "PP0031", "FatalError");
this.$Realm.handleError(error);
throw new _errors.FatalError();
}
let elements = this.values.getElements();
if (elements.size === 1) {
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
return cv.$HasProperty(P);
}
(0, _invariant.default)(false);
} else {
let hasProp = false;
let doesNotHaveProp = false;
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
if (cv.$HasProperty(P)) hasProp = true;else doesNotHaveProp = true;
}
if (hasProp && doesNotHaveProp) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
return hasProp;
}
} // ECMA262 9.1.8
$Get(_P, Receiver) {
let P = _P;
if (P instanceof _index.StringValue) P = P.value;
if (this.values.isTop()) {
let generateAbstractGet = () => {
let ob = Receiver;
if (this.kind === "explicit conversion to object") ob = this.args[0];
let type = _index.Value;
if (P === "length" && _index.Value.isTypeCompatibleWith(this.getType(), _index.ArrayValue)) type = _index.NumberValue; // shape logic
let shapeContainer = this.kind === "explicit conversion to object" ? this.args[0] : this;
(0, _invariant.default)(shapeContainer instanceof _index.AbstractValue);
let realm = this.$Realm;
let shape = shapeContainer.shape;
let propertyShape, propertyGetter; // propertyShape expects only a string value
if ((realm.instantRender.enabled || realm.react.enabled) && shape !== undefined && (typeof P === "string" || P instanceof _index.StringValue)) {
propertyShape = shape.getPropertyShape(P instanceof _index.StringValue ? P.value : P);
if (propertyShape !== undefined) {
type = propertyShape.getAbstractType();
propertyGetter = propertyShape.getGetter();
}
} // P can also be a SymbolValue
if (typeof P === "string") {
P = new _index.StringValue(this.$Realm, P);
} // Create an temporal array with widened properties
if (type === _index.ArrayValue) {
return _index.ArrayValue.createTemporalWithWidenedNumericProperty(realm, [ob, P], (0, _generator.createOperationDescriptor)("ABSTRACT_OBJECT_GET", {
propertyGetter
}));
}
let propAbsVal = _index.AbstractValue.createTemporalFromBuildFunction(realm, type, [ob, P], (0, _generator.createOperationDescriptor)("ABSTRACT_OBJECT_GET", {
propertyGetter
}), {
skipInvariant: true,
isPure: true,
shape: propertyShape
});
return propAbsVal;
};
if (this.isSimpleObject() && this.isIntrinsic()) {
return generateAbstractGet();
} else if (this.$Realm.isInPureScope()) {
// This object might have leaked to a getter.
_singletons.Leak.value(this.$Realm, Receiver); // The getter might throw anything.
return this.$Realm.evaluateWithPossibleThrowCompletion(generateAbstractGet, _index2.TypesDomain.topVal, _index2.ValuesDomain.topVal);
}
let error = new _errors.CompilerDiagnostic("property access on unknown object", this.$Realm.currentLocation, "PP0031", "FatalError");
this.$Realm.handleError(error);
throw new _errors.FatalError();
}
let realm = this.$Realm;
let elements = this.values.getElements();
if (elements.size === 1) {
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
return cv.$Get(P, Receiver);
}
(0, _invariant.default)(false);
} else if (this.kind === "conditional") {
// this is the join of two concrete/abstract objects
let [cond, ob1, ob2] = this.args;
(0, _invariant.default)(cond instanceof _index.AbstractValue);
(0, _invariant.default)(ob1 instanceof _index.ObjectValue || ob1 instanceof AbstractObjectValue);
(0, _invariant.default)(ob2 instanceof _index.ObjectValue || ob2 instanceof AbstractObjectValue); // Evaluate the effect of each getter separately and join the result.
return realm.evaluateWithAbstractConditional(cond, () => realm.evaluateForEffects(() => ob1.$Get(P, Receiver), undefined, "ConditionalGet/1"), () => realm.evaluateForEffects(() => ob2.$Get(P, Receiver), undefined, "ConditionalGet/2"));
} else {
let result;
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
let cond = _index.AbstractValue.createFromBinaryOp(this.$Realm, "===", this, cv, this.expressionLocation);
(0, _invariant.default)(cond instanceof _index.AbstractValue);
result = realm.evaluateWithAbstractConditional(cond, () => realm.evaluateForEffects(() => cv.$Get(P, Receiver), undefined, "AbstractGet"), () => (0, _realm.construct_empty_effects)(realm, result === undefined ? undefined : new _completions.SimpleNormalCompletion(result)));
}
(0, _invariant.default)(result !== undefined);
return result;
}
}
$GetPartial(P, Receiver) {
if (!(P instanceof _index.AbstractValue)) return this.$Get(P, Receiver);
if (this.values.isTop() || !this.isSimpleObject()) {
if (this.isSimpleObject() && this.isIntrinsic()) {
return _index.AbstractValue.createTemporalFromBuildFunction(this.$Realm, _index.Value, [this, P], (0, _generator.createOperationDescriptor)("ABSTRACT_OBJECT_GET_PARTIAL"), {
skipInvariant: true,
isPure: true
});
}
if (this.$Realm.isInPureScope()) {
// If we're in a pure scope, we can leak the key and the instance,
// and leave the residual property access in place.
// We assume that if the receiver is different than this object,
// then we only got here because there can be no other keys with
// this name on earlier parts of the prototype chain.
// We have to leak since the property may be a getter or setter,
// which can run unknown code that has access to Receiver and
// (even in pure mode) can modify it in unknown ways.
_singletons.Leak.value(this.$Realm, Receiver); // Coercion can only have effects on anything reachable from the key.
_singletons.Leak.value(this.$Realm, P);
return _index.AbstractValue.createTemporalFromBuildFunction(this.$Realm, _index.Value, [Receiver, P], (0, _generator.createOperationDescriptor)("ABSTRACT_OBJECT_GET_PARTIAL"), {
skipInvariant: true,
isPure: true
});
}
let error = new _errors.CompilerDiagnostic("property access on unknown object", this.$Realm.currentLocation, "PP0031", "FatalError");
this.$Realm.handleError(error);
throw new _errors.FatalError();
}
let realm = this.$Realm;
let elements = this.values.getElements();
if (elements.size === 1) {
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
return cv.$GetPartial(P, Receiver);
}
(0, _invariant.default)(false);
} else if (this.kind === "conditional") {
// this is the join of two concrete/abstract objects
let [cond, ob1, ob2] = this.args;
(0, _invariant.default)(cond instanceof _index.AbstractValue);
(0, _invariant.default)(ob1 instanceof _index.ObjectValue || ob1 instanceof AbstractObjectValue);
(0, _invariant.default)(ob2 instanceof _index.ObjectValue || ob2 instanceof AbstractObjectValue); // Evaluate the effect of each getter separately and join the result.
return realm.evaluateWithAbstractConditional(cond, () => realm.evaluateForEffects(() => ob1.$GetPartial(P, Receiver), undefined, "ConditionalGet/1"), () => realm.evaluateForEffects(() => ob2.$GetPartial(P, Receiver), undefined, "ConditionalGet/2"));
} else {
let result;
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
let cond = _index.AbstractValue.createFromBinaryOp(this.$Realm, "===", this, cv, this.expressionLocation);
(0, _invariant.default)(cond instanceof _index.AbstractValue);
result = realm.evaluateWithAbstractConditional(cond, () => realm.evaluateForEffects(() => cv.$GetPartial(P, Receiver), undefined, "AbstractGet"), () => (0, _realm.construct_empty_effects)(realm, result === undefined ? undefined : new _completions.SimpleNormalCompletion(result)));
}
(0, _invariant.default)(result !== undefined);
return result;
}
} // ECMA262 9.1.9
$Set(P, V, Receiver) {
if (this.values.isTop()) {
return this.$SetPartial(P, V, Receiver);
}
let realm = this.$Realm;
let elements = this.values.getElements();
if (elements.size === 1) {
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
return cv.$Set(P, V, Receiver);
}
(0, _invariant.default)(false);
} else if (this.kind === "conditional") {
// this is the join of two concrete/abstract objects
let [cond, ob1, ob2] = this.args;
(0, _invariant.default)(cond instanceof _index.AbstractValue);
(0, _invariant.default)(ob1 instanceof _index.ObjectValue || ob1 instanceof AbstractObjectValue);
(0, _invariant.default)(ob2 instanceof _index.ObjectValue || ob2 instanceof AbstractObjectValue); // Evaluate the effect of each setter separately and join the effects.
let result = realm.evaluateWithAbstractConditional(cond, () => realm.evaluateForEffects(() => new _index.BooleanValue(realm, ob1.$Set(P, V, Receiver)), undefined, "ConditionalSet/1"), () => realm.evaluateForEffects(() => new _index.BooleanValue(realm, ob2.$Set(P, V, Receiver)), undefined, "ConditionalSet/2"));
if (!(result instanceof _index.BooleanValue)) {
let error = new _errors.CompilerDiagnostic("object could have both succeeded and failed updating", realm.currentLocation, "PP0041", "RecoverableError");
if (realm.handleError(error) === "Recover") {
return true;
}
throw new _errors.FatalError();
}
return result.value;
} else {
let sawTrue = false;
let sawFalse = false;
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue); // Evaluate the effect of each setter separately and join the effects.
let cond = _index.AbstractValue.createFromBinaryOp(this.$Realm, "===", this, cv, this.expressionLocation);
(0, _invariant.default)(cond instanceof _index.AbstractValue);
realm.evaluateWithAbstractConditional(cond, () => realm.evaluateForEffects(() => {
if (cv.$Set(P, V, Receiver)) {
sawTrue = true;
} else {
sawFalse = true;
}
return realm.intrinsics.empty;
}, undefined, "AbstractSet"), () => (0, _realm.construct_empty_effects)(realm));
}
if (sawTrue && sawFalse) {
let error = new _errors.CompilerDiagnostic("object could have both succeeded and failed updating", realm.currentLocation, "PP0041", "RecoverableError");
if (realm.handleError(error) === "Recover") {
return true;
}
}
return sawTrue;
}
}
$SetPartial(_P, V, Receiver) {
let P = _P;
if (!this.values.isTop() && !(P instanceof _index.AbstractValue)) return this.$Set(P, V, Receiver);
if (this.values.isTop()) {
if (this.$Realm.isInPureScope()) {
// If we're in a pure scope, we can leak the key and the instance,
// and leave the residual property assignment in place.
// We assume that if the receiver is different than this object,
// then we only got here because there can be no other keys with
// this name on earlier parts of the prototype chain.
// We have to leak since the property may be a getter or setter,
// which can run unknown code that has access to Receiver and
// (even in pure mode) can modify it in unknown ways.
_singletons.Leak.value(this.$Realm, Receiver); // We also need to leaked the value since it might leak to a setter.
_singletons.Leak.value(this.$Realm, V);
this.$Realm.evaluateWithPossibleThrowCompletion(() => {
let generator = this.$Realm.generator;
(0, _invariant.default)(generator);
if (typeof P !== "string" && !(P instanceof _index.StringValue)) {
// Coercion can only have effects on anything reachable from the key.
_singletons.Leak.value(this.$Realm, P);
}
generator.emitPropertyAssignment(Receiver, P, V);
return this.$Realm.intrinsics.undefined;
}, _index2.TypesDomain.topVal, _index2.ValuesDomain.topVal); // The emitted assignment might throw at runtime but if it does, that
// is handled by evaluateWithPossibleThrowCompletion. Anything that
// happens after this, can assume we didn't throw and therefore,
// we return true here.
return true;
}
let error = new _errors.CompilerDiagnostic("property access on unknown object", this.$Realm.currentLocation, "PP0031", "FatalError");
this.$Realm.handleError(error);
throw new _errors.FatalError();
}
let realm = this.$Realm;
let elements = this.values.getElements();
if (elements.size === 1) {
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
return cv.$SetPartial(P, V, Receiver);
}
(0, _invariant.default)(false);
} else if (this.kind === "conditional") {
// this is the join of two concrete/abstract objects
let [cond, ob1, ob2] = this.args;
(0, _invariant.default)(cond instanceof _index.AbstractValue);
(0, _invariant.default)(ob1 instanceof _index.ObjectValue || ob1 instanceof AbstractObjectValue);
(0, _invariant.default)(ob2 instanceof _index.ObjectValue || ob2 instanceof AbstractObjectValue); // Evaluate the effect of each setter separately and join the effects.
let result = realm.evaluateWithAbstractConditional(cond, () => realm.evaluateForEffects(() => new _index.BooleanValue(realm, ob1.$SetPartial(P, V, Receiver)), undefined, "ConditionalSet/1"), () => realm.evaluateForEffects(() => new _index.BooleanValue(realm, ob2.$SetPartial(P, V, Receiver)), undefined, "ConditionalSet/2"));
if (!(result instanceof _index.BooleanValue)) {
let error = new _errors.CompilerDiagnostic("object could have both succeeded and failed updating", realm.currentLocation, "PP0041", "RecoverableError");
if (realm.handleError(error) === "Recover") {
return true;
}
throw new _errors.FatalError();
}
return result.value;
} else {
let sawTrue = false;
let sawFalse = false;
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue); // Evaluate the effect of each setter separately and join the effects.
let cond = _index.AbstractValue.createFromBinaryOp(this.$Realm, "===", this, cv, this.expressionLocation);
(0, _invariant.default)(cond instanceof _index.AbstractValue);
realm.evaluateWithAbstractConditional(cond, () => realm.evaluateForEffects(() => {
if (cv.$SetPartial(P, V, Receiver)) {
sawTrue = true;
} else {
sawFalse = true;
}
return realm.intrinsics.empty;
}, undefined, "AbstractSet"), () => (0, _realm.construct_empty_effects)(realm));
}
if (sawTrue && sawFalse) {
let error = new _errors.CompilerDiagnostic("object could have both succeeded and failed updating", realm.currentLocation, "PP0041", "RecoverableError");
if (realm.handleError(error) === "Recover") {
return true;
}
}
return sawTrue;
}
} // ECMA262 9.1.10
$Delete(_P) {
let P = _P;
if (P instanceof _index.StringValue) P = P.value;
if (this.values.isTop()) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
let elements = this.values.getElements();
if (elements.size === 1) {
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
return cv.$Delete(P);
}
(0, _invariant.default)(false);
} else if (this.kind === "conditional") {
// this is the join of two concrete/abstract objects
let [cond, ob1, ob2] = this.args;
(0, _invariant.default)(cond instanceof _index.AbstractValue);
(0, _invariant.default)(ob1 instanceof _index.ObjectValue || ob1 instanceof AbstractObjectValue);
(0, _invariant.default)(ob2 instanceof _index.ObjectValue || ob2 instanceof AbstractObjectValue);
let d1 = ob1.$GetOwnProperty(P);
let d2 = ob2.$GetOwnProperty(P);
let oldVal1 = d1 === undefined ? this.$Realm.intrinsics.empty : (0, _index3.IsDataDescriptor)(this.$Realm, d1) ? d1.value : undefined;
let oldVal2 = d2 === undefined ? this.$Realm.intrinsics.empty : (0, _index3.IsDataDescriptor)(this.$Realm, d2) ? d2.value : undefined;
if (oldVal1 === undefined || oldVal2 === undefined) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
(0, _invariant.default)(oldVal1 instanceof _index.Value);
(0, _invariant.default)(oldVal2 instanceof _index.Value);
let newVal1 = _index.AbstractValue.createFromConditionalOp(this.$Realm, cond, this.$Realm.intrinsics.empty, oldVal1);
let newVal2 = _index.AbstractValue.createFromConditionalOp(this.$Realm, cond, oldVal2, this.$Realm.intrinsics.empty);
let result1 = true;
let result2 = true;
if (d1 !== undefined) {
d1 = d1.throwIfNotConcrete(this.$Realm);
let newDesc1 = (0, _descriptors.cloneDescriptor)(d1);
(0, _invariant.default)(newDesc1);
newDesc1 = newDesc1.throwIfNotConcrete(this.$Realm);
newDesc1.value = newVal1;
result1 = ob1.$DefineOwnProperty(P, newDesc1);
}
if (d2 !== undefined) {
d2 = d2.throwIfNotConcrete(this.$Realm);
let newDesc2 = (0, _descriptors.cloneDescriptor)(d2);
(0, _invariant.default)(newDesc2);
newDesc2 = newDesc2.throwIfNotConcrete(this.$Realm);
newDesc2.value = newVal2;
result2 = ob2.$DefineOwnProperty(P, newDesc2);
}
if (result1 !== result2) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
return result1;
} else {
let sawTrue = false;
let sawFalse = false;
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
let d = cv.$GetOwnProperty(P);
if (d === undefined) continue;
if (!(0, _index3.IsDataDescriptor)(this.$Realm, d)) {
_index.AbstractValue.reportIntrospectionError(this, P);
throw new _errors.FatalError();
}
let cond = _index.AbstractValue.createFromBinaryOp(this.$Realm, "===", this, cv, this.expressionLocation);
let dval = d.value;
(0, _invariant.default)(dval instanceof _index.Value);
let v = _index.AbstractValue.createFromConditionalOp(this.$Realm, cond, this.$Realm.intrinsics.empty, dval);
let newDesc = (0, _descriptors.cloneDescriptor)(d);
(0, _invariant.default)(newDesc);
newDesc.value = v;
if (cv.$DefineOwnProperty(P, newDesc)) sawTrue = true;else sawFalse = true;
}
if (sawTrue && sawFalse) {
let error = new _errors.CompilerDiagnostic("object could have both succeeded and failed updating", this.$Realm.currentLocation, "PP0041", "RecoverableError");
if (this.$Realm.handleError(error) === "Recover") {
return true;
}
}
return sawTrue;
}
}
$OwnPropertyKeys(getOwnPropertyKeysEvenIfPartial = false) {
if (this.values.isTop()) {
_index.AbstractValue.reportIntrospectionError(this);
throw new _errors.FatalError();
}
let elements = this.values.getElements();
if (elements.size === 1) {
for (let cv of elements) {
(0, _invariant.default)(cv instanceof _index.ObjectValue);
return cv.$OwnPropertyKeys(getOwnPropertyKeysEvenIfPartial);
}
(0, _invariant.default)(false);
} else {
_index.AbstractValue.reportIntrospectionError(this);
throw new _errors.FatalError();
}
}
}
exports.default = AbstractObjectValue;
//# sourceMappingURL=AbstractObjectValue.js.map