UNPKG

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
"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