/**
   * @param fnName The name of this function. This either the name of the variable to which the
   *     function is assigned or the name from the FUNCTION node.
   * @param fnNode The FUNCTION node of the function to inspect.
   * @return Whether the function node meets the minimum requirements for inlining.
   */
  boolean doesFunctionMeetMinimumRequirements(final String fnName, Node fnNode) {
    Node block = NodeUtil.getFunctionBody(fnNode);

    // Basic restrictions on functions that can be inlined:
    // 1) It contains a reference to itself.
    // 2) It uses its parameters indirectly using "arguments" (it isn't
    //    handled yet.
    // 3) It references "eval". Inline a function containing eval can have
    //    large performance implications.

    final String fnRecursionName = fnNode.getFirstChild().getString();
    Preconditions.checkState(fnRecursionName != null);

    Predicate<Node> p =
        new Predicate<Node>() {
          public boolean apply(Node n) {
            if (n.getType() == Token.NAME) {
              return n.getString().equals("arguments")
                  || n.getString().equals("eval")
                  || n.getString().equals(fnName)
                  || (fnRecursionName.length() != 0 && n.getString().equals(fnRecursionName));
            }
            return false;
          }
        };

    return !NodeUtil.has(block, p, Predicates.<Node>alwaysTrue());
  }
  /**
   * 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;
  }
 /**
  * @param fnNode The function to inspect.
  * @return Whether the function has parameters, var, or function declarations.
  */
 private static boolean hasLocalNames(Node fnNode) {
   Node block = NodeUtil.getFunctionBody(fnNode);
   return NodeUtil.getFunctionParameters(fnNode).hasChildren()
       || NodeUtil.has(
           block, new NodeUtil.MatchDeclaration(), new NodeUtil.MatchShallowStatement());
 }