UNPKG

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
"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