334 lines
19 KiB
JavaScript
334 lines
19 KiB
JavaScript
"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
|