"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); /** * 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. */ 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 _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); } function simplifyAndRefineAbstractValue(realm, isCondition, // The value is only used after converting it to a Boolean value) { var savedHandler = realm.errorHandler; var savedIsReadOnly = realm.isReadOnly; realm.isReadOnly = true; try { realm.errorHandler = function () { throw new _errors.FatalError(); }; return simplify(realm, value, isCondition); } catch (e) { return value; } finally { realm.errorHandler = savedHandler; realm.isReadOnly = savedIsReadOnly; } } function simplify(realm, value) { var isCondition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 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; } var loc = value.expressionLocation; var op = value.kind; switch (op) { case "!": { var _value$args = _slicedToArray(value.args, 1), x0 = _value$args[0]; var x = simplify(realm, x0, true); return negate(realm, x, loc, x0.equals(x) ? value : undefined); } case "||": case "&&": { var _value$args2 = _slicedToArray(value.args, 2), _x2 = _value$args2[0], y0 = _value$args2[1]; var _x3 = simplify(realm, _x2); var y = simplify(realm, y0); if (_x3 instanceof _index2.AbstractValue && _x3.equals(y)) return _x3; // true && y <=> y // true || y <=> true if (!_x3.mightNotBeTrue()) return op === "&&" ? y : _x3; // (x == false) && y <=> x // false || y <=> y if (!_x3.mightNotBeFalse()) return op === "||" ? y : _x3; if (isCondition || _x3.getType() === _index2.BooleanValue && y.getType() === _index2.BooleanValue) { // (x: boolean) && true <=> x // x || true <=> true if (!y.mightNotBeTrue()) return op === "&&" ? _x3 : realm.intrinsics.true; // (x: boolean) && false <=> false // (x: boolean) || false <=> x if (!y.mightNotBeFalse()) return op === "||" ? _x3 : realm.intrinsics.false; } if (op === "||" && y instanceof _index2.AbstractValue && y.kind === "||" && _x3.equals(y.args[0]) && !y.args[1].mightNotBeTrue()) return y; if (_x3.equals(_x2) && y.equals(y0)) return value; return _index2.AbstractValue.createFromLogicalOp(realm, value.kind, _x3, y, loc); } case "==": case "!=": case "===": case "!==": return simplifyEquality(realm, value); case "conditional": { var _value$args3 = _slicedToArray(value.args, 3), c0 = _value$args3[0], _x4 = _value$args3[1], _y = _value$args3[2]; var c = simplify(realm, c0, true); var cs = simplify(realm, c0); var _x5 = simplify(realm, _x4); var _y2 = simplify(realm, _y); if (!c.mightNotBeTrue()) return _x5; if (!c.mightNotBeFalse()) return _y2; (0, _invariant2.default)(c instanceof _index2.AbstractValue); if (_singletons.Path.implies(c)) return _x5; var notc = _index2.AbstractValue.createFromUnaryOp(realm, "!", c); if (!notc.mightNotBeTrue()) return _y2; if (!notc.mightNotBeFalse()) return _x5; (0, _invariant2.default)(notc instanceof _index2.AbstractValue); if (_singletons.Path.implies(notc)) return _y2; if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "===", value, _x5))) return _x5; if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "!==", value, _x5))) return _y2; if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "!==", value, _y2))) return _x5; if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "===", value, _y2))) return _y2; // c ? x : x <=> x if (_x5.equals(_y2)) return _x5; // x ? x : y <=> x || y if (cs.equals(_x5)) return _index2.AbstractValue.createFromLogicalOp(realm, "||", _x5, _y2, loc); // y ? x : y <=> y && x if (cs.equals(_y2)) return _index2.AbstractValue.createFromLogicalOp(realm, "&&", _y2, _x5, loc); // c ? (c ? xx : xy) : y <=> c ? xx : y if (_x5 instanceof _index2.AbstractValue && _x5.kind === "conditional") { var _x5$args = _slicedToArray(_x5.args, 2), xc = _x5$args[0], xx = _x5$args[1]; if (c.equals(xc)) return _index2.AbstractValue.createFromConditionalOp(realm, c, xx, _y2); } // c ? x : (c ? y : z) : z <=> c ? x : z if (_y2 instanceof _index2.AbstractValue && _y2.kind === "conditional") { var _y2$args = _slicedToArray(_y2.args, 3), yc = _y2$args[0], z = _y2$args[2]; if (c.equals(yc)) return _index2.AbstractValue.createFromConditionalOp(realm, c, _x5, z); } if (_x5.getType() === _index2.BooleanValue && _y2.getType() === _index2.BooleanValue) { // c ? true : false <=> c if (!_x5.mightNotBeTrue() && !_y2.mightNotBeFalse()) return c; // c ? false : true <=> !c if (!_x5.mightNotBeFalse() && !_y2.mightNotBeTrue()) return _index2.AbstractValue.createFromUnaryOp(realm, "!", c, true, loc); } if (c.equals(c0) && _x5.equals(_x4) && _y2.equals(_y)) return value; return _index2.AbstractValue.createFromConditionalOp(realm, c, _x5, _y2, value.expressionLocation); } case "abstractConcreteUnion": { // The union of an abstract value with one or more concrete values. if (realm.pathConditions.length === 0) return value; var _value$args4 = _toArray(value.args), abstractValue = _value$args4[0], concreteValues = _value$args4.slice(1); (0, _invariant2.default)(abstractValue instanceof _index2.AbstractValue); var remainingConcreteValues = []; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = concreteValues[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var concreteValue = _step.value; 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); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } if (remainingConcreteValues.length === 0) return abstractValue; if (remainingConcreteValues.length === concreteValues.length) return value; return _index2.AbstractValue.createAbstractConcreteUnion.apply(_index2.AbstractValue, [realm, abstractValue].concat(remainingConcreteValues)); } default: return value; } } function simplifyEquality(realm, equality) { var loc = equality.expressionLocation; var op = equality.kind; var _equality$args = _slicedToArray(equality.args, 2), x = _equality$args[0], y = _equality$args[1]; if (x instanceof _index2.ConcreteValue) { ; var _ref = [y, x]; x = _ref[0]; y = _ref[1]; }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" var _x$args = _slicedToArray(x.args, 3), cond = _x$args[0], xx = _x$args[1], xy = _x$args[2]; (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) { var loc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 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); var 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) { var loc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined; var unsimplifiedNegation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : undefined; if (value instanceof _index2.ConcreteValue) return _index.ValuesDomain.computeUnary(realm, "!", value); (0, _invariant2.default)(value instanceof _index2.AbstractValue); if (value.kind === "!") { var _value$args5 = _slicedToArray(value.args, 1), x = _value$args5[0]; 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()) { var invertedComparison = void 0; 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) { var left = simplify(realm, value.args[0]); var right = simplify(realm, value.args[1]); return _index2.AbstractValue.createFromBinaryOp(realm, invertedComparison, left, right, loc || value.expressionLocation); } var invertedLogicalOp = void 0; switch (value.kind) { case "&&": invertedLogicalOp = "||"; break; case "||": invertedLogicalOp = "&&"; break; default: break; } if (invertedLogicalOp !== undefined) { var _left = negate(realm, value.args[0]); var _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