prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
512 lines (369 loc) • 22.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ArgumentListEvaluation = ArgumentListEvaluation;
exports.Invoke = Invoke;
exports.EvaluateCall = EvaluateCall;
exports.PrepareForOrdinaryCall = PrepareForOrdinaryCall;
exports.OrdinaryCallBindThis = OrdinaryCallBindThis;
exports.OrdinaryCallEvaluateBody = OrdinaryCallEvaluateBody;
exports.EvaluateDirectCall = EvaluateDirectCall;
exports.EvaluateDirectCallWithArgList = EvaluateDirectCallWithArgList;
exports.PrepareForTailCall = PrepareForTailCall;
exports.Call = Call;
var _environment = require("../environment.js");
var _errors = require("../errors.js");
var _realm = require("../realm.js");
var _Value = _interopRequireDefault(require("../values/Value.js"));
var _index = require("../values/index.js");
var _index2 = require("./index.js");
var _generator = require("./generator.js");
var _completions = require("../completions.js");
var _get = require("./get.js");
var _singletons = require("../singletons.js");
var _invariant = _interopRequireDefault(require("../invariant.js"));
var _generator2 = 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.
*/
// ECMA262 12.3.6.1
function ArgumentListEvaluation(realm, strictCode, env, argNodes) {
if (Array.isArray(argNodes)) {
let args = [];
for (let node_ of argNodes) {
if (node_.type === "SpreadElement") {
let node = node_; // 1. Let list be a new empty List.
let list = args; // 2. Let spreadRef be the result of evaluating AssignmentExpression.
let spreadRef = env.evaluate(node.argument, strictCode); // 3. Let spreadObj be ? GetValue(spreadRef).
let spreadObj = _singletons.Environment.GetValue(realm, spreadRef); // 4. Let iterator be ? GetIterator(spreadObj).
let iterator = (0, _index2.GetIterator)(realm, spreadObj); // 5. Repeat
while (true) {
// a. Let next be ? IteratorStep(iterator).
let next = (0, _index2.IteratorStep)(realm, iterator); // b. If next is false, return list.
if (!next) {
break;
} // c. Let nextArg be ? IteratorValue(next).
let nextArg = (0, _index2.IteratorValue)(realm, next); // d. Append nextArg as the last element of list.
list.push(nextArg);
}
} else {
let ref = env.evaluate(node_, strictCode);
let expr = _singletons.Environment.GetValue(realm, ref);
args.push(expr);
}
}
return args;
} else {
let node = argNodes;
if (node.expressions.length === 0) {
// 1. Let templateLiteral be this TemplateLiteral.
let templateLiteral = node; // 2. Let siteObj be GetTemplateObject(templateLiteral).
let siteObj = (0, _get.GetTemplateObject)(realm, templateLiteral); // 3. Return a List containing the one element which is siteObj.
return [siteObj];
} else {
// 1. Let templateLiteral be this TemplateLiteral.
let templateLiteral = node; // 2. Let siteObj be GetTemplateObject(templateLiteral).
let siteObj = (0, _get.GetTemplateObject)(realm, templateLiteral); // 3. Let firstSubRef be the result of evaluating Expression.
let firstSubRef = env.evaluate(node.expressions[0], strictCode); // 4. Let firstSub be ? GetValue(firstSubRef).
let firstSub = _singletons.Environment.GetValue(realm, firstSubRef); // 5. Let restSub be SubstitutionEvaluation of TemplateSpans.
let restSub = node.expressions.slice(1, node.expressions.length).map(expr => {
return _singletons.Environment.GetValue(realm, env.evaluate(expr, strictCode));
}); // 6. ReturnIfAbrupt(restSub).
// 7. Assert: restSub is a List.
(0, _invariant.default)(restSub.constructor === Array, "restSub is a List"); // 8. Return a List whose first element is siteObj, whose second elements is firstSub, and whose subsequent elements are the elements of restSub, in order. restSub may contain no elements.
return [siteObj, firstSub, ...restSub];
}
}
} // ECMA262 7.3.18
function Invoke(realm, V, P, argumentsList) {
// 1. Assert: IsPropertyKey(P) is true.
(0, _invariant.default)((0, _index2.IsPropertyKey)(realm, P), "expected property key"); // 2. If argumentsList was not passed, let argumentsList be a new empty List.
if (!argumentsList) argumentsList = []; // 3. Let func be ? GetV(V, P).
let func = (0, _get.GetV)(realm, V, P); // 4. Return ? Call(func, V, argumentsList).
return Call(realm, func, V, argumentsList);
} // ECMA262 12.3.4.2
function EvaluateCall(realm, strictCode, env, ref, args) {
let thisValue; // 1. Let func be ? GetValue(ref).
let func = _singletons.Environment.GetValue(realm, ref); // 2. 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, _get.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 {
// 3. Else Type(ref) is not Reference,
// a. Let thisValue be undefined.
thisValue = realm.intrinsics.undefined;
} // 4. Return ? EvaluateDirectCall(func, thisValue, arguments, tailPosition).
return EvaluateDirectCall(realm, strictCode, env, ref, func, thisValue, args);
} // ECMA262 9.2.1.1
function PrepareForOrdinaryCall(realm, F, newTarget) {
// 1. Assert: Type(newTarget) is Undefined or Object.
(0, _invariant.default)(newTarget === undefined || newTarget instanceof _index.ObjectValue, "expected undefined or object value for new target"); // 2. Let callerContext be the running execution context.
let callerContext = realm.getRunningContext(); // 3. Let calleeContext be a new ECMAScript code execution context.
let calleeContext = realm.createExecutionContext(); // 4. Set the Function of calleeContext to F.
calleeContext.setFunction(F);
calleeContext.setCaller(realm.getRunningContext()); // 5. Let calleeRealm be the value of F's [[Realm]] internal slot.
let calleeRealm = realm; // 6. Set the Realm of calleeContext to calleeRealm.
calleeContext.realm = calleeRealm; // 7. Set the ScriptOrModule of calleeContext to the value of F's [[ScriptOrModule]] internal slot.
calleeContext.ScriptOrModule = F.$ScriptOrModule; // 8. Let localEnv be NewFunctionEnvironment(F, newTarget).
let localEnv = _singletons.Environment.NewFunctionEnvironment(realm, F, newTarget); // 9. Set the LexicalEnvironment of calleeContext to localEnv.
calleeContext.lexicalEnvironment = localEnv; // 10. Set the VariableEnvironment of calleeContext to localEnv.
calleeContext.variableEnvironment = localEnv; // 11. If callerContext is not already suspended, suspend callerContext.
callerContext.suspend(); // 12. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
try {
realm.pushContext(calleeContext);
} catch (error) {
// `realm.pushContext` may throw if we have exceeded the maximum stack size.
realm.onDestroyScope(localEnv);
throw error;
} // 13. NOTE Any exception objects produced after this point are associated with calleeRealm.
// 14. Return calleeContext.
return calleeContext;
} // ECMA262 9.2.1.2
function OrdinaryCallBindThis(realm, F, calleeContext, thisArgument) {
// 1. Let thisMode be the value of F's [[ThisMode]] internal slot.
let thisMode = F.$ThisMode; // 2. If thisMode is lexical, return NormalCompletion(undefined).
if (thisMode === "lexical") return realm.intrinsics.undefined; // 3. Let calleeRealm be the value of F's [[Realm]] internal slot.
let calleeRealm = F.$Realm; // 4. Let localEnv be the LexicalEnvironment of calleeContext.
let localEnv = calleeContext.lexicalEnvironment;
let thisValue; // 5. If thisMode is strict, let thisValue be thisArgument.
if (thisMode === "strict") {
thisValue = thisArgument;
} else {
// 6. Else,
// a. If thisArgument is null or undefined, then
if ((0, _index2.HasSomeCompatibleType)(thisArgument, _index.NullValue, _index.UndefinedValue)) {
// i. Let globalEnv be calleeRealm.[[GlobalEnv]].
let globalEnv = realm.$GlobalEnv; // ii. Let globalEnvRec be globalEnv's EnvironmentRecord.
let globalEnvRec = globalEnv.environmentRecord;
(0, _invariant.default)(globalEnvRec instanceof _environment.GlobalEnvironmentRecord); // iii. Let thisValue be globalEnvRec.[[GlobalThisValue]].
thisValue = globalEnvRec.$GlobalThisValue;
} else {
// b. Else,
// i. Let thisValue be ! ToObject(thisArgument).
thisValue = _singletons.To.ToObject(calleeRealm, thisArgument); // ii. NOTE ToObject produces wrapper objects using calleeRealm.
}
} // 7. Let envRec be localEnv's EnvironmentRecord.
(0, _invariant.default)(localEnv !== undefined);
let envRec = localEnv.environmentRecord; // 8. Assert: The next step never returns an abrupt completion because envRec.[[ThisBindingStatus]] is not "initialized".
// 9. Return envRec.BindThisValue(thisValue).
return envRec.BindThisValue(thisValue);
}
function callNativeFunctionValue(realm, f, argumentsList) {
let env = realm.getRunningContext().lexicalEnvironment;
let context = env.environmentRecord.GetThisBinding(); // we have an inConditional flag, as we do not want to return
const functionCall = (contextVal, inConditional) => {
try {
(0, _invariant.default)(contextVal instanceof _index.AbstractObjectValue || contextVal instanceof _index.ObjectValue || contextVal instanceof _index.NullValue || contextVal instanceof _index.UndefinedValue || (0, _environment.mightBecomeAnObject)(contextVal));
let completion = f.callCallback( // TODO: this is not right. Either fix the type signature of callCallback or wrap contextVal in a coercion
contextVal, argumentsList, env.environmentRecord.$NewTarget);
return inConditional ? completion.value : completion;
} catch (err) {
if (err instanceof _completions.AbruptCompletion) {
return inConditional ? err.value : err;
} else if (err instanceof Error) {
throw err;
} else {
throw new _errors.FatalError(err);
}
}
};
const wrapInReturnCompletion = contextVal => new _completions.ReturnCompletion(contextVal, realm.currentLocation);
if (context instanceof _index.AbstractObjectValue && context.kind === "conditional") {
let [condValue, consequentVal, alternateVal] = context.args;
(0, _invariant.default)(condValue instanceof _index.AbstractValue);
return wrapInReturnCompletion(realm.evaluateWithAbstractConditional(condValue, () => {
return realm.evaluateForEffects(() => functionCall(consequentVal, true), null, "callNativeFunctionValue consequent");
}, () => {
return realm.evaluateForEffects(() => functionCall(alternateVal, true), null, "callNativeFunctionValue alternate");
}));
}
let c = functionCall(context, false);
if (c instanceof _completions.AbruptCompletion) return c;
return undefined;
} // ECMA262 9.2.1.3
function OrdinaryCallEvaluateBody(realm, f, argumentsList) {
if (f instanceof _index.NativeFunctionValue) {
return callNativeFunctionValue(realm, f, argumentsList);
} else {
(0, _invariant.default)(f instanceof _index.ECMAScriptSourceFunctionValue);
let F = f;
if (F.$FunctionKind === "generator") {
// 1. Perform ? FunctionDeclarationInstantiation(functionObject, argumentsList).
_singletons.Functions.FunctionDeclarationInstantiation(realm, F, argumentsList); // 2. Let G be ? OrdinaryCreateFromConstructor(functionObject, "%GeneratorPrototype%", « [[GeneratorState]], [[GeneratorContext]] »).
let G = _singletons.Create.OrdinaryCreateFromConstructor(realm, F, "GeneratorPrototype", {
$GeneratorState: undefined,
$GeneratorContext: undefined
}); // 3. Perform GeneratorStart(G, FunctionBody).
let code = F.$ECMAScriptCode;
(0, _invariant.default)(code !== undefined);
(0, _generator.GeneratorStart)(realm, G, code); // 4. Return Completion{[[Type]]: return, [[Value]]: G, [[Target]]: empty}.
return new _completions.ReturnCompletion(G, realm.currentLocation);
} else {
// TODO #1586: abstractRecursionSummarization is disabled for now, as it is likely too limiting
// (as observed in large internal tests).
const abstractRecursionSummarization = false;
if (!realm.useAbstractInterpretation || realm.pathConditions.isEmpty() || !abstractRecursionSummarization) return normalCall();
let savedIsSelfRecursive = F.isSelfRecursive;
try {
F.isSelfRecursive = false;
let effects = realm.evaluateForEffects(guardedCall, undefined, "OrdinaryCallEvaluateBody");
if (F.isSelfRecursive) {
_index.AbstractValue.reportIntrospectionError(F, "call to function that calls itself");
throw new _errors.FatalError(); //todo: need to emit a specialized function that temporally captures the heap state at this point
} else {
realm.applyEffects(effects);
let c = effects.result;
return processResult(() => {
if (c instanceof _completions.AbruptCompletion || c instanceof _completions.JoinedNormalAndAbruptCompletions) return c;
return undefined;
});
}
} finally {
F.isSelfRecursive = savedIsSelfRecursive;
}
function guardedCall() {
let currentLocation = realm.currentLocation;
if (F.activeArguments !== undefined && F.activeArguments.has(currentLocation)) {
let [previousPathLength, previousArguments] = F.activeArguments.get(currentLocation);
if (realm.pathConditions.getLength() > previousPathLength) {
(0, _invariant.default)(previousArguments !== undefined); // F is being called recursively while a call to it is still active
F.isSelfRecursive = true;
let widenedArgumentsList = _singletons.Widen.widenValues(realm, previousArguments, argumentsList);
if (_singletons.Widen.containsArraysOfValue(realm, previousArguments, widenedArgumentsList)) {
// Reached a fixed point. Executing this call will not add any knowledge
// about the effects of the original call.
return realm.intrinsics.undefined;
} else {
argumentsList = widenedArgumentsList;
}
}
}
try {
if (F.activeArguments === undefined) F.activeArguments = new Map();
F.activeArguments.set(currentLocation, [realm.pathConditions.getLength(), argumentsList]);
return normalCall() || realm.intrinsics.undefined;
} finally {
F.activeArguments.delete(currentLocation);
}
}
function normalCall() {
// 1. Perform ? FunctionDeclarationInstantiation(F, argumentsList).
_singletons.Functions.FunctionDeclarationInstantiation(realm, F, argumentsList); // 2. Return the result of EvaluateBody of the parsed code that is the value of F's
// [[ECMAScriptCode]] internal slot passing F as the argument.
let code = F.$ECMAScriptCode;
(0, _invariant.default)(code !== undefined);
let context = realm.getRunningContext();
return processResult(() => {
let c = context.lexicalEnvironment.evaluateCompletionDeref(code, F.$Strict);
if (c instanceof _completions.AbruptCompletion || c instanceof _completions.JoinedNormalAndAbruptCompletions) return c;
return undefined;
});
}
function processResult(getCompletion) {
// We don't want the callee to see abrupt completions from the caller.
let priorSavedCompletion = realm.savedCompletion;
realm.savedCompletion = undefined;
let c;
try {
c = getCompletion();
} catch (e) {
(0, _invariant.default)(!(e instanceof _completions.AbruptCompletion));
throw e;
}
c = _singletons.Functions.incorporateSavedCompletion(realm, c); // in case the callee had conditional abrupt completions
realm.savedCompletion = priorSavedCompletion;
if (c === undefined) return undefined; // the callee had no returns or throws
if (c instanceof _completions.ThrowCompletion || c instanceof _completions.ReturnCompletion) return c; // Non mixed completions will not be joined completions, but single completions with joined values.
// At this point it must be true that
// c contains return completions and possibly also normal completions (which are implicitly "return undefined;")
// and c also contains throw completions. Hence we assert:
(0, _invariant.default)(c instanceof _completions.JoinedAbruptCompletions || c instanceof _completions.JoinedNormalAndAbruptCompletions); // We want to add only the throw completions to priorSavedCompletion (but must keep their conditions in tact).
// The (joined) return completions must be returned to our caller
let rc = c;
_completions.Completion.makeAllNormalCompletionsResultInUndefined(c);
c = _completions.Completion.normalizeSelectedCompletions(r => r instanceof _completions.ReturnCompletion, c);
(0, _invariant.default)(c.containsSelectedCompletion(r => r instanceof _completions.NormalCompletion));
let rv = _singletons.Join.joinValuesOfSelectedCompletions(r => r instanceof _completions.NormalCompletion, c);
if (c.containsSelectedCompletion(r => r instanceof _completions.ThrowCompletion)) {
realm.composeWithSavedCompletion(c);
if (rv instanceof _index.AbstractValue) {
rv = realm.simplifyAndRefineAbstractValue(rv);
}
}
rc = new _completions.ReturnCompletion(rv);
return rc;
}
}
}
} // ECMA262 12.3.4.3
function EvaluateDirectCall(realm, strictCode, env, ref, func, thisValue, args, tailPosition) {
// 1. Let argList be ? ArgumentListEvaluation(arguments).
let argList = ArgumentListEvaluation(realm, strictCode, env, args);
return EvaluateDirectCallWithArgList(realm, strictCode, env, ref, func, thisValue, argList, tailPosition);
}
function EvaluateDirectCallWithArgList(realm, strictCode, env, ref, func, thisValue, argList, tailPosition) {
if (func instanceof _index.AbstractObjectValue && _Value.default.isTypeCompatibleWith(func.getType(), _index.FunctionValue)) {
return _index.AbstractValue.createTemporalFromBuildFunction(realm, func.functionResultType || _Value.default, [func].concat(argList), (0, _generator2.createOperationDescriptor)("DIRECT_CALL_WITH_ARG_LIST"));
}
func = func.throwIfNotConcrete(); // 2. If Type(func) is not Object, throw a TypeError exception.
if (!(func instanceof _index.ObjectValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an object");
} // 3. If IsCallable(func) is false, throw a TypeError exception.
if (!(0, _index2.IsCallable)(realm, func)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not callable");
} // 4. If tailPosition is true, perform PrepareForTailCall().
if (tailPosition === true) PrepareForTailCall(realm); // 5. Let result be Call(func, thisValue, argList).
let result = Call(realm, func, thisValue, argList); // 6. Assert: If tailPosition is true, the above call will not return here, but instead
// evaluation will continue as if the following return has already occurred.
// 7. Assert: If result is not an abrupt completion, then Type(result) is an ECMAScript language type.
(0, _invariant.default)(result instanceof _Value.default, "expected language value type"); // 8. Return result.
return result;
} // ECMA262 14.6.3
function PrepareForTailCall(realm) {
// 1. Let leafContext be the running execution context.
let leafContext = realm.getRunningContext(); // 2. Suspend leafContext.
leafContext.suspend(); // 3. Pop leafContext from the execution context stack. The execution context now on the
// top of the stack becomes the running execution context.
realm.onDestroyScope(leafContext.lexicalEnvironment);
realm.popContext(leafContext); // TODO #1008 4. Assert: leafContext has no further use. It will never be activated as the running execution context.
} // ECMA262 7.3.12
function Call(realm, F, V, argsList) {
// 1. If argumentsList was not passed, let argumentsList be a new empty List.
argsList = argsList || []; // 2. If IsCallable(F) is false, throw a TypeError exception.
if ((0, _index2.IsCallable)(realm, F) === false) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not callable");
}
if (F instanceof _index.AbstractValue && _Value.default.isTypeCompatibleWith(F.getType(), _index.FunctionValue)) {
_singletons.Leak.value(realm, V);
for (let arg of argsList) {
_singletons.Leak.value(realm, arg);
}
if (V === realm.intrinsics.undefined) {
let fullArgs = [F].concat(argsList);
return _index.AbstractValue.createTemporalFromBuildFunction(realm, _Value.default, fullArgs, (0, _generator2.createOperationDescriptor)("CALL_ABSTRACT_FUNC"));
} else {
let fullArgs = [F, V].concat(argsList);
return _index.AbstractValue.createTemporalFromBuildFunction(realm, _Value.default, fullArgs, (0, _generator2.createOperationDescriptor)("CALL_ABSTRACT_FUNC_THIS"));
}
}
(0, _invariant.default)(F instanceof _index.ObjectValue); // 3. Return ? F.[[Call]](V, argumentsList).
(0, _invariant.default)(F.$Call, "no call method on this value");
return F.$Call(V, argsList);
}
//# sourceMappingURL=call.js.map