prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
327 lines (263 loc) • 12.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (ast, strictCode, env, realm, labelSet) {
let expression = ast.discriminant;
let cases = ast.cases;
// 1. Let exprRef be the result of evaluating Expression.
let exprRef = env.evaluate(expression, strictCode);
// 2. Let switchValue be ? GetValue(exprRef).
let switchValue = _singletons.Environment.GetValue(realm, exprRef);
// 3. Let oldEnv be the running execution context's LexicalEnvironment.
let oldEnv = realm.getRunningContext().lexicalEnvironment;
// 4. Let blockEnv be NewDeclarativeEnvironment(oldEnv).
let blockEnv = _singletons.Environment.NewDeclarativeEnvironment(realm, oldEnv);
// 5. Perform BlockDeclarationInstantiation(CaseBlock, blockEnv).
let CaseBlock = cases.map(c => c.consequent).reduce((stmts, case_blk) => stmts.concat(case_blk), []);
_singletons.Environment.BlockDeclarationInstantiation(realm, strictCode, CaseBlock, blockEnv);
// 6. Set the running execution context's LexicalEnvironment to blockEnv.
realm.getRunningContext().lexicalEnvironment = blockEnv;
let R;
try {
// 7. Let R be the result of performing CaseBlockEvaluation of CaseBlock with argument switchValue.
R = CaseBlockEvaluation(cases, switchValue, strictCode, blockEnv, realm);
// 9. Return R.
return R;
} catch (e) {
if (e instanceof _completions.BreakCompletion) {
if (!e.target) return (0, _index2.UpdateEmpty)(realm, e, realm.intrinsics.undefined).value;
}
throw e;
} finally {
// 8. Set the running execution context's LexicalEnvironment to oldEnv.
realm.getRunningContext().lexicalEnvironment = oldEnv;
realm.onDestroyScope(blockEnv);
}
};
var _errors = require("../errors.js");
var _environment = require("../environment.js");
var _BinaryExpression = require("./BinaryExpression.js");
var _completions = require("../completions.js");
var _ForOfStatement = require("./ForOfStatement.js");
var _index = require("../values/index.js");
var _index2 = require("../methods/index.js");
var _singletons = require("../singletons.js");
var _invariant = require("../invariant.js");
var _invariant2 = _interopRequireDefault(_invariant);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// 13.12.10 Runtime Semantics: CaseSelectorEvaluation
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
function CaseSelectorEvaluation(expression, strictCode, env, realm) {
// 1. Let exprRef be the result of evaluating Expression.
let exprRef = env.evaluate(expression, strictCode);
// 2. Return ? GetValue(exprRef).
return _singletons.Environment.GetValue(realm, exprRef);
}
function AbstractCaseBlockEvaluation(cases, defaultCaseIndex, input, strictCode, env, realm) {
(0, _invariant2.default)(realm.useAbstractInterpretation);
let DefiniteCaseEvaluation = caseIndex => {
let result = realm.intrinsics.undefined;
// we start at the case we've been asked to evaluate, and process statements
// until there is either a break statement or exception thrown (this means we
// implicitly fall through correctly in the absence of a break statement).
while (caseIndex < cases.length) {
let c = cases[caseIndex];
for (let i = 0; i < c.consequent.length; i += 1) {
let node = c.consequent[i];
let r = env.evaluateCompletion(node, strictCode);
(0, _invariant2.default)(!(r instanceof _environment.Reference));
if (r instanceof _completions.PossiblyNormalCompletion) {
// TODO correct handling of PossiblyNormal and AbruptCompletion
let diagnostic = new _errors.CompilerDiagnostic("case block containing a throw, return or continue is not yet supported", r.location, "PP0027", "FatalError");
realm.handleError(diagnostic);
throw new _errors.FatalError();
}
result = (0, _index2.UpdateEmpty)(realm, r, result);
if (result instanceof _completions.Completion) break;
}
if (result instanceof _completions.Completion) break;
caseIndex++;
}
if (result instanceof _completions.BreakCompletion) {
return result.value;
} else if (result instanceof _completions.AbruptCompletion) {
// TODO correct handling of PossiblyNormal and AbruptCompletion
let diagnostic = new _errors.CompilerDiagnostic("case block containing a throw, return or continue is not yet supported", result.location, "PP0027", "FatalError");
realm.handleError(diagnostic);
throw new _errors.FatalError();
} else {
(0, _invariant2.default)(result instanceof _index.Value);
return result;
}
};
let AbstractCaseEvaluation = caseIndex => {
if (caseIndex === defaultCaseIndex) {
// skip the default case until we've exhausted all other options
return AbstractCaseEvaluation(caseIndex + 1);
} else if (caseIndex >= cases.length) {
// this is the stop condition for our recursive search for a matching case.
// we tried every available case index and since nothing matches we return
// the default (and if none exists....just empty)
if (defaultCaseIndex !== -1) {
return DefiniteCaseEvaluation(defaultCaseIndex);
} else {
return realm.intrinsics.empty;
}
}
// else we have a normal in-range case index
let c = cases[caseIndex];
let test = c.test;
(0, _invariant2.default)(test);
let selector = CaseSelectorEvaluation(test, strictCode, env, realm);
let selectionResult = (0, _BinaryExpression.computeBinary)(realm, "===", input, selector);
if (!selectionResult.mightNotBeTrue()) {
// we have a winning result for the switch case, bubble it back up!
return DefiniteCaseEvaluation(caseIndex);
} else if (!selectionResult.mightNotBeFalse()) {
// we have a case that is definitely *not* taken
// so we go and look at the next one in the hope of finding a match
return AbstractCaseEvaluation(caseIndex + 1);
} else {
(0, _invariant2.default)(selectionResult instanceof _index.AbstractValue);
// we can't be sure whether the case selector evaluates true or not
// so we evaluate the case in the abstract as an if-else with the else
// leading to the next case statement
let trueEffects = _singletons.Path.withCondition(selectionResult, () => {
return realm.evaluateForEffects(() => {
return DefiniteCaseEvaluation(caseIndex);
}, undefined, "AbstractCaseEvaluation/1");
});
let falseEffects = _singletons.Path.withInverseCondition(selectionResult, () => {
return realm.evaluateForEffects(() => {
return AbstractCaseEvaluation(caseIndex + 1);
}, undefined, "AbstractCaseEvaluation/2");
});
let joinedEffects = _singletons.Join.joinEffects(realm, selectionResult, trueEffects, falseEffects);
let completion = joinedEffects[0];
if (completion instanceof _completions.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.
// Consequently we have to continue tracking changes until the point where
// all the branches come together into one.
completion = realm.composeWithSavedCompletion(completion);
}
// Note that the effects of (non joining) abrupt branches are not included
// in joinedEffects, but are tracked separately inside completion.
realm.applyEffects(joinedEffects);
// return or throw completion
if (completion instanceof _completions.AbruptCompletion) throw completion;
(0, _invariant2.default)(completion instanceof _index.Value);
return completion;
}
};
// let the recursive search for a matching case begin!
return AbstractCaseEvaluation(0);
}
function CaseBlockEvaluation(cases, input, strictCode, env, realm) {
let EvaluateCase = c => {
let r = realm.intrinsics.empty;
for (let node of c.consequent) {
let res = env.evaluateCompletion(node, strictCode);
if (res instanceof _completions.AbruptCompletion) return (0, _index2.UpdateEmpty)(realm, res, r);
if (!(res instanceof _index.EmptyValue)) r = res;
}
return r;
};
let EvaluateCaseClauses = (A, V) => {
// 2. Let A be the List of CaseClause items in CaseClauses, in source text order.
// A is passed in
// 3. Let found be false.
let found = false;
// 4. Repeat for each CaseClause C in A,
for (let C of A) {
// a. If found is false, then
if (!found) {
// i. Let clauseSelector be the result of CaseSelectorEvaluation of C.
let test = C.test;
(0, _invariant2.default)(test);
let clauseSelector = CaseSelectorEvaluation(test, strictCode, env, realm);
// ii. ReturnIfAbrupt(clauseSelector).
// above will throw a Completion which will return
// iii. Let found be the result of performing Strict Equality Comparison input === clauseSelector.[[Value]].
found = (0, _index2.StrictEqualityComparisonPartial)(realm, input, clauseSelector);
}
if (found) {
// b. If found is true, then
// i. Let R be the result of evaluating C.
let R = EvaluateCase(C);
// ii. If R.[[Value]] is not empty, let V be R.[[Value]].
let val = (0, _ForOfStatement.InternalGetResultValue)(realm, R);
if (!(val instanceof _index.EmptyValue)) V = val;
// iii. If R is an abrupt completion, return Completion(UpdateEmpty(R, V)).
if (R instanceof _completions.AbruptCompletion) {
throw (0, _index2.UpdateEmpty)(realm, R, V);
}
}
}
return [found, V];
};
// CaseBlock:{}
// 1. Return NormalCompletion(undefined).
if (cases.length === 0) return realm.intrinsics.undefined;
// CaseBlock:{CaseClauses DefaultClause CaseClauses}
let default_case_num = cases.findIndex(clause => {
return clause.test === null;
});
// Abstract interpretation of case blocks is a significantly different process
// from regular interpretation, so we fork off early to keep things tidily separated.
if (input instanceof _index.AbstractValue) {
return AbstractCaseBlockEvaluation(cases, default_case_num, input, strictCode, env, realm);
}
if (default_case_num !== -1) {
// 2. Let A be the List of CaseClause items in the first CaseClauses, in source text order. If the first CaseClauses is not present, A is « ».
let A = cases.slice(0, default_case_num);
let V = realm.intrinsics.undefined;
// 4. Repeat for each CaseClause C in A
[, V] = EvaluateCaseClauses(A, V);
// 5. Let foundInB be false.
let foundInB = false;
// 6. Let B be the List containing the CaseClause items in the second CaseClauses, in source text order. If the second CaseClauses is not present, B is « ».
let B = cases.slice(default_case_num + 1);
[foundInB, V] = EvaluateCaseClauses(B, V);
// 8. If foundInB is true, return NormalCompletion(V).
if (foundInB) return V;
// 9. Let R be the result of evaluating DefaultClause.
let R = EvaluateCase(cases[default_case_num]);
// 10. If R.[[Value]] is not empty, let V be R.[[Value]].
let val = (0, _ForOfStatement.InternalGetResultValue)(realm, R);
if (!(val instanceof _index.EmptyValue)) V = val;
// 11. If R is an abrupt completion, return Completion(UpdateEmpty(R, V)).
if (R instanceof _completions.AbruptCompletion) {
throw (0, _index2.UpdateEmpty)(realm, R, V);
}
// 12: Repeat for each CaseClause C in B (NOTE this is another complete iteration of the second CaseClauses)
for (let C of B) {
// a. Let R be the result of evaluating CaseClause C.
R = EvaluateCase(C);
// b. If R.[[Value]] is not empty, let V be R.[[Value]].
let value = (0, _ForOfStatement.InternalGetResultValue)(realm, R);
if (!(value instanceof _index.EmptyValue)) V = value;
// c. If R is an abrupt completion, return Completion(UpdateEmpty(R, V)).
if (R instanceof _completions.AbruptCompletion) {
throw (0, _index2.UpdateEmpty)(realm, R, V);
}
}
// 13. Return NormalCompletion(V).
return V;
} else {
// CaseBlock:{CaseClauses}
let V;
[, V] = EvaluateCaseClauses(cases, realm.intrinsics.undefined);
return V;
}
}
// 13.12.11
//# sourceMappingURL=SwitchStatement.js.map