Fix FatalErrors that are actually Recoverable (#2371)

Summary:
Release Notes: None

When optimizing a function fails, we can usually continue to emit a correct program while ignoring that call to `__optimize`, thus most of these errors should be `Warning`s and not `FatalError`s.

A few times (like when we have multiple sets of `Effects` for one optimized function), we can sensibly continue Prepacking code, but our output may be incorrect. In these cases, we can issue a `RecoverableError` instead of a `FatalError`
Pull Request resolved: https://github.com/facebook/prepack/pull/2371

Differential Revision: D9186240

Pulled By: cblappert

fbshipit-source-id: c30fc63fb713a3bf504cc1ec4f02410a8566cc42
This commit is contained in:
Christopher Blappert 2018-08-07 16:48:05 -07:00 committed by Facebook Github Bot
parent be3779315e
commit 3e8c638071
3 changed files with 20 additions and 14 deletions

View File

@ -118,6 +118,7 @@ export default function(realm: Realm): void {
// NB: If we interpret one of these calls in an evaluateForEffects context // NB: If we interpret one of these calls in an evaluateForEffects context
// that is not subsequently applied, the function will not be registered // that is not subsequently applied, the function will not be registered
// (because prepack won't have a correct value for the FunctionValue itself) // (because prepack won't have a correct value for the FunctionValue itself)
// If we encounter an invalid input, we will emit a warning and not optimize the function
global.$DefineOwnProperty("__optimize", { global.$DefineOwnProperty("__optimize", {
value: new NativeFunctionValue(realm, "global.__optimize", "__optimize", 1, (context, [value, argModelString]) => { value: new NativeFunctionValue(realm, "global.__optimize", "__optimize", 1, (context, [value, argModelString]) => {
let argModel; let argModel;
@ -134,9 +135,10 @@ export default function(realm: Realm): void {
"__optimize called twice with different argModelStrings", "__optimize called twice with different argModelStrings",
realm.currentLocation, realm.currentLocation,
"PP1008", "PP1008",
"FatalError" "Warning"
); );
if (realm.handleError(argModelError) !== "Recover") throw new FatalError(); if (realm.handleError(argModelError) !== "Recover") throw new FatalError();
else return realm.intrinsics.undefined;
} }
} }
realm.optimizedFunctions.set(value, argModel); realm.optimizedFunctions.set(value, argModel);
@ -150,10 +152,11 @@ export default function(realm: Realm): void {
`Optimized Function Value ${location} is an not a function or react element`, `Optimized Function Value ${location} is an not a function or react element`,
realm.currentLocation, realm.currentLocation,
"PP0033", "PP0033",
"FatalError" "Warning"
) )
); );
if (result !== "Recover") throw new FatalError(); if (result !== "Recover") throw new FatalError();
else return realm.intrinsics.undefined;
} }
return value; return value;
}), }),

View File

@ -97,15 +97,16 @@ export class Functions {
} }
let location = optionalStringOfLocation(value.expressionLocation); let location = optionalStringOfLocation(value.expressionLocation);
realm.handleError( let result = realm.handleError(
new CompilerDiagnostic( new CompilerDiagnostic(
`Optimized Function Value ${location} is an not a function or react element`, `Optimized Function Value ${location} is an not a function or react element`,
realm.currentLocation, realm.currentLocation,
"PP0033", "PP0033",
"FatalError" "Warning"
) )
); );
throw new FatalError("Optimized Function Values must be functions or react elements"); // Here we can recover by ignoring the __optimize call and emit correct code
if (result !== "Recover") throw new FatalError("Optimized Function Values must be functions or react elements");
} }
_generateInitialAdditionalFunctions(globalKey: string): Array<AdditionalFunctionEntry> { _generateInitialAdditionalFunctions(globalKey: string): Array<AdditionalFunctionEntry> {
@ -253,11 +254,13 @@ export class Functions {
"Trying to optimize a function with two parent optimized functions, which is not currently allowed.", "Trying to optimize a function with two parent optimized functions, which is not currently allowed.",
functionValue.expressionLocation, functionValue.expressionLocation,
"PP1009", "PP1009",
"FatalError" "RecoverableError"
); );
// we can recover by assuming one set of effects to show further diagnostics
if (realm.handleError(error) !== "Recover") throw new FatalError(); if (realm.handleError(error) !== "Recover") throw new FatalError();
} else {
this.writeEffects.set(functionValue, additionalFunctionEffects);
} }
this.writeEffects.set(functionValue, additionalFunctionEffects);
// Conceptually this will ensure that the nested additional function is defined // Conceptually this will ensure that the nested additional function is defined
// although for later cases, we'll apply the effects of the parents only. // although for later cases, we'll apply the effects of the parents only.
@ -292,13 +295,13 @@ export class Functions {
invariant(e1 !== undefined); invariant(e1 !== undefined);
if (e1.result instanceof Completion && !e1.result instanceof PossiblyNormalCompletion) { if (e1.result instanceof Completion && !e1.result instanceof PossiblyNormalCompletion) {
let error = new CompilerDiagnostic( let error = new CompilerDiagnostic(
`Additional function ${fun1Name} may terminate abruptly`, `Additional function ${fun1Name} will terminate abruptly`,
e1.result.location, e1.result.location,
"PP1002", "PP1002",
"FatalError" "RecoverableError"
); );
this.realm.handleError(error); // We generate correct code in this case, but the user probably doesn't want us to emit an unconditional throw
throw new FatalError(); if (this.realm.handleError(error) !== "Recover") throw new FatalError();
} }
for (let fun2 of additionalFunctions) { for (let fun2 of additionalFunctions) {
if (fun1 === fun2) continue; if (fun1 === fun2) continue;
@ -327,8 +330,8 @@ export class Functions {
} }
} }
if (conflicts.size > 0) { if (conflicts.size > 0) {
for (let diagnostic of conflicts.values()) this.realm.handleError(diagnostic); for (let diagnostic of conflicts.values())
throw new FatalError(); if (this.realm.handleError(diagnostic) !== "Recover") throw new FatalError();
} }
} }

View File

@ -1,4 +1,4 @@
// expected FatalError: PP1009 // expected RecoverableError: PP1009
// does not contain: = 5 // does not contain: = 5
function fn() { function fn() {