UNPKG

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