Check invariant that path conditions no longer get mutated after being used as a base (#2538)

Summary:
Release notes: None

Otherwise, this could cause issues, as path conditions get captured e.g. in generators.
Pull Request resolved: https://github.com/facebook/prepack/pull/2538

Reviewed By: hermanventer

Differential Revision: D9762541

Pulled By: NTillmann

fbshipit-source-id: 4d12681e3921d2c0f10f3c9f3f0702823a2b0d99
This commit is contained in:
Nikolai Tillmann 2018-09-10 18:33:11 -07:00 committed by Facebook Github Bot
parent a973f896ac
commit 1e1c03c533
2 changed files with 28 additions and 7 deletions

View File

@ -377,6 +377,10 @@ export class PathConditions {
return false;
}
isReadOnly(): boolean {
return false;
}
getLength(): number {
return 0;
}

View File

@ -19,11 +19,16 @@ export class PathConditionsImplementation extends PathConditions {
constructor(baseConditions?: void | PathConditions) {
super();
this._assumedConditions = new Set();
invariant(baseConditions === undefined || baseConditions instanceof PathConditionsImplementation);
this._baseConditions = baseConditions;
this._readonly = false;
if (baseConditions !== undefined) {
invariant(baseConditions instanceof PathConditionsImplementation);
baseConditions._readonly = true;
this._baseConditions = baseConditions;
}
}
_assumedConditions: Set<AbstractValue>;
_readonly: boolean;
_baseConditions: void | PathConditionsImplementation;
_impliedConditions: void | Set<AbstractValue>;
_impliedNegatives: void | Set<AbstractValue>;
@ -31,9 +36,14 @@ export class PathConditionsImplementation extends PathConditions {
_failedNegativeImplications: void | Set<AbstractValue>;
add(c: AbstractValue): void {
invariant(!this._readonly);
this._assumedConditions.add(c);
}
isReadOnly(): boolean {
return this._readonly;
}
implies(e: Value, depth: number = 0): boolean {
if (!e.mightNotBeTrue()) return true;
if (!e.mightNotBeFalse()) return false;
@ -156,7 +166,11 @@ export class PathConditionsImplementation extends PathConditions {
return this._assumedConditions;
}
refineBaseConditons(realm: Realm, totalRefinements: number = 0): void {
refineBaseConditons(
realm: Realm,
totalRefinements: number = 0,
refinementTarget: PathConditionsImplementation = this
): void {
if (realm.abstractValueImpliesMax > 0) return;
let total = totalRefinements;
let refine = (condition: AbstractValue) => {
@ -164,10 +178,11 @@ export class PathConditionsImplementation extends PathConditions {
if (refinedCondition !== condition) {
if (!refinedCondition.mightNotBeFalse()) throw new InfeasiblePathError();
if (refinedCondition instanceof AbstractValue) {
this.add(refinedCondition);
// These might have different answers now that we're adding another path condition
this._failedImplications = undefined;
this._failedNegativeImplications = undefined;
refinementTarget._failedImplications = undefined;
refinementTarget._failedNegativeImplications = undefined;
refinementTarget._readonly = false;
refinementTarget.add(refinedCondition);
}
}
};
@ -184,7 +199,7 @@ export class PathConditionsImplementation extends PathConditions {
} finally {
this._baseConditions = savedBaseConditions;
}
savedBaseConditions.refineBaseConditons(realm, total);
savedBaseConditions.refineBaseConditons(realm, total, refinementTarget);
}
}
}
@ -276,6 +291,7 @@ export class PathImplementation {
// A path condition is an abstract value that must be true in this particular code path, so we want to assume as much
function pushPathCondition(condition: Value): void {
let realm = condition.$Realm;
if (realm.pathConditions.isReadOnly()) realm.pathConditions = new PathConditionsImplementation(realm.pathConditions);
if (!condition.mightNotBeFalse()) {
if (realm.impliesCounterOverflowed) throw new InfeasiblePathError();
invariant(false, "assuming that false equals true is asking for trouble");
@ -326,6 +342,7 @@ function pushPathCondition(condition: Value): void {
// 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: Value): void {
let realm = condition.$Realm;
if (realm.pathConditions.isReadOnly()) realm.pathConditions = new PathConditionsImplementation(realm.pathConditions);
if (!condition.mightNotBeTrue()) {
if (realm.impliesCounterOverflowed) throw new InfeasiblePathError();
invariant(false, "assuming that false equals true is asking for trouble");