/**
   * 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;
  }