prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
274 lines (260 loc) • 12.9 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 = require("../invariant.js");
var _invariant2 = _interopRequireDefault(_invariant);
var _realm = require("../realm.js");
var _index2 = require("../values/index.js");
var _singletons = require("../singletons.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function simplifyAndRefineAbstractValue(realm, isCondition, // The value is only used after converting it to a Boolean
value) {
let savedHandler = realm.errorHandler;
let savedIsReadOnly = realm.isReadOnly;
realm.isReadOnly = true;
try {
realm.errorHandler = () => {
throw new _errors.FatalError();
};
return simplify(realm, value, isCondition);
} catch (e) {
return value;
} finally {
realm.errorHandler = savedHandler;
realm.isReadOnly = savedIsReadOnly;
}
} /**
* 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 simplify(realm, value, isCondition = false) {
if (value instanceof _index2.ConcreteValue) return value;
(0, _invariant2.default)(value instanceof _index2.AbstractValue);
if (isCondition || value.getType() === _index2.BooleanValue) {
if (_singletons.Path.implies(value)) return realm.intrinsics.true;
if (_singletons.Path.impliesNot(value)) return realm.intrinsics.false;
}
let loc = value.expressionLocation;
let op = value.kind;
switch (op) {
case "!":
{
let [x0] = value.args;
(0, _invariant2.default)(x0 instanceof _index2.AbstractValue);
if (x0.kind === "!") {
(0, _invariant2.default)(x0 instanceof _index2.AbstractValue);
let [x00] = x0.args;
let xx = simplify(realm, x00, true);
if (xx.getType() === _index2.BooleanValue) return xx;
}
let x = simplify(realm, x0, true);
return negate(realm, x, loc, x0.equals(x) ? value : undefined);
}
case "||":
case "&&":
{
let [x0, y0] = value.args;
let x = simplify(realm, x0, isCondition);
let y = simplify(realm, y0, isCondition);
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 === "||" && y instanceof _index2.AbstractValue && y.kind === "||" && x.equals(y.args[0]) && !y.args[1].mightNotBeTrue()) return y;
if (x.equals(x0) && y.equals(y0)) return value;
return _index2.AbstractValue.createFromLogicalOp(realm, value.kind, x, y, loc, isCondition);
}
case "==":
case "!=":
case "===":
case "!==":
return simplifyEquality(realm, value);
case "conditional":
{
let [c0, x0, y0] = value.args;
let c = simplify(realm, c0, true);
let cs = simplify(realm, c0, isCondition);
let x = simplify(realm, x0, isCondition);
let y = simplify(realm, y0, isCondition);
if (!c.mightNotBeTrue()) return x;
if (!c.mightNotBeFalse()) return y;
(0, _invariant2.default)(c instanceof _index2.AbstractValue);
if (_singletons.Path.implies(c)) return x;
let notc = _index2.AbstractValue.createFromUnaryOp(realm, "!", c, true, loc, isCondition);
if (!notc.mightNotBeTrue()) return y;
if (!notc.mightNotBeFalse()) return x;
(0, _invariant2.default)(notc instanceof _index2.AbstractValue);
if (_singletons.Path.implies(notc)) return y;
if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "===", value, x))) return x;
if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "!==", value, x))) return y;
if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "!==", value, y))) return x;
if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "===", value, y))) return y;
// c ? x : x <=> x
if (x.equals(y)) return x;
// x ? x : y <=> x || y
if (cs.equals(x)) return _index2.AbstractValue.createFromLogicalOp(realm, "||", x, y, loc, isCondition);
// y ? x : y <=> y && x
if (cs.equals(y)) return _index2.AbstractValue.createFromLogicalOp(realm, "&&", y, x, loc, isCondition);
// 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);
}
// 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);
}
if (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);
}
if (c.equals(c0) && x.equals(x0) && y.equals(y0)) return value;
return _index2.AbstractValue.createFromConditionalOp(realm, c, x, y, value.expressionLocation, isCondition);
}
case "abstractConcreteUnion":
{
// The union of an abstract value with one or more concrete values.
if (realm.pathConditions.length === 0) return value;
let [abstractValue, ...concreteValues] = value.args;
(0, _invariant2.default)(abstractValue instanceof _index2.AbstractValue);
let remainingConcreteValues = [];
for (let concreteValue of concreteValues) {
if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "!==", value, concreteValue))) continue;
if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "===", value, concreteValue))) 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 simplifyEquality(realm, equality) {
let loc = equality.expressionLocation;
let op = equality.kind;
let [x, y] = equality.args;
if (x instanceof _index2.ConcreteValue) [x, y] = [y, x];
if (x instanceof _index2.AbstractValue && x.kind === "conditional" && (!y.mightNotBeUndefined() || !y.mightNotBeNull())) {
// try to simplify "(cond ? xx : xy) op undefined/null" to just "cond" or "!cond"
let [cond, xx, xy] = x.args;
(0, _invariant2.default)(cond instanceof _index2.AbstractValue); // otherwise the the conditional should not have been created
if (op === "===" || op === "!==") {
// if xx === undefined && xy !== undefined then cond <=> x === undefined
if (!y.mightNotBeUndefined() && !xx.mightNotBeUndefined() && !xy.mightBeUndefined()) return op === "===" ? makeBoolean(realm, cond, loc) : negate(realm, cond, loc);
// if xx !== undefined && xy === undefined then !cond <=> x === undefined
if (!y.mightNotBeUndefined() && !xx.mightBeUndefined() && !xy.mightNotBeUndefined()) return op === "===" ? negate(realm, cond, loc) : makeBoolean(realm, cond, loc);
// if xx === null && xy !== null then cond <=> x === null
if (!y.mightNotBeNull() && !xx.mightNotBeNull() && !xy.mightBeNull()) return op === "===" ? makeBoolean(realm, cond, loc) : negate(realm, cond, loc);
// if xx !== null && xy === null then !cond <=> x === null
if (!y.mightNotBeNull() && !xx.mightBeNull() && !xy.mightNotBeNull()) return op === "===" ? negate(realm, cond, loc) : makeBoolean(realm, cond, loc);
} else {
(0, _invariant2.default)(op === "==" || op === "!=");
// if xx cannot be undefined/null and xy is undefined/null then !cond <=> x == undefined/null
if (!xx.mightBeUndefined() && !xx.mightBeNull() && (!xy.mightNotBeUndefined() || !xy.mightNotBeNull())) return op === "==" ? negate(realm, cond, loc) : makeBoolean(realm, cond, loc);
// if xx is undefined/null and xy cannot be undefined/null then cond <=> x == undefined/null
if ((!xx.mightNotBeUndefined() || !xx.mightNotBeNull()) && !xy.mightBeUndefined() && !xy.mightBeNull()) return op === "==" ? makeBoolean(realm, cond, loc) : negate(realm, cond, loc);
}
}
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, _invariant2.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, _invariant2.default)(v instanceof _index2.AbstractValue);
return _index2.AbstractValue.createFromUnaryOp(realm, "!", v, true, loc || value.expressionLocation);
}
function negate(realm, value, loc = undefined, unsimplifiedNegation = undefined) {
if (value instanceof _index2.ConcreteValue) return _index.ValuesDomain.computeUnary(realm, "!", value);
(0, _invariant2.default)(value instanceof _index2.AbstractValue);
if (value.kind === "!") {
let [x] = value.args;
if (x.getType() === _index2.BooleanValue) return simplify(realm, x, true);
if (unsimplifiedNegation !== undefined) return unsimplifiedNegation;
return makeBoolean(realm, x, loc);
}
if (!value.mightNotBeTrue()) return realm.intrinsics.false;
if (!value.mightNotBeFalse()) return realm.intrinsics.true;
// 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]);
let right = simplify(realm, value.args[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]);
let right = negate(realm, value.args[1]);
return _index2.AbstractValue.createFromLogicalOp(realm, invertedLogicalOp, left, right, loc || value.expressionLocation);
}
}
if (unsimplifiedNegation !== undefined) return unsimplifiedNegation;
return _index2.AbstractValue.createFromUnaryOp(realm, "!", value, true, loc || value.expressionLocation);
}
//# sourceMappingURL=simplifier.js.map