UNPKG

prepack

Version:

Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.

205 lines (179 loc) 9.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = function (ast, strictCode, env, realm) { // evaluate left let lref = env.evaluate(ast.left, strictCode); let lval = _singletons.Environment.GetValue(realm, lref); // evaluate right let rref = env.evaluate(ast.right, strictCode); let rval = _singletons.Environment.GetValue(realm, rref); return computeBinary(realm, ast.operator, lval, rval, ast.left.loc, ast.right.loc, ast.loc); }; exports.getPureBinaryOperationResultType = getPureBinaryOperationResultType; exports.computeBinary = computeBinary; var _index = require("../domains/index.js"); var _errors = require("../errors.js"); var _index2 = require("../values/index.js"); var _completions = require("../completions.js"); var _singletons = require("../singletons.js"); var _babelTypes = require("babel-types"); var t = _interopRequireWildcard(_babelTypes); var _invariant = require("../invariant.js"); var _invariant2 = _interopRequireDefault(_invariant); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } let unknownValueOfOrToString = "might be an object with an unknown valueOf or toString or Symbol.toPrimitive method"; // Returns result type if binary operation is pure (terminates, does not throw exception, does not read or write heap), otherwise undefined. /** * 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 getPureBinaryOperationResultType(realm, op, lval, rval, lloc, rloc) { function reportErrorIfNotPure(purityTest, typeIfPure) { let leftPure = purityTest(realm, lval); let rightPure = purityTest(realm, rval); if (leftPure && rightPure) return typeIfPure; let loc = !leftPure ? lloc : rloc; let error = new _errors.CompilerDiagnostic(unknownValueOfOrToString, loc, "PP0002", "RecoverableError"); if (realm.handleError(error) === "Recover") { // Assume that an unknown value is actually a primitive or otherwise a well behaved object. return typeIfPure; } throw new _errors.FatalError(); } if (op === "+") { let ltype = _singletons.To.GetToPrimitivePureResultType(realm, lval); let rtype = _singletons.To.GetToPrimitivePureResultType(realm, rval); if (ltype === undefined || rtype === undefined) { let loc = ltype === undefined ? lloc : rloc; let error = new _errors.CompilerDiagnostic(unknownValueOfOrToString, loc, "PP0002", "RecoverableError"); if (realm.handleError(error) === "Recover") { // Assume that the unknown value is actually a primitive or otherwise a well behaved object. ltype = lval.getType(); rtype = rval.getType(); if (ltype === _index2.StringValue || rtype === _index2.StringValue) return _index2.StringValue; if (ltype === _index2.IntegralValue && rtype === _index2.IntegralValue) return _index2.IntegralValue; if ((ltype === _index2.NumberValue || ltype === _index2.IntegralValue) && (rtype === _index2.NumberValue || rtype === _index2.IntegralValue)) return _index2.NumberValue; return _index2.Value; } throw new _errors.FatalError(); } if (ltype === _index2.StringValue || rtype === _index2.StringValue) return _index2.StringValue; return _index2.NumberValue; } else if (op === "<" || op === ">" || op === ">=" || op === "<=") { return reportErrorIfNotPure(_singletons.To.IsToPrimitivePure.bind(_singletons.To), _index2.BooleanValue); } else if (op === "!=" || op === "==") { let ltype = lval.getType(); let rtype = rval.getType(); if (ltype === _index2.NullValue || ltype === _index2.UndefinedValue || rtype === _index2.NullValue || rtype === _index2.UndefinedValue) return _index2.BooleanValue; return reportErrorIfNotPure(_singletons.To.IsToPrimitivePure.bind(_singletons.To), _index2.BooleanValue); } else if (op === "===" || op === "!==") { return _index2.BooleanValue; } else if (op === ">>>" || op === "<<" || op === ">>" || op === "&" || op === "|" || op === "^" || op === "**" || op === "%" || op === "/" || op === "*" || op === "-") { return reportErrorIfNotPure(_singletons.To.IsToNumberPure.bind(_singletons.To), _index2.NumberValue); } else if (op === "in" || op === "instanceof") { if (rval.mightNotBeObject()) { let error = new _errors.CompilerDiagnostic(`might not be an object, hence the ${op} operator might throw a TypeError`, rloc, "PP0003", "RecoverableError"); if (realm.handleError(error) === "Recover") { // Assume that the object is actually a well behaved object. return _index2.BooleanValue; } throw new _errors.FatalError(); } if (!rval.mightNotBeObject()) { // Simple object won't throw here, aren't proxy objects or typed arrays and do not have @@hasInstance properties. if (rval.isSimpleObject()) return _index2.BooleanValue; } let error = new _errors.CompilerDiagnostic(`might be an object that behaves badly for the ${op} operator`, rloc, "PP0004", "RecoverableError"); if (realm.handleError(error) === "Recover") { // Assume that the object is actually a well behaved object. return _index2.BooleanValue; } throw new _errors.FatalError(); } (0, _invariant2.default)(false, "unimplemented " + op); } function computeBinary(realm, op, lval, rval, lloc, rloc, loc) { // partial evaluation shortcut for a particular pattern if (realm.useAbstractInterpretation && (op === "==" || op === "===" || op === "!=" || op === "!==")) { if (!lval.mightNotBeObject() && (rval instanceof _index2.NullValue || rval instanceof _index2.UndefinedValue) || (lval instanceof _index2.NullValue || lval instanceof _index2.UndefinedValue) && !rval.mightNotBeObject()) { return new _index2.BooleanValue(realm, op[0] !== "="); } } let resultType; const compute = () => { if (lval instanceof _index2.AbstractValue || rval instanceof _index2.AbstractValue) { // generate error if binary operation might throw or have side effects resultType = getPureBinaryOperationResultType(realm, op, lval, rval, lloc, rloc); return _index2.AbstractValue.createFromBinaryOp(realm, op, lval, rval, loc); } else { // ECMA262 12.10.3 // 5. If Type(rval) is not Object, throw a TypeError exception. if (op === "in" && !(rval instanceof _index2.ObjectValue)) { throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError); } (0, _invariant2.default)(lval instanceof _index2.ConcreteValue); (0, _invariant2.default)(rval instanceof _index2.ConcreteValue); const result = _index.ValuesDomain.computeBinary(realm, op, lval, rval); resultType = result.getType(); return result; } }; if (realm.isInPureScope()) { // If we're in pure mode we can recover even if this operation might not be pure. // To do that, we'll temporarily override the error handler. const previousErrorHandler = realm.errorHandler; let isPure = true; realm.errorHandler = diagnostic => { isPure = false; return "Recover"; }; let effects; try { effects = realm.evaluateForEffects(compute, undefined, "computeBinary"); } catch (x) { if (x instanceof _errors.FatalError) { isPure = false; } else { throw x; } } finally { realm.errorHandler = previousErrorHandler; } if (isPure && effects) { let completion = effects[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 effects, but are tracked separately inside completion. realm.applyEffects(effects); // return or throw completion if (completion instanceof _completions.AbruptCompletion) throw completion; (0, _invariant2.default)(completion instanceof _index2.Value); return completion; } // If this ended up reporting an error, it might not be pure, so we'll leave it in // as a temporal operation with a known return type. // Some of these values may trigger side-effectful user code such as valueOf. // To be safe, we have to Havoc them. _singletons.Havoc.value(realm, lval, loc); if (op !== "in") { // The "in" operator have side-effects on its right val other than throw. _singletons.Havoc.value(realm, rval, loc); } return realm.evaluateWithPossibleThrowCompletion(() => _index2.AbstractValue.createTemporalFromBuildFunction(realm, resultType, [lval, rval], ([lnode, rnode]) => t.binaryExpression(op, lnode, rnode)), _index.TypesDomain.topVal, _index.ValuesDomain.topVal); } return compute(); } //# sourceMappingURL=BinaryExpression.js.map