Handle PossiblyNormal completions inside try

Summary:
If the try block of a try-catch statement possibly throws an exception, bring the effects of the catch handler into the state of the exceptional control flow and carry on.
Closes https://github.com/facebook/prepack/pull/1046

Differential Revision: D5989477

Pulled By: hermanventer

fbshipit-source-id: 37dcd36e817d642122602554f71e0ba19706c400
This commit is contained in:
Herman Venter 2017-10-06 10:54:02 -07:00 committed by Facebook Github Bot
parent 237d9bd5ab
commit 076d234acb
5 changed files with 62 additions and 18 deletions

View File

@ -11,22 +11,56 @@
import type { Realm } from "../realm.js"; import type { Realm } from "../realm.js";
import type { LexicalEnvironment } from "../environment.js"; import type { LexicalEnvironment } from "../environment.js";
import { AbruptCompletion, ThrowCompletion } from "../completions.js"; import { AbruptCompletion, Completion, PossiblyNormalCompletion, ThrowCompletion } from "../completions.js";
import { UpdateEmpty } from "../methods/index.js"; import { joinEffects, UpdateEmpty } from "../methods/index.js";
import { Value } from "../values/index.js"; import { Value } from "../values/index.js";
import type { BabelNodeTryStatement } from "babel-types"; import type { BabelNodeTryStatement } from "babel-types";
import invariant from "../invariant.js"; import invariant from "../invariant.js";
export default function(ast: BabelNodeTryStatement, strictCode: boolean, env: LexicalEnvironment, realm: Realm): Value { export default function(
ast: BabelNodeTryStatement,
strictCode: boolean,
env: LexicalEnvironment,
realm: Realm
): PossiblyNormalCompletion | Value {
let completions = []; let completions = [];
let blockRes = env.evaluateCompletion(ast.block, strictCode); let blockRes = env.evaluateAbstractCompletion(ast.block, strictCode);
if (blockRes instanceof PossiblyNormalCompletion) {
let abruptCompletion;
let abruptEffects;
if (blockRes.consequent instanceof AbruptCompletion) {
abruptCompletion = blockRes.consequent;
abruptEffects = blockRes.consequentEffects;
} else {
abruptCompletion = blockRes.alternate;
abruptEffects = blockRes.alternateEffects;
}
if (abruptCompletion instanceof ThrowCompletion && ast.handler) {
let normalEffects = realm.getCapturedEffects(blockRes.value);
invariant(normalEffects !== undefined);
realm.stopEffectCaptureAndUndoEffects();
let handlerEffects = realm.evaluateForEffects(() => {
realm.applyEffects(abruptEffects);
invariant(ast.handler);
return env.evaluateAbstractCompletion(ast.handler, strictCode, abruptCompletion);
});
let jointEffects;
if (blockRes.consequent instanceof AbruptCompletion)
jointEffects = joinEffects(realm, blockRes.joinCondition, handlerEffects, normalEffects);
else jointEffects = joinEffects(realm, blockRes.joinCondition, normalEffects, handlerEffects);
realm.applyEffects(jointEffects);
completions.unshift(jointEffects[0]);
} else {
completions.unshift(blockRes);
}
} else {
if (blockRes instanceof ThrowCompletion && ast.handler) { if (blockRes instanceof ThrowCompletion && ast.handler) {
completions.unshift(env.evaluateCompletion(ast.handler, strictCode, blockRes)); completions.unshift(env.evaluateCompletion(ast.handler, strictCode, blockRes));
} else { } else {
completions.unshift(blockRes); completions.unshift(blockRes);
} }
}
if (ast.finalizer) { if (ast.finalizer) {
completions.unshift(env.evaluateCompletion(ast.finalizer, strictCode)); completions.unshift(env.evaluateCompletion(ast.finalizer, strictCode));
@ -34,7 +68,7 @@ export default function(ast: BabelNodeTryStatement, strictCode: boolean, env: Le
// use the last completion record // use the last completion record
for (let completion of completions) { for (let completion of completions) {
if (completion && completion instanceof AbruptCompletion) throw completion; if (completion instanceof AbruptCompletion) throw completion;
} }
if (ast.finalizer) { if (ast.finalizer) {
@ -43,7 +77,7 @@ export default function(ast: BabelNodeTryStatement, strictCode: boolean, env: Le
// otherwise use the last returned value // otherwise use the last returned value
for (let completion of completions) { for (let completion of completions) {
if (completion && completion instanceof Value) if (completion instanceof Value || completion instanceof Completion)
return (UpdateEmpty(realm, completion, realm.intrinsics.undefined): any); return (UpdateEmpty(realm, completion, realm.intrinsics.undefined): any);
} }

View File

@ -1,4 +1,3 @@
// throws introspection error
let x = global.__abstract ? __abstract("boolean", "true") : true; let x = global.__abstract ? __abstract("boolean", "true") : true;
let y; let y;
try { try {

View File

@ -1,6 +1,4 @@
// throws introspection error let x = global.__abstract ? __abstract("boolean", "true") : true;
let x = __abstract("boolean", "true");
try { try {
if (x) throw new Error("is true"); if (x) throw new Error("is true");
@ -8,3 +6,5 @@ try {
} catch (e) { } catch (e) {
z = e; z = e;
} }
inspect = function() { return z; }

View File

@ -1,9 +1,9 @@
// throws introspection error let x = global.__abstract ? __abstract("boolean", "true") : true;
let x = __abstract("boolean", "true");
try { try {
if (x) z = "is true"; else throw "is false"; if (x) z = "is true"; else throw "is false";
} catch (e) { } catch (e) {
z = e; z = e;
} }
inspect = function() { return z; }

View File

@ -0,0 +1,11 @@
// throws introspection error
let x = global.__abstract ? __abstract("boolean", "true") : true;
try {
if (x) z = "is true"; else throw "is false";
} finally {
z = "is finally";
}
inspect = function() { return z; }