prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
337 lines (252 loc) • 13.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _default;
var _errors = require("../errors.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 = _interopRequireDefault(require("../invariant.js"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* 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.
*/
// 13.12.10 Runtime Semantics: CaseSelectorEvaluation
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, _invariant.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.evaluateCompletionDeref(node, strictCode);
if (r instanceof _completions.JoinedNormalAndAbruptCompletions) {
r = realm.composeWithSavedCompletion(r);
}
result = (0, _index2.UpdateEmpty)(realm, r, result);
if (result instanceof _completions.Completion) break;
}
if (result instanceof _completions.Completion) break;
caseIndex++;
}
let sc = _singletons.Functions.incorporateSavedCompletion(realm, result);
(0, _invariant.default)(sc !== undefined);
result = sc;
if (result instanceof _completions.JoinedAbruptCompletions || result instanceof _completions.JoinedNormalAndAbruptCompletions) {
let selector = c => c instanceof _completions.BreakCompletion && !c.target;
let jc = _index.AbstractValue.createJoinConditionForSelectedCompletions(selector, result);
let jv = _index.AbstractValue.createFromConditionalOp(realm, jc, realm.intrinsics.empty, result.value);
result = _completions.Completion.normalizeSelectedCompletions(selector, result);
realm.composeWithSavedCompletion(result);
return jv;
} else if (result instanceof _completions.BreakCompletion) {
return result.value;
} else if (result instanceof _completions.AbruptCompletion) {
throw result;
} else {
(0, _invariant.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, _invariant.default)(test);
let selector = CaseSelectorEvaluation(test, strictCode, env, realm);
let selectionResult = (0, _BinaryExpression.computeBinary)(realm, "===", input, selector);
if (_singletons.Path.implies(selectionResult)) {
// we have a winning result for the switch case, bubble it back up!
return DefiniteCaseEvaluation(caseIndex);
} else if (_singletons.Path.impliesNot(selectionResult)) {
// 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 {
// 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;
try {
trueEffects = _singletons.Path.withCondition(selectionResult, () => {
return realm.evaluateForEffects(() => {
return DefiniteCaseEvaluation(caseIndex);
}, undefined, "AbstractCaseEvaluation/1");
});
} catch (e) {
if (e instanceof _errors.InfeasiblePathError) {
// selectionResult cannot be true in this path, after all.
return AbstractCaseEvaluation(caseIndex + 1);
}
throw e;
}
let falseEffects;
try {
falseEffects = _singletons.Path.withInverseCondition(selectionResult, () => {
return realm.evaluateForEffects(() => {
return AbstractCaseEvaluation(caseIndex + 1);
}, undefined, "AbstractCaseEvaluation/2");
});
} catch (e) {
if (e instanceof _errors.InfeasiblePathError) {
// selectionResult cannot be false in this path, after all.
return DefiniteCaseEvaluation(caseIndex);
}
throw e;
}
(0, _invariant.default)(trueEffects !== undefined);
(0, _invariant.default)(falseEffects !== undefined);
let joinedEffects = _singletons.Join.joinEffects(selectionResult, trueEffects, falseEffects);
realm.applyEffects(joinedEffects);
return realm.returnOrThrowCompletion(joinedEffects.result);
}
}; // 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, _invariant.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 && cases.length < 6) {
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
function _default(ast, strictCode, env, realm, labelSet) {
let expression = ast.discriminant; // 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);
if (switchValue instanceof _index.AbstractValue && !switchValue.values.isTop()) {
let elems = switchValue.values.getElements();
let n = elems.size;
if (n > 1 && n < 10) {
return _singletons.Join.mapAndJoin(realm, elems, concreteSwitchValue => _index.AbstractValue.createFromBinaryOp(realm, "===", switchValue, concreteSwitchValue), concreteSwitchValue => evaluationHelper(ast, concreteSwitchValue, strictCode, env, realm, labelSet));
}
}
return evaluationHelper(ast, switchValue, strictCode, env, realm, labelSet);
}
function evaluationHelper(ast, switchValue, strictCode, env, realm, labelSet) {
let cases = ast.cases; // 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.forEach(c => CaseBlock.push(...c.consequent));
_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);
}
}
//# sourceMappingURL=SwitchStatement.js.map