mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-09-11 14:46:37 +03:00
Emit code for exceptions that make it to the top level.
Summary: Release note: Generate code for uncaught exceptions This is a high priority fix for a land blocking issue. Please review ASAP. Exceptions that (conditionally or unconditionally) propagate to the top level program used to cause Prepack to fail. The new behavior is to generate top level code that will (conditionally) throw the exceptions at runtime. Closes https://github.com/facebook/prepack/pull/1083 Differential Revision: D6050038 Pulled By: hermanventer fbshipit-source-id: 666c058befdebf2ed5f97bed74818db57b86f34b
This commit is contained in:
parent
acf67a3362
commit
b0e977136c
@ -209,8 +209,12 @@ function runTest(name, code, options, args) {
|
||||
let unique = 27277;
|
||||
let oldUniqueSuffix = "";
|
||||
try {
|
||||
expected = exec(`${addedCode}\n(function () {${code} // keep newline here as code may end with comment
|
||||
}).call(this);`);
|
||||
try {
|
||||
expected = exec(`${addedCode}\n(function () {${code} // keep newline here as code may end with comment
|
||||
}).call(this);`);
|
||||
} catch (e) {
|
||||
expected = e;
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
let max = addedCode ? 1 : 4;
|
||||
@ -237,7 +241,11 @@ function runTest(name, code, options, args) {
|
||||
}
|
||||
}
|
||||
if (markersIssue) break;
|
||||
actual = exec(addedCode + newCode);
|
||||
try {
|
||||
actual = exec(addedCode + newCode);
|
||||
} catch (e) {
|
||||
actual = e;
|
||||
}
|
||||
if (expected !== actual) {
|
||||
console.log(chalk.red("Output mismatch!"));
|
||||
break;
|
||||
|
@ -109,7 +109,7 @@ export default class TypesDomain {
|
||||
}
|
||||
|
||||
static logicalOp(op: BabelNodeLogicalOperator, left: TypesDomain, right: TypesDomain): TypesDomain {
|
||||
return TypesDomain.joinValues(left, right);
|
||||
return left.joinWith(right.getType());
|
||||
}
|
||||
|
||||
// return the type of the result in the case where there is no exception
|
||||
|
@ -9,11 +9,10 @@
|
||||
|
||||
/* @flow */
|
||||
|
||||
import { Completion } from "../completions.js";
|
||||
import { Completion, JoinedAbruptCompletions, ThrowCompletion } from "../completions.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import { FatalError } from "../errors.js";
|
||||
import { Value, EmptyValue } from "../values/index.js";
|
||||
import { AbstractValue, Value, EmptyValue } from "../values/index.js";
|
||||
import { GlobalEnvironmentRecord } from "../environment.js";
|
||||
import { FindVarScopedDeclarations } from "../methods/function.js";
|
||||
import { BoundNames } from "../methods/index.js";
|
||||
@ -21,6 +20,7 @@ import IsStrict from "../utils/strict.js";
|
||||
import invariant from "../invariant.js";
|
||||
import traverseFast from "../utils/traverse-fast.js";
|
||||
import type { BabelNodeProgram } from "babel-types";
|
||||
import * as t from "babel-types";
|
||||
|
||||
// ECMA262 15.1.11
|
||||
export function GlobalDeclarationInstantiation(
|
||||
@ -232,12 +232,19 @@ export default function(ast: BabelNodeProgram, strictCode: boolean, env: Lexical
|
||||
let potentialVal = env.evaluateCompletion(node, strictCode);
|
||||
if (potentialVal instanceof Completion) {
|
||||
if (!realm.useAbstractInterpretation) throw potentialVal;
|
||||
// todo: emit code to throw exeptions at runtime
|
||||
throw new FatalError();
|
||||
if (potentialVal instanceof JoinedAbruptCompletions) {
|
||||
emitConditionalThrow(potentialVal.joinCondition, potentialVal.consequent, potentialVal.alternate);
|
||||
potentialVal = potentialVal.value;
|
||||
} else if (potentialVal instanceof ThrowCompletion) {
|
||||
emitThrow(potentialVal.value);
|
||||
potentialVal = realm.intrinsics.undefined;
|
||||
} else {
|
||||
invariant(false); // other kinds of abrupt completions should not get this far
|
||||
}
|
||||
}
|
||||
if (context.savedCompletion !== undefined) {
|
||||
// todo: emit conditional code to throw exeptions at runtime
|
||||
throw new FatalError();
|
||||
let sc = context.savedCompletion;
|
||||
if (sc !== undefined) {
|
||||
emitConditionalThrow(sc.joinCondition, sc.consequent, sc.alternate);
|
||||
}
|
||||
if (!(potentialVal instanceof EmptyValue)) val = potentialVal;
|
||||
}
|
||||
@ -251,4 +258,55 @@ export default function(ast: BabelNodeProgram, strictCode: boolean, env: Lexical
|
||||
|
||||
invariant(val === undefined || val instanceof Value);
|
||||
return val || realm.intrinsics.empty;
|
||||
|
||||
function emitThrow(value: Value) {
|
||||
let generator = realm.generator;
|
||||
invariant(generator !== undefined);
|
||||
generator.emitStatement([value], ([argument]) => t.throwStatement(argument));
|
||||
}
|
||||
|
||||
function emitConditionalThrow(
|
||||
condition: AbstractValue,
|
||||
trueBranch: Completion | Value,
|
||||
falseBranch: Completion | Value
|
||||
) {
|
||||
let generator = realm.generator;
|
||||
invariant(generator !== undefined);
|
||||
let [args, buildfunc] = deconstruct(condition, trueBranch, falseBranch);
|
||||
generator.emitStatement(args, buildfunc);
|
||||
}
|
||||
|
||||
function deconstruct(condition: AbstractValue, trueBranch: Completion | Value, falseBranch: Completion | Value) {
|
||||
let targs;
|
||||
let tfunc;
|
||||
let fargs;
|
||||
let ffunc;
|
||||
if (trueBranch instanceof JoinedAbruptCompletions) {
|
||||
[targs, tfunc] = deconstruct(trueBranch.joinCondition, trueBranch.consequent, trueBranch.alternate);
|
||||
} else if (trueBranch instanceof ThrowCompletion) {
|
||||
targs = [trueBranch.value];
|
||||
tfunc = ([argument]) => t.throwStatement(argument);
|
||||
} else {
|
||||
targs = [];
|
||||
tfunc = nodes => t.emptyStatement();
|
||||
}
|
||||
if (falseBranch instanceof JoinedAbruptCompletions) {
|
||||
[fargs, ffunc] = deconstruct(falseBranch.joinCondition, falseBranch.consequent, falseBranch.alternate);
|
||||
} else if (falseBranch instanceof ThrowCompletion) {
|
||||
fargs = [falseBranch.value];
|
||||
ffunc = ([argument]) => t.throwStatement(argument);
|
||||
} else {
|
||||
fargs = [];
|
||||
ffunc = nodes => t.emptyStatement();
|
||||
}
|
||||
let args = [condition].concat(targs).concat(fargs);
|
||||
let func = nodes => {
|
||||
return t.ifStatement(
|
||||
nodes[0],
|
||||
tfunc(nodes.splice(1, targs.length)),
|
||||
ffunc(nodes.splice(targs.length + 1, fargs.length))
|
||||
);
|
||||
};
|
||||
return [args, func];
|
||||
}
|
||||
}
|
||||
|
@ -1078,6 +1078,6 @@ export function KeyedBindingInitialization(
|
||||
}
|
||||
|
||||
// 3. Return the result of performing BindingInitialization for BindingPattern passing v and environment as arguments.
|
||||
return BindingInitialization(realm, node, v, strictCode, environment);
|
||||
return BindingInitialization(realm, node, v, strictCode, env);
|
||||
}
|
||||
}
|
||||
|
@ -1130,7 +1130,7 @@ export function PerformEval(realm: Realm, x: Value, evalRealm: Realm, strictCall
|
||||
}
|
||||
}
|
||||
|
||||
export function incorporateSavedCompletion(realm: Realm, c: void | Completion | Value): Completion | Value {
|
||||
export function incorporateSavedCompletion(realm: Realm, c: void | Completion | Value): void | Completion | Value {
|
||||
let context = realm.getRunningContext();
|
||||
let savedCompletion = context.savedCompletion;
|
||||
if (savedCompletion !== undefined) {
|
||||
|
@ -413,7 +413,9 @@ function joinResults(
|
||||
return new JoinedAbruptCompletions(realm, joinCondition, result1, e1, result2, e2);
|
||||
}
|
||||
if (result1 instanceof Value && result2 instanceof Value) {
|
||||
return joinValues(realm, result1, result2, getAbstractValue);
|
||||
let val = joinValues(realm, result1, result2, getAbstractValue);
|
||||
invariant(val instanceof Value);
|
||||
return val;
|
||||
}
|
||||
if (result1 instanceof PossiblyNormalCompletion && result2 instanceof PossiblyNormalCompletion) {
|
||||
return composePossiblyNormalCompletions(realm, result1, result2);
|
||||
|
@ -710,6 +710,7 @@ export function ToPropertyKeyPartial(
|
||||
): AbstractValue | SymbolValue | string /* but not StringValue */ {
|
||||
if (arg instanceof ConcreteValue) return ToPropertyKey(realm, arg);
|
||||
if (arg.mightNotBeString()) arg.throwIfNotConcrete();
|
||||
invariant(arg instanceof AbstractValue);
|
||||
return arg;
|
||||
}
|
||||
|
||||
|
11
src/realm.js
11
src/realm.js
@ -343,7 +343,7 @@ export class Realm {
|
||||
return this.evaluateForEffects(() => env.evaluateAbstractCompletion(ast, strictCode), state);
|
||||
}
|
||||
|
||||
evaluateAndRevertInGlobalEnv(func: () => void): void {
|
||||
evaluateAndRevertInGlobalEnv(func: () => Value): void {
|
||||
this.wrapInGlobalEnv(() => this.evaluateForEffects(func));
|
||||
}
|
||||
|
||||
@ -386,6 +386,7 @@ export class Realm {
|
||||
c = f();
|
||||
if (c instanceof Reference) c = GetValue(this, c);
|
||||
c = incorporateSavedCompletion(this, c);
|
||||
invariant(c !== undefined);
|
||||
|
||||
invariant(this.generator !== undefined);
|
||||
invariant(this.modifiedBindings !== undefined);
|
||||
@ -476,10 +477,10 @@ export class Realm {
|
||||
}
|
||||
context.savedEffects = [
|
||||
this.intrinsics.undefined,
|
||||
this.generator,
|
||||
this.modifiedBindings,
|
||||
this.modifiedProperties,
|
||||
this.createdObjects,
|
||||
(this.generator: any),
|
||||
(this.modifiedBindings: any),
|
||||
(this.modifiedProperties: any),
|
||||
(this.createdObjects: any),
|
||||
];
|
||||
this.generator = new Generator(this);
|
||||
this.modifiedBindings = new Map();
|
||||
|
@ -1273,6 +1273,7 @@ export class ResidualHeapSerializer {
|
||||
this.realm.restoreProperties(modifiedProperties);
|
||||
}
|
||||
}
|
||||
return this.realm.intrinsics.undefined;
|
||||
};
|
||||
this.realm.evaluateAndRevertInGlobalEnv(processAdditionalFunctionValuesFn);
|
||||
return rewrittenAdditionalFunctions;
|
||||
|
@ -616,6 +616,7 @@ export class ResidualHeapVisitor {
|
||||
});
|
||||
}
|
||||
}
|
||||
return this.realm.intrinsics.undefined;
|
||||
}
|
||||
|
||||
visitRoots(): void {
|
||||
|
@ -226,7 +226,14 @@ export class Generator {
|
||||
args: Array<Value>,
|
||||
kind?: string
|
||||
): AbstractValue {
|
||||
return this.derive(types, values, args, nodes => t.callExpression(createCallee(), nodes));
|
||||
return this.derive(types, values, args, (nodes: any) => t.callExpression(createCallee(), nodes));
|
||||
}
|
||||
|
||||
emitStatement(args: Array<Value>, buildNode_: (Array<BabelNodeExpression>) => BabelNodeStatement) {
|
||||
this.addEntry({
|
||||
args,
|
||||
buildNode: buildNode_,
|
||||
});
|
||||
}
|
||||
|
||||
emitVoidExpression(
|
||||
|
@ -1,5 +1,4 @@
|
||||
// throws introspection error
|
||||
let x = __abstract("boolean", "true")
|
||||
let x = global.__abstract ? __abstract("boolean", "true") : true;
|
||||
|
||||
y = 1;
|
||||
y1 = 2;
|
||||
@ -16,3 +15,5 @@ function f(b) {
|
||||
}
|
||||
|
||||
z = f(!x);
|
||||
|
||||
inspect = function() { return z; }
|
||||
|
@ -1,5 +1,4 @@
|
||||
// throws introspection error
|
||||
let x = __abstract("boolean", "true")
|
||||
let x = global.__abstract ? __abstract("boolean", "true") : true
|
||||
|
||||
y = 1;
|
||||
y1 = 2;
|
||||
@ -16,3 +15,5 @@ function f(b) {
|
||||
}
|
||||
|
||||
z = f(!x);
|
||||
|
||||
inspect = function() { return z; }
|
||||
|
@ -1,5 +1,4 @@
|
||||
// throws introspection error
|
||||
let x = __abstract("boolean", "true");
|
||||
let x = global.__abstract ? __abstract("boolean", "true") : true
|
||||
|
||||
y = 1;
|
||||
|
||||
@ -9,3 +8,5 @@ function f(b) {
|
||||
}
|
||||
|
||||
z = f(!x);
|
||||
|
||||
inspect = function() { return z; }
|
||||
|
@ -1,5 +1,4 @@
|
||||
// throws introspection error
|
||||
let x = __abstract("boolean", "true");
|
||||
let x = global.__abstract ? __abstract("boolean", "true") : true
|
||||
|
||||
y = 1;
|
||||
|
||||
@ -13,3 +12,5 @@ function g(b) {
|
||||
}
|
||||
|
||||
z = g(x);
|
||||
|
||||
inspect = function() { return z; }
|
||||
|
10
test/serializer/abstract/Throw6.js
Normal file
10
test/serializer/abstract/Throw6.js
Normal file
@ -0,0 +1,10 @@
|
||||
let x = global.__abstract ? __abstract("boolean", "true") : true;
|
||||
|
||||
function foo(b) {
|
||||
if (b) throw new Error("is true");
|
||||
return "is false";
|
||||
}
|
||||
|
||||
z = foo(!x);
|
||||
|
||||
inspect = function() { return z; }
|
Loading…
Reference in New Issue
Block a user