UNPKG

prepack

Version:

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

694 lines (545 loc) 29.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResidualFunctions = void 0; var _errors = require("../errors.js"); var _realm = require("../realm.js"); var _index = require("../values/index.js"); var t = _interopRequireWildcard(require("@babel/types")); var _invariant = _interopRequireDefault(require("../invariant.js")); var _types2 = require("./types.js"); var _statistics = require("./statistics.js"); var _ResidualFunctionInstantiator = require("./ResidualFunctionInstantiator.js"); var _modules = require("../utils/modules.js"); var _ResidualFunctionInitializers = require("./ResidualFunctionInitializers.js"); var _babelhelpers = require("../utils/babelhelpers.js"); var _Referentializer = require("./Referentializer.js"); var _utils = require("./utils.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 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)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } /** * 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. */ class ResidualFunctions { constructor(realm, options, modules, requireReturns, locationService, prelude, factoryNameGenerator, residualFunctionInfos, residualFunctionInstances, residualClassMethodInstances, additionalFunctionValueInfos, additionalFunctionValueNestedFunctions, referentializer) { this.realm = realm; this.modules = modules; this.requireReturns = requireReturns; this.locationService = locationService; this.prelude = prelude; this.factoryNameGenerator = factoryNameGenerator; this.functionPrototypes = new Map(); this.firstFunctionUsages = new Map(); this.functions = new Map(); this.classes = new Map(); this.functionInstances = []; this.residualFunctionInitializers = new _ResidualFunctionInitializers.ResidualFunctionInitializers(locationService); this.residualFunctionInfos = residualFunctionInfos; this.residualFunctionInstances = residualFunctionInstances; this.residualClassMethodInstances = residualClassMethodInstances; this.additionalFunctionValueInfos = additionalFunctionValueInfos; this.referentializer = referentializer; for (let instance of residualFunctionInstances.values()) { (0, _invariant.default)(instance !== undefined); if (!additionalFunctionValueInfos.has(instance.functionValue)) this.addFunctionInstance(instance); } this.additionalFunctionValueNestedFunctions = additionalFunctionValueNestedFunctions; this.additionalFunctionPreludes = new Map(); for (let functionValue of additionalFunctionValueInfos.keys()) { this.additionalFunctionPreludes.set(functionValue, []); } } getStatistics() { (0, _invariant.default)(this.realm.statistics instanceof _statistics.SerializerStatistics, "serialization requires SerializerStatistics"); return this.realm.statistics; } addFunctionInstance(instance) { this.functionInstances.push(instance); let code = instance.functionValue.$ECMAScriptCode; (0, _invariant.default)(code != null); (0, _utils.getOrDefault)(this.functions, code, () => []).push(instance); } setFunctionPrototype(constructor, prototypeId) { this.functionPrototypes.set(constructor, prototypeId); } addFunctionUsage(val, bodyReference) { if (!this.firstFunctionUsages.has(val)) this.firstFunctionUsages.set(val, bodyReference); } _shouldUseFactoryFunction(funcBody, instances) { (0, _invariant.default)(instances.length > 0); function shouldInlineFunction() { if (instances[0].scopeInstances.size > 0) return false; let shouldInline = true; if (funcBody.start && funcBody.end) { let bodySize = funcBody.end - funcBody.start; shouldInline = bodySize <= 30; } return shouldInline; } let functionInfo = this.residualFunctionInfos.get(funcBody); (0, _invariant.default)(functionInfo); let { usesArguments } = functionInfo; let hasAnyLeakedIds = false; for (const instance of instances) for (const scope of instance.scopeInstances.values()) if (scope.leakedIds.length > 0) hasAnyLeakedIds = true; return !shouldInlineFunction() && instances.length > 1 && !usesArguments && !hasAnyLeakedIds; } _getIdentifierReplacements(funcBody, residualFunctionBindings) { let functionInfo = this.residualFunctionInfos.get(funcBody); (0, _invariant.default)(functionInfo); let { unbound } = functionInfo; let res = new Map(); for (let [name, nodes] of unbound) { let residualFunctionBinding = residualFunctionBindings.get(name); if (residualFunctionBinding === undefined) continue; // Let's skip bindings that are referring to // 1) something global (without an environment record), and // 2) have not been assigned a value (which would mean that they have a var/let binding and Prepack will take the liberty to rename them). if (residualFunctionBinding.declarativeEnvironmentRecord === null && residualFunctionBinding.value === undefined) { continue; } let serializedValue = residualFunctionBinding.serializedValue; (0, _invariant.default)(serializedValue !== undefined); let replacement = (0, _ResidualFunctionInstantiator.getReplacement)(serializedValue, residualFunctionBinding.referentialized ? undefined : residualFunctionBinding.value); for (let node of nodes) res.set(node, replacement); } return res; } _getCallReplacements(funcBody) { let functionInfo = this.residualFunctionInfos.get(funcBody); (0, _invariant.default)(functionInfo); let { requireCalls, modified } = functionInfo; let res = new Map(); for (let [callNode, moduleId] of requireCalls) { this.getStatistics().requireCalls++; if (modified.has(callNode.callee.name)) continue; let replacement = this.requireReturns.get("" + moduleId); if (replacement !== undefined) { this.getStatistics().requireCallsReplaced++; res.set(callNode, replacement); } } return res; } // Note: this function takes linear time. Please do not call it inside loop. _hasRewrittenFunctionInstance(rewrittenAdditionalFunctions, instances) { return instances.find(instance => rewrittenAdditionalFunctions.has(instance.functionValue)) !== undefined; } _generateFactoryFunctionInfos(rewrittenAdditionalFunctions) { const factoryFunctionInfos = new Map(); for (const [functionBody, instances] of this.functions) { (0, _invariant.default)(instances.length > 0); let factoryId; const suffix = instances[0].functionValue.__originalName || this.realm.debugNames ? "factoryFunction" : ""; if (this._shouldUseFactoryFunction(functionBody, instances)) { // Rewritten function should never use factory function. (0, _invariant.default)(!this._hasRewrittenFunctionInstance(rewrittenAdditionalFunctions, instances)); factoryId = t.identifier(this.factoryNameGenerator.generate(suffix)); } else { // For inline function body case, use the first function as the factory function. factoryId = this.locationService.getLocation(instances[0].functionValue); } const functionUniqueTag = functionBody.uniqueOrderedTag; (0, _invariant.default)(functionUniqueTag); const functionInfo = this.residualFunctionInfos.get(functionBody); (0, _invariant.default)(functionInfo); let anyContainingAdditionalFunction = !instances.every(instance => instance.containingAdditionalFunction === undefined); factoryFunctionInfos.set(functionUniqueTag, { factoryId, functionInfo, anyContainingAdditionalFunction }); } return factoryFunctionInfos; } // Preserve residual functions' ordering based on its ast dfs traversal order. // This is necessary to prevent unexpected code locality issues. _sortFunctionByOriginalOrdering(functionEntries) { functionEntries.sort((funcA, funcB) => { const funcAUniqueTag = funcA[0].uniqueOrderedTag; (0, _invariant.default)(funcAUniqueTag); const funcBUniqueTag = funcB[0].uniqueOrderedTag; (0, _invariant.default)(funcBUniqueTag); return funcAUniqueTag - funcBUniqueTag; }); } _createFunctionExpression(params, body, isLexical) { // Additional statements might be inserted at the beginning of the body, so we clone it. body = Object.assign({}, body); return isLexical ? t.arrowFunctionExpression(params, body) : t.functionExpression(null, params, body); } spliceFunctions(rewrittenAdditionalFunctions) { this.residualFunctionInitializers.scrubFunctionInitializers(); let functionBodies = new Map(); // these need to get spliced in at the end let additionalFunctionModifiedBindingsSegment = new Map(); let getModifiedBindingsSegment = additionalFunction => (0, _utils.getOrDefault)(additionalFunctionModifiedBindingsSegment, additionalFunction, () => []); let getFunctionBody = instance => (0, _utils.getOrDefault)(functionBodies, instance, () => []); let getPrelude = instance => { let additionalFunction = instance.containingAdditionalFunction; let b; if (additionalFunction !== undefined) { b = this.additionalFunctionPreludes.get(additionalFunction); (0, _invariant.default)(b !== undefined); } else { b = this.prelude; } return b; }; let functionEntries = Array.from(this.functions.entries()); this._sortFunctionByOriginalOrdering(functionEntries); this.getStatistics().functions = functionEntries.length; let unstrictFunctionBodies = []; let strictFunctionBodies = []; let registerFunctionStrictness = (node, strict) => { if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) { (strict ? strictFunctionBodies : unstrictFunctionBodies).push(node); } }; let funcNodes = new Map(); let defineFunction = (instance, funcId, funcOrClassNode) => { let { functionValue } = instance; if (instance.initializationStatements.length > 0) { // always add initialization statements to insertion point let initializationBody = getFunctionBody(instance); Array.prototype.push.apply(initializationBody, instance.initializationStatements); } let body; if (t.isFunctionExpression(funcOrClassNode)) { funcNodes.set(functionValue, funcOrClassNode); body = getPrelude(instance); } else { (0, _invariant.default)(t.isCallExpression(funcOrClassNode) || t.isClassExpression(funcOrClassNode) || t.isArrowFunctionExpression(funcOrClassNode)); // .bind call body = getFunctionBody(instance); } body.push(t.variableDeclaration("var", [t.variableDeclarator(funcId, funcOrClassNode)])); let prototypeId = this.functionPrototypes.get(functionValue); if (prototypeId !== undefined) { let id = this.locationService.getLocation(functionValue); (0, _invariant.default)(id !== undefined); body.push(t.variableDeclaration("var", [t.variableDeclarator(prototypeId, t.memberExpression(id, t.identifier("prototype")))])); } }; // Emit code for ModifiedBindings for additional functions for (let [funcValue, funcInfo] of this.additionalFunctionValueInfos) { let scopes = new Set(); for (let [, residualBinding] of funcInfo.modifiedBindings) { let scope = residualBinding.scope; if (scope === undefined || scopes.has(scope)) continue; scopes.add(scope); (0, _invariant.default)(residualBinding.referentialized); // Find the proper prelude to emit to (global vs additional function's prelude) let bodySegment = getModifiedBindingsSegment(funcValue); // binding has been referentialized, so setup the scope to be able to // access bindings from other __captured_scopes initializers if (scope.referentializationScope !== funcValue) { let init = this.referentializer.getReferentializedScopeInitialization(scope, t.numericLiteral(scope.id)); // flow forces me to do this Array.prototype.push.apply(bodySegment, init); } } } // Process Additional Functions for (let [funcValue, additionalFunctionInfo] of this.additionalFunctionValueInfos.entries()) { let { instance } = additionalFunctionInfo; let functionValue = funcValue; let params = functionValue.$FormalParameters; let isLexical = functionValue.$ThisMode === "lexical"; (0, _invariant.default)(params !== undefined); let rewrittenBody = rewrittenAdditionalFunctions.get(funcValue); (0, _invariant.default)(rewrittenBody); // rewritten functions shouldn't have references fixed up because the body, // consists of serialized code. For simplicity we emit their instances in a naive way let functionBody = t.blockStatement(rewrittenBody); let funcOrClassNode; if (this.residualClassMethodInstances.has(funcValue)) { let classMethodInstance = this.residualClassMethodInstances.get(funcValue); (0, _invariant.default)(classMethodInstance); let { methodType, classMethodKeyNode, classSuperNode, classMethodComputed, classPrototype, classMethodIsStatic } = classMethodInstance; let isConstructor = methodType === "constructor"; (0, _invariant.default)(classPrototype instanceof _index.ObjectValue); (0, _invariant.default)(classMethodKeyNode && (t.isExpression(classMethodKeyNode) || t.isIdentifier(classMethodKeyNode))); // we use the classPrototype as the key to get the class expression ast node funcOrClassNode = this._getOrCreateClassNode(classPrototype); let classMethod = t.classMethod(methodType, classMethodKeyNode, params, functionBody, classMethodComputed, classMethodIsStatic); // add the class method to the class expression node body if (isConstructor) { funcOrClassNode.body.body.unshift(classMethod); } else { funcOrClassNode.body.body.push(classMethod); } // we only return the funcOrClassNode if this is the constructor if (!isConstructor) { continue; } // handle the class super if (classSuperNode !== undefined) { funcOrClassNode.superClass = classSuperNode; } } else { funcOrClassNode = isLexical ? t.arrowFunctionExpression(params, functionBody) : t.functionExpression(null, params, functionBody); } let id = this.locationService.getLocation(funcValue); (0, _invariant.default)(id !== undefined); registerFunctionStrictness(funcOrClassNode, funcValue instanceof _index.ECMAScriptSourceFunctionValue && funcValue.$Strict); defineFunction(instance, id, funcOrClassNode); } // Process normal functions const factoryFunctionInfos = this._generateFactoryFunctionInfos(rewrittenAdditionalFunctions); for (let [funcBody, instances] of functionEntries) { let functionInfo = this.residualFunctionInfos.get(funcBody); (0, _invariant.default)(functionInfo); let { unbound, usesThis } = functionInfo; let params = instances[0].functionValue.$FormalParameters; (0, _invariant.default)(params !== undefined); // Split instances into normal or nested in an additional function let normalInstances = []; let additionalFunctionNestedInstances = []; for (let instance of instances) { if (this.additionalFunctionValueNestedFunctions.has(instance.functionValue)) additionalFunctionNestedInstances.push(instance);else normalInstances.push(instance); } let naiveProcessInstances = instancesToSplice => { this.getStatistics().functionClones += instancesToSplice.length; for (let instance of instancesToSplice) { let { functionValue, residualFunctionBindings, scopeInstances } = instance; let funcOrClassNode; if (this.residualClassMethodInstances.has(functionValue)) { let classMethodInstance = this.residualClassMethodInstances.get(functionValue); (0, _invariant.default)(classMethodInstance); let { classSuperNode, classMethodKeyNode, methodType, classMethodComputed, classPrototype, classMethodIsStatic } = classMethodInstance; let isConstructor = methodType === "constructor"; (0, _invariant.default)(classPrototype instanceof _index.ObjectValue); (0, _invariant.default)(classMethodKeyNode); (0, _invariant.default)(t.isExpression(classMethodKeyNode) || t.isIdentifier(classMethodKeyNode)); // we use the classPrototype as the key to get the class expression ast node funcOrClassNode = this._getOrCreateClassNode(classPrototype); // if we are dealing with a constructor, don't serialize it if the original // had an empty user-land constructor (because we create a constructor behind the scenes for them) let hasEmptyConstructor = !!functionValue.$HasEmptyConstructor; if (!isConstructor || isConstructor && !hasEmptyConstructor) { let methodParams = params.slice(); let classMethod = new _ResidualFunctionInstantiator.ResidualFunctionInstantiator(factoryFunctionInfos, this._getIdentifierReplacements(funcBody, residualFunctionBindings), this._getCallReplacements(funcBody), t.classMethod(methodType, classMethodKeyNode, methodParams, funcBody, classMethodComputed, classMethodIsStatic)).instantiate(); // add the class method to the class expression node body if (isConstructor) { funcOrClassNode.body.body.unshift(classMethod); } else { funcOrClassNode.body.body.push(classMethod); } } // we only return the funcOrClassNode if this is the constructor if (!isConstructor) { continue; } // handle the class super if (classSuperNode !== undefined) { funcOrClassNode.superClass = classSuperNode; } } else { let isLexical = instance.functionValue.$ThisMode === "lexical"; funcOrClassNode = new _ResidualFunctionInstantiator.ResidualFunctionInstantiator(factoryFunctionInfos, this._getIdentifierReplacements(funcBody, residualFunctionBindings), this._getCallReplacements(funcBody), this._createFunctionExpression(params, funcBody, isLexical)).instantiate(); let scopeInitialization = []; for (let scope of scopeInstances.values()) { scopeInitialization = scopeInitialization.concat(this.referentializer.getReferentializedScopeInitialization(scope, t.numericLiteral(scope.id))); } if (scopeInitialization.length > 0) { let funcOrClassNodeBody = funcOrClassNode.body; (0, _invariant.default)(t.isBlockStatement(funcOrClassNodeBody)); funcOrClassNodeBody.body = scopeInitialization.concat(funcOrClassNodeBody.body); } } let id = this.locationService.getLocation(functionValue); (0, _invariant.default)(id !== undefined); registerFunctionStrictness(funcOrClassNode, functionValue.$Strict); (0, _invariant.default)(id !== undefined); (0, _invariant.default)(funcOrClassNode !== undefined); defineFunction(instance, id, funcOrClassNode); } }; if (additionalFunctionNestedInstances.length > 0) naiveProcessInstances(additionalFunctionNestedInstances); if (normalInstances.length > 0 && !this._shouldUseFactoryFunction(funcBody, normalInstances)) { naiveProcessInstances(normalInstances); this.getStatistics().functionClones--; } else if (normalInstances.length > 0) { const functionUniqueTag = funcBody.uniqueOrderedTag; (0, _invariant.default)(functionUniqueTag); const factoryInfo = factoryFunctionInfos.get(functionUniqueTag); (0, _invariant.default)(factoryInfo); const { factoryId } = factoryInfo; // filter included variables to only include those that are different let factoryNames = []; let sameResidualBindings = new Map(); for (let name of unbound.keys()) { let isDifferent = false; let lastBinding; let firstBinding = normalInstances[0].residualFunctionBindings.get(name); (0, _invariant.default)(firstBinding); if (firstBinding.modified) { // Must modify for traversal sameResidualBindings.set(name, firstBinding); continue; } for (let _ref of normalInstances) { let { residualFunctionBindings } = _ref; let residualBinding = residualFunctionBindings.get(name); (0, _invariant.default)(residualBinding); (0, _invariant.default)(!residualBinding.modified); if (!lastBinding) { lastBinding = residualBinding; } else if (!(0, _types2.AreSameResidualBinding)(this.realm, residualBinding, lastBinding)) { isDifferent = true; break; } } if (isDifferent) { factoryNames.push(name); } else { (0, _invariant.default)(lastBinding); sameResidualBindings.set(name, lastBinding); } } let factoryParams = []; for (let key of factoryNames) { factoryParams.push(t.identifier(key)); } let scopeInitialization = []; for (let [scopeName, scope] of normalInstances[0].scopeInstances) { let scopeNameId = t.identifier(scopeName); factoryParams.push(scopeNameId); scopeInitialization = scopeInitialization.concat(this.referentializer.getReferentializedScopeInitialization(scope, scopeNameId)); } factoryParams = factoryParams.concat(params).slice(); let factoryNode = new _ResidualFunctionInstantiator.ResidualFunctionInstantiator(factoryFunctionInfos, this._getIdentifierReplacements(funcBody, sameResidualBindings), this._getCallReplacements(funcBody), this._createFunctionExpression(factoryParams, funcBody, false)).instantiate(); if (scopeInitialization.length > 0) { let factoryNodeBody = factoryNode.body; (0, _invariant.default)(t.isBlockStatement(factoryNodeBody)); factoryNodeBody.body = scopeInitialization.concat(factoryNodeBody.body); } // factory functions do not depend on any nested generator scope, so they go to the prelude let factoryDeclaration = t.variableDeclaration("var", [t.variableDeclarator(factoryId, factoryNode)]); this.prelude.push(factoryDeclaration); registerFunctionStrictness(factoryNode, normalInstances[0].functionValue.$Strict); for (let instance of normalInstances) { let { functionValue, residualFunctionBindings, insertionPoint } = instance; let functionId = this.locationService.getLocation(functionValue); (0, _invariant.default)(functionId !== undefined); let hasFunctionArg = false; let flatArgs = factoryNames.map(name => { let residualBinding = residualFunctionBindings.get(name); (0, _invariant.default)(residualBinding); let serializedValue = residualBinding.serializedValue; hasFunctionArg = hasFunctionArg || residualBinding.value && residualBinding.value instanceof _index.FunctionValue; (0, _invariant.default)(serializedValue); return serializedValue; }); let hasAnyLeakedIds = false; for (const scope of instance.scopeInstances.values()) { flatArgs.push(t.numericLiteral(scope.id)); if (scope.leakedIds.length > 0) hasAnyLeakedIds = true; } let funcNode; let firstUsage = this.firstFunctionUsages.get(functionValue); // todo: why can this be undefined? (0, _invariant.default)(insertionPoint !== undefined); if ( // The same free variables in shared instances may refer to objects with different initialization values // so a stub forward function is needed during delay initializations. this.residualFunctionInitializers.hasInitializerStatement(functionValue) || usesThis || hasFunctionArg || firstUsage === undefined || !firstUsage.isNotEarlierThan(insertionPoint) || this.functionPrototypes.get(functionValue) !== undefined || hasAnyLeakedIds) { let callArgs = [t.thisExpression()]; for (let flatArg of flatArgs) callArgs.push(flatArg); for (let param of params) { if (param.type !== "Identifier") { throw new _errors.FatalError("TODO: do not know how to deal with non-Identifier parameters"); } callArgs.push(param); } let callee = t.memberExpression(factoryId, t.identifier("call")); let childBody = t.blockStatement([t.returnStatement(t.callExpression(callee, callArgs))]); funcNode = t.functionExpression(null, params, childBody); registerFunctionStrictness(funcNode, functionValue.$Strict); } else { funcNode = t.callExpression(t.memberExpression(factoryId, t.identifier("bind")), [_babelhelpers.nullExpression].concat(flatArgs)); } defineFunction(instance, functionId, funcNode); } } } for (let referentializationScope of this.referentializer.referentializationState.keys()) { let prelude; // Get the prelude for this additional function value if (referentializationScope !== "GLOBAL") { let additionalFunction = referentializationScope; prelude = this.additionalFunctionPreludes.get(additionalFunction); (0, _invariant.default)(prelude !== undefined); } else { prelude = this.prelude; } prelude.unshift(...this.referentializer.createCapturedScopesPrelude(referentializationScope), ...this.referentializer.createLeakedIds(referentializationScope)); } for (let instance of this.functionInstances.reverse()) { let functionBody = functionBodies.get(instance); if (functionBody !== undefined) { let insertionPoint = instance.insertionPoint; (0, _invariant.default)(insertionPoint instanceof _types2.BodyReference); // v8 seems to do something clever with array splicing, so this potentially // expensive operations seems to be actually cheap. insertionPoint.body.entries.splice(insertionPoint.index, 0, ...functionBody); } } // Inject initializer code for indexed vars into functions (for delay initializations) for (let [functionValue, funcNode] of funcNodes) { let initializerStatement = this.residualFunctionInitializers.getInitializerStatement(functionValue); if (initializerStatement !== undefined) { (0, _invariant.default)(t.isFunctionExpression(funcNode)); let blockStatement = funcNode.body; blockStatement.body.unshift(initializerStatement); } } for (let [additionalFunction, body] of Array.from(rewrittenAdditionalFunctions.entries()).reverse()) { let additionalFunctionInfo = this.additionalFunctionValueInfos.get(additionalFunction); (0, _invariant.default)(additionalFunctionInfo); // Modified bindings initializers of optimized function let bodySegment = additionalFunctionModifiedBindingsSegment.get(additionalFunction); // initializers from Referentialization let initializationStatements = getFunctionBody(additionalFunctionInfo.instance); let prelude = this.additionalFunctionPreludes.get(additionalFunction); (0, _invariant.default)(prelude !== undefined); let insertionPoint = additionalFunctionInfo.instance.insertionPoint; (0, _invariant.default)(insertionPoint); // TODO: I think this inserts things in the wrong place insertionPoint.body.entries.splice(insertionPoint.index, 0, ...initializationStatements); if (bodySegment) body.unshift(...bodySegment); body.unshift(...prelude); } return { unstrictFunctionBodies, strictFunctionBodies }; } _getOrCreateClassNode(classPrototype) { if (!this.classes.has(classPrototype)) { let funcOrClassNode = t.classExpression(null, null, t.classBody([]), []); this.classes.set(classPrototype, funcOrClassNode); return funcOrClassNode; } else { let funcOrClassNode = this.classes.get(classPrototype); (0, _invariant.default)(funcOrClassNode && t.isClassExpression(funcOrClassNode)); return funcOrClassNode; } } } exports.ResidualFunctions = ResidualFunctions; //# sourceMappingURL=ResidualFunctions.js.map