@Override
    public void visit(NodeTraversal t, Node n, Node parent) {
      switch (n.getType()) {
          // Function calls
        case Token.CALL:
          Node child = n.getFirstChild();
          String name = null;
          // NOTE: The normalization pass insures that local names do not
          // collide with global names.
          if (child.isName()) {
            name = child.getString();
          } else if (child.isFunction()) {
            name = anonFunctionMap.get(child);
          } else if (NodeUtil.isFunctionObjectCall(n)) {
            Preconditions.checkState(NodeUtil.isGet(child));
            Node fnIdentifingNode = child.getFirstChild();
            if (fnIdentifingNode.isName()) {
              name = fnIdentifingNode.getString();
            } else if (fnIdentifingNode.isFunction()) {
              name = anonFunctionMap.get(fnIdentifingNode);
            }
          }

          if (name != null) {
            FunctionState fs = functionMap.get(name);

            // Only visit call-sites for functions that can be inlined.
            if (fs != null) {
              callback.visitCallSite(t, n, fs);
            }
          }
          break;
      }
    }
 /**
  * Returns the nth argument node given a usage site for a direct function call or for a
  * func.call() node.
  */
 private static Node getArgumentForCallOrNewOrDotCall(UseSite site, final int argIndex) {
   int adjustedArgIndex = argIndex;
   Node parent = site.node.getParent();
   if (NodeUtil.isFunctionObjectCall(parent)) {
     adjustedArgIndex++;
   }
   return NodeUtil.getArgumentForCallOrNew(parent, adjustedArgIndex);
 }
  /**
   * Determines whether a function can be inlined at a particular call site. There are several
   * criteria that the function and reference must hold in order for the functions to be inlined: 1)
   * If a call's arguments have side effects, the corresponding argument in the function must only
   * be referenced once. For instance, this will not be inlined:
   *
   * <pre>
   *     function foo(a) { return a + a }
   *     x = foo(i++);
   * </pre>
   */
  private CanInlineResult canInlineReferenceDirectly(Node callNode, Node fnNode) {
    if (!isDirectCallNodeReplacementPossible(fnNode)) {
      return CanInlineResult.NO;
    }

    Node block = fnNode.getLastChild();

    // CALL NODE: [ NAME, ARG1, ARG2, ... ]
    Node cArg = callNode.getFirstChild().getNext();

    // Functions called via 'call' and 'apply' have a this-object as
    // the first parameter, but this is not part of the called function's
    // parameter list.
    if (callNode.getFirstChild().getType() != Token.NAME) {
      if (NodeUtil.isFunctionObjectCall(callNode)) {
        // TODO(johnlenz): Support replace this with a value.
        Preconditions.checkNotNull(cArg);
        Preconditions.checkState(cArg.getType() == Token.THIS);
        cArg = cArg.getNext();
      } else {
        // ".apply" call should be filtered before this.
        Preconditions.checkState(!NodeUtil.isFunctionObjectApply(callNode));
      }
    }

    // FUNCTION NODE -> LP NODE: [ ARG1, ARG2, ... ]
    Node fnParam = NodeUtil.getFnParameters(fnNode).getFirstChild();
    while (cArg != null || fnParam != null) {
      // For each named parameter check if a mutable argument use more than one.
      if (fnParam != null) {
        if (cArg != null) {
          // Check for arguments that are evaluated more than once.
          // Note: Unlike block inlining, there it is not possible that a
          // parameter reference will be in a loop.
          if (NodeUtil.mayEffectMutableState(cArg)
              && NodeUtil.getNameReferenceCount(block, fnParam.getString()) > 1) {
            return CanInlineResult.NO;
          }
        }

        // Move to the next name.
        fnParam = fnParam.getNext();
      }

      // For every call argument check for side-effects, even if there
      // isn't a named parameter to match.
      if (cArg != null) {
        if (NodeUtil.mayHaveSideEffects(cArg)) {
          return CanInlineResult.NO;
        }
        cArg = cArg.getNext();
      }
    }

    return CanInlineResult.YES;
  }
  /**
   * Only ".call" calls and direct calls to functions are supported.
   *
   * @param callNode The call evaluate.
   * @return Whether the call is of a type that is supported.
   */
  private boolean isSupportedCallType(Node callNode) {
    if (callNode.getFirstChild().getType() != Token.NAME) {
      if (NodeUtil.isFunctionObjectCall(callNode)) {
        Node thisValue = callNode.getFirstChild().getNext();
        if (thisValue == null || thisValue.getType() != Token.THIS) {
          return false;
        }
      } else if (NodeUtil.isFunctionObjectApply(callNode)) {
        return false;
      }
    }

    return true;
  }
    /**
     * @return Whether the definitionSite represents a function whose call signature can be
     *     modified.
     */
    private boolean canChangeSignature(Node function) {
      Definition definition = getFunctionDefinition(function);
      CodingConvention convention = compiler.getCodingConvention();

      Preconditions.checkState(!definition.isExtern());

      Collection<UseSite> useSites = defFinder.getUseSites(definition);
      for (UseSite site : useSites) {
        Node parent = site.node.getParent();

        // This was a use site removed by something else before we run.
        // 1. By another pass before us which means the definition graph is
        //    no updated properly.
        // 2. By the continuations algorithm above.
        if (parent == null) {
          continue; // Ignore it.
        }

        // Ignore references within goog.inherits calls.
        if (NodeUtil.isCall(parent) && convention.getClassesDefinedByCall(parent) != null) {
          continue;
        }

        // Accessing the property directly prevents rewrite.
        if (!SimpleDefinitionFinder.isCallOrNewSite(site)) {
          if (!(NodeUtil.isGetProp(parent) && NodeUtil.isFunctionObjectCall(parent.getParent()))) {
            return false;
          }
        }

        if (NodeUtil.isFunctionObjectApply(parent)) {
          return false;
        }

        // TODO(johnlenz): support specialization

        // Multiple definitions prevent rewrite.
        // Attempt to validate the state of the simple definition finder.
        Node nameNode = site.node;
        Collection<Definition> singleSiteDefinitions =
            defFinder.getDefinitionsReferencedAt(nameNode);
        Preconditions.checkState(singleSiteDefinitions.size() == 1);
        Preconditions.checkState(singleSiteDefinitions.contains(definition));
      }

      return true;
    }
  /**
   * @param t The traversal use to reach the call site.
   * @param callNode The CALL node.
   * @param fnNode The function to evaluate for inlining.
   * @param needAliases A set of function parameter names that can not be used without aliasing.
   *     Returned by getUnsafeParameterNames().
   * @param mode Inlining mode to be used.
   * @param referencesThis Whether fnNode contains references to its this object.
   * @param containsFunctions Whether fnNode contains inner functions.
   * @return Whether the inlining can occur.
   */
  CanInlineResult canInlineReferenceToFunction(
      NodeTraversal t,
      Node callNode,
      Node fnNode,
      Set<String> needAliases,
      InliningMode mode,
      boolean referencesThis,
      boolean containsFunctions) {
    // TODO(johnlenz): This function takes too many parameter, without
    // context.  Modify the API to take a structure describing the function.

    // Allow direct function calls or "fn.call" style calls.
    if (!isSupportedCallType(callNode)) {
      return CanInlineResult.NO;
    }

    // Limit where functions that contain functions can be inline.  Introducing
    // an inner function into another function can capture a variable and cause
    // a memory leak.  This isn't a problem in the global scope as those values
    // last until explicitly cleared.
    if (containsFunctions && !t.inGlobalScope()) {
      // TODO(johnlenz): Allow inlining into any scope without local names or
      // inner functions.
      return CanInlineResult.NO;
    }

    // TODO(johnlenz): Add support for 'apply'
    if (referencesThis && !NodeUtil.isFunctionObjectCall(callNode)) {
      // TODO(johnlenz): Allow 'this' references to be replaced with a
      // global 'this' object.
      return CanInlineResult.NO;
    }

    if (mode == InliningMode.DIRECT) {
      return canInlineReferenceDirectly(callNode, fnNode);
    } else {
      return canInlineReferenceAsStatementBlock(t, callNode, fnNode, needAliases);
    }
  }
    /**
     * Find function expressions that are called directly in the form of
     * (function(a,b,...){...})(a,b,...) or (function(a,b,...){...}).call(this,a,b, ...)
     */
    public void findFunctionExpressions(NodeTraversal t, Node n) {
      switch (n.getType()) {
          // Functions expressions in the form of:
          //   (function(){})();
        case Token.CALL:
          Node fnNode = null;
          if (n.getFirstChild().isFunction()) {
            fnNode = n.getFirstChild();
          } else if (NodeUtil.isFunctionObjectCall(n)) {
            Node fnIdentifingNode = n.getFirstChild().getFirstChild();
            if (fnIdentifingNode.isFunction()) {
              fnNode = fnIdentifingNode;
            }
          }

          // If a interesting function was discovered, add it.
          if (fnNode != null) {
            Function fn = new FunctionExpression(fnNode, callsSeen++);
            maybeAddFunction(fn, t.getModule());
            anonFns.put(fnNode, fn.getName());
          }
          break;
      }
    }