prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
596 lines (465 loc) • 24.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = simplifyAndRefineAbstractValue;
var _errors = require("../errors.js");
var _index = require("../domains/index.js");
var _invariant = _interopRequireDefault(require("../invariant.js"));
var _realm = require("../realm.js");
var _index2 = require("../values/index.js");
var _singletons = require("../singletons.js");
var _EmptyValue = _interopRequireDefault(require("../values/EmptyValue.js"));
var _generator = require("./generator.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.
*/
function simplifyAndRefineAbstractValue(realm, isCondition, // The value is only used after converting it to a Boolean
value) {
if (value.intrinsicName !== undefined) return value;
let savedHandler = realm.errorHandler;
let savedIsReadOnly = realm.isReadOnly;
realm.isReadOnly = true;
let isRootSimplification = false;
realm.statistics.simplificationAttempts++;
if (!realm.inSimplificationPath) {
realm.inSimplificationPath = isRootSimplification = true;
}
try {
realm.errorHandler = diagnostic => {
if (diagnostic.errorCode === "PP0029") {
throw new _errors.FatalError(`${diagnostic.errorCode}: ${diagnostic.message}`);
}
throw new _errors.FatalError();
};
let result = simplify(realm, value, isCondition, 0);
if (result !== value) realm.statistics.simplifications++;
return result;
} catch (e) {
if (e.name === "Invariant Violation") throw e;
if (e instanceof _errors.FatalError && typeof e.message === "string" && e.message.includes("PP0029")) {
if (isRootSimplification) {
return value;
}
throw e;
}
return value;
} finally {
if (isRootSimplification) {
realm.abstractValueImpliesCounter = 0;
realm.inSimplificationPath = false;
}
realm.errorHandler = savedHandler;
realm.isReadOnly = savedIsReadOnly;
}
}
function simplify(realm, value, isCondition = false, depth) {
if (value instanceof _index2.ConcreteValue || depth > 5) return value;
(0, _invariant.default)(value instanceof _index2.AbstractValue);
if (isCondition || value.getType() === _index2.BooleanValue) {
if (_singletons.Path.implies(value, depth + 1)) return realm.intrinsics.true;
if (_singletons.Path.impliesNot(value, depth + 1)) return realm.intrinsics.false;
}
let loc = value.expressionLocation;
let op = value.kind;
switch (op) {
case "!":
{
let [x0] = value.args;
(0, _invariant.default)(x0 instanceof _index2.AbstractValue);
if (x0.kind === "!") {
(0, _invariant.default)(x0 instanceof _index2.AbstractValue);
let [x00] = x0.args;
let xx = simplify(realm, x00, true, depth + 1);
if (isCondition || xx.getType() === _index2.BooleanValue) return xx;
}
return negate(realm, x0, depth + 1, loc, value, isCondition);
}
case "||":
case "&&":
{
let [x0, y0] = value.args;
let x = simplify(realm, x0, isCondition, depth + 1);
let y = simplify(realm, y0, isCondition, depth + 1);
if (x instanceof _index2.AbstractValue && x.equals(y)) return x; // true && y <=> y
// true || y <=> true
if (!x.mightNotBeTrue()) return op === "&&" ? y : x; // (x == false) && y <=> x
// false || y <=> y
if (!x.mightNotBeFalse()) return op === "||" ? y : x;
if (isCondition || x.getType() === _index2.BooleanValue && y.getType() === _index2.BooleanValue) {
// (x: boolean) && true <=> x
// x || true <=> true
if (!y.mightNotBeTrue()) return op === "&&" ? x : realm.intrinsics.true; // (x: boolean) && false <=> false
// (x: boolean) || false <=> x
if (!y.mightNotBeFalse()) return op === "||" ? x : realm.intrinsics.false;
}
if (op === "||") {
if (y instanceof _index2.AbstractValue && y.kind === "||") {
// x || x || y <=> x || y
if (x.equals(y.args[0])) return y;
if (x instanceof _index2.AbstractValue && x.kind === "!") {
// !x0 || y0 || x0 <=> true, if isCondition
if (isCondition && x.args[0].equals(y.args[1])) return realm.intrinsics.true;
}
}
}
if (op === "&&") {
if (x instanceof _index2.AbstractValue) {
if (x.kind === "&&") {
// (x && y) && x <=> x && y, if isCondition
if (isCondition && x.args[0].equals(y)) return x; // (x && y) && y <=> x && y
if (x.args[1].equals(y)) return x;
} else if (x.kind === "!") {
// !x && x <=> false, if isCondition
if (isCondition && x.args[0].equals(y)) return realm.intrinsics.false;
}
}
if (y instanceof _index2.AbstractValue && y.kind === "&&") {
// x && (x && y) <=> x && y
// y && (x && y) <=> x && y
if (x.equals(y.args[0]) || x.equals(y.args[1])) return y;
} // x && (x && y || x && z) <=> x && (y || z)
if (y instanceof _index2.AbstractValue && y.kind === "||") {
let [yx, yy] = y.args;
let yxs, yys;
if (yx instanceof _index2.AbstractValue && yx.kind === "&&") {
if (x.equals(yx.args[0])) yxs = yx.args[1];else if (x.equals(yx.args[1])) yxs = yx.args[0];
}
if (yy instanceof _index2.AbstractValue && yy.kind === "&&") {
if (x.equals(yy.args[0])) yys = yy.args[1];else if (x.equals(yy.args[1])) yys = yy.args[0];
}
if (yxs !== undefined || yys !== undefined) {
let ys = _index2.AbstractValue.createFromLogicalOp(realm, "||", yxs || yx, yys || yy, undefined, isCondition);
return _index2.AbstractValue.createFromLogicalOp(realm, "&&", x, ys, undefined, isCondition);
}
}
}
if (realm.instantRender.enabled) {
if (op === "||" && x0 instanceof _index2.AbstractValue && y0 instanceof _index2.AbstractValue) {
if (x0.kind === "===" && y0.kind === "===") {
let [xa, xb] = x0.args;
let [ya, yb] = y0.args;
if (xa.equals(ya) && !xb.equals(yb) && nullOrUndefined(xb) && nullOrUndefined(yb)) return rewrite(xa);else if (xb.equals(yb) && !xa.equals(ya) && nullOrUndefined(xa) && nullOrUndefined(ya)) return rewrite(xb);else if (xa.equals(yb) && !xb.equals(ya) && nullOrUndefined(xb) && nullOrUndefined(ya)) return rewrite(xa);else if (xb.equals(ya) && !xa.equals(yb) && nullOrUndefined(xa) && nullOrUndefined(yb)) return rewrite(xb);
function nullOrUndefined(z) {
return !z.mightNotBeNull() || !z.mightNotBeUndefined();
}
function rewrite(z) {
return _index2.AbstractValue.createFromBuildFunction(realm, _index2.BooleanValue, [xa], (0, _generator.createOperationDescriptor)("CANNOT_BECOME_OBJECT"), {
kind: "global.__cannotBecomeObject(A)"
});
}
}
}
}
if (x.equals(x0) && y.equals(y0)) return value;
return _index2.AbstractValue.createFromLogicalOp(realm, value.kind, x, y, loc, isCondition, true);
}
case "<":
case "<=":
case ">":
case ">=":
return distributeConditional(realm, value, isCondition, args => _index2.AbstractValue.createFromBinaryOp(realm, op, args[0], args[1], loc, undefined, isCondition, true));
case "==":
case "!=":
case "===":
case "!==":
return simplifyEquality(realm, value, depth + 1);
case "conditional":
{
let [c0, x0, y0] = value.args;
let c = simplify(realm, c0, true, depth + 1);
let x, y;
if (c0 instanceof _index2.AbstractValue && c.mightBeFalse() && c.mightBeTrue()) {
try {
x = _singletons.Path.withCondition(c0, () => simplify(realm, x0, isCondition, depth + 1));
} catch (e) {
if (e instanceof _errors.InfeasiblePathError) {
// We now know that c0 cannot be be true on this path
return simplify(realm, y0, isCondition, depth + 1);
}
throw e;
}
try {
y = _singletons.Path.withInverseCondition(c0, () => simplify(realm, y0, isCondition, depth + 1));
} catch (e) {
if (e instanceof _errors.InfeasiblePathError) {
// We now know that c0 cannot be be false on this path
return x;
}
throw e;
}
}
let cIsFalse = !c.mightNotBeFalse();
let cIsTrue = !c.mightNotBeTrue();
if (x === undefined && !cIsFalse) x = simplify(realm, x0, isCondition, depth + 1);
if (cIsTrue) {
(0, _invariant.default)(x !== undefined); // cIsTrue ==> !cIsFalse
return x;
}
if (y === undefined) y = simplify(realm, y0, isCondition, depth + 1);
if (cIsFalse) return y;
(0, _invariant.default)(x !== undefined); // because !csIsFalse
(0, _invariant.default)(c instanceof _index2.AbstractValue);
if (_singletons.Path.implies(c, depth + 1)) return x;
let notc = _index2.AbstractValue.createFromUnaryOp(realm, "!", c, true, loc, isCondition, true);
if (!notc.mightNotBeTrue()) return y;
if (!notc.mightNotBeFalse()) return x;
(0, _invariant.default)(notc instanceof _index2.AbstractValue);
if (_singletons.Path.implies(notc, depth + 1)) return y;
if (!isCondition) {
if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "===", value, x), depth + 1)) return x;
if (!x.mightBeNumber() && _singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "!==", value, x), depth + 1)) return y;
if (!y.mightBeNumber() && _singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "!==", value, y), depth + 1)) return x;
if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "===", value, y), depth + 1)) return y;
} // c ? x : x <=> x
if (x.equals(y)) return x; // x ? x : y <=> x || y
let cs = isCondition ? c : simplify(realm, c0, false, depth + 1);
if (cs.equals(x)) return _index2.AbstractValue.createFromLogicalOp(realm, "||", x, y, loc, isCondition, true); // y ? x : y <=> y && x
if (cs.equals(y)) return _index2.AbstractValue.createFromLogicalOp(realm, "&&", y, x, loc, isCondition, true); // c ? (c ? xx : xy) : y <=> c ? xx : y
if (x instanceof _index2.AbstractValue && x.kind === "conditional") {
let [xc, xx] = x.args;
if (c.equals(xc)) return _index2.AbstractValue.createFromConditionalOp(realm, c, xx, y, value.expressionLocation, isCondition, true);
} // c ? x : (c ? y : z) : z <=> c ? x : z
if (y instanceof _index2.AbstractValue && y.kind === "conditional") {
let [yc,, z] = y.args;
if (c.equals(yc)) return _index2.AbstractValue.createFromConditionalOp(realm, c, x, z, value.expressionLocation, isCondition, true);
}
if (isCondition || x.getType() === _index2.BooleanValue && y.getType() === _index2.BooleanValue) {
// c ? true : false <=> c
if (!x.mightNotBeTrue() && !y.mightNotBeFalse()) return c; // c ? false : true <=> !c
if (!x.mightNotBeFalse() && !y.mightNotBeTrue()) return _index2.AbstractValue.createFromUnaryOp(realm, "!", c, true, loc, true);
}
if (c.equals(c0) && x.equals(x0) && y.equals(y0)) return value;
return _index2.AbstractValue.createFromConditionalOp(realm, c, x, y, value.expressionLocation, isCondition, true);
}
case "abstractConcreteUnion":
{
// The union of an abstract value with one or more concrete values.
if (realm.pathConditions.isEmpty()) return value;
let [abstractValue, concreteValues] = _index2.AbstractValue.dischargeValuesFromUnion(realm, value);
(0, _invariant.default)(abstractValue instanceof _index2.AbstractValue);
let remainingConcreteValues = [];
for (let concreteValue of concreteValues) {
if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "!==", value, concreteValue), depth + 1)) continue;
if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "===", value, concreteValue), depth + 1)) return concreteValue;
remainingConcreteValues.push(concreteValue);
}
if (remainingConcreteValues.length === 0) return abstractValue;
if (remainingConcreteValues.length === concreteValues.length) return value;
return _index2.AbstractValue.createAbstractConcreteUnion(realm, abstractValue, remainingConcreteValues);
}
default:
return value;
}
}
function distributeConditional(realm, value, isCondition, create) {
// Find a conditional argument
let condition;
let args = value.args;
for (let arg of args) if (arg instanceof _index2.AbstractValue && arg.kind === "conditional") {
if (condition === undefined) condition = arg.args[0];else if (condition !== arg.args[0]) return value; // giving up, multiple conditions involved
}
if (condition === undefined) return value; // no conditional found, nothing to do
// We have at least one conditional argument; if there are more than one, they all share the same condition
let leftArgs = args.slice(0);
let rightArgs = args.slice(0);
for (let i = 0; i < args.length; i++) {
let arg = args[i];
if (arg instanceof _index2.AbstractValue && arg.kind === "conditional") {
leftArgs[i] = arg.args[1];
rightArgs[i] = arg.args[2];
}
}
return _index2.AbstractValue.createFromConditionalOp(realm, condition, create(leftArgs), create(rightArgs), condition.expressionLocation, isCondition, true);
}
function simplifyNullCheck(realm, op, value, depth, loc) {
if (op === "==" || op === "!=") {
if (!value.mightNotBeNull() || !value.mightNotBeUndefined()) return op === "==" ? realm.intrinsics.true : realm.intrinsics.false;
if (!value.mightBeNull() && !value.mightBeUndefined()) return op === "==" ? realm.intrinsics.false : realm.intrinsics.true;
} else {
if (!value.mightNotBeNull()) return op === "===" ? realm.intrinsics.true : realm.intrinsics.false;
if (!value.mightBeNull()) return op === "===" ? realm.intrinsics.false : realm.intrinsics.true;
}
(0, _invariant.default)(value instanceof _index2.AbstractValue); // concrete values will either be null or not null
// try to simplify "(cond ? x : y) op null" to just "cond" or "!cond"
// failing that, use "cond ? x op null : y op null" if either of the subexpressions simplify
if (value.kind === "conditional" && depth < 10) {
let [cond, x, y] = value.args;
let sx = simplifyNullCheck(realm, op, x, depth + 1, loc);
let sy = simplifyNullCheck(realm, op, y, depth + 1, loc);
if (sx !== undefined && sy !== undefined) {
if (!sx.mightNotBeTrue() && !sy.mightNotBeFalse()) return makeBoolean(realm, cond, loc);
if (!sx.mightNotBeFalse() && !sy.mightNotBeTrue()) return negate(realm, cond, depth + 1, loc);
}
if (sx !== undefined || sy !== undefined) {
if (sx === undefined) sx = _index2.AbstractValue.createFromBinaryOp(realm, op, x, realm.intrinsics.null, x.expressionLocation, undefined, false, true);
if (sy === undefined) sy = _index2.AbstractValue.createFromBinaryOp(realm, op, y, realm.intrinsics.null, y.expressionLocation, undefined, false, true);
return _index2.AbstractValue.createFromConditionalOp(realm, cond, sx, sy, loc, true, true);
}
}
}
function simplifyUndefinedCheck(realm, op, value, depth, loc) {
if (op === "==" || op === "!=") {
if (!value.mightNotBeNull() || !value.mightNotBeUndefined()) return op === "==" ? realm.intrinsics.true : realm.intrinsics.false;
if (!value.mightBeNull() && !value.mightBeUndefined()) return op === "==" ? realm.intrinsics.false : realm.intrinsics.true;
} else {
if (!value.mightNotBeUndefined()) return op === "===" ? realm.intrinsics.true : realm.intrinsics.false;
if (!value.mightBeUndefined()) return op === "===" ? realm.intrinsics.false : realm.intrinsics.true;
}
(0, _invariant.default)(value instanceof _index2.AbstractValue); // concrete values will either be undefined or not undefined
// try to simplify "(cond ? x : y) op undefined" to just "cond" or "!cond"
// failing that, use "cond ? x op undefined : y op undefined" if either of the subexpressions simplify
if (value.kind === "conditional" && depth < 10) {
let [cond, x, y] = value.args;
let sx = simplifyUndefinedCheck(realm, op, x, depth + 1, loc);
let sy = simplifyUndefinedCheck(realm, op, y, depth + 1, loc);
if (sx !== undefined && sy !== undefined) {
if (!sx.mightNotBeTrue() && !sy.mightNotBeFalse()) return makeBoolean(realm, cond, loc);
if (!sx.mightNotBeFalse() && !sy.mightNotBeTrue()) return negate(realm, cond, depth + 1, loc);
}
if (sx !== undefined || sy !== undefined) {
if (sx === undefined) sx = _index2.AbstractValue.createFromBinaryOp(realm, op, x, realm.intrinsics.undefined, x.expressionLocation, undefined, false, true);
if (sy === undefined) sy = _index2.AbstractValue.createFromBinaryOp(realm, op, y, realm.intrinsics.undefined, y.expressionLocation, undefined, false, true);
return _index2.AbstractValue.createFromConditionalOp(realm, cond, sx, sy, loc, true, true);
}
}
}
function simplifyEquality(realm, equality, depth) {
let loc = equality.expressionLocation;
let op = equality.kind;
let [x, y] = equality.args;
if (y instanceof _EmptyValue.default) return equality;
if (x instanceof _index2.ConcreteValue) [x, y] = [y, x];
if (op === "===" || op === "==" || op === "!==" || op === "==") {
if (!x.mightNotBeNull()) {
let sy = simplifyNullCheck(realm, op, y, depth + 1);
if (sy !== undefined) return sy;
}
if (!y.mightNotBeNull()) {
let sx = simplifyNullCheck(realm, op, x, depth + 1);
if (sx !== undefined) return sx;
}
if (!x.mightNotBeUndefined()) {
let sy = simplifyUndefinedCheck(realm, op, y, depth + 1);
if (sy !== undefined) return sy;
}
if (!y.mightNotBeUndefined()) {
let sx = simplifyUndefinedCheck(realm, op, x, depth + 1);
if (sx !== undefined) return sx;
}
}
if (op === "===") {
let xType = x.getType();
let yType = y.getType();
if (xType !== yType) {
if (xType === _index2.Value || xType === _index2.PrimitiveValue || yType === _index2.Value || yType === _index2.PrimitiveValue) return equality;
if (_index2.Value.isTypeCompatibleWith(xType, _index2.NumberValue) && _index2.Value.isTypeCompatibleWith(yType, _index2.NumberValue) || _index2.Value.isTypeCompatibleWith(xType, _index2.ObjectValue) && _index2.Value.isTypeCompatibleWith(yType, _index2.ObjectValue)) return equality;
return realm.intrinsics.false;
} else if (x instanceof _index2.AbstractValue && x.kind === "conditional") {
let [cond, xx, xy] = x.args; // ((cond ? xx : xy) === y) && xx === y && xy !== y <=> cond
if (xx.equals(y) && !xy.equals(y)) return cond; // ((!cond ? xx : xy) === y) && xx !== y && xy === y <=> !cond
if (!xx.equals(y) && xy.equals(y)) return negate(realm, cond, depth + 1, loc);
} else if (y instanceof _index2.AbstractValue && y.kind === "conditional") {
let [cond, yx, yy] = y.args; // (x === (cond ? yx : yy) === y) && x === yx && x !== yy <=> cond
if (yx.equals(x) && !yy.equals(x)) return cond; // (x === (!cond ? yx : yy) === y) && x !== yx && x === yy <=> !cond
if (!x.equals(yx) && x.equals(yy)) return negate(realm, cond, depth + 1, loc);
}
} else if (op === "==") {
let xType = x.getType();
let xIsNullOrUndefined = xType === _index2.NullValue || xType === _index2.UndefinedValue;
let yType = y.getType();
let yIsNullOrUndefined = yType === _index2.NullValue || yType === _index2.UndefinedValue; // If x and y are both known to be null/undefined we should never get here because both should be concrete values.
(0, _invariant.default)(!xIsNullOrUndefined || !yIsNullOrUndefined);
if (xIsNullOrUndefined) {
return yType === _index2.Value || yType === _index2.PrimitiveValue ? equality : realm.intrinsics.false;
}
if (yIsNullOrUndefined) {
return xType === _index2.Value || xType === _index2.PrimitiveValue ? equality : realm.intrinsics.false;
}
}
return equality;
}
function makeBoolean(realm, value, loc = undefined) {
if (value.getType() === _index2.BooleanValue) return value;
if (value instanceof _index2.ConcreteValue) return new _index2.BooleanValue(realm, _singletons.To.ToBoolean(realm, value));
(0, _invariant.default)(value instanceof _index2.AbstractValue);
let v = _index2.AbstractValue.createFromUnaryOp(realm, "!", value, true, value.expressionLocation);
if (v instanceof _index2.ConcreteValue) return new _index2.BooleanValue(realm, !_singletons.To.ToBoolean(realm, v));
(0, _invariant.default)(v instanceof _index2.AbstractValue);
return _index2.AbstractValue.createFromUnaryOp(realm, "!", v, true, loc || value.expressionLocation);
}
function negate(realm, value, depth = 0, loc = undefined, unsimplifiedNegation = undefined, isCondition) {
if (value instanceof _index2.ConcreteValue) return _index.ValuesDomain.computeUnary(realm, "!", value);
(0, _invariant.default)(value instanceof _index2.AbstractValue);
value = simplify(realm, value, true, depth + 1);
if (!value.mightNotBeTrue()) return realm.intrinsics.false;
if (!value.mightNotBeFalse()) return realm.intrinsics.true;
(0, _invariant.default)(value instanceof _index2.AbstractValue);
if (value.kind === "!") {
let [x] = value.args;
if (isCondition || x.getType() === _index2.BooleanValue) return simplify(realm, x, true, depth + 1);
if (unsimplifiedNegation !== undefined) return unsimplifiedNegation;
return makeBoolean(realm, x, loc);
} // If NaN is not an issue, invert binary ops
if (value.args.length === 2 && !value.args[0].mightBeNumber() && !value.args[1].mightBeNumber()) {
let invertedComparison;
switch (value.kind) {
case "===":
invertedComparison = "!==";
break;
case "==":
invertedComparison = "!=";
break;
case "!==":
invertedComparison = "===";
break;
case "!=":
invertedComparison = "==";
break;
case "<":
invertedComparison = ">=";
break;
case "<=":
invertedComparison = ">";
break;
case ">":
invertedComparison = "<=";
break;
case ">=":
invertedComparison = "<";
break;
default:
break;
}
if (invertedComparison !== undefined) {
let left = simplify(realm, value.args[0], false, depth + 1);
let right = simplify(realm, value.args[1], false, depth + 1);
return _index2.AbstractValue.createFromBinaryOp(realm, invertedComparison, left, right, loc || value.expressionLocation);
}
let invertedLogicalOp;
switch (value.kind) {
case "&&":
invertedLogicalOp = "||";
break;
case "||":
invertedLogicalOp = "&&";
break;
default:
break;
}
if (invertedLogicalOp !== undefined) {
let left = negate(realm, value.args[0], depth + 1);
let right = negate(realm, value.args[1], depth + 1);
return _index2.AbstractValue.createFromLogicalOp(realm, invertedLogicalOp, left, right, loc || value.expressionLocation, true);
}
}
if (unsimplifiedNegation !== undefined) return unsimplifiedNegation;
return _index2.AbstractValue.createFromUnaryOp(realm, "!", value, true, loc || value.expressionLocation, true);
}
//# sourceMappingURL=simplifier.js.map