UNPKG

prepack

Version:

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

308 lines (240 loc) 14.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = _default; var _errors = require("../errors.js"); var _environment = require("../environment.js"); var _index = require("../domains/index.js"); var _index2 = require("../values/index.js"); var _singletons = require("../singletons.js"); var _index3 = require("../methods/index.js"); var _invariant = _interopRequireDefault(require("../invariant.js")); var _SuperCall = _interopRequireDefault(require("./SuperCall.js")); var _generator = require("../utils/generator.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. */ function _default(ast, strictCode, env, realm) { if (ast.callee.type === "Super") { return (0, _SuperCall.default)(ast.arguments, strictCode, env, realm); } // ECMA262 12.3.4.1 // 1. Let ref be the result of evaluating MemberExpression. let ref = env.evaluate(ast.callee, strictCode); let previousLoc = realm.setNextExecutionContextLocation(ast.loc); try { return evaluateReference(ref, ast, strictCode, env, realm); } finally { realm.setNextExecutionContextLocation(previousLoc); } } function getPrimitivePrototypeFromType(realm, value) { switch (value.getType()) { case _index2.IntegralValue: case _index2.NumberValue: return realm.intrinsics.NumberPrototype; case _index2.StringValue: return realm.intrinsics.StringPrototype; case _index2.BooleanValue: return realm.intrinsics.BooleanPrototype; case _index2.SymbolValue: return realm.intrinsics.SymbolPrototype; default: return undefined; } } function evaluateReference(ref, ast, strictCode, env, realm) { if (ref instanceof _environment.Reference && ref.base instanceof _index2.AbstractValue && // TODO: what about ref.base conditionals that mightBeObjects? ref.base.mightNotBeObject() && realm.isInPureScope()) { let base = ref.base; if (base.kind === "conditional") { let [condValue, consequentVal, alternateVal] = base.args; (0, _invariant.default)(condValue instanceof _index2.AbstractValue); return evaluateConditionalReferenceBase(ref, condValue, consequentVal, alternateVal, ast, strictCode, env, realm); } else if (base.kind === "||") { let [leftValue, rightValue] = base.args; (0, _invariant.default)(leftValue instanceof _index2.AbstractValue); return evaluateConditionalReferenceBase(ref, leftValue, leftValue, rightValue, ast, strictCode, env, realm); } else if (base.kind === "&&") { let [leftValue, rightValue] = base.args; (0, _invariant.default)(leftValue instanceof _index2.AbstractValue); return evaluateConditionalReferenceBase(ref, leftValue, rightValue, leftValue, ast, strictCode, env, realm); } let referencedName = ref.referencedName; // When dealing with a PrimitiveValue, like StringValue, NumberValue, IntegralValue etc // if we are referencing a prototype method, then it's safe to access, even // on an abstract value as the value is immutable and can't have a property // that matches the prototype method (unless the prototype was modified). // We assume the global prototype of built-ins has not been altered since // global code has finished. See #1233 for more context in regards to unmodified // global prototypes. let prototypeIfPrimitive = getPrimitivePrototypeFromType(realm, base); if (prototypeIfPrimitive !== undefined && typeof referencedName === "string") { let possibleMethodValue = prototypeIfPrimitive._SafeGetDataPropertyValue(referencedName); if (possibleMethodValue instanceof _index2.FunctionValue) { return EvaluateCall(ref, possibleMethodValue, ast, strictCode, env, realm); } } // avoid explicitly converting ref.base to an object because that will create a generator entry // leading to two object allocations rather than one. return realm.evaluateWithPossibleThrowCompletion(() => generateRuntimeCall(ref, base, ast, strictCode, env, realm), _index.TypesDomain.topVal, _index.ValuesDomain.topVal); } // 2. Let func be ? GetValue(ref). let func = _singletons.Environment.GetValue(realm, ref); return EvaluateCall(ref, func, ast, strictCode, env, realm); } function evaluateConditionalReferenceBase(ref, condValue, consequentVal, alternateVal, ast, strictCode, env, realm) { return realm.evaluateWithAbstractConditional(condValue, () => { return realm.evaluateForEffects(() => { if ((0, _environment.isValidBaseValue)(consequentVal)) { let consequentRef = new _environment.Reference(consequentVal, ref.referencedName, ref.strict, ref.thisValue); return evaluateReference(consequentRef, ast, strictCode, env, realm); } return consequentVal; }, null, "evaluateConditionalReferenceBase consequent"); }, () => { return realm.evaluateForEffects(() => { if ((0, _environment.isValidBaseValue)(alternateVal)) { let alternateRef = new _environment.Reference(alternateVal, ref.referencedName, ref.strict, ref.thisValue); return evaluateReference(alternateRef, ast, strictCode, env, realm); } return alternateVal; }, null, "evaluateConditionalReferenceBase alternate"); }); } function callBothFunctionsAndJoinTheirEffects(condValue, consequentVal, alternateVal, ast, strictCode, env, realm) { return realm.evaluateWithAbstractConditional(condValue, () => { return realm.evaluateForEffects(() => EvaluateCall(consequentVal, consequentVal, ast, strictCode, env, realm), null, "callBothFunctionsAndJoinTheirEffects consequent"); }, () => { return realm.evaluateForEffects(() => EvaluateCall(alternateVal, alternateVal, ast, strictCode, env, realm), null, "callBothFunctionsAndJoinTheirEffects alternate"); }); } function generateRuntimeCall(ref, func, ast, strictCode, env, realm) { let args = [func]; let [thisArg, propName] = ref instanceof _environment.Reference ? [ref.base, ref.referencedName] : []; if (thisArg instanceof _index2.Value) args = [thisArg]; if (propName !== undefined && typeof propName !== "string") args.push(propName); args = args.concat((0, _index3.ArgumentListEvaluation)(realm, strictCode, env, ast.arguments)); for (let arg of args) { if (arg !== func) { // Since we don't know which function we are calling, we assume that any unfrozen object // passed as an argument has leaked to the environment as is any other object that is known to be reachable from this object. // NB: Note that this is still optimistic, particularly if the environment exposes the same object // to Prepack via alternative means, thus creating aliasing that is not tracked by Prepack. _singletons.Leak.value(realm, arg, ast.loc); } } let resultType = (func instanceof _index2.AbstractObjectValue ? func.functionResultType : undefined) || _index2.Value; return _index2.AbstractValue.createTemporalFromBuildFunction(realm, resultType, args, (0, _generator.createOperationDescriptor)("CALL_BAILOUT", { propRef: propName, thisArg })); } function tryToEvaluateCallOrLeaveAsAbstract(ref, func, ast, strictCode, env, realm, thisValue, tailCall) { (0, _invariant.default)(!realm.instantRender.enabled); let effects; let savedSuppressDiagnostics = realm.suppressDiagnostics; try { realm.suppressDiagnostics = !(func instanceof _index2.NativeFunctionValue) || func.name !== "__optimize"; effects = realm.evaluateForEffects(() => (0, _index3.EvaluateDirectCall)(realm, strictCode, env, ref, func, thisValue, ast.arguments, tailCall), undefined, "tryToEvaluateCallOrLeaveAsAbstract"); } catch (error) { if (error instanceof _errors.FatalError) { if (func instanceof _index2.NativeFunctionValue && func.name === "__fatal") throw error; realm.suppressDiagnostics = savedSuppressDiagnostics; _singletons.Leak.value(realm, func, ast.loc); return realm.evaluateWithPossibleThrowCompletion(() => generateRuntimeCall(ref, func, ast, strictCode, env, realm), _index.TypesDomain.topVal, _index.ValuesDomain.topVal); } else { throw error; } } finally { realm.suppressDiagnostics = savedSuppressDiagnostics; } realm.applyEffects(effects); return realm.returnOrThrowCompletion(effects.result); } function EvaluateCall(ref, func, ast, strictCode, env, realm) { if (func instanceof _index2.AbstractValue) { let loc = ast.callee.type === "MemberExpression" ? ast.callee.property.loc : ast.callee.loc; if (func.kind === "conditional") { let [condValue, consequentVal, alternateVal] = func.args; (0, _invariant.default)(condValue instanceof _index2.AbstractValue); // If neither values are functions than do not try and call both functions with a conditional if (_index2.Value.isTypeCompatibleWith(consequentVal.getType(), _index2.FunctionValue) || _index2.Value.isTypeCompatibleWith(alternateVal.getType(), _index2.FunctionValue)) { return callBothFunctionsAndJoinTheirEffects(condValue, consequentVal, alternateVal, ast, strictCode, env, realm); } } else if (func.kind === "||") { let [leftValue, rightValue] = func.args; (0, _invariant.default)(leftValue instanceof _index2.AbstractValue); // If neither values are functions than do not try and call both functions with a conditional if (_index2.Value.isTypeCompatibleWith(leftValue.getType(), _index2.FunctionValue) || _index2.Value.isTypeCompatibleWith(rightValue.getType(), _index2.FunctionValue)) { return callBothFunctionsAndJoinTheirEffects(leftValue, leftValue, rightValue, ast, strictCode, env, realm); } } else if (func.kind === "&&") { let [leftValue, rightValue] = func.args; (0, _invariant.default)(leftValue instanceof _index2.AbstractValue); // If neither values are functions than do not try and call both functions with a conditional if (_index2.Value.isTypeCompatibleWith(leftValue.getType(), _index2.FunctionValue) || _index2.Value.isTypeCompatibleWith(rightValue.getType(), _index2.FunctionValue)) { return callBothFunctionsAndJoinTheirEffects(leftValue, rightValue, leftValue, ast, strictCode, env, realm); } } if (!_index2.Value.isTypeCompatibleWith(func.getType(), _index2.FunctionValue)) { if (!realm.isInPureScope()) { // If this is not a function, this call might throw which can change the state of the program. // If this is called from a pure function we handle it using evaluateWithPossiblyAbruptCompletion. let error = new _errors.CompilerDiagnostic("might not be a function", loc, "PP0005", "RecoverableError"); if (realm.handleError(error) === "Fail") throw new _errors.FatalError(); } } else {// Assume that it is a safe function. TODO #705: really? } if (realm.isInPureScope()) { // In pure functions we allow abstract functions to throw, which this might. return realm.evaluateWithPossibleThrowCompletion(() => generateRuntimeCall(ref, func, ast, strictCode, env, realm), _index.TypesDomain.topVal, _index.ValuesDomain.topVal); } return generateRuntimeCall(ref, func, ast, strictCode, env, realm); } (0, _invariant.default)(func instanceof _index2.ConcreteValue); // 3. If Type(ref) is Reference and IsPropertyReference(ref) is false and GetReferencedName(ref) is "eval", then if (ref instanceof _environment.Reference && !_singletons.Environment.IsPropertyReference(realm, ref) && _singletons.Environment.GetReferencedName(realm, ref) === "eval") { // a. If SameValue(func, %eval%) is true, then if ((0, _index3.SameValue)(realm, func, realm.intrinsics.eval)) { // i. Let argList be ? ArgumentListEvaluation(Arguments). let argList = (0, _index3.ArgumentListEvaluation)(realm, strictCode, env, ast.arguments); // ii. If argList has no elements, return undefined. if (argList.length === 0) return realm.intrinsics.undefined; // iii. Let evalText be the first element of argList. let evalText = argList[0]; // iv. If the source code matching this CallExpression is strict code, let strictCaller be true. Otherwise let strictCaller be false. let strictCaller = strictCode; // v. Let evalRealm be the current Realm Record. let evalRealm = realm; // vi. Return ? PerformEval(evalText, evalRealm, strictCaller, true). if (evalText instanceof _index2.AbstractValue) { let loc = ast.arguments[0].loc; let error = new _errors.CompilerDiagnostic("eval argument must be a known value", loc, "PP0006", "RecoverableError"); if (realm.handleError(error) === "Fail") throw new _errors.FatalError(); // Assume that it is a safe eval with no visible heap changes or abrupt control flow. return generateRuntimeCall(ref, func, ast, strictCode, env, realm); } return _singletons.Functions.PerformEval(realm, evalText, evalRealm, strictCaller, true); } } let thisValue; // 4. If Type(ref) is Reference, then if (ref instanceof _environment.Reference) { // a. If IsPropertyReference(ref) is true, then if (_singletons.Environment.IsPropertyReference(realm, ref)) { // i. Let thisValue be GetThisValue(ref). thisValue = (0, _index3.GetThisValue)(realm, ref); } else { // b. Else, the base of ref is an Environment Record // i. Let refEnv be GetBase(ref). let refEnv = _singletons.Environment.GetBase(realm, ref); (0, _invariant.default)(refEnv instanceof _environment.EnvironmentRecord); // ii. Let thisValue be refEnv.WithBaseObject(). thisValue = refEnv.WithBaseObject(); } } else { // 5. Else Type(ref) is not Reference, // a. Let thisValue be undefined. thisValue = realm.intrinsics.undefined; } // 6. Let thisCall be this CallExpression. let thisCall = ast; // 7. Let tailCall be IsInTailPosition(thisCall). (See 14.6.1) let tailCall = (0, _index3.IsInTailPosition)(realm, thisCall); // 8. Return ? EvaluateDirectCall(func, thisValue, Arguments, tailCall). if (realm.isInPureScope() && !realm.instantRender.enabled) { return tryToEvaluateCallOrLeaveAsAbstract(ref, func, ast, strictCode, env, realm, thisValue, tailCall); } else { return (0, _index3.EvaluateDirectCall)(realm, strictCode, env, ref, func, thisValue, ast.arguments, tailCall); } } //# sourceMappingURL=CallExpression.js.map