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
JavaScript
;
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