UNPKG

prepack

Version:

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

538 lines (442 loc) 22.3 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 = require("../values/Value.js"); var _Value2 = _interopRequireDefault(_Value); var _index = require("../values/index.js"); var _index2 = require("./index.js"); var _generator = require("../methods/generator.js"); var _completions = require("../completions.js"); var _get = require("../methods/get.js"); var _singletons = require("../singletons.js"); var _invariant = require("../invariant.js"); var _invariant2 = _interopRequireDefault(_invariant); var _babelTypes = require("babel-types"); var t = _interopRequireWildcard(_babelTypes); 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 }; } // 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, _invariant2.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 /** * 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 Invoke(realm, V, P, argumentsList) { // 1. Assert: IsPropertyKey(P) is true. (0, _invariant2.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, _invariant2.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, _invariant2.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. realm.pushContext(calleeContext); // 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, _invariant2.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.ToObjectPartial(calleeRealm, thisArgument); // ii. NOTE ToObject produces wrapper objects using calleeRealm. } } // 7. Let envRec be localEnv's EnvironmentRecord. (0, _invariant2.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); } // ECMA262 9.2.1.3 function OrdinaryCallEvaluateBody(realm, f, argumentsList) { if (f instanceof _index.NativeFunctionValue) { let env = realm.getRunningContext().lexicalEnvironment; try { return f.callCallback(env.environmentRecord.GetThisBinding(), argumentsList, env.environmentRecord.$NewTarget); } catch (err) { if (err instanceof _completions.AbruptCompletion) { return err; } else if (err instanceof Error) { throw err; } else { throw new _errors.FatalError(err); } } } else { (0, _invariant2.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, _invariant2.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.length === 0 || !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[0]; return processResult(() => { (0, _invariant2.default)(c instanceof _Value2.default || c instanceof _completions.AbruptCompletion); return c; }); } } 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.length > previousPathLength) { (0, _invariant2.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 _index.AbstractValue.createFromType(realm, _Value2.default, "widened return result"); } else { argumentsList = widenedArgumentsList; } } } try { if (F.activeArguments === undefined) F.activeArguments = new Map(); F.activeArguments.set(currentLocation, [realm.pathConditions.length, argumentsList]); return normalCall(); } 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, _invariant2.default)(code !== undefined); let context = realm.getRunningContext(); return processResult(() => context.lexicalEnvironment.evaluateCompletionDeref(code, F.$Strict)); } function processResult(getCompletion) { let priorSavedCompletion = realm.savedCompletion; try { realm.savedCompletion = undefined; let c = getCompletion(); // We are about the leave this function and this presents a join point where all non exeptional control flows // converge into a single flow using the joined effects as the new state. c = _singletons.Functions.incorporateSavedCompletion(realm, c); let joinedEffects; if (c instanceof _completions.PossiblyNormalCompletion) { let e = realm.getCapturedEffects(c); if (e !== undefined) { // There were earlier, conditional exits from the function // We join together the current effects with the effects of any earlier returns that are tracked in c. realm.stopEffectCaptureAndUndoEffects(c); } else { e = (0, _realm.construct_empty_effects)(realm); } joinedEffects = _singletons.Join.joinEffectsAndPromoteNestedReturnCompletions(realm, c, e); } else if (c instanceof _completions.JoinedAbruptCompletions) { joinedEffects = _singletons.Join.joinEffectsAndPromoteNestedReturnCompletions(realm, c, (0, _realm.construct_empty_effects)(realm)); } if (joinedEffects !== undefined) { let result = joinedEffects[0]; if (result instanceof _completions.ReturnCompletion) { realm.applyEffects(joinedEffects); return result; } (0, _invariant2.default)(result instanceof _completions.JoinedAbruptCompletions); if (!(result.consequent instanceof _completions.ReturnCompletion || result.alternate instanceof _completions.ReturnCompletion)) { realm.applyEffects(joinedEffects); throw result; } // There is a normal return exit, but also one or more throw completions. // The throw completions must be extracted into a saved possibly normal completion // so that the caller can pick them up in its next completion. joinedEffects = extractAndSavePossiblyNormalCompletion(result); result = joinedEffects[0]; (0, _invariant2.default)(result instanceof _completions.ReturnCompletion); realm.applyEffects(joinedEffects); return result; } else { (0, _invariant2.default)(c instanceof _Value2.default || c instanceof _completions.AbruptCompletion); return c; } } finally { realm.incorporatePriorSavedCompletion(priorSavedCompletion); } } } } function extractAndSavePossiblyNormalCompletion(c) { // There are throw completions that conditionally escape from the the call. // We need to carry on in normal mode (after arranging to capturing effects) // while stashing away the throw completions so that the next completion we return let [joinedEffects, possiblyNormalCompletion] = _singletons.Join.unbundleReturnCompletion(realm, c); realm.composeWithSavedCompletion(possiblyNormalCompletion); return joinedEffects; } } // 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 && _Value2.default.isTypeCompatibleWith(func.getType(), _index.FunctionValue)) { return _index.AbstractValue.createTemporalFromBuildFunction(realm, func.functionResultType || _Value2.default, [func].concat(argList), nodes => { let fun_args = nodes.slice(1); return t.callExpression(nodes[0], fun_args); }); } 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, _invariant2.default)(result instanceof _Value2.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 && _Value2.default.isTypeCompatibleWith(F.getType(), _index.FunctionValue)) { _singletons.Havoc.value(realm, V); for (let arg of argsList) { _singletons.Havoc.value(realm, arg); } if (V === realm.intrinsics.undefined) { let fullArgs = [F].concat(argsList); return _index.AbstractValue.createTemporalFromBuildFunction(realm, _Value2.default, fullArgs, nodes => { let fun_args = nodes.slice(1); return t.callExpression(nodes[0], fun_args); }); } else { let fullArgs = [F, V].concat(argsList); return _index.AbstractValue.createTemporalFromBuildFunction(realm, _Value2.default, fullArgs, nodes => { let fun_args = nodes.slice(1); return t.callExpression(t.memberExpression(nodes[0], t.identifier("call")), fun_args); }); } } (0, _invariant2.default)(F instanceof _index.ObjectValue); // 3. Return ? F.[[Call]](V, argumentsList). (0, _invariant2.default)(F.$Call, "no call method on this value"); return F.$Call(V, argsList); } //# sourceMappingURL=call.js.map