Do not conflate prior saved completion with one from call.

Summary:
Release note: Fix bug #1262

The saved completion that is present before a function call should not be incorporated into the saved completion that is created inside the call, until after return completions have been appropriately joined and applied.
Closes https://github.com/facebook/prepack/pull/1263

Differential Revision: D6560937

Pulled By: hermanventer

fbshipit-source-id: 76e8d67f0ad2227dec9cf54f5f035a34f1941be3
This commit is contained in:
Herman Venter 2017-12-13 15:00:29 -08:00 committed by Facebook Github Bot
parent f4271d56f0
commit 5e86f790aa
3 changed files with 68 additions and 39 deletions

View File

@ -331,49 +331,55 @@ export function OrdinaryCallEvaluateBody(
// 2. Return the result of EvaluateBody of the parsed code that is the value of F's
// [[ECMAScriptCode]] internal slot passing F as the argument.
let code = F.$ECMAScriptCode;
invariant(code !== undefined);
let context = realm.getRunningContext();
let c = context.lexicalEnvironment.evaluateCompletionDeref(code, F.$Strict);
// We are about the leave this function and this presents a join point where all non exeptional control flows
// converge into a single flow using the joined effects as the new state.
c = Functions.incorporateSavedCompletion(realm, c);
let joinedEffects;
if (c instanceof PossiblyNormalCompletion) {
let e = realm.getCapturedEffects(c);
if (e !== undefined) {
// There were earlier, conditional exits from the function
// We join together the current effects with the effects of any earlier returns that are tracked in c.
realm.stopEffectCaptureAndUndoEffects(c);
} else {
e = construct_empty_effects(realm);
let priorSavedCompletion = realm.savedCompletion;
try {
realm.savedCompletion = undefined;
let code = F.$ECMAScriptCode;
invariant(code !== undefined);
let context = realm.getRunningContext();
let c = context.lexicalEnvironment.evaluateCompletionDeref(code, F.$Strict);
// We are about the leave this function and this presents a join point where all non exeptional control flows
// converge into a single flow using the joined effects as the new state.
c = Functions.incorporateSavedCompletion(realm, c);
let joinedEffects;
if (c instanceof PossiblyNormalCompletion) {
let e = realm.getCapturedEffects(c);
if (e !== undefined) {
// There were earlier, conditional exits from the function
// We join together the current effects with the effects of any earlier returns that are tracked in c.
realm.stopEffectCaptureAndUndoEffects(c);
} else {
e = construct_empty_effects(realm);
}
joinedEffects = Join.joinEffectsAndPromoteNestedReturnCompletions(realm, c, e);
} else if (c instanceof JoinedAbruptCompletions) {
joinedEffects = Join.joinEffectsAndPromoteNestedReturnCompletions(realm, c, construct_empty_effects(realm));
}
joinedEffects = Join.joinEffectsAndPromoteNestedReturnCompletions(realm, c, e);
} else if (c instanceof JoinedAbruptCompletions) {
joinedEffects = Join.joinEffectsAndPromoteNestedReturnCompletions(realm, c, construct_empty_effects(realm));
}
if (joinedEffects !== undefined) {
let result = joinedEffects[0];
if (result instanceof ReturnCompletion) {
if (joinedEffects !== undefined) {
let result = joinedEffects[0];
if (result instanceof ReturnCompletion) {
realm.applyEffects(joinedEffects);
return result;
}
invariant(result instanceof JoinedAbruptCompletions);
if (!(result.consequent instanceof ReturnCompletion || result.alternate instanceof ReturnCompletion)) {
realm.applyEffects(joinedEffects);
throw result;
}
// There is a normal return exit, but also one or more throw completions.
// The throw completions must be extracted into a saved possibly normal completion
// so that the caller can pick them up in its next completion.
joinedEffects = extractAndSavePossiblyNormalCompletion(result);
result = joinedEffects[0];
invariant(result instanceof ReturnCompletion);
realm.applyEffects(joinedEffects);
return result;
} else {
invariant(c instanceof Value || c instanceof AbruptCompletion);
return c;
}
invariant(result instanceof JoinedAbruptCompletions);
if (!(result.consequent instanceof ReturnCompletion || result.alternate instanceof ReturnCompletion)) {
realm.applyEffects(joinedEffects);
throw result;
}
// There is a normal return exit, but also one or more throw completions.
// The throw completions must be extracted into a saved possibly normal completion
// so that the caller can pick them up in its next completion.
joinedEffects = extractAndSavePossiblyNormalCompletion(result);
result = joinedEffects[0];
invariant(result instanceof ReturnCompletion);
realm.applyEffects(joinedEffects);
return result;
} else {
invariant(c instanceof Value || c instanceof AbruptCompletion);
return c;
} finally {
realm.incorporatePriorSavedCompletion(priorSavedCompletion);
}
}
}

View File

@ -612,6 +612,16 @@ export class Realm {
return completion.value;
}
incorporatePriorSavedCompletion(priorCompletion: void | PossiblyNormalCompletion) {
if (priorCompletion === undefined) return;
if (this.savedCompletion === undefined) {
this.savedCompletion = priorCompletion;
this.captureEffects(priorCompletion);
} else {
this.savedCompletion = Join.composePossiblyNormalCompletions(this, priorCompletion, this.savedCompletion);
}
}
captureEffects(completion: PossiblyNormalCompletion) {
if (completion.savedEffects !== undefined) {
// Already called captureEffects, just carry on

View File

@ -0,0 +1,13 @@
let b = global.__abstract ? __abstract("boolean", "true") : true;
function f() {}
function g() {
if (b) return "foo";
f();
return "bar";
}
var x = g();
inspect = function() { return x; }