prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
239 lines (202 loc) • 11.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (ast, strictCode, env, realm) {
if (ast.callee.type === "Super") {
return (0, _SuperCall2.default)(ast.arguments, strictCode, env, realm);
}
// ECMA262 12.3.4.1
realm.setNextExecutionContextLocation(ast.loc);
// 1. Let ref be the result of evaluating MemberExpression.
let ref = env.evaluate(ast.callee, strictCode);
// 2. Let func be ? GetValue(ref).
let func = _singletons.Environment.GetValue(realm, ref);
return EvaluateCall(ref, func, ast, strictCode, env, realm);
};
var _errors = require("../errors.js");
var _completions = require("../completions.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 = require("../invariant.js");
var _invariant2 = _interopRequireDefault(_invariant);
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var _SuperCall = require("./SuperCall");
var _SuperCall2 = _interopRequireDefault(_SuperCall);
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; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function callBothFunctionsAndJoinTheirEffects(args, ast, strictCode, env, realm) {
let [cond, func1, func2] = args;
(0, _invariant2.default)(cond instanceof _index2.AbstractValue && cond.getType() === _index2.BooleanValue);
(0, _invariant2.default)(_index2.Value.isTypeCompatibleWith(func1.getType(), _index2.FunctionValue));
(0, _invariant2.default)(_index2.Value.isTypeCompatibleWith(func2.getType(), _index2.FunctionValue));
let [compl1, gen1, bindings1, properties1, createdObj1] = realm.evaluateForEffects(() => EvaluateCall(func1, func1, ast, strictCode, env, realm), undefined, "callBothFunctionsAndJoinTheirEffects/1");
let [compl2, gen2, bindings2, properties2, createdObj2] = realm.evaluateForEffects(() => EvaluateCall(func2, func2, ast, strictCode, env, realm), undefined, "callBothFunctionsAndJoinTheirEffects/2");
let joinedEffects = _singletons.Join.joinEffects(realm, cond, [compl1, gen1, bindings1, properties1, createdObj1], [compl2, gen2, bindings2, properties2, createdObj2]);
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 _index2.Value);
return completion;
} /**
* 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 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 and is henceforth in an unknown (havoced) state,
// 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.Havoc.value(realm, arg, ast.loc);
}
}
let resultType = (func instanceof _index2.AbstractObjectValue ? func.functionResultType : undefined) || _index2.Value;
return _index2.AbstractValue.createTemporalFromBuildFunction(realm, resultType, args, nodes => {
let callFunc;
let argStart = 1;
if (thisArg instanceof _index2.Value) {
if (typeof propName === "string") {
callFunc = t.isValidIdentifier(propName) ? t.memberExpression(nodes[0], t.identifier(propName), false) : t.memberExpression(nodes[0], t.stringLiteral(propName), true);
} else {
callFunc = t.memberExpression(nodes[0], nodes[1], true);
argStart = 2;
}
} else {
callFunc = nodes[0];
}
let fun_args = nodes.slice(argStart);
return t.callExpression(callFunc, fun_args);
});
}
function tryToEvaluateCallOrLeaveAsAbstract(ref, func, ast, strictCode, env, realm, thisValue, tailCall) {
let effects;
try {
effects = realm.evaluateForEffects(() => (0, _index3.EvaluateDirectCall)(realm, strictCode, env, ref, func, thisValue, ast.arguments, tailCall), undefined, "tryToEvaluateCallOrLeaveAsAbstract");
} catch (error) {
if (error instanceof _errors.FatalError) {
return realm.evaluateWithPossibleThrowCompletion(() => generateRuntimeCall(ref, func, ast, strictCode, env, realm), _index.TypesDomain.topVal, _index.ValuesDomain.topVal);
} else {
throw error;
}
}
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;
}
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 (!_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 if (func.kind === "conditional") {
return callBothFunctionsAndJoinTheirEffects(func.args, ast, strictCode, env, realm);
} 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, _invariant2.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, _invariant2.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()) {
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