/** @see #resolveInlineConflicts */
  private void resolveInlineConflictsForFunction(FunctionState fs) {
    // Functions that aren't referenced don't cause conflicts.
    if (!fs.hasReferences() || !fs.canInline()) {
      return;
    }

    Node fnNode = fs.getFn().getFunctionNode();
    Set<String> names = findCalledFunctions(fnNode);
    if (!names.isEmpty()) {
      // Prevent the removal of the referenced functions.
      for (String name : names) {
        FunctionState fsCalled = fns.get(name);
        if (fsCalled != null && fsCalled.canRemove()) {
          fsCalled.setRemove(false);
          // For functions that can no longer be removed, check if they should
          // still be inlined.
          if (!mimimizeCost(fsCalled)) {
            // It can't be inlined remove it from the list.
            fsCalled.setInline(false);
          }
        }
      }

      // Make a copy of the Node, so it isn't changed by other inlines.
      fs.setSafeFnNode(fs.getFn().getFunctionNode().cloneTree());
    }
  }
    private boolean maybeAddReferenceUsingMode(
        NodeTraversal t, FunctionState fs, Node callNode, JSModule module, InliningMode mode) {

      // If many functions are inlined into the same function F in the same
      // inlining round, then the size of F may exceed the max size.
      // This could be avoided if we bail later, during the inlining phase, eg,
      // in Inline#visitCallSite. However, that is not safe, because at that
      // point expression decomposition has already run, and we want to
      // decompose expressions only for the calls that are actually inlined.
      if (enforceMaxSizeAfterInlining && targetSizeAfterInlineExceedsLimit(t, fs)) {
        return false;
      }

      Reference candidate = new Reference(callNode, t.getScope(), module, mode);
      CanInlineResult result =
          injector.canInlineReferenceToFunction(
              candidate,
              fs.getFn().getFunctionNode(),
              fs.getNamesToAlias(),
              fs.getReferencesThis(),
              fs.hasInnerFunctions());
      if (result != CanInlineResult.NO) {
        // Yeah!
        candidate.setRequiresDecomposition(result == CanInlineResult.AFTER_PREPARATION);
        fs.addReference(candidate);
        return true;
      }

      return false;
    }
    private boolean maybeAddReferenceUsingMode(
        NodeTraversal t, FunctionState fs, Node callNode, JSModule module, InliningMode mode) {

      if (specializationState != null) {
        // If we're specializing, make sure we can fixup
        // the containing function before inlining
        Node containingFunction = getContainingFunction(t);
        if (containingFunction != null
            && !specializationState.canFixupFunction(containingFunction)) {
          return false;
        }
      }

      CanInlineResult result =
          injector.canInlineReferenceToFunction(
              t,
              callNode,
              fs.getFn().getFunctionNode(),
              fs.getNamesToAlias(),
              mode,
              fs.getReferencesThis(),
              fs.hasInnerFunctions());
      if (result != CanInlineResult.NO) {
        // Yeah!
        boolean decompose = (result == CanInlineResult.AFTER_PREPARATION);
        fs.addReference(new Reference(callNode, module, mode, decompose));
        return true;
      }

      return false;
    }
 /** @return Whether inlining the function reduces code size. */
 private boolean inliningLowersCost(FunctionState fs) {
   return injector.inliningLowersCost(
       fs.getModule(),
       fs.getFn().getFunctionNode(),
       fs.getReferences(),
       fs.getNamesToAlias(),
       fs.canRemove(),
       fs.getReferencesThis());
 }
    /** Inline a function into the call site. */
    private void inlineFunction(NodeTraversal t, Reference ref, FunctionState fs) {
      Function fn = fs.getFn();
      String fnName = fn.getName();
      Node fnNode = fs.getSafeFnNode();

      Node newExpr = injector.inline(ref, fnName, fnNode);
      if (!newExpr.isEquivalentTo(ref.callNode)) {
        t.getCompiler().reportChangeToEnclosingScope(newExpr);
      }
      t.getCompiler().addToDebugLog("Inlined function: " + fn.getName());
    }
 /** Removed inlined functions that no longer have any references. */
 void removeInlinedFunctions() {
   for (FunctionState fs : fns.values()) {
     if (fs.canRemove()) {
       Function fn = fs.getFn();
       Preconditions.checkState(fs.canInline());
       Preconditions.checkState(fn != null);
       verifyAllReferencesInlined(fs);
       fn.remove();
     }
   }
 }
  /** Removed inlined functions that no longer have any references. */
  void removeInlinedFunctions() {
    for (FunctionState fs : fns.values()) {
      if (fs.canRemove()) {
        Function fn = fs.getFn();
        Preconditions.checkState(fs.canInline());
        Preconditions.checkState(fn != null);
        verifyAllReferencesInlined(fs);

        if (specializationState != null) {
          specializationState.reportRemovedFunction(fn.getFunctionNode(), fn.getDeclaringBlock());
        }

        fn.remove();
      }
    }
  }
 private boolean targetSizeAfterInlineExceedsLimit(NodeTraversal t, FunctionState fs) {
   Node containingFunction = getContainingFunction(t);
   // Always inline at the top level,
   // unless maybeAddFunction has marked fs as not inlinable.
   if (containingFunction == null) {
     return false;
   }
   Node inlinedFun = fs.getFn().getFunctionNode();
   if (isAlwaysInlinable(inlinedFun)) {
     return false;
   }
   int inlinedFunSize =
       NodeUtil.countAstSizeUpToLimit(NodeUtil.getFunctionBody(inlinedFun), maxSizeAfterInlining);
   int targetFunSize = NodeUtil.countAstSizeUpToLimit(containingFunction, maxSizeAfterInlining);
   return inlinedFunSize + targetFunSize > maxSizeAfterInlining;
 }