/**
   * Inline a function that fulfills the requirements of canInlineReferenceDirectly into the call
   * site, replacing only the CALL node.
   */
  private Node inlineReturnValue(Node callNode, Node fnNode) {
    Node block = fnNode.getLastChild();
    Node callParentNode = callNode.getParent();

    // NOTE: As the normalize pass guarantees globals aren't being
    // shadowed and an expression can't introduce new names, there is
    // no need to check for conflicts.

    // Create an argName -> expression map, checking for side effects.
    Map<String, Node> argMap =
        FunctionArgumentInjector.getFunctionCallParameterMap(
            fnNode, callNode, this.safeNameIdSupplier);

    Node newExpression;
    if (!block.hasChildren()) {
      Node srcLocation = block;
      newExpression = NodeUtil.newUndefinedNode(srcLocation);
    } else {
      Node returnNode = block.getFirstChild();
      Preconditions.checkArgument(returnNode.getType() == Token.RETURN);

      // Clone the return node first.
      Node safeReturnNode = returnNode.cloneTree();
      Node inlineResult = FunctionArgumentInjector.inject(safeReturnNode, null, argMap);
      Preconditions.checkArgument(safeReturnNode == inlineResult);
      newExpression = safeReturnNode.removeFirstChild();
    }

    callParentNode.replaceChild(callNode, newExpression);
    return newExpression;
  }
  /**
   * Determines whether a function can be inlined at a particular call site. - Don't inline if the
   * calling function contains an inner function and inlining would introduce new globals.
   */
  private boolean callMeetsBlockInliningRequirements(
      NodeTraversal t, Node callNode, Node fnNode, Set<String> namesToAlias) {
    // Note: functions that contain function definitions are filtered out
    // in isCanidateFunction.

    // TODO(johnlenz): Determining if the called function contains VARs
    // or if the caller contains inner functions accounts for 20% of the
    // runtime cost of this pass.

    // Don't inline functions with var declarations into a scope with inner
    // functions as the new vars would leak into the inner function and
    // cause memory leaks.
    boolean fnContainsVars =
        NodeUtil.has(
            NodeUtil.getFunctionBody(fnNode),
            new NodeUtil.MatchDeclaration(),
            new NodeUtil.MatchShallowStatement());
    boolean callerContainsFunction = false;
    if (!t.inGlobalScope()) {
      Node fnCaller = t.getScopeRoot();
      Node fnCallerBody = fnCaller.getLastChild();

      callerContainsFunction = NodeUtil.containsFunction(fnCallerBody);
    }

    if (fnContainsVars && callerContainsFunction) {
      return false;
    }

    // If the caller contains functions, verify we aren't adding any
    // additional VAR declarations because aliasing is needed.
    if (callerContainsFunction) {
      Map<String, Node> args =
          FunctionArgumentInjector.getFunctionCallParameterMap(
              fnNode, callNode, this.safeNameIdSupplier);
      boolean hasArgs = !args.isEmpty();
      if (hasArgs) {
        // Limit the inlining
        Set<String> allNamesToAlias = Sets.newHashSet(namesToAlias);
        FunctionArgumentInjector.maybeAddTempsForCallArguments(
            fnNode, args, allNamesToAlias, compiler.getCodingConvention());
        if (!allNamesToAlias.isEmpty()) {
          return false;
        }
      }
    }

    return true;
  }
  /**
   * Updates the FunctionState object for the given function. Checks if the given function matches
   * the criteria for an inlinable function.
   */
  private void maybeAddFunction(Function fn, JSModule module) {
    String name = fn.getName();
    FunctionState fs = getOrCreateFunctionState(name);

    // TODO(johnlenz): Maybe "smarten" FunctionState by adding this logic
    // to it?

    // If the function has multiple definitions, don't inline it.
    if (fs.hasExistingFunctionDefinition()) {
      fs.setInline(false);
      return;
    }
    Node fnNode = fn.getFunctionNode();
    if (enforceMaxSizeAfterInlining
        && !isAlwaysInlinable(fnNode)
        && maxSizeAfterInlining <= NodeUtil.countAstSizeUpToLimit(fnNode, maxSizeAfterInlining)) {
      fs.setInline(false);
      return;
    }
    // verify the function hasn't already been marked as "don't inline"
    if (fs.canInline()) {
      // store it for use when inlining.
      fs.setFn(fn);
      if (FunctionInjector.isDirectCallNodeReplacementPossible(fn.getFunctionNode())) {
        fs.inlineDirectly(true);
      }

      // verify the function meets all the requirements.
      // TODO(johnlenz): Minimum requirement checks are about 5% of the
      // run-time cost of this pass.
      if (!isCandidateFunction(fn)) {
        // It doesn't meet the requirements.
        fs.setInline(false);
      }

      // Set the module and gather names that need temporaries.
      if (fs.canInline()) {
        fs.setModule(module);

        Set<String> namesToAlias = FunctionArgumentInjector.findModifiedParameters(fnNode);
        if (!namesToAlias.isEmpty()) {
          fs.inlineDirectly(false);
          fs.setNamesToAlias(namesToAlias);
        }

        Node block = NodeUtil.getFunctionBody(fnNode);
        if (NodeUtil.referencesThis(block)) {
          fs.setReferencesThis(true);
        }

        if (NodeUtil.containsFunction(block)) {
          fs.setHasInnerFunctions(true);
          // If there are inner functions, we can inline into global scope
          // if there are no local vars or named functions.
          // TODO(johnlenz): this can be improved by looking at the possible
          // values for locals.  If there are simple values, or constants
          // we could still inline.
          if (!assumeMinimumCapture && hasLocalNames(fnNode)) {
            fs.setInline(false);
          }
        }
      }

      // Check if block inlining is allowed.
      if (fs.canInline() && !fs.canInlineDirectly()) {
        if (!blockFunctionInliningEnabled) {
          fs.setInline(false);
        }
      }
    }
  }