prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
474 lines (355 loc) • 18.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.PathImplementation = exports.PathConditionsImplementation = void 0;
var _errors = require("../errors.js");
var _invariant = _interopRequireDefault(require("../invariant.js"));
var _realm = require("../realm.js");
var _types = require("../types.js");
var _index = require("../values/index.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.
*/
/* strict-local */
class PathConditionsImplementation extends _types.PathConditions {
constructor(baseConditions) {
super();
this._assumedConditions = new Set();
this._readonly = false;
if (baseConditions !== undefined) {
(0, _invariant.default)(baseConditions instanceof PathConditionsImplementation);
this._baseConditions = baseConditions;
}
}
add(c) {
(0, _invariant.default)(!this._readonly);
this._assumedConditions.add(c);
this._failedImplications = undefined;
this._failedNegativeImplications = undefined;
}
isReadOnly() {
return this._readonly;
} // this => val. A false value does not imply that !(this => val).
implies(e, depth = 0) {
if (!e.mightNotBeTrue()) return true;
if (!e.mightNotBeFalse()) return false;
(0, _invariant.default)(e instanceof _index.AbstractValue);
if (this._assumedConditions.has(e)) return true;
if (this._impliedConditions !== undefined && this._impliedConditions.has(e)) return true;
if (this._impliedNegatives !== undefined && this._impliedNegatives.has(e)) return false;
if (this._failedImplications !== undefined && this._failedImplications.has(e)) return false;
if (depth > 10) return false;
if (this._baseConditions !== undefined && this._baseConditions.implies(e, depth + 1)) return true;
for (let assumedCondition of this._assumedConditions) {
if (assumedCondition.implies(e, depth + 1)) return this.cacheImplicationSuccess(e);
}
if (e.kind === "||") {
let [x, y] = e.args; // this => x || true, regardless of the value of x
// this => true || y, regardless of the value of y
if (!x.mightNotBeTrue() || !y.mightNotBeTrue()) return this.cacheImplicationSuccess(e); // this => false || y, if this => y
if (!x.mightNotBeFalse() && this.implies(y, depth + 1)) return this.cacheImplicationSuccess(e); // this => x || false if this => x
if (!y.mightNotBeFalse() && this.implies(x, depth + 1)) return this.cacheImplicationSuccess(e); // this => x || y if this => x
if (this.implies(x, depth + 1)) return this.cacheImplicationSuccess(e); // this => x || y if this => y
if (this.implies(y, depth + 1)) return this.cacheImplicationSuccess(e);
}
if (e.kind === "!==" || e.kind === "!=") {
let [x, y] = e.args;
if (x instanceof _index.AbstractValue) {
// this => x !== null && x !== undefined, if this => x
// this => x != null && x != undefined, if this => x
if ((y instanceof _index.NullValue || y instanceof _index.UndefinedValue) && this.implies(x, depth + 1)) return this.cacheImplicationSuccess(e);
} else {
(0, _invariant.default)(y instanceof _index.AbstractValue); // otherwise e would have been simplied
// this => null !== y && undefined !== y, if this => y
// this => null != y && undefined != y, if this => y
if ((x instanceof _index.NullValue || x instanceof _index.UndefinedValue) && this.implies(y, depth + 1)) return this.cacheImplicationSuccess(e);
}
}
if (e.kind === "!") {
let [x] = e.args;
if (this.impliesNot(x, depth + 1)) return this.cacheImplicationSuccess(e);
}
if (this._failedImplications === undefined) this._failedImplications = new Set();
this._failedImplications.add(e);
return false;
}
cacheImplicationSuccess(e) {
if (this._impliedConditions === undefined) this._impliedConditions = new Set();
this._impliedConditions.add(e);
return true;
} // this => !val. A false value does not imply that !(this => !val).
impliesNot(e, depth = 0) {
if (!e.mightNotBeTrue()) return false;
if (!e.mightNotBeFalse()) return true;
(0, _invariant.default)(e instanceof _index.AbstractValue);
if (this._assumedConditions.has(e)) return false;
if (this._impliedConditions !== undefined && this._impliedConditions.has(e)) return false;
if (this._impliedNegatives !== undefined && this._impliedNegatives.has(e)) return true;
if (this._failedNegativeImplications !== undefined && this._failedNegativeImplications.has(e)) return false;
if (depth > 10) return false;
if (this._baseConditions !== undefined && this._baseConditions.impliesNot(e, depth + 1)) return true;
for (let assumedCondition of this._assumedConditions) {
if (assumedCondition.impliesNot(e, depth + 1)) return this.cacheNegativeImplicationSuccess(e);
}
if (e.kind === "&&") {
let [x, y] = e.args; // this => !(false && y) regardless of the value of y
// this => !(x && false) regardless of the value of x
if (!x.mightNotBeFalse() || !y.mightNotBeFalse()) return this.cacheNegativeImplicationSuccess(e); // this => !(true && y), if this => !y
if (!x.mightNotBeTrue() && this.impliesNot(y, depth + 1)) return this.cacheNegativeImplicationSuccess(e); // this => !(x && true) if this => !x
if (!y.mightNotBeTrue() && this.impliesNot(x, depth + 1)) return this.cacheNegativeImplicationSuccess(e); // this => !(x && y) if this => !x
if (this.impliesNot(x, depth + 1)) return this.cacheNegativeImplicationSuccess(e); // this => !(x && y) if this => !y
if (this.impliesNot(y, depth + 1)) return this.cacheNegativeImplicationSuccess(e);
}
if (e.kind === "===" || e.kind === "==") {
let [x, y] = e.args;
if (x instanceof _index.AbstractValue) {
// this => !(x === null) && !(x === undefined), if this => x
// this => !(x == null) && !(x == undefined), if this => x
if ((y instanceof _index.NullValue || y instanceof _index.UndefinedValue) && this.implies(x, depth + 1)) return this.cacheNegativeImplicationSuccess(e);
} else {
(0, _invariant.default)(y instanceof _index.AbstractValue); // otherwise e would have been simplied
// this => !(null === y) && !(undefined === y), if this => y
// this => !(null == y) && !(undefined == y), if this => y
if ((x instanceof _index.NullValue || x instanceof _index.UndefinedValue) && this.implies(y, depth + 1)) return this.cacheNegativeImplicationSuccess(e);
}
}
if (e.kind === "!") {
let [x] = e.args;
if (this.implies(x, depth + 1)) return this.cacheNegativeImplicationSuccess(e);
}
if (this._failedNegativeImplications === undefined) this._failedNegativeImplications = new Set();
this._failedNegativeImplications.add(e);
return false;
}
cacheNegativeImplicationSuccess(e) {
if (this._impliedNegatives === undefined) this._impliedNegatives = new Set();
this._impliedNegatives.add(e);
return true;
}
isEmpty() {
return this._assumedConditions.size === 0;
}
getLength() {
return this._assumedConditions.size;
}
getAssumedConditions() {
return this._assumedConditions;
} // Refinement may temporarily make a target non-read-only, but marks the target as read-only at the end.
refineBaseConditons(realm, totalRefinements = 0, refinementTarget = this) {
try {
if (realm.abstractValueImpliesMax > 0) return;
let total = totalRefinements;
let refine = condition => {
let refinedCondition = realm.simplifyAndRefineAbstractCondition(condition);
if (refinedCondition !== condition) {
if (!refinedCondition.mightNotBeFalse()) throw new _errors.InfeasiblePathError();
if (refinedCondition instanceof _index.AbstractValue) {
refinementTarget._readonly = false;
refinementTarget.add(refinedCondition);
}
}
};
if (this._baseConditions !== undefined) {
let savedBaseConditions = this._baseConditions;
try {
this._baseConditions = undefined;
for (let assumedCondition of savedBaseConditions._assumedConditions) {
if (assumedCondition.kind === "||") {
if (++total > 4) break;
refine(assumedCondition);
}
}
} finally {
this._baseConditions = savedBaseConditions;
}
savedBaseConditions.refineBaseConditons(realm, total, refinementTarget);
}
} finally {
refinementTarget._readonly = true;
}
}
}
exports.PathConditionsImplementation = PathConditionsImplementation;
class PathImplementation {
// this => val. A false value does not imply that !(this => val).
implies(condition, depth = 0) {
if (!condition.mightNotBeTrue()) return true; // any path implies true
if (!condition.mightNotBeFalse()) return false; // no path condition is false
(0, _invariant.default)(condition instanceof _index.AbstractValue);
return condition.$Realm.pathConditions.implies(condition, depth);
} // this => !val. A false value does not imply that !(this => !val).
impliesNot(condition, depth = 0) {
if (!condition.mightNotBeFalse()) return true; // any path implies !false
if (!condition.mightNotBeTrue()) return false; // no path condition is false, so none can imply !true
(0, _invariant.default)(condition instanceof _index.AbstractValue);
return condition.$Realm.pathConditions.impliesNot(condition, depth);
}
withCondition(condition, evaluate) {
let realm = condition.$Realm;
if (!condition.mightNotBeFalse()) {
if (realm.impliesCounterOverflowed) throw new _errors.InfeasiblePathError();
(0, _invariant.default)(false, "assuming that false equals true is asking for trouble");
}
let savedPath = realm.pathConditions;
realm.pathConditions = new PathConditionsImplementation(savedPath);
try {
pushPathCondition(condition);
realm.pathConditions.refineBaseConditons(realm);
return evaluate();
} catch (e) {
if (e instanceof _errors.InfeasiblePathError) {
// if condition is true, one of the saved path conditions must be false
// since we have to assume that those conditions are true we now know that on this path, condition is false
realm.pathConditions = savedPath;
pushInversePathCondition(condition);
}
throw e;
} finally {
realm.pathConditions = savedPath;
}
}
withInverseCondition(condition, evaluate) {
let realm = condition.$Realm;
if (!condition.mightNotBeTrue()) {
if (realm.impliesCounterOverflowed) throw new _errors.InfeasiblePathError();
(0, _invariant.default)(false, "assuming that false equals true is asking for trouble");
}
let savedPath = realm.pathConditions;
realm.pathConditions = new PathConditionsImplementation(savedPath);
try {
pushInversePathCondition(condition);
realm.pathConditions.refineBaseConditons(realm);
return evaluate();
} catch (e) {
if (e instanceof _errors.InfeasiblePathError) {
// if condition is false, one of the saved path conditions must be false
// since we have to assume that those conditions are true we now know that on this path, condition is true
realm.pathConditions = savedPath;
pushPathCondition(condition);
}
throw e;
} finally {
realm.pathConditions = savedPath;
}
}
pushAndRefine(condition) {
let realm = condition.$Realm;
let savedPath = realm.pathConditions;
realm.pathConditions = new PathConditionsImplementation(savedPath);
pushPathCondition(condition);
realm.pathConditions.refineBaseConditons(realm);
}
pushInverseAndRefine(condition) {
let realm = condition.$Realm;
let savedPath = realm.pathConditions;
realm.pathConditions = new PathConditionsImplementation(savedPath);
pushInversePathCondition(condition);
realm.pathConditions.refineBaseConditons(realm);
}
} // A path condition is an abstract value that must be true in this particular code path, so we want to assume as much
exports.PathImplementation = PathImplementation;
function pushPathCondition(condition) {
let realm = condition.$Realm;
if (realm.pathConditions.isReadOnly()) realm.pathConditions = new PathConditionsImplementation(realm.pathConditions);
if (!condition.mightNotBeFalse()) {
if (realm.impliesCounterOverflowed) throw new _errors.InfeasiblePathError();
(0, _invariant.default)(false, "assuming that false equals true is asking for trouble");
}
if (condition instanceof _index.ConcreteValue) return;
if (!condition.mightNotBeTrue()) return;
(0, _invariant.default)(condition instanceof _index.AbstractValue);
if (condition.kind === "&&") {
let left = condition.args[0];
let right = condition.args[1];
(0, _invariant.default)(left instanceof _index.AbstractValue); // it is a mistake to create an abstract value when concrete value will do
pushPathCondition(left);
pushPathCondition(right);
} else if (condition.kind === "===") {
let [left, right] = condition.args;
if (right instanceof _index.AbstractValue && right.kind === "conditional") [left, right] === [right, left];
if (left instanceof _index.AbstractValue && left.kind === "conditional") {
let [cond, x, y] = left.args;
if (right instanceof _index.ConcreteValue && x instanceof _index.ConcreteValue && y instanceof _index.ConcreteValue) {
if (right.equals(x) && !right.equals(y)) {
pushPathCondition(cond);
} else if (!right.equals(x) && right.equals(y)) {
pushInversePathCondition(cond);
}
}
}
realm.pathConditions.add(condition);
} else {
if (condition.kind === "!=" || condition.kind === "==") {
let left = condition.args[0];
let right = condition.args[1];
if (left instanceof _index.ConcreteValue && right instanceof _index.AbstractValue) [left, right] = [right, left];
if (left instanceof _index.AbstractValue && (right instanceof _index.UndefinedValue || right instanceof _index.NullValue)) {
if (condition.kind === "!=") {
// x != null => x!==null && x!==undefined
pushPathCondition(left);
let leftNeNull = _index.AbstractValue.createFromBinaryOp(realm, "!==", left, realm.intrinsics.null);
let leftNeUndefined = _index.AbstractValue.createFromBinaryOp(realm, "!==", left, realm.intrinsics.undefined);
pushPathCondition(leftNeNull);
pushPathCondition(leftNeUndefined);
} else if (condition.kind === "==") {
// x == null => x===null || x===undefined
pushInversePathCondition(left);
let leftEqNull = _index.AbstractValue.createFromBinaryOp(realm, "===", left, realm.intrinsics.null);
let leftEqUndefined = _index.AbstractValue.createFromBinaryOp(realm, "===", left, realm.intrinsics.undefined);
let c;
if (!leftEqNull.mightNotBeFalse()) c = leftEqUndefined;else if (!leftEqUndefined.mightNotBeFalse()) c = leftEqNull;else c = _index.AbstractValue.createFromLogicalOp(realm, "||", leftEqNull, leftEqUndefined);
pushPathCondition(c);
}
return;
}
}
realm.pathConditions.add(condition);
}
} // An inverse path condition is an abstract value that must be false in this particular code path, so we want to assume as much
function pushInversePathCondition(condition) {
let realm = condition.$Realm;
if (realm.pathConditions.isReadOnly()) realm.pathConditions = new PathConditionsImplementation(realm.pathConditions);
if (!condition.mightNotBeTrue()) {
if (realm.impliesCounterOverflowed) throw new _errors.InfeasiblePathError();
(0, _invariant.default)(false, "assuming that false equals true is asking for trouble");
}
if (condition instanceof _index.ConcreteValue) return;
(0, _invariant.default)(condition instanceof _index.AbstractValue);
if (condition.kind === "||") {
let left = condition.args[0];
let right = condition.args[1];
(0, _invariant.default)(left instanceof _index.AbstractValue); // it is a mistake to create an abstract value when concrete value will do
pushInversePathCondition(left);
if (right instanceof _index.AbstractValue) right = realm.simplifyAndRefineAbstractCondition(right);
if (right.mightNotBeTrue()) pushInversePathCondition(right);
} else {
if (condition.kind === "!=" || condition.kind === "==") {
let left = condition.args[0];
let right = condition.args[1];
if (left instanceof _index.ConcreteValue && right instanceof _index.AbstractValue) [left, right] = [right, left];
if (left instanceof _index.AbstractValue && (right instanceof _index.UndefinedValue || right instanceof _index.NullValue)) {
let op = condition.kind === "!=" ? "===" : "!==";
if (op === "!==") pushPathCondition(left);else pushInversePathCondition(left);
let leftEqNull = _index.AbstractValue.createFromBinaryOp(realm, op, left, realm.intrinsics.null);
if (leftEqNull.mightNotBeFalse()) pushPathCondition(leftEqNull);
let leftEqUndefined = _index.AbstractValue.createFromBinaryOp(realm, op, left, realm.intrinsics.undefined);
if (leftEqUndefined.mightNotBeFalse()) pushPathCondition(leftEqUndefined);
return;
}
}
let inverseCondition = _index.AbstractValue.createFromUnaryOp(realm, "!", condition, false, undefined, true, true);
pushPathCondition(inverseCondition);
if (inverseCondition instanceof _index.AbstractValue) {
let simplifiedInverseCondition = realm.simplifyAndRefineAbstractCondition(inverseCondition);
if (!simplifiedInverseCondition.equals(inverseCondition)) pushPathCondition(simplifiedInverseCondition);
}
}
}
//# sourceMappingURL=paths.js.map