UNPKG

prepack

Version:

Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.

1,035 lines (830 loc) 42.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _completions = require("../completions.js"); var _errors = require("../errors.js"); var _generator = require("../utils/generator.js"); var _PreludeGenerator = require("../utils/PreludeGenerator.js"); var _index = require("./index.js"); var _index2 = require("../methods/index.js"); var _index3 = require("../domains/index.js"); var _invariant = _interopRequireDefault(require("../invariant.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 AbstractValue extends _index.Value { constructor(realm, types, values, hashValue, args, operationDescriptor, optionalArgs) { (0, _invariant.default)(realm.useAbstractInterpretation); super(realm, optionalArgs ? optionalArgs.intrinsicName : undefined); (0, _invariant.default)(!_index.Value.isTypeCompatibleWith(types.getType(), _index.ObjectValue) || this instanceof _index.AbstractObjectValue); (0, _invariant.default)(types.getType() !== _index.NullValue && types.getType() !== _index.UndefinedValue); this.types = types; this.values = values; this.mightBeEmpty = false; this.operationDescriptor = operationDescriptor; this.args = args; this.hashValue = hashValue; this.kind = optionalArgs ? optionalArgs.kind : undefined; this.shape = optionalArgs ? optionalArgs.shape : undefined; } toDisplayString() { return "[Abstract " + this.hashValue.toString() + "]"; } addSourceLocationsTo(locations, seenValues = new Set()) { if (seenValues.has(this)) return; seenValues.add(this); for (let val of this.args) { if (val instanceof AbstractValue) val.addSourceLocationsTo(locations, seenValues); } } addSourceNamesTo(names, visited = new Set()) { if (visited.has(this)) return; visited.add(this); let realm = this.$Realm; function add_intrinsic(name) { if (name.startsWith("_$")) { let temporalOperationEntryArgs = realm.derivedIds.get(name); (0, _invariant.default)(temporalOperationEntryArgs !== undefined); add_args(temporalOperationEntryArgs.args); } else if (names.indexOf(name) < 0) { names.push(name); } } function add_args(args) { if (args === undefined) return; for (let val of args) { if (val.intrinsicName) { add_intrinsic(val.intrinsicName); } else if (val instanceof AbstractValue) { val.addSourceNamesTo(names, visited); } else if (val instanceof _index.StringValue) { if (val.value.startsWith("__")) { names.push(val.value.slice(2)); } } } } if (this.intrinsicName) { add_intrinsic(this.intrinsicName); } add_args(this.args); } equals(x) { if (x instanceof _index.ConcreteValue) return false; let thisArgs = this.args; let n = thisArgs.length; let argsAreEqual = () => { (0, _invariant.default)(x instanceof AbstractValue); let xArgs = x.args; let m = xArgs.length; (0, _invariant.default)(n === m); // Will be true if kinds are the same. Caller should see to it. for (let i = 0; i < n; i++) { let a = thisArgs[i]; let b = xArgs[i]; if (!a.equals(b)) return false; } return true; }; return this === x || x instanceof AbstractValue && this.kind === x.kind && this.hashValue === x.hashValue && (this.intrinsicName && this.intrinsicName.length > 0 && this.intrinsicName === x.intrinsicName || n > 0 && argsAreEqual()); } getHash() { return this.hashValue; } getType() { return this.types.getType(); } getIdentifier() { (0, _invariant.default)(this.hasIdentifier()); (0, _invariant.default)(this.operationDescriptor !== undefined); let { id } = this.operationDescriptor.data; (0, _invariant.default)(id !== undefined); return id; } hasIdentifier() { return this.operationDescriptor ? this.operationDescriptor.type === "IDENTIFIER" : false; } // this => val. A false value does not imply that !(this => val). implies(val, depth = 0) { if (depth > 5) return false; if (this.equals(val)) return true; // x => x regardless of its value if (this.kind === "||") { let [x, y] = this.args; let xi = x.implies(val, depth + 1) || x instanceof AbstractValue && this.$Realm.pathConditions.impliesNot(x, depth + 1); if (!xi) return false; let yi = y.implies(val, depth + 1) || y instanceof AbstractValue && this.$Realm.pathConditions.impliesNot(y, depth + 1); return yi; } else if (this.kind === "&&") { let [x, y] = this.args; let xi = x.implies(val, depth + 1) || x instanceof AbstractValue && this.$Realm.pathConditions.impliesNot(x, depth + 1); if (xi) return true; let yi = y.implies(val, depth + 1) || y instanceof AbstractValue && this.$Realm.pathConditions.impliesNot(y, depth + 1); return yi; } else if (this.kind === "!") { let [nx] = this.args; (0, _invariant.default)(nx instanceof AbstractValue); if (nx.kind === "!") { // !!x => val if x => val let [x] = nx.args; (0, _invariant.default)(x instanceof AbstractValue); return x.implies(val, depth + 1); } } else if (this.kind === "conditional") { let [c, x, y] = this.args; // (c ? x : y) => val if x is true and y is false and c = val if (!x.mightNotBeTrue() && !y.mightNotBeFalse()) { return c.equals(val); } if (val.kind === "!==" || val.kind === "!=") { let [vx, vy] = val.args; if (!x.mightNotBeFalse()) { // (c ? false : y) => vx !== undefined && vx !== null if y => vx, since val is false unless y is true if (vx instanceof AbstractValue && (vy instanceof _index.NullValue || vy instanceof _index.UndefinedValue)) return y.implies(vx, depth + 1); // (c ? false : y) => undefined !== vy && null !== vy if y => vy, since val is false unless y is true if ((vx instanceof _index.NullValue || vx instanceof _index.UndefinedValue) && vy instanceof AbstractValue) return y.implies(vy, depth + 1); } else if (!y.mightNotBeFalse()) { // (c ? x : false) => vx !== undefined && vx !== null if x => vx, since val is false unless x is true if (vx instanceof AbstractValue && (vy instanceof _index.NullValue || vy instanceof _index.UndefinedValue)) return x.implies(vx, depth + 1); // (c ? x : false) => undefined !== vy && null !== vy if x => vy, since val is false unless x is true if ((vx instanceof _index.NullValue || vx instanceof _index.UndefinedValue) && vy instanceof AbstractValue) return x.implies(vy, depth + 1); } } // (c ? x : false) => c && x (if c or x were falsy, (c ? x : false) could not be true) if (!y.mightNotBeFalse()) { if (c.implies(val, depth + 1)) return true; if (x.implies(val, depth + 1)) return true; } } else if (this.kind === "!==") { // (0 !== x) => x since undefined, null, false, 0, NaN and "" are excluded by the !== and all other values are thruthy let [x, y] = this.args; if (x instanceof _index.NumberValue && x.value === 0) return y.equals(val); if (y instanceof _index.NumberValue && y.value === 0) return x.equals(val); } else if ((this.kind === "===" || this.kind === "==") && (val.kind === "===" || val.kind === "==")) { let [x, y] = this.args; let [vx, vy] = val.args; if (x instanceof _index.NullValue || x instanceof _index.UndefinedValue) { if (val.kind === "==") { // null/undefined === y && null/undefined === vy && y === vy => null/undefined == vy if (vx instanceof _index.NullValue || vx instanceof _index.UndefinedValue) return y.equals(vy); // null/undefined === y && vx === null/undefined && y === vx => null/undefined == vx if (vy instanceof _index.NullValue || vy instanceof _index.UndefinedValue) return y.equals(vx); } else { (0, _invariant.default)(val.kind === "==="); // null === y && null === vy && y === vy => null === vy // undefined === y && undefined === vy && y === vy => undefined === vy if (x.equals(vx)) return y.equals(vy); // null === y && vx === null && y === vx => vx === null // undefined === y && vx === undefined && y === vx => vx === undefined if (x.equals(vy)) return y.equals(vx); } } if (y instanceof _index.NullValue || y instanceof _index.UndefinedValue) { if (val.kind === "==") { // x === null/undefined && null/undefined === vy && x === vy => null/undefined == vy if (vx instanceof _index.NullValue || vx instanceof _index.UndefinedValue) return x.equals(vy); // x === null/undefined && vx === null/undefined && x === vx => null/undefined == vx if (vy instanceof _index.NullValue || vy instanceof _index.UndefinedValue) return x.equals(vx); } else { (0, _invariant.default)(val.kind === "==="); // x === null && null === vy && x === vy => null === vy // x == undefined && undefined === vy && x === vy => undefined === vy if (y.equals(vx)) return x.equals(vy); // x === null && vx === null && x === vx => null == vy // x === undefined && vx === undefined && x === vx => vx === undefined if (y.equals(vy)) return x.equals(vx); } } } // x => !y if y => !x if (val.kind === "!") { let [y] = val.args; (0, _invariant.default)(y instanceof AbstractValue); return y.impliesNot(this, depth + 1); } return false; } // this => !val. A false value does not imply that !(this => !val). impliesNot(val, depth = 0) { if (depth > 5) return false; if (this.equals(val)) return false; // x => x regardless of its value, hence x => !val is false if (this.kind === "||") { let [x, y] = this.args; let xi = x.impliesNot(val, depth + 1); if (!xi) return false; let yi = y.impliesNot(val, depth + 1); return yi; } else if (this.kind === "&&") { let [x, y] = this.args; let xi = x.impliesNot(val, depth + 1); if (xi) return true; let yi = y.impliesNot(val, depth + 1); return yi; } else if (this.kind === "!") { let [nx] = this.args; (0, _invariant.default)(nx instanceof AbstractValue); if (nx.kind === "!") { // !!x => !y if y => !x let [x] = nx.args; (0, _invariant.default)(x instanceof AbstractValue); return x.impliesNot(val, depth + 1); } if (nx.kind === "abstractConcreteUnion") return false; // can't use two valued logic for this. // !x => !val if val => x since if val is false x can be any value and if val is true then x must be true return val.implies(nx); } else if (this.kind === "conditional") { let [c, x, y] = this.args; // (c ? x : y) => !val if x is false and y is true and c = val if (!x.mightNotBeFalse() && !y.mightNotBeTrue()) { return c.equals(val); } if (val.kind === "===" || val.kind === "==") { let [vx, vy] = val.args; if (!x.mightNotBeFalse()) { // (c ? false : y) => !(vx === undefined) && !(vx === null) if y => vx, since val is false unless y is true if (vx instanceof AbstractValue && (vy instanceof _index.NullValue || vy instanceof _index.UndefinedValue)) return y.implies(vx, depth + 1); // (c ? false : y) => !(undefined === vy) && !(null === vy) if y => vy, since val is false unless y is true if ((vx instanceof _index.NullValue || vx instanceof _index.UndefinedValue) && vy instanceof AbstractValue) return y.implies(vy, depth + 1); } else if (!y.mightNotBeFalse()) { // (c ? x : false) => !(vx === undefined) && !(vx === null) if x => vx, since val is false unless x is true if (vx instanceof AbstractValue && (vy instanceof _index.NullValue || vy instanceof _index.UndefinedValue)) return x.implies(vx, depth + 1); // (c ? x : false) => !(undefined === vy) && !(null !== vy) if x => vy, since val is false unless x is true if ((vx instanceof _index.NullValue || vx instanceof _index.UndefinedValue) && vy instanceof AbstractValue) return x.implies(vy, depth + 1); } } // (c ? x : false) => c && x (if c or x were falsy, (c ? x : false) could not be true) if (!y.mightNotBeFalse()) { if (c.impliesNot(val, depth + 1)) return true; if (x.impliesNot(val, depth + 1)) return true; } } else if (this.kind === "===" && val.kind === "===") { // x === y and y !== z => !(x === z) let [x1, y1] = this.args; let [x2, y2] = val.args; if (x1.equals(x2) && y1 instanceof _index.ConcreteValue && y2 instanceof _index.ConcreteValue && !y1.equals(y2)) return true; // x === y and x !== z => !(z === y) if (y1.equals(y2) && x1 instanceof _index.ConcreteValue && x2 instanceof _index.ConcreteValue && !x1.equals(x2)) { return true; } } return false; } isTemporal() { return this.$Realm.getTemporalOperationEntryFromDerivedValue(this) !== undefined; } // todo: abstract values should never be of type UndefinedValue or NullValue, assert this mightBeFalse() { let valueType = this.getType(); if (valueType === _index.UndefinedValue) return true; if (valueType === _index.NullValue) return true; if (valueType === _index.SymbolValue) return false; if (_index.Value.isTypeCompatibleWith(valueType, _index.ObjectValue)) return false; if (this.kind === "abstractConcreteUnion") { for (let arg of this.args) if (arg.mightBeFalse()) return true; return false; } if (this.values.isTop()) return true; return this.values.mightBeFalse(); } mightNotBeFalse() { let valueType = this.getType(); if (valueType === _index.UndefinedValue) return false; if (valueType === _index.NullValue) return false; if (valueType === _index.SymbolValue) return true; if (_index.Value.isTypeCompatibleWith(valueType, _index.ObjectValue)) return true; if (this.kind === "abstractConcreteUnion") { for (let arg of this.args) if (arg.mightNotBeFalse()) return true; return false; } if (this.values.isTop()) return true; return this.values.mightNotBeFalse(); } mightBeNull() { let valueType = this.getType(); if (valueType === _index.NullValue) return true; if (valueType !== _index.PrimitiveValue && valueType !== _index.Value) return false; if (this.kind === "abstractConcreteUnion") { for (let arg of this.args) if (arg.mightBeNull()) return true; return false; } if (this.values.isTop()) return true; return this.values.includesValueOfType(_index.NullValue); } mightNotBeNull() { let valueType = this.getType(); if (valueType === _index.NullValue) return false; if (valueType !== _index.PrimitiveValue && valueType !== _index.Value) return true; if (this.kind === "abstractConcreteUnion") { for (let arg of this.args) if (arg.mightNotBeNull()) return true; return false; } if (this.values.isTop()) return true; return this.values.includesValueNotOfType(_index.NullValue); } mightBeNumber() { let valueType = this.getType(); if (_index.Value.isTypeCompatibleWith(valueType, _index.NumberValue)) return true; if (valueType !== _index.PrimitiveValue && valueType !== _index.Value) return false; if (this.kind === "abstractConcreteUnion") { for (let arg of this.args) if (arg.mightBeNumber()) return true; return false; } if (this.values.isTop()) return true; return this.values.includesValueOfType(_index.NumberValue); } mightNotBeNumber() { let valueType = this.getType(); if (_index.Value.isTypeCompatibleWith(valueType, _index.NumberValue)) return false; if (valueType !== _index.PrimitiveValue && valueType !== _index.Value) return true; if (this.kind === "abstractConcreteUnion") { for (let arg of this.args) if (arg.mightNotBeNumber()) return true; return false; } if (this.values.isTop()) return true; return this.values.includesValueNotOfType(_index.NumberValue); } mightNotBeObject() { let valueType = this.getType(); if (_index.Value.isTypeCompatibleWith(valueType, _index.PrimitiveValue)) return true; if (_index.Value.isTypeCompatibleWith(valueType, _index.ObjectValue)) return false; if (this.kind === "abstractConcreteUnion") { for (let arg of this.args) if (arg.mightNotBeObject()) return true; return false; } if (this.values.isTop()) return true; return this.values.includesValueNotOfType(_index.ObjectValue); } mightBeObject() { let valueType = this.getType(); if (_index.Value.isTypeCompatibleWith(valueType, _index.PrimitiveValue)) return false; if (_index.Value.isTypeCompatibleWith(valueType, _index.ObjectValue)) return true; if (this.kind === "abstractConcreteUnion") { for (let arg of this.args) if (arg.mightBeObject()) return true; return false; } if (this.values.isTop()) return true; return this.values.includesValueOfType(_index.ObjectValue); } mightBeString() { let valueType = this.getType(); if (valueType === _index.StringValue) return true; if (valueType !== _index.PrimitiveValue && valueType !== _index.Value) return false; if (this.kind === "abstractConcreteUnion") { for (let arg of this.args) if (arg.mightBeString()) return true; return false; } if (this.values.isTop()) return true; return this.values.includesValueOfType(_index.StringValue); } mightNotBeString() { let valueType = this.getType(); if (valueType === _index.StringValue) return false; if (valueType !== _index.PrimitiveValue && valueType !== _index.Value) return true; if (this.kind === "abstractConcreteUnion") { for (let arg of this.args) if (arg.mightNotBeString()) return true; return false; } if (this.values.isTop()) return true; return this.values.includesValueNotOfType(_index.StringValue); } mightBeUndefined() { let valueType = this.getType(); if (valueType === _index.UndefinedValue) return true; if (valueType !== _index.PrimitiveValue && valueType !== _index.Value) return false; if (this.kind === "abstractConcreteUnion") { for (let arg of this.args) if (arg.mightBeUndefined()) return true; return false; } if (this.values.isTop()) return true; return this.values.includesValueOfType(_index.UndefinedValue); } mightNotBeUndefined() { let valueType = this.getType(); if (valueType === _index.UndefinedValue) return false; if (valueType === _index.EmptyValue) return false; if (valueType !== _index.PrimitiveValue && valueType !== _index.Value) return true; if (this.kind === "abstractConcreteUnion") { for (let arg of this.args) if (arg.mightNotBeUndefined()) return true; return false; } if (this.values.isTop()) return true; return this.values.includesValueNotOfType(_index.UndefinedValue); } mightHaveBeenDeleted() { return this.mightBeEmpty; } promoteEmptyToUndefined() { if (this.values.isTop()) return this; if (!this.mightBeEmpty) return this; let cond = AbstractValue.createFromBinaryOp(this.$Realm, "===", this, this.$Realm.intrinsics.empty); let result = AbstractValue.createFromConditionalOp(this.$Realm, cond, this.$Realm.intrinsics.undefined, this); if (result instanceof AbstractValue) result.values = this.values.promoteEmptyToUndefined(); return result; } throwIfNotConcrete() { AbstractValue.reportIntrospectionError(this); throw new _errors.FatalError(); } throwIfNotConcreteNumber() { AbstractValue.reportIntrospectionError(this); throw new _errors.FatalError(); } throwIfNotConcreteString() { AbstractValue.reportIntrospectionError(this); throw new _errors.FatalError(); } throwIfNotConcreteBoolean() { AbstractValue.reportIntrospectionError(this); throw new _errors.FatalError(); } throwIfNotConcreteSymbol() { AbstractValue.reportIntrospectionError(this); throw new _errors.FatalError(); } throwIfNotConcreteObject() { AbstractValue.reportIntrospectionError(this); throw new _errors.FatalError(); } throwIfNotConcretePrimitive() { AbstractValue.reportIntrospectionError(this); throw new _errors.FatalError(); } throwIfNotObject() { (0, _invariant.default)(!(this instanceof _index.AbstractObjectValue)); AbstractValue.reportIntrospectionError(this); throw new _errors.FatalError(); } static createJoinConditionForSelectedCompletions(selector, completion) { let jcw; let jc = completion.joinCondition; let realm = jc.$Realm; let njc = AbstractValue.createFromUnaryOp(realm, "!", jc, true, undefined, true); if (completion instanceof _completions.JoinedNormalAndAbruptCompletions && completion.composedWith !== undefined) { jcw = AbstractValue.createJoinConditionForSelectedCompletions(selector, completion.composedWith); jc = AbstractValue.createFromLogicalOp(realm, "&&", jcw, jc, undefined, true); njc = AbstractValue.createFromLogicalOp(realm, "&&", jcw, njc, undefined, true); } let c = completion.consequent; let a = completion.alternate; let cContainsSelectedCompletion = c.containsSelectedCompletion(selector); let aContainsSelectedCompletion = a.containsSelectedCompletion(selector); if (!cContainsSelectedCompletion && !aContainsSelectedCompletion) { if (jcw !== undefined) return jcw; return realm.intrinsics.false; } let cCond = jc; if (cContainsSelectedCompletion) { if (c instanceof _completions.JoinedAbruptCompletions || c instanceof _completions.JoinedNormalAndAbruptCompletions) { let jcc = AbstractValue.createJoinConditionForSelectedCompletions(selector, c); cCond = AbstractValue.createFromLogicalOp(realm, "&&", cCond, jcc, undefined, true); } if (!aContainsSelectedCompletion) return cCond; } let aCond = njc; if (aContainsSelectedCompletion) { if (a instanceof _completions.JoinedAbruptCompletions || a instanceof _completions.JoinedNormalAndAbruptCompletions) { let jac = AbstractValue.createJoinConditionForSelectedCompletions(selector, a); aCond = AbstractValue.createFromLogicalOp(realm, "&&", aCond, jac, undefined, true); } if (!cContainsSelectedCompletion) return aCond; } let or = AbstractValue.createFromLogicalOp(realm, "||", cCond, aCond, undefined, true); if (completion instanceof _completions.JoinedNormalAndAbruptCompletions && completion.composedWith !== undefined) { let composedCond = AbstractValue.createJoinConditionForSelectedCompletions(selector, completion.composedWith); let and = AbstractValue.createFromLogicalOp(realm, "&&", composedCond, or, undefined, true); return and; } return or; } static createFromBinaryOp(realm, op, left, right, loc, kind, isCondition, doNotSimplify) { let leftTypes, leftValues; if (left instanceof AbstractValue) { leftTypes = left.types; leftValues = left.values; } else { leftTypes = new _index3.TypesDomain(left.getType()); (0, _invariant.default)(left instanceof _index.ConcreteValue); leftValues = new _index3.ValuesDomain(left); } let rightTypes, rightValues; if (right instanceof AbstractValue) { rightTypes = right.types; rightValues = right.values; } else { rightTypes = new _index3.TypesDomain(right.getType()); (0, _invariant.default)(right instanceof _index.ConcreteValue); rightValues = new _index3.ValuesDomain(right); } let resultTypes = _index3.TypesDomain.binaryOp(op, leftTypes, rightTypes); let resultValues = kind === "template for property name condition" ? _index3.ValuesDomain.topVal : _index3.ValuesDomain.binaryOp(realm, op, leftValues, rightValues); let [hash, args] = kind === undefined ? (0, _index2.hashBinary)(op, left, right) : (0, _index2.hashCall)(kind, left, right); let operationDescriptor = (0, _generator.createOperationDescriptor)("BINARY_EXPRESSION", { binaryOperator: op }); let result = new AbstractValue(realm, resultTypes, resultValues, hash, args, operationDescriptor); result.kind = kind || op; result.expressionLocation = loc; if (doNotSimplify) return result; return isCondition ? realm.simplifyAndRefineAbstractCondition(result) : realm.simplifyAndRefineAbstractValue(result); } static createFromLogicalOp(realm, op, left, right, loc, isCondition, doNotSimplify) { let leftTypes, leftValues; if (left instanceof AbstractValue) { leftTypes = left.types; leftValues = left.values; } else { leftTypes = new _index3.TypesDomain(left.getType()); (0, _invariant.default)(left instanceof _index.ConcreteValue); leftValues = new _index3.ValuesDomain(left); } let rightTypes, rightValues; if (right instanceof AbstractValue) { rightTypes = right.types; rightValues = right.values; } else { rightTypes = new _index3.TypesDomain(right.getType()); (0, _invariant.default)(right instanceof _index.ConcreteValue); rightValues = new _index3.ValuesDomain(right); } let resultTypes = _index3.TypesDomain.logicalOp(op, leftTypes, rightTypes); let resultValues = _index3.ValuesDomain.logicalOp(realm, op, leftValues, rightValues); let [hash, args] = (0, _index2.hashCall)(op, left, right); let Constructor = _index.Value.isTypeCompatibleWith(resultTypes.getType(), _index.ObjectValue) ? _index.AbstractObjectValue : AbstractValue; let operationDescriptor = (0, _generator.createOperationDescriptor)("LOGICAL_EXPRESSION", { logicalOperator: op }); let result = new Constructor(realm, resultTypes, resultValues, hash, args, operationDescriptor); result.kind = op; result.expressionLocation = loc; if (doNotSimplify) return result; return isCondition ? realm.simplifyAndRefineAbstractCondition(result) : realm.simplifyAndRefineAbstractValue(result); } static createFromConditionalOp(realm, condition, left, right, loc, isCondition, doNotSimplify) { if (left === right) { return left || realm.intrinsics.undefined; } if (!condition.mightNotBeTrue()) return left || realm.intrinsics.undefined; if (!condition.mightNotBeFalse()) return right || realm.intrinsics.undefined; let types = _index3.TypesDomain.joinValues(left, right); if (types.getType() === _index.NullValue) return realm.intrinsics.null; if (types.getType() === _index.UndefinedValue) return realm.intrinsics.undefined; let values = _index3.ValuesDomain.joinValues(realm, left, right); let [hash, args] = (0, _index2.hashTernary)(condition, left || realm.intrinsics.undefined, right || realm.intrinsics.undefined); let Constructor = _index.Value.isTypeCompatibleWith(types.getType(), _index.ObjectValue) ? _index.AbstractObjectValue : AbstractValue; let operationDescriptor = (0, _generator.createOperationDescriptor)("CONDITIONAL_EXPRESSION"); let result = new Constructor(realm, types, values, hash, args, operationDescriptor, { kind: "conditional" }); result.expressionLocation = loc; if (left) result.mightBeEmpty = left.mightHaveBeenDeleted(); if (right && !result.mightBeEmpty) result.mightBeEmpty = right.mightHaveBeenDeleted(); if (doNotSimplify || result.mightBeEmpty) return result; return isCondition ? realm.simplifyAndRefineAbstractCondition(result) : realm.simplifyAndRefineAbstractValue(result); } static createFromUnaryOp(realm, op, operand, prefix, loc, isCondition, doNotSimplify) { (0, _invariant.default)(op !== "delete" && op !== "++" && op !== "--"); // The operation must be pure let resultTypes = _index3.TypesDomain.unaryOp(op, new _index3.TypesDomain(operand.getType())); let resultValues = _index3.ValuesDomain.unaryOp(realm, op, operand.values); let operationDescriptor = (0, _generator.createOperationDescriptor)("UNARY_EXPRESSION", { unaryOperator: op, prefix }); let result = new AbstractValue(realm, resultTypes, resultValues, (0, _index2.hashUnary)(op, operand), [operand], operationDescriptor); result.kind = op; result.expressionLocation = loc; if (doNotSimplify) return result; return isCondition ? realm.simplifyAndRefineAbstractCondition(result) : realm.simplifyAndRefineAbstractValue(result); } /* Note that the template is parameterized by the names A, B, C and so on. When the abstract value is serialized, the serialized operations are substituted for the corresponding parameters and the resulting template is parsed into an AST subtree that is incorporated into the AST produced by the serializer. */ static createFromTemplate(realm, templateSource, resultType, operands, loc) { let kind = AbstractValue.makeKind("template", templateSource); let resultTypes = new _index3.TypesDomain(resultType); let resultValues = _index3.ValuesDomain.topVal; let hash; [hash, operands] = (0, _index2.hashCall)(kind, ...operands); let Constructor = _index.Value.isTypeCompatibleWith(resultType, _index.ObjectValue) ? _index.AbstractObjectValue : AbstractValue; (0, _invariant.default)(_PreludeGenerator.Placeholders.length >= operands.length); let operationDescriptor = (0, _generator.createOperationDescriptor)("ABSTRACT_FROM_TEMPLATE", { templateSource }); // This doesn't mean that the function is not pure, just that it creates // a new object on each call and thus is a future optimization opportunity. if (_index.Value.isTypeCompatibleWith(resultType, _index.ObjectValue)) hash = ++realm.objectCount; let result = new Constructor(realm, resultTypes, resultValues, hash, operands, operationDescriptor); result.kind = kind; result.expressionLocation = loc || realm.currentLocation; return result; } static createFromType(realm, resultType, kind, operands) { let types = new _index3.TypesDomain(resultType); let Constructor = _index.Value.isTypeCompatibleWith(resultType, _index.ObjectValue) ? _index.AbstractObjectValue : AbstractValue; let [hash, args] = (0, _index2.hashCall)(resultType.name + (kind || ""), ...(operands || [])); if (_index.Value.isTypeCompatibleWith(resultType, _index.ObjectValue)) hash = ++realm.objectCount; let result = new Constructor(realm, types, _index3.ValuesDomain.topVal, hash, args); if (kind) result.kind = kind; result.expressionLocation = realm.currentLocation; return result; } /* Emits a declaration for an identifier into the generator at the current point in time and initializes it with an expression constructed from the given template. Returns an abstract value that refers to the newly declared identifier. Note that the template must generate an expression which has no side-effects on the prepack state. It is assumed, however, that there could be side-effects on the native state unless the isPure option is specified. */ static createTemporalFromTemplate(realm, templateSource, resultType, operands, optionalArgs) { (0, _invariant.default)(resultType !== _index.UndefinedValue); let temp = AbstractValue.createFromTemplate(realm, templateSource, resultType, operands); let types = temp.types; let values = temp.values; let args = temp.args; (0, _invariant.default)(realm.generator !== undefined); (0, _invariant.default)(temp.operationDescriptor !== undefined); return realm.generator.deriveAbstract(types, values, args, temp.operationDescriptor, optionalArgs); } static createFromBuildFunction(realm, resultType, args, operationDescriptor, optionalArgs) { let types = new _index3.TypesDomain(resultType); let values = _index3.ValuesDomain.topVal; let Constructor = _index.Value.isTypeCompatibleWith(resultType, _index.ObjectValue) ? _index.AbstractObjectValue : AbstractValue; let kind = optionalArgs && optionalArgs.kind || "build function"; let hash; [hash, args] = (0, _index2.hashCall)(kind, ...args); let result = new Constructor(realm, types, values, hash, args, operationDescriptor); result.kind = kind; return result; } static createTemporalFromBuildFunction(realm, resultType, args, operationDescriptor, optionalArgs) { let types = new _index3.TypesDomain(resultType); let values = _index3.ValuesDomain.topVal; (0, _invariant.default)(realm.generator !== undefined); if (resultType === _index.UndefinedValue) { return realm.generator.emitVoidExpression(types, values, args, operationDescriptor); } else { return realm.generator.deriveAbstract(types, values, args, operationDescriptor, optionalArgs); } } static convertToTemporalIfArgsAreTemporal(realm, val, condArgs) { if (condArgs === undefined) condArgs = val.args; let temporalArg = condArgs.find(arg => arg.isTemporal()); if (temporalArg !== undefined) { let realmGenerator = realm.generator; (0, _invariant.default)(realmGenerator !== undefined); (0, _invariant.default)(val.operationDescriptor !== undefined); return realmGenerator.deriveAbstract(val.types, val.values, val.args, val.operationDescriptor); } else { return val; } } static dischargeValuesFromUnion(realm, union) { (0, _invariant.default)(union instanceof AbstractValue && union.kind === "abstractConcreteUnion"); let abstractValue = union.args[0]; (0, _invariant.default)(abstractValue instanceof AbstractValue); let concreteValues = union.args.filter(e => e instanceof _index.ConcreteValue); (0, _invariant.default)(concreteValues.length === union.args.length - 1); if (!abstractValue.isTemporal()) { // We make the abstract value in an abstract concrete union temporal, as it is predicated // on the conditions that preclude the concrete values in the union. The type invariant // also only applies in that condition, so it is skipped when deriving the value // See #2327 let realmGenerator = realm.generator; (0, _invariant.default)(realmGenerator !== undefined); (0, _invariant.default)(abstractValue.operationDescriptor !== undefined); abstractValue = realmGenerator.deriveAbstract(abstractValue.types, abstractValue.values, abstractValue.args, abstractValue.operationDescriptor, { isPure: true, skipInvariant: true }); } return [abstractValue, concreteValues]; } // Creates a union of an abstract value with one or more concrete values. // The operation descriptor for the abstract values becomes the operation descriptor for the union. // Use this only to allow instrinsic abstract objects to be null and/or undefined. static createAbstractConcreteUnion(realm, abstractValue, concreteValues) { (0, _invariant.default)(concreteValues.length > 0); (0, _invariant.default)(abstractValue instanceof AbstractValue); let checkedConcreteValues = concreteValues.filter(e => e instanceof _index.ConcreteValue); (0, _invariant.default)(checkedConcreteValues.length === concreteValues.length); let concreteSet = new Set(checkedConcreteValues); let values; if (!abstractValue.values.isTop()) { abstractValue.values.getElements().forEach(v => concreteSet.add(v)); values = new _index3.ValuesDomain(concreteSet); } else { values = _index3.ValuesDomain.topVal; } let types = _index3.TypesDomain.topVal; let [hash, operands] = (0, _index2.hashCall)("abstractConcreteUnion", abstractValue, ...checkedConcreteValues); let result = new AbstractValue(realm, types, values, hash, operands, (0, _generator.createOperationDescriptor)("SINGLE_ARG"), { kind: "abstractConcreteUnion" }); result.expressionLocation = realm.currentLocation; return result; } static createFromWidenedProperty(realm, resultTemplate, args, operationDescriptor) { let types = resultTemplate.types; let values = resultTemplate.values; let [hash] = (0, _index2.hashCall)("widened property", ...args); let Constructor = _index.Value.isTypeCompatibleWith(types.getType(), _index.ObjectValue) ? _index.AbstractObjectValue : AbstractValue; let result = new Constructor(realm, types, values, hash, args, operationDescriptor); result.kind = "widened property"; result.mightBeEmpty = resultTemplate.mightBeEmpty; result.expressionLocation = resultTemplate.expressionLocation; return result; } static createFromWidening(realm, value1, value2) { // todo: #1174 look at kind and figure out much narrower widenings let types = _index3.TypesDomain.joinValues(value1, value2); let values = _index3.ValuesDomain.topVal; let [hash] = (0, _index2.hashCall)("widened"); let Constructor = _index.Value.isTypeCompatibleWith(types.getType(), _index.ObjectValue) ? _index.AbstractObjectValue : AbstractValue; let result = new Constructor(realm, types, values, hash, []); result.kind = "widened"; result.mightBeEmpty = value1.mightHaveBeenDeleted() || value2.mightHaveBeenDeleted(); result.expressionLocation = value1.expressionLocation; return result; } static createAbstractArgument(realm, name, location, type = _index.Value, shape = undefined) { if (!realm.useAbstractInterpretation) { throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "realm is not partial"); } let realmPreludeGenerator = realm.preludeGenerator; (0, _invariant.default)(realmPreludeGenerator); let types = new _index3.TypesDomain(type); let values = _index3.ValuesDomain.topVal; let Constructor = _index.Value.isTypeCompatibleWith(type, _index.ObjectValue) ? _index.AbstractObjectValue : AbstractValue; let operationDescriptor = (0, _generator.createOperationDescriptor)("IDENTIFIER", { id: name }); let result = new Constructor(realm, types, values, 943586754858 + (0, _index2.hashString)(name), [], operationDescriptor); result.kind = AbstractValue.makeKind("abstractCounted", (realm.objectCount++).toString()); // need not be an object, but must be unique result.expressionLocation = location; result.shape = shape; return result; } static generateErrorInformationForAbstractVal(val) { let names = []; val.addSourceNamesTo(names); return `abstract value${names.length > 1 ? "s" : ""} ${names.join(" and ")}`; } static describe(val, propertyName) { let realm = val.$Realm; let identity; if (val === realm.$GlobalObject) identity = "global";else if (val instanceof AbstractValue) { identity = this.generateErrorInformationForAbstractVal(val); } else identity = val.intrinsicName || "(some value)"; let source_locations = []; if (val instanceof AbstractValue) val.addSourceLocationsTo(source_locations); let location; if (propertyName instanceof _index.SymbolValue) { let desc = propertyName.$Description; if (desc) { location = `at symbol [${desc.throwIfNotConcreteString().value}]`; } else { location = `at symbol [${"(no description)"}]`; } } else if (propertyName instanceof _index.StringValue) location = `at ${propertyName.value}`;else if (typeof propertyName === "string") location = `at ${propertyName}`;else location = source_locations.length === 0 ? "" : `at ${source_locations.join("\n")}`; return `${identity} ${location}`; } static reportIntrospectionError(val, propertyName) { let message = ""; if (!val.$Realm.suppressDiagnostics) message = `This operation is not yet supported on ${AbstractValue.describe(val, propertyName)}`; let realm = val.$Realm; return realm.reportIntrospectionError(message); } static createAbstractObject(realm, name, templateOrShape) { let value; if (templateOrShape === undefined) { templateOrShape = new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype); } value = AbstractValue.createFromTemplate(realm, name, _index.ObjectValue, []); if (!realm.isNameStringUnique(name)) { value.hashValue = ++realm.objectCount; } else { realm.saveNameString(name); } value.intrinsicName = name; if (templateOrShape instanceof _index.ObjectValue) { templateOrShape.makePartial(); templateOrShape.makeSimple(); value.values = new _index3.ValuesDomain(new Set([templateOrShape])); realm.rebuildNestedProperties(value, name); } else { value.shape = templateOrShape; } (0, _invariant.default)(value instanceof _index.AbstractObjectValue); return value; } static makeKind(prefix, suffix) { return `${prefix}:${suffix}`; } static createTemporalObjectAssign(realm, to, sources) { // Tell serializer that it may add properties to to only after temporalTo has been emitted let temporalArgs = [to, ...sources]; let preludeGenerator = realm.preludeGenerator; (0, _invariant.default)(preludeGenerator !== undefined); let temporalTo = AbstractValue.createTemporalFromBuildFunction(realm, _index.ObjectValue, temporalArgs, (0, _generator.createOperationDescriptor)("OBJECT_ASSIGN"), { skipInvariant: true, mutatesOnly: [to] }); (0, _invariant.default)(temporalTo instanceof _index.AbstractObjectValue); if (to instanceof _index.AbstractObjectValue) { temporalTo.values = to.values; } else { (0, _invariant.default)(to instanceof _index.ObjectValue); temporalTo.values = new _index3.ValuesDomain(to); } to.temporalAlias = temporalTo; return temporalTo; } } exports.default = AbstractValue; //# sourceMappingURL=AbstractValue.js.map