UNPKG

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