Effects contain NormalCompletion not Value (#2110)

Summary:
Release Notes: None

In an effort to unify Completions and Effects (many completions are tied to effects whose result should be the completion), I change evaluateForEffects to return a `NormalCompletion` whereever it would otherwise return a `Value`. Now all `EvaluationResults` are `Completions` or `References`.

Fairly mechanical refactor (except slightly tricky because PossiblyNormalCompletions are NormalCompletions, so instead of checking `instanceof Value` you should check `instanceof NormalCompletion && ! instanceof PossiblyNormalCompletion`).
Closes https://github.com/facebook/prepack/pull/2110

Differential Revision: D8431970

Pulled By: cblappert

fbshipit-source-id: d70d6c5d4100eec52f60b8e4328f2a0b4afabbed
This commit is contained in:
Chris Blappert 2018-06-18 14:55:41 -07:00 committed by Facebook Github Bot
parent 03b7fd37ff
commit b59e918fd6
22 changed files with 267 additions and 121 deletions

View File

@ -24,11 +24,19 @@ export class Completion {
value: Value;
target: ?string;
location: ?BabelNodeSourceLocation;
toDisplayString(): string {
return "[" + this.constructor.name + " value " + (this.value ? this.value.toDisplayString() : "undefined") + "]";
}
}
// Normal completions are returned just like spec completions
export class NormalCompletion extends Completion {}
// SimpleNormalCompletions are returned just like spec completions. This class exists as the parallel for
// PossiblyNormalCompletion to make comparisons easier.
export class SimpleNormalCompletion extends NormalCompletion {}
// Abrupt completions are thrown as exeptions, to make it a easier
// to quickly get to the matching high level construct.
export class AbruptCompletion extends Completion {}
@ -86,6 +94,13 @@ export class ForkedAbruptCompletion extends AbruptCompletion {
alternate: AbruptCompletion;
alternateEffects: Effects;
toDisplayString(): string {
let superString = super.toDisplayString().slice(0, -1);
return (
superString + " c: [" + this.consequent.toDisplayString() + "] a: [" + this.alternate.toDisplayString() + "]]"
);
}
containsCompletion(CompletionType: typeof Completion): boolean {
if (this.consequent instanceof CompletionType) return true;
if (this.alternate instanceof CompletionType) return true;
@ -130,31 +145,22 @@ export class PossiblyNormalCompletion extends NormalCompletion {
constructor(
value: Value,
joinCondition: AbstractValue,
consequent: Completion | Value,
consequent: Completion,
consequentEffects: Effects,
alternate: Completion | Value,
alternate: Completion,
alternateEffects: Effects,
savedPathConditions: Array<AbstractValue>,
savedEffects: void | Effects = undefined
) {
invariant(consequent === consequentEffects.result);
invariant(alternate === alternateEffects.result);
invariant(
consequent instanceof NormalCompletion ||
consequent instanceof Value ||
alternate instanceof NormalCompletion ||
alternate instanceof Value
);
invariant(consequent instanceof NormalCompletion || alternate instanceof NormalCompletion);
invariant(consequent instanceof AbruptCompletion || alternate instanceof AbruptCompletion);
invariant(
value === consequent ||
consequent instanceof AbruptCompletion ||
(consequent instanceof NormalCompletion && value === consequent.value)
consequent instanceof AbruptCompletion || (consequent instanceof NormalCompletion && value === consequent.value)
);
invariant(
value === alternate ||
alternate instanceof AbruptCompletion ||
(alternate instanceof NormalCompletion && value === alternate.value)
alternate instanceof AbruptCompletion || (alternate instanceof NormalCompletion && value === alternate.value)
);
let loc =
consequent instanceof AbruptCompletion
@ -173,14 +179,39 @@ export class PossiblyNormalCompletion extends NormalCompletion {
}
joinCondition: AbstractValue;
consequent: Completion | Value;
consequent: Completion;
consequentEffects: Effects;
alternate: Completion | Value;
alternate: Completion;
alternateEffects: Effects;
savedEffects: void | Effects;
// The path conditions that applied at the time of the oldest fork that caused this completion to arise.
savedPathConditions: Array<AbstractValue>;
toDisplayString(): string {
let superString = super.toDisplayString().slice(0, -1);
return (
superString + " c: [" + this.consequent.toDisplayString() + "] a: [" + this.alternate.toDisplayString() + "]]"
);
}
getNormalCompletion(): SimpleNormalCompletion {
let result;
if (this.alternate instanceof SimpleNormalCompletion) {
result = this.alternate;
} else if (this.consequent instanceof SimpleNormalCompletion) {
result = this.consequent;
} else {
if (this.alternate instanceof PossiblyNormalCompletion) {
result = this.alternate.getNormalCompletion();
} else {
invariant(this.consequent instanceof PossiblyNormalCompletion);
result = this.consequent.getNormalCompletion();
}
}
invariant(result.value === this.value);
return result;
}
containsCompletion(CompletionType: typeof Completion): boolean {
if (this.consequent instanceof CompletionType) return true;
if (this.alternate instanceof CompletionType) return true;

View File

@ -25,7 +25,7 @@ import {
UndefinedValue,
Value,
} from "../values/index.js";
import { AbruptCompletion, PossiblyNormalCompletion } from "../completions.js";
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
import { Environment, Havoc, To } from "../singletons.js";
import type {
BabelBinaryOperator,
@ -227,6 +227,8 @@ export function computeBinary(
// Consequently we have to continue tracking changes until the point where
// all the branches come together into one.
completion = realm.composeWithSavedCompletion(completion);
} else if (completion instanceof SimpleNormalCompletion) {
completion = completion.value;
}
// return or throw completion
if (completion instanceof AbruptCompletion) throw completion;

View File

@ -10,7 +10,7 @@
/* @flow */
import { CompilerDiagnostic, FatalError } from "../errors.js";
import { AbruptCompletion, PossiblyNormalCompletion } from "../completions.js";
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
import type { Realm } from "../realm.js";
import { Effects } from "../realm.js";
import { type LexicalEnvironment, type BaseValue, mightBecomeAnObject } from "../environment.js";
@ -182,6 +182,7 @@ function callBothFunctionsAndJoinTheirEffects(
new Effects(e2.result, e2.generator, e2.modifiedBindings, e2.modifiedProperties, e2.createdObjects)
);
let completion = joinedEffects.result;
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
if (completion instanceof PossiblyNormalCompletion) {
// in this case one of the branches may complete abruptly, which means that
// not all control flow branches join into one flow at this point.
@ -290,6 +291,7 @@ function tryToEvaluateCallOrLeaveAsAbstract(
}
// return or throw completion
if (completion instanceof AbruptCompletion) throw completion;
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
invariant(completion instanceof Value);
return completion;
}

View File

@ -16,7 +16,7 @@ import { Value } from "../values/index.js";
import { EmptyValue } from "../values/index.js";
import { UpdateEmpty } from "../methods/index.js";
import { LoopContinues, InternalGetResultValue, TryToApplyEffectsOfJoiningBranches } from "./ForOfStatement.js";
import { AbruptCompletion, BreakCompletion, ForkedAbruptCompletion } from "../completions.js";
import { AbruptCompletion, BreakCompletion, ForkedAbruptCompletion, SimpleNormalCompletion } from "../completions.js";
import { Environment, To } from "../singletons.js";
import invariant from "../invariant.js";
import type { BabelNodeDoWhileStatement } from "babel-types";
@ -73,6 +73,7 @@ export default function(
// so instead try to compute a fixpoint for it
let iteration = () => {
let bodyResult = env.evaluateCompletion(body, strictCode);
if (bodyResult instanceof Value) bodyResult = new SimpleNormalCompletion(bodyResult);
let exprRef = env.evaluate(test, strictCode);
let testResult = Environment.GetConditionValue(realm, exprRef);
return [testResult, bodyResult];
@ -86,8 +87,8 @@ export default function(
let generator = realm.generator;
invariant(generator !== undefined);
generator.emitDoWhileStatement(cond, bodyGenerator);
invariant(rval instanceof Value, "todo: handle loops that throw exceptions or return");
return rval;
invariant(rval instanceof SimpleNormalCompletion, "todo: handle loops that throw exceptions or return");
return rval.value;
}
// If we get here the fixpoint computation failed as well. Report the diagnostic from the unrolling and throw.

View File

@ -11,7 +11,7 @@
import type { Realm } from "../realm.js";
import type { LexicalEnvironment } from "../environment.js";
import { BreakCompletion } from "../completions.js";
import { BreakCompletion, SimpleNormalCompletion } from "../completions.js";
import { DeclarativeEnvironmentRecord } from "../environment.js";
import { CompilerDiagnostic, FatalError } from "../errors.js";
import { ForInOfHeadEvaluation, ForInOfBodyEvaluation } from "./ForOfStatement.js";
@ -142,7 +142,12 @@ function emitResidualLoopIfSafe(
strictCode,
blockEnv
);
if (result instanceof Value && gen.empty() && modifiedBindings.size === 0 && modifiedProperties.size === 1) {
if (
result instanceof SimpleNormalCompletion &&
gen.empty() &&
modifiedBindings.size === 0 &&
modifiedProperties.size === 1
) {
invariant(createdObjects.size === 0); // or there will be more than one property
let targetObject;
let sourceObject;

View File

@ -11,7 +11,7 @@
import type { Realm } from "../realm.js";
import { Effects } from "../realm.js";
import { AbruptCompletion, PossiblyNormalCompletion } from "../completions.js";
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
import { InfeasiblePathError } from "../errors.js";
import { construct_empty_effects } from "../realm.js";
import type { LexicalEnvironment } from "../environment.js";
@ -93,13 +93,19 @@ export default function(
realm,
lval,
new Effects(result2, generator2, modifiedBindings2, modifiedProperties2, createdObjects2),
new Effects(lval, generator1, modifiedBindings1, modifiedProperties1, createdObjects1)
new Effects(new SimpleNormalCompletion(lval), generator1, modifiedBindings1, modifiedProperties1, createdObjects1)
);
} else {
joinedEffects = Join.joinForkOrChoose(
realm,
lval,
new Effects(lval, generator1, modifiedBindings1, modifiedProperties1, createdObjects1),
new Effects(
new SimpleNormalCompletion(lval),
generator1,
modifiedBindings1,
modifiedProperties1,
createdObjects1
),
new Effects(result2, generator2, modifiedBindings2, modifiedProperties2, createdObjects2)
);
}
@ -117,7 +123,9 @@ export default function(
// return or throw completion
if (completion instanceof AbruptCompletion) throw completion;
invariant(completion instanceof Value); // references do not survive join
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
if (result2 instanceof SimpleNormalCompletion) result2 = result2.value;
invariant(completion instanceof Value);
if (lval instanceof Value && result2 instanceof Value) {
// joinForkOrChoose does the right thing for the side effects of the second expression but for the result the join
// produces a conditional expressions of the form (a ? b : a) for a && b and (a ? a : b) for a || b

View File

@ -11,7 +11,7 @@
import type { Realm } from "../realm.js";
import type { LexicalEnvironment } from "../environment.js";
import { AbruptCompletion, PossiblyNormalCompletion } from "../completions.js";
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
import { TypesDomain, ValuesDomain } from "../domains/index.js";
import { ObjectValue, Value, AbstractObjectValue, AbstractValue } from "../values/index.js";
import { Environment, Havoc } from "../singletons.js";
@ -125,6 +125,7 @@ function tryToEvaluateConstructOrLeaveAsAbstract(
// return or throw completion
if (completion instanceof AbruptCompletion) throw completion;
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
invariant(completion instanceof ObjectValue || completion instanceof AbstractObjectValue);
return completion;
}

View File

@ -14,7 +14,13 @@ import type { LexicalEnvironment } from "../environment.js";
import { CompilerDiagnostic, InfeasiblePathError } from "../errors.js";
import { Reference } from "../environment.js";
import { computeBinary } from "./BinaryExpression.js";
import { AbruptCompletion, BreakCompletion, PossiblyNormalCompletion, Completion } from "../completions.js";
import {
AbruptCompletion,
BreakCompletion,
SimpleNormalCompletion,
PossiblyNormalCompletion,
Completion,
} from "../completions.js";
import { InternalGetResultValue } from "./ForOfStatement.js";
import { EmptyValue, AbstractValue, Value } from "../values/index.js";
import { StrictEqualityComparisonPartial, UpdateEmpty } from "../methods/index.js";
@ -186,6 +192,9 @@ function AbstractCaseBlockEvaluation(
// return or throw completion
if (completion instanceof AbruptCompletion) throw completion;
if (completion instanceof SimpleNormalCompletion) {
completion = completion.value;
}
invariant(completion instanceof Value);
return completion;
}
@ -369,6 +378,9 @@ export default function(
// all the branches come together into one.
result = realm.composeWithSavedCompletion(result);
}
if (result instanceof SimpleNormalCompletion) {
result = result.value;
}
invariant(result instanceof Value); // since evaluationHelper returns a value in non abrupt cases
return result;
}

View File

@ -11,7 +11,14 @@
import type { Effects, Realm } from "../realm.js";
import { type LexicalEnvironment } from "../environment.js";
import { AbruptCompletion, ForkedAbruptCompletion, PossiblyNormalCompletion, ThrowCompletion } from "../completions.js";
import {
AbruptCompletion,
ForkedAbruptCompletion,
PossiblyNormalCompletion,
ThrowCompletion,
SimpleNormalCompletion,
NormalCompletion,
} from "../completions.js";
import { UpdateEmpty } from "../methods/index.js";
import { Functions, Join } from "../singletons.js";
import { Value } from "../values/index.js";
@ -69,7 +76,7 @@ export default function(ast: BabelNodeTryStatement, strictCode: boolean, env: Le
let finalizerEffects = composeNestedEffectsWithFinalizer(blockRes);
finalizerRes = finalizerEffects.result;
// The result may become abrupt because of the finalizer, but it cannot become normal.
invariant(!(finalizerRes instanceof Value));
invariant(!(finalizerRes instanceof SimpleNormalCompletion));
} else {
// A single thread of control has produced a normal blockRes and the global state is up to date.
finalizerRes = env.evaluateCompletion(ast.finalizer, strictCode);
@ -78,7 +85,7 @@ export default function(ast: BabelNodeTryStatement, strictCode: boolean, env: Le
if (finalizerRes instanceof AbruptCompletion) throw finalizerRes;
if (finalizerRes instanceof PossiblyNormalCompletion) realm.composeWithSavedCompletion(finalizerRes);
if (handlerRes instanceof PossiblyNormalCompletion) handlerRes = handlerRes.value;
if (handlerRes instanceof NormalCompletion) handlerRes = handlerRes.value;
if (handlerRes instanceof Value) return (UpdateEmpty(realm, handlerRes, realm.intrinsics.undefined): any);
throw handlerRes;

View File

@ -11,7 +11,7 @@
import type { Realm } from "../realm.js";
import type { LexicalEnvironment } from "../environment.js";
import { AbruptCompletion, PossiblyNormalCompletion } from "../completions.js";
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
import { CompilerDiagnostic, FatalError } from "../errors.js";
import { TypesDomain, ValuesDomain } from "../domains/index.js";
import {
@ -143,6 +143,7 @@ function tryToEvaluateOperationOrLeaveAsAbstract(
// return or throw completion
if (completion instanceof AbruptCompletion) throw completion;
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
invariant(completion instanceof Value);
return completion;
}

View File

@ -22,6 +22,7 @@ import {
ContinueCompletion,
PossiblyNormalCompletion,
ForkedAbruptCompletion,
SimpleNormalCompletion,
NormalCompletion,
ReturnCompletion,
ThrowCompletion,
@ -152,7 +153,7 @@ export class JoinImplementation {
invariant(c.savedEffects === undefined); // the caller should ensure this
let savedPathConditions = pnc.savedPathConditions;
if (pnc.consequent instanceof AbruptCompletion) {
if (pnc.alternate instanceof Value) {
if (pnc.alternate instanceof SimpleNormalCompletion) {
let { generator, modifiedBindings, modifiedProperties, createdObjects } = pnc.alternateEffects;
let newAlternateEffects = new Effects(c, generator, modifiedBindings, modifiedProperties, createdObjects);
return new PossiblyNormalCompletion(
@ -182,7 +183,7 @@ export class JoinImplementation {
);
} else {
invariant(pnc.alternate instanceof AbruptCompletion);
if (pnc.consequent instanceof Value) {
if (pnc.consequent instanceof SimpleNormalCompletion) {
let { generator, modifiedBindings, modifiedProperties, createdObjects } = pnc.consequentEffects;
let newConsequentEffects = new Effects(c, generator, modifiedBindings, modifiedProperties, createdObjects);
return new PossiblyNormalCompletion(
@ -219,10 +220,10 @@ export class JoinImplementation {
subsequentEffects: Effects
) {
let v = subsequentEffects.result;
invariant(v instanceof Value);
pnc.value = v;
invariant(v instanceof SimpleNormalCompletion);
pnc.value = v.value;
if (pnc.consequent instanceof AbruptCompletion) {
if (pnc.alternate instanceof Value) {
if (pnc.alternate instanceof SimpleNormalCompletion) {
pnc.alternate = v;
pnc.alternateEffects.result = v;
pnc.alternateEffects = realm.composeEffects(pnc.alternateEffects, subsequentEffects);
@ -231,7 +232,7 @@ export class JoinImplementation {
this.updatePossiblyNormalCompletionWithSubsequentEffects(realm, pnc.alternate, subsequentEffects);
}
} else {
if (pnc.consequent instanceof Value) {
if (pnc.consequent instanceof SimpleNormalCompletion) {
pnc.consequent = v;
pnc.consequentEffects.result = v;
pnc.consequentEffects = realm.composeEffects(pnc.consequentEffects, subsequentEffects);
@ -243,13 +244,15 @@ export class JoinImplementation {
}
updatePossiblyNormalCompletionWithValue(realm: Realm, pnc: PossiblyNormalCompletion, v: Value) {
let nc = new SimpleNormalCompletion(v);
pnc.value = v;
if (pnc.consequent instanceof AbruptCompletion) {
Path.withInverseCondition(pnc.joinCondition, () => {
if (v instanceof AbstractValue) v = realm.simplifyAndRefineAbstractValue(v);
if (pnc.alternate instanceof Value) {
pnc.alternate = v;
pnc.alternateEffects.result = v;
if (pnc.alternate instanceof SimpleNormalCompletion) {
nc.value = v;
pnc.alternate = nc;
pnc.alternateEffects.result = nc;
pnc.value = v;
} else {
invariant(pnc.alternate instanceof PossiblyNormalCompletion);
@ -261,9 +264,10 @@ export class JoinImplementation {
} else {
Path.withCondition(pnc.joinCondition, () => {
if (v instanceof AbstractValue) v = realm.simplifyAndRefineAbstractValue(v);
if (pnc.consequent instanceof Value) {
pnc.consequent = v;
pnc.consequentEffects.result = v;
if (pnc.consequent instanceof SimpleNormalCompletion) {
nc.value = v;
pnc.consequent = nc;
pnc.consequentEffects.result = nc;
pnc.value = v;
} else {
invariant(pnc.consequent instanceof PossiblyNormalCompletion);
@ -289,7 +293,7 @@ export class JoinImplementation {
let pncc = pnc.consequent;
if (pncc instanceof AbruptCompletion) {
e = realm.composeEffects(pnc.alternateEffects, e);
if (pnc.alternate instanceof Value) {
if (pnc.alternate instanceof SimpleNormalCompletion) {
return new ForkedAbruptCompletion(realm, pnc.joinCondition, pncc, pnc.consequentEffects, ac, e);
}
invariant(pnc.alternate instanceof PossiblyNormalCompletion);
@ -301,7 +305,7 @@ export class JoinImplementation {
let pnca = pnc.alternate;
invariant(pnca instanceof AbruptCompletion);
e = realm.composeEffects(pnc.consequentEffects, e);
if (pnc.consequent instanceof Value) {
if (pnc.consequent instanceof SimpleNormalCompletion) {
return new ForkedAbruptCompletion(realm, pnc.joinCondition, ac, e, pnca, pnc.alternateEffects);
}
invariant(pnc.consequent instanceof PossiblyNormalCompletion);
@ -312,52 +316,78 @@ export class JoinImplementation {
}
}
updatePossiblyNormalCompletionWithConditionalValue(
updatePossiblyNormalCompletionWithConditionalSimpleNormalCompletion(
realm: Realm,
joinCondition: AbstractValue,
pnc: PossiblyNormalCompletion,
v: Value
nc: SimpleNormalCompletion
) {
let v = nc.value;
if (pnc.consequent instanceof AbruptCompletion) {
if (pnc.alternate instanceof Value) {
pnc.alternate = AbstractValue.createFromConditionalOp(realm, joinCondition, pnc.alternate, v);
if (pnc.alternate instanceof SimpleNormalCompletion) {
nc.value = AbstractValue.createFromConditionalOp(realm, joinCondition, pnc.alternate.value, v);
pnc.alternate = nc;
pnc.alternateEffects.result = pnc.alternate;
} else {
invariant(pnc.alternate instanceof PossiblyNormalCompletion);
this.updatePossiblyNormalCompletionWithConditionalValue(realm, joinCondition, pnc.alternate, v);
this.updatePossiblyNormalCompletionWithConditionalSimpleNormalCompletion(
realm,
joinCondition,
pnc.alternate,
nc
);
}
} else {
if (pnc.consequent instanceof Value) {
pnc.consequent = AbstractValue.createFromConditionalOp(realm, joinCondition, pnc.consequent, v);
if (pnc.consequent instanceof SimpleNormalCompletion) {
nc.value = AbstractValue.createFromConditionalOp(realm, joinCondition, pnc.consequent.value, v);
pnc.consequent = nc;
pnc.consequentEffects.result = pnc.consequent;
} else {
invariant(pnc.consequent instanceof PossiblyNormalCompletion);
this.updatePossiblyNormalCompletionWithConditionalValue(realm, joinCondition, pnc.consequent, v);
this.updatePossiblyNormalCompletionWithConditionalSimpleNormalCompletion(
realm,
joinCondition,
pnc.consequent,
nc
);
}
}
}
updatePossiblyNormalCompletionWithInverseConditionalValue(
updatePossiblyNormalCompletionWithInverseConditionalSimpleNormalCompletion(
realm: Realm,
joinCondition: AbstractValue,
pnc: PossiblyNormalCompletion,
v: Value
nc: SimpleNormalCompletion
) {
let v = nc.value;
if (pnc.consequent instanceof AbruptCompletion) {
if (pnc.alternate instanceof Value) {
pnc.alternate = AbstractValue.createFromConditionalOp(realm, joinCondition, v, pnc.alternate);
if (pnc.alternate instanceof SimpleNormalCompletion) {
nc.value = AbstractValue.createFromConditionalOp(realm, joinCondition, v, pnc.alternate.value);
pnc.alternate = nc;
pnc.alternateEffects.result = pnc.alternate;
} else {
invariant(pnc.alternate instanceof PossiblyNormalCompletion);
this.updatePossiblyNormalCompletionWithInverseConditionalValue(realm, joinCondition, pnc.alternate, v);
this.updatePossiblyNormalCompletionWithInverseConditionalSimpleNormalCompletion(
realm,
joinCondition,
pnc.alternate,
nc
);
}
} else {
if (pnc.consequent instanceof Value) {
pnc.consequent = AbstractValue.createFromConditionalOp(realm, joinCondition, v, pnc.consequent);
if (pnc.consequent instanceof SimpleNormalCompletion) {
nc.value = AbstractValue.createFromConditionalOp(realm, joinCondition, v, pnc.consequent.value);
pnc.consequent = nc;
pnc.consequentEffects.result = pnc.consequent;
} else {
invariant(pnc.consequent instanceof PossiblyNormalCompletion);
this.updatePossiblyNormalCompletionWithInverseConditionalValue(realm, joinCondition, pnc.consequent, v);
this.updatePossiblyNormalCompletionWithInverseConditionalSimpleNormalCompletion(
realm,
joinCondition,
pnc.consequent,
nc
);
}
}
}
@ -368,12 +398,17 @@ export class JoinImplementation {
c: PossiblyNormalCompletion,
a: PossiblyNormalCompletion
): PossiblyNormalCompletion {
let getAbstractValue = (v1: void | Value, v2: void | Value): Value => {
if (v1 instanceof EmptyValue) return v2 || realm.intrinsics.undefined;
if (v2 instanceof EmptyValue) return v1 || realm.intrinsics.undefined;
return AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
};
let ac = new AbruptCompletion(realm.intrinsics.empty);
let ee = construct_empty_effects(realm, ac);
let fc = this.replacePossiblyNormalCompletionWithForkedAbruptCompletion(realm, c, ac, ee);
let ce = construct_empty_effects(realm, fc);
let ae = construct_empty_effects(realm, a);
let rv = this.collapseResults(realm, joinCondition, c.value, a.value);
let rv = this.joinValues(realm, c.value, a.value, getAbstractValue);
invariant(rv instanceof Value);
a.value = rv;
return new PossiblyNormalCompletion(rv, joinCondition, fc, ce, a, ae, []);
@ -409,7 +444,9 @@ export class JoinImplementation {
if (ce.result instanceof CompletionType) {
// Erase completions of type CompletionType and prepare for transformation of c to a possibly normal completion
if (c.consequent instanceof CompletionType) {
c.consequentEffects.result = c.consequent = convertToPNC ? (realm.intrinsics.empty: any) : dummyCompletion;
c.consequentEffects.result = c.consequent = convertToPNC
? new SimpleNormalCompletion(realm.intrinsics.empty)
: dummyCompletion;
convertToPNC = false;
} else if (convertToPNC && c.consequent instanceof ForkedAbruptCompletion) {
c.consequentEffects.result = c.consequent = (c.consequent.transferChildrenToPossiblyNormalCompletion(): any);
@ -427,7 +464,9 @@ export class JoinImplementation {
if (ae.result instanceof CompletionType) {
// Erase completions of type CompletionType and prepare for transformation of c to a possibly normal completion
if (c.alternate instanceof CompletionType) {
c.alternateEffects.result = c.alternate = convertToPNC ? (realm.intrinsics.empty: any) : dummyCompletion;
c.alternateEffects.result = c.alternate = convertToPNC
? new SimpleNormalCompletion(realm.intrinsics.empty)
: dummyCompletion;
} else if (convertToPNC && c.alternate instanceof ForkedAbruptCompletion) {
c.alternateEffects.result = c.alternate = (c.alternate.transferChildrenToPossiblyNormalCompletion(): any);
}
@ -498,7 +537,7 @@ export class JoinImplementation {
return new Effects(result, generator, bindings, properties, createdObjects);
}
joinNestedEffects(realm: Realm, c: Completion | Value, precedingEffects?: Effects): Effects {
joinNestedEffects(realm: Realm, c: Completion, precedingEffects?: Effects): Effects {
if (c instanceof PossiblyNormalCompletion || c instanceof ForkedAbruptCompletion) {
let e1 = this.joinNestedEffects(realm, c.consequent, c.consequentEffects);
let e2 = this.joinNestedEffects(realm, c.alternate, c.alternateEffects);
@ -517,8 +556,8 @@ export class JoinImplementation {
joinCondition: AbstractValue,
result1: EvaluationResult,
result2: EvaluationResult
): AbruptCompletion | PossiblyNormalCompletion | Value {
let getAbstractValue = (v1: void | Value, v2: void | Value) => {
): Completion {
let getAbstractValue = (v1: void | Value, v2: void | Value): Value => {
if (v1 instanceof EmptyValue) return v2 || realm.intrinsics.undefined;
if (v2 instanceof EmptyValue) return v1 || realm.intrinsics.undefined;
return AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
@ -548,8 +587,8 @@ export class JoinImplementation {
invariant(val instanceof Value);
return new ThrowCompletion(val, result1.location);
}
if (result1 instanceof Value && result2 instanceof Value) {
return getAbstractValue(result1, result2);
if (result1 instanceof SimpleNormalCompletion && result2 instanceof SimpleNormalCompletion) {
return new SimpleNormalCompletion(getAbstractValue(result1.value, result2.value));
}
AbstractValue.reportIntrospectionError(joinCondition);
throw new FatalError();
@ -562,7 +601,7 @@ export class JoinImplementation {
result2: EvaluationResult,
e1: Effects,
e2: Effects
): AbruptCompletion | PossiblyNormalCompletion | Value {
): Completion {
let getAbstractValue = (v1: void | Value, v2: void | Value) => {
return AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
};
@ -570,10 +609,10 @@ export class JoinImplementation {
AbstractValue.reportIntrospectionError(joinCondition);
throw new FatalError();
}
if (result1 instanceof Value && result2 instanceof Value) {
let val = this.joinValues(realm, result1, result2, getAbstractValue);
if (result1 instanceof SimpleNormalCompletion && result2 instanceof SimpleNormalCompletion) {
let val = this.joinValues(realm, result1.value, result2.value, getAbstractValue);
invariant(val instanceof Value);
return val;
return new SimpleNormalCompletion(val);
}
if (result1 instanceof AbruptCompletion && result2 instanceof AbruptCompletion) {
return new ForkedAbruptCompletion(realm, joinCondition, result1, e1, result2, e2);
@ -582,17 +621,17 @@ export class JoinImplementation {
return this.joinPossiblyNormalCompletions(realm, joinCondition, result1, result2);
}
if (result1 instanceof AbruptCompletion) {
let value = result2;
let completion = result2;
let savedEffects;
let savedPathConditions = [];
if (result2 instanceof PossiblyNormalCompletion) {
value = result2.value;
completion = result2.getNormalCompletion();
savedEffects = result2.savedEffects;
savedPathConditions = result2.savedPathConditions;
}
invariant(value instanceof Value);
invariant(completion instanceof SimpleNormalCompletion);
return new PossiblyNormalCompletion(
value,
completion.value,
joinCondition,
result1,
e1,
@ -603,17 +642,17 @@ export class JoinImplementation {
);
}
if (result2 instanceof AbruptCompletion) {
let value = result1;
let completion = result1;
let savedEffects;
let savedPathConditions = [];
if (result1 instanceof PossiblyNormalCompletion) {
value = result1.value;
completion = result1.value;
savedEffects = result1.savedEffects;
savedPathConditions = result1.savedPathConditions;
}
invariant(value instanceof Value);
invariant(completion instanceof SimpleNormalCompletion);
return new PossiblyNormalCompletion(
value,
completion.value,
joinCondition,
result1,
e1,
@ -624,13 +663,18 @@ export class JoinImplementation {
);
}
if (result1 instanceof PossiblyNormalCompletion) {
invariant(result2 instanceof Value);
this.updatePossiblyNormalCompletionWithConditionalValue(realm, joinCondition, result1, result2);
invariant(result2 instanceof SimpleNormalCompletion);
this.updatePossiblyNormalCompletionWithConditionalSimpleNormalCompletion(realm, joinCondition, result1, result2);
return result1;
}
if (result2 instanceof PossiblyNormalCompletion) {
invariant(result1 instanceof Value);
this.updatePossiblyNormalCompletionWithInverseConditionalValue(realm, joinCondition, result2, result1);
invariant(result1 instanceof SimpleNormalCompletion);
this.updatePossiblyNormalCompletionWithInverseConditionalSimpleNormalCompletion(
realm,
joinCondition,
result2,
result1
);
return result2;
}
invariant(false);

View File

@ -9,7 +9,7 @@
/* @flow */
import { AbruptCompletion, PossiblyNormalCompletion } from "../completions.js";
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
import { construct_empty_effects, type Realm, Effects } from "../realm.js";
import type { Descriptor, PropertyBinding, PropertyKeyValue } from "../types.js";
import {
@ -341,6 +341,7 @@ export class PropertiesImplementation {
// return or throw completion
if (completion instanceof AbruptCompletion) throw completion;
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
invariant(completion instanceof Value);
return To.ToBooleanPartial(realm, completion);
}

View File

@ -15,7 +15,7 @@ import type { Bindings, BindingEntry, EvaluationResult, PropertyBindings, Create
import { Effects } from "../realm.js";
import type { Descriptor, PropertyBinding } from "../types.js";
import { AbruptCompletion, PossiblyNormalCompletion } from "../completions.js";
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
import { Reference } from "../environment.js";
import { cloneDescriptor, equalDescriptors, IsDataDescriptor, StrictEqualityComparison } from "../methods/index.js";
import { Generator } from "../utils/generator.js";
@ -100,16 +100,20 @@ export class WidenImplementation {
return new Effects(result, generator, bindings, properties, createdObjects);
}
widenResults(realm: Realm, result1: EvaluationResult, result2: EvaluationResult): PossiblyNormalCompletion | Value {
widenResults(
realm: Realm,
result1: EvaluationResult,
result2: EvaluationResult
): PossiblyNormalCompletion | SimpleNormalCompletion {
invariant(!(result1 instanceof Reference || result2 instanceof Reference), "loop bodies should not result in refs");
invariant(
!(result1 instanceof AbruptCompletion || result2 instanceof AbruptCompletion),
"if a loop iteration ends abruptly, there is no need for fixed point computation"
);
if (result1 instanceof Value && result2 instanceof Value) {
let val = this.widenValues(realm, result1, result2);
if (result1 instanceof SimpleNormalCompletion && result2 instanceof SimpleNormalCompletion) {
let val = this.widenValues(realm, result1.value, result2.value);
invariant(val instanceof Value);
return val;
return new SimpleNormalCompletion(val);
}
if (result1 instanceof PossiblyNormalCompletion || result2 instanceof PossiblyNormalCompletion) {
//todo: #1174 figure out how to deal with loops that have embedded conditional exits
@ -348,7 +352,8 @@ export class WidenImplementation {
}
containsResults(result1: EvaluationResult, result2: EvaluationResult): boolean {
if (result1 instanceof Value && result2 instanceof Value) return this._containsValues(result1, result2);
if (result1 instanceof SimpleNormalCompletion && result2 instanceof SimpleNormalCompletion)
return this._containsValues(result1.value, result2.value);
return false;
}

View File

@ -14,7 +14,7 @@ import type { Realm } from "../realm.js";
import { Effects } from "../realm.js";
import type { LexicalEnvironment } from "../environment.js";
import { AbruptCompletion, Completion, PossiblyNormalCompletion } from "../completions.js";
import { AbruptCompletion, Completion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
import { EnvironmentRecord, Reference } from "../environment.js";
import { EvaluateDirectCallWithArgList, GetThisValue, IsInTailPosition, SameValue } from "../methods/index.js";
import { Environment, Functions, Join } from "../singletons.js";
@ -134,6 +134,7 @@ function callBothFunctionsAndJoinTheirEffects(
realm.applyEffects(joinedEffects);
// return or throw completion
if (joinedCompletion instanceof SimpleNormalCompletion) joinedCompletion = joinedCompletion.value;
invariant(joinedCompletion instanceof AbruptCompletion || joinedCompletion instanceof Value);
return joinedCompletion;
}

View File

@ -15,6 +15,7 @@
import type { Realm } from "../../realm.js";
import { ReactStatistics } from "../../serializer/types.js";
import { SimpleNormalCompletion } from "../../completions.js";
import {
AbstractObjectValue,
AbstractValue,
@ -456,8 +457,8 @@ function handleNestedOptimizedFunctions(realm: Realm, reconciler: Reconciler, st
[closureEffects],
() => {
let serverRenderer = new ReactDOMServerRenderer(realm, staticMarkup);
invariant(closureEffects.result instanceof Value);
return serverRenderer.render(closureEffects.result);
invariant(closureEffects.result instanceof SimpleNormalCompletion);
return serverRenderer.render(closureEffects.result.value);
},
"handleNestedOptimizedFunctions"
);
@ -498,9 +499,9 @@ export function renderToString(
);
invariant(effects);
realm.applyEffects(effects);
invariant(effects.result instanceof Value);
invariant(effects.result instanceof SimpleNormalCompletion);
let serverRenderer = new ReactDOMServerRenderer(realm, staticMarkup);
let renderValue = serverRenderer.render(effects.result);
let renderValue = serverRenderer.render(effects.result.value);
handleNestedOptimizedFunctions(realm, reconciler, staticMarkup);
return renderValue;
}

View File

@ -55,6 +55,7 @@ import { cloneDescriptor, Construct } from "./methods/index.js";
import {
AbruptCompletion,
Completion,
SimpleNormalCompletion,
ForkedAbruptCompletion,
PossiblyNormalCompletion,
ThrowCompletion,
@ -78,7 +79,7 @@ export type BindingEntry = {
previousValue: void | Value,
};
export type Bindings = Map<Binding, BindingEntry>;
export type EvaluationResult = Completion | Reference | Value;
export type EvaluationResult = Completion | Reference;
export type PropertyBindings = Map<PropertyBinding, void | Descriptor>;
export type CreatedObjects = Set<ObjectValue>;
@ -191,7 +192,10 @@ export class ExecutionContext {
}
}
export function construct_empty_effects(realm: Realm, c: Completion | Value = realm.intrinsics.empty): Effects {
export function construct_empty_effects(
realm: Realm,
c: Completion = new SimpleNormalCompletion(realm.intrinsics.empty)
): Effects {
return new Effects(c, new Generator(realm, "construct_empty_effects"), new Map(), new Map(), new Set());
}
@ -827,6 +831,7 @@ export class Realm {
*/
// Return the captured state changes and evaluation result
if (c instanceof Value) c = new SimpleNormalCompletion(c);
result = new Effects(c, astGenerator, astBindings, astProperties, astCreatedObjects);
return result;
} finally {
@ -875,7 +880,7 @@ export class Realm {
undefined,
"evaluateWithUndo"
);
return effects.result instanceof Value ? effects.result : defaultValue;
return effects.result instanceof SimpleNormalCompletion ? effects.result.value : defaultValue;
} finally {
this.errorHandler = oldErrorHandler;
}
@ -901,8 +906,8 @@ export class Realm {
// all the branches come together into one.
resultVal = this.composeWithSavedCompletion(resultVal);
}
invariant(resultVal instanceof Value);
return resultVal;
invariant(resultVal instanceof SimpleNormalCompletion);
return resultVal.value;
} catch (e) {
if (diagnostic !== undefined) return diagnostic;
throw e;
@ -918,7 +923,7 @@ export class Realm {
let result;
[test, result] = iteration();
if (!(test instanceof AbstractValue)) throw new FatalError("loop terminates before fixed point");
invariant(result instanceof Completion || result instanceof Value);
invariant(result instanceof Completion);
return result;
};
let effects1 = this.evaluateForEffects(f, undefined, "evaluateForFixpointEffects/1");
@ -999,6 +1004,7 @@ export class Realm {
// return or throw completion
if (completion instanceof AbruptCompletion) throw completion;
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
invariant(completion instanceof Value);
return completion;
}
@ -1255,7 +1261,7 @@ export class Realm {
captureEffects(completion: PossiblyNormalCompletion) {
invariant(completion.savedEffects === undefined);
completion.savedEffects = new Effects(
this.intrinsics.undefined,
new SimpleNormalCompletion(this.intrinsics.undefined),
(this.generator: any),
(this.modifiedBindings: any),
(this.modifiedProperties: any),
@ -1272,7 +1278,13 @@ export class Realm {
invariant(this.modifiedBindings !== undefined);
invariant(this.modifiedProperties !== undefined);
invariant(this.createdObjects !== undefined);
return new Effects(v, this.generator, this.modifiedBindings, this.modifiedProperties, this.createdObjects);
return new Effects(
new SimpleNormalCompletion(v),
this.generator,
this.modifiedBindings,
this.modifiedProperties,
this.createdObjects
);
}
stopEffectCaptureAndUndoEffects(completion: PossiblyNormalCompletion) {

View File

@ -32,8 +32,9 @@ import {
AbruptCompletion,
Completion,
ForkedAbruptCompletion,
NormalCompletion,
SimpleNormalCompletion,
PossiblyNormalCompletion,
NormalCompletion,
} from "./completions.js";
import { EnvironmentRecord, LexicalEnvironment, Reference } from "./environment.js";
import { Generator } from "./utils/generator.js";
@ -739,18 +740,18 @@ export type JoinType = {
e: Effects
): ForkedAbruptCompletion,
updatePossiblyNormalCompletionWithConditionalValue(
updatePossiblyNormalCompletionWithConditionalSimpleNormalCompletion(
realm: Realm,
joinCondition: AbstractValue,
pnc: PossiblyNormalCompletion,
v: Value
nc: SimpleNormalCompletion
): void,
updatePossiblyNormalCompletionWithInverseConditionalValue(
updatePossiblyNormalCompletionWithInverseConditionalSimpleNormalCompletion(
realm: Realm,
joinCondition: AbstractValue,
pnc: PossiblyNormalCompletion,
v: Value
nc: SimpleNormalCompletion
): void,
extractAndJoinCompletionsOfType(
@ -769,7 +770,7 @@ export type JoinType = {
joinCondition: AbstractValue,
result1: EvaluationResult,
result2: EvaluationResult
): AbruptCompletion | PossiblyNormalCompletion | Value,
): Completion,
joinOrForkResults(
realm: Realm,
@ -778,7 +779,7 @@ export type JoinType = {
result2: EvaluationResult,
e1: Effects,
e2: Effects
): AbruptCompletion | PossiblyNormalCompletion | Value,
): Completion,
composeGenerators(realm: Realm, generator1: Generator, generator2: Generator): Generator,

View File

@ -41,6 +41,7 @@ import {
ThrowCompletion,
ReturnCompletion,
PossiblyNormalCompletion,
SimpleNormalCompletion,
} from "../completions.js";
import type {
BabelNodeExpression,
@ -436,9 +437,7 @@ export class Generator {
}
if (result instanceof UndefinedValue) return output;
if (result instanceof Value) {
output.emitReturnValue(result);
} else if (result instanceof ReturnCompletion) {
if (result instanceof SimpleNormalCompletion || result instanceof ReturnCompletion) {
output.emitReturnValue(result.value);
} else if (result instanceof PossiblyNormalCompletion || result instanceof ForkedAbruptCompletion) {
output.emitIfThenElse(result, realm);

View File

@ -64,7 +64,7 @@ export class Logger {
undefined,
"tryQuery"
);
invariant(effects.result === realm.intrinsics.undefined);
invariant(effects.result.value === realm.intrinsics.undefined);
return ((result: any): T);
} finally {
realm.errorHandler = oldErrorHandler;

View File

@ -14,7 +14,7 @@ import { CompilerDiagnostic, FatalError } from "../errors.js";
import { Realm, Tracer } from "../realm.js";
import type { Effects } from "../realm.js";
import { Get } from "../methods/index.js";
import { AbruptCompletion, PossiblyNormalCompletion } from "../completions.js";
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
import { Environment } from "../singletons.js";
import {
AbstractValue,
@ -251,9 +251,9 @@ export class ModuleTracer extends Tracer {
);
} else {
result = effects.result;
if (result instanceof Value) {
if (result instanceof SimpleNormalCompletion) {
realm.applyEffects(effects, `initialization of module ${moduleIdValue}`);
this.modules.recordModuleInitialized(moduleIdValue, result);
this.modules.recordModuleInitialized(moduleIdValue, result.value);
} else if (result instanceof PossiblyNormalCompletion) {
let warning = new CompilerDiagnostic(
"Module import may fail with an exception",
@ -273,6 +273,7 @@ export class ModuleTracer extends Tracer {
invariant(popped === moduleIdValue);
this.log(`<require(${moduleIdValue})`);
}
if (result instanceof SimpleNormalCompletion) result = result.value;
invariant(result instanceof Value);
return result;
});
@ -625,6 +626,7 @@ export class Modules {
invariant(modifiedBindings);
if (result instanceof AbruptCompletion) return undefined;
if (result instanceof SimpleNormalCompletion) result = result.value;
invariant(result instanceof Value);
if (!generator.empty() || (result instanceof ObjectValue && createdObjects.has(result))) return undefined;

View File

@ -123,6 +123,10 @@ export default class AbstractValue extends Value {
args: Array<Value>;
_buildNode: void | AbstractValueBuildNodeFunction | BabelNodeExpression;
toDisplayString(): string {
return "[Abstract " + this.hashValue.toString() + "]";
}
addSourceLocationsTo(locations: Array<BabelNodeSourceLocation>, seenValues?: Set<AbstractValue> = new Set()) {
if (seenValues.has(this)) return;
seenValues.add(this);

View File

@ -34,6 +34,12 @@ export default class Value {
// Name from original source if existant
__originalName: void | string;
toDisplayString(): string {
return (
"[" + this.constructor.name + " originally; " + (this.__originalName ? this.__originalName : "undefined") + "]"
);
}
equals(x: Value): boolean {
invariant(false, "abstract method; please override");
}