UNPKG

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