UNPKG

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
"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