"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ResidualFunctions = undefined;

var _errors = require("../errors.js");

var _realm = require("../realm.js");

var _index = require("../values/index.js");

var _babelTypes = require("babel-types");

var t = _interopRequireWildcard(_babelTypes);

var _babelTraverse = require("babel-traverse");

var _babelTraverse2 = _interopRequireDefault(_babelTraverse);

var _invariant = require("../invariant.js");

var _invariant2 = _interopRequireDefault(_invariant);

var _types = require("./types.js");

var _visitors = require("./visitors.js");

var _modules = require("../utils/modules.js");

var _ResidualFunctionInitializers = require("./ResidualFunctionInitializers.js");

var _internalizer = require("../utils/internalizer.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)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

class ResidualFunctions {
  constructor(realm, statistics, options, modules, requireReturns, locationService, prelude, initializerNameGenerator, factoryNameGenerator, residualFunctionInfos, residualFunctionInstances, residualClassMethodInstances, additionalFunctionValueInfos, additionalFunctionValueNestedFunctions, referentializer) {
    this.realm = realm;
    this.statistics = statistics;
    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, prelude, initializerNameGenerator);
    this.residualFunctionInfos = residualFunctionInfos;
    this.residualFunctionInstances = residualFunctionInstances;
    this.residualClassMethodInstances = residualClassMethodInstances;
    this.additionalFunctionValueInfos = additionalFunctionValueInfos;
    this.referentializer = referentializer;
    for (let instance of residualFunctionInstances.values()) {
      (0, _invariant2.default)(instance !== undefined);
      if (!additionalFunctionValueInfos.has(instance.functionValue)) this.addFunctionInstance(instance);
    }
    this.additionalFunctionValueNestedFunctions = additionalFunctionValueNestedFunctions;
    this.simpleClosures = !!options.simpleClosures;
  }

  addFunctionInstance(instance) {
    this.functionInstances.push(instance);
    let code = instance.functionValue.$ECMAScriptCode;
    (0, _invariant2.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) {
    function shouldInlineFunction() {
      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, _invariant2.default)(functionInfo);
    let { usesArguments } = functionInfo;
    return !shouldInlineFunction() && instances.length > 1 && !usesArguments && !this.simpleClosures;
  }

  // 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, _invariant2.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, _invariant2.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, _invariant2.default)(functionUniqueTag);

      const functionInfo = this.residualFunctionInfos.get(functionBody);
      (0, _invariant2.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, _invariant2.default)(funcAUniqueTag);

      const funcBUniqueTag = funcB[0].uniqueOrderedTag;
      (0, _invariant2.default)(funcBUniqueTag);
      return funcAUniqueTag - funcBUniqueTag;
    });
  }

  spliceFunctions(rewrittenAdditionalFunctions) {
    this.residualFunctionInitializers.scrubFunctionInitializers();

    let functionBodies = new Map();
    // these need to get spliced in at the end
    let additionalFunctionPreludes = new Map();
    let additionalFunctionModifiedBindingsSegment = new Map();
    let getModifiedBindingsSegment = additionalFunction => (0, _utils.getOrDefault)(additionalFunctionModifiedBindingsSegment, additionalFunction, () => []);
    let getFunctionBody = instance => (0, _utils.getOrDefault)(functionBodies, instance, () => []);
    let globalPrelude = this.prelude;
    function getPrelude(instance) {
      let additionalFunction = instance.containingAdditionalFunction;
      let b;
      if (additionalFunction) {
        b = (0, _utils.getOrDefault)(additionalFunctionPreludes, additionalFunction, () => []);
      } else {
        b = globalPrelude;
      }
      return b;
    }

    let functionEntries = Array.from(this.functions.entries());
    this._sortFunctionByOriginalOrdering(functionEntries);
    this.statistics.functions = functionEntries.length;
    let unstrictFunctionBodies = [];
    let strictFunctionBodies = [];
    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, _invariant2.default)(t.isCallExpression(funcOrClassNode) || t.isClassExpression(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, _invariant2.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) {
      for (let [, residualBinding] of funcInfo.modifiedBindings) {
        let scope = residualBinding.scope;
        if (scope === undefined) continue;

        // TODO #989: This should probably be an invariant once captures work properly
        // Currently we don't referentialize bindings in additional functions (but we
        // do for bindings nested in additional functions)
        if (!residualBinding.referentialized) continue;

        // 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;
      (0, _invariant2.default)(params !== undefined);

      let rewrittenBody = rewrittenAdditionalFunctions.get(funcValue);
      (0, _invariant2.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 funcParams = params.slice();
      let funcOrClassNode;

      if (this.residualClassMethodInstances.has(funcValue)) {
        let classMethodInstance = this.residualClassMethodInstances.get(funcValue);
        (0, _invariant2.default)(classMethodInstance);
        let {
          methodType,
          classMethodKeyNode,
          classSuperNode,
          classMethodComputed,
          classPrototype,
          classMethodIsStatic
        } = classMethodInstance;

        let isConstructor = methodType === "constructor";
        (0, _invariant2.default)(classPrototype instanceof _index.ObjectValue);
        (0, _invariant2.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, funcParams, 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 = t.functionExpression(null, funcParams, functionBody);
      }
      let id = this.locationService.getLocation(funcValue);
      (0, _invariant2.default)(id !== undefined);

      if (funcValue instanceof _index.ECMAScriptSourceFunctionValue && funcValue.$Strict) {
        strictFunctionBodies.push(funcOrClassNode);
      } else {
        unstrictFunctionBodies.push(funcOrClassNode);
      }
      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, _invariant2.default)(functionInfo);
      let { unbound, modified, usesThis } = functionInfo;
      let params = instances[0].functionValue.$FormalParameters;
      (0, _invariant2.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.statistics.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, _invariant2.default)(classMethodInstance);
            let {
              classSuperNode,
              classMethodKeyNode,
              methodType,
              classMethodComputed,
              classPrototype,
              classMethodIsStatic
            } = classMethodInstance;

            let isConstructor = methodType === "constructor";
            (0, _invariant2.default)(classPrototype instanceof _index.ObjectValue);
            (0, _invariant2.default)(classMethodKeyNode);
            (0, _invariant2.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 methodBody = t.cloneDeep(funcBody);
              // create the class method AST
              let classMethod = t.classMethod(methodType, classMethodKeyNode, methodParams, methodBody, classMethodComputed, classMethodIsStatic);
              // traverse and replace refs in the class method
              (0, _babelTraverse2.default)(t.file(t.program([t.expressionStatement(t.classExpression(null, null, t.classBody([classMethod]), []))])), _visitors.ClosureRefReplacer, null, {
                residualFunctionBindings,
                modified,
                requireReturns: this.requireReturns,
                statistics: this.statistics,
                getModuleIdIfNodeIsRequireFunction: this.modules.getGetModuleIdIfNodeIsRequireFunction(methodParams, [functionValue]),
                factoryFunctionInfos
              });
              // 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 funcParams = params.slice();
            funcOrClassNode = t.functionExpression(null, funcParams, t.cloneDeep(funcBody));
            let scopeInitialization = [];
            for (let scope of scopeInstances.values()) {
              scopeInitialization = scopeInitialization.concat(this.referentializer.getReferentializedScopeInitialization(scope, t.numericLiteral(scope.id)));
            }
            funcOrClassNode.body.body = scopeInitialization.concat(funcOrClassNode.body.body);

            (0, _babelTraverse2.default)(t.file(t.program([t.expressionStatement(funcOrClassNode)])), _visitors.ClosureRefReplacer, null, {
              residualFunctionBindings,
              modified,
              requireReturns: this.requireReturns,
              statistics: this.statistics,
              getModuleIdIfNodeIsRequireFunction: this.modules.getGetModuleIdIfNodeIsRequireFunction(funcParams, [functionValue]),
              factoryFunctionInfos
            });
          }
          let id = this.locationService.getLocation(functionValue);
          (0, _invariant2.default)(id !== undefined);

          if (functionValue.$Strict) {
            strictFunctionBodies.push(funcOrClassNode);
          } else {
            unstrictFunctionBodies.push(funcOrClassNode);
          }
          (0, _invariant2.default)(id !== undefined);
          (0, _invariant2.default)(funcOrClassNode !== undefined);
          defineFunction(instance, id, funcOrClassNode);
        }
      };

      if (additionalFunctionNestedInstances.length > 0) naiveProcessInstances(additionalFunctionNestedInstances);
      if (!this._shouldUseFactoryFunction(funcBody, normalInstances)) {
        naiveProcessInstances(normalInstances);
        this.statistics.functionClones--;
      } else if (normalInstances.length > 0) {
        const functionUniqueTag = funcBody.uniqueOrderedTag;
        (0, _invariant2.default)(functionUniqueTag);
        const factoryInfo = factoryFunctionInfos.get(functionUniqueTag);
        (0, _invariant2.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) {
          let isDifferent = false;
          let lastBinding;

          let firstBinding = normalInstances[0].residualFunctionBindings.get(name);
          (0, _invariant2.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, _invariant2.default)(residualBinding);
            (0, _invariant2.default)(!residualBinding.modified);
            if (!lastBinding) {
              lastBinding = residualBinding;
            } else if (!(0, _types.AreSameResidualBinding)(this.realm, residualBinding, lastBinding)) {
              isDifferent = true;
              break;
            }
          }

          if (isDifferent) {
            factoryNames.push(name);
          } else {
            (0, _invariant2.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();

        // The Replacer below mutates the AST while the original AST may still be referenced
        // by another outer residual function so let's clone the original AST to avoid modifying it.
        let factoryNode = t.functionExpression(null, factoryParams, t.cloneDeep(funcBody));

        if (normalInstances[0].functionValue.$Strict) {
          strictFunctionBodies.push(factoryNode);
        } else {
          unstrictFunctionBodies.push(factoryNode);
        }

        factoryNode.body.body = scopeInitialization.concat(factoryNode.body.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);

        (0, _babelTraverse2.default)(t.file(t.program([t.expressionStatement(factoryNode)])), _visitors.ClosureRefReplacer, null, {
          residualFunctionBindings: sameResidualBindings,
          modified,
          requireReturns: this.requireReturns,
          statistics: this.statistics,
          getModuleIdIfNodeIsRequireFunction: this.modules.getGetModuleIdIfNodeIsRequireFunction(factoryParams, normalInstances.map(instance => instance.functionValue)),
          factoryFunctionInfos
        });

        for (let instance of normalInstances) {
          let { functionValue, residualFunctionBindings, insertionPoint } = instance;
          let functionId = this.locationService.getLocation(functionValue);
          (0, _invariant2.default)(functionId !== undefined);
          let hasFunctionArg = false;
          let flatArgs = factoryNames.map(name => {
            let residualBinding = residualFunctionBindings.get(name);
            (0, _invariant2.default)(residualBinding);
            let serializedValue = residualBinding.serializedValue;
            hasFunctionArg = hasFunctionArg || residualBinding.value && residualBinding.value instanceof _index.FunctionValue;
            (0, _invariant2.default)(serializedValue);
            return serializedValue;
          });
          for (let entry of instance.scopeInstances) {
            flatArgs.push(t.numericLiteral(entry[1].id));
          }
          let funcNode;
          let firstUsage = this.firstFunctionUsages.get(functionValue);
          (0, _invariant2.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 || this.simpleClosures) {
            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);
            if (functionValue.$Strict) {
              strictFunctionBodies.push(funcNode);
            } else {
              unstrictFunctionBodies.push(funcNode);
            }
          } else {
            funcNode = t.callExpression(t.memberExpression(factoryId, t.identifier("bind")), [_internalizer.nullExpression].concat(flatArgs));
          }

          defineFunction(instance, functionId, funcNode);
        }
      }
    }

    for (let referentializationScope of this.referentializer.referentializationState.keys()) {
      let prelude = this.prelude;
      // Get the prelude for this additional function value
      if (referentializationScope !== "GLOBAL") {
        let additionalFunction = referentializationScope;
        prelude = (0, _utils.getOrDefault)(additionalFunctionPreludes, additionalFunction, () => []);
      }
      prelude.unshift(this.referentializer.createCaptureScopeAccessFunction(referentializationScope));
      prelude.unshift(this.referentializer.createCapturedScopesArrayInitialization(referentializationScope));
    }

    for (let instance of this.functionInstances.reverse()) {
      let functionBody = functionBodies.get(instance);
      if (functionBody !== undefined) {
        let insertionPoint = instance.insertionPoint;
        (0, _invariant2.default)(insertionPoint instanceof _types.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);
      }
    }

    for (let [additionalFunction, body] of Array.from(rewrittenAdditionalFunctions.entries()).reverse()) {
      let additionalFunctionInfo = this.additionalFunctionValueInfos.get(additionalFunction);
      (0, _invariant2.default)(additionalFunctionInfo);
      // Modified bindings initializers of optimized function
      let bodySegment = additionalFunctionModifiedBindingsSegment.get(additionalFunction);
      // initializers from Referentialization
      let initializationStatements = getFunctionBody(additionalFunctionInfo.instance);
      let prelude = additionalFunctionPreludes.get(additionalFunction);
      let insertionPoint = additionalFunctionInfo.instance.insertionPoint;
      (0, _invariant2.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);
      if (prelude) body.unshift(...prelude);
    }

    // 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, _invariant2.default)(t.isFunctionExpression(funcNode));
        let blockStatement = funcNode.body;
        blockStatement.body.unshift(initializerStatement);
      }
    }

    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, _invariant2.default)(funcOrClassNode && t.isClassExpression(funcOrClassNode));
      return funcOrClassNode;
    }
  }
}
exports.ResidualFunctions = ResidualFunctions; /**
                                                * 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.
                                                */
//# sourceMappingURL=ResidualFunctions.js.map