/** @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());
    }
  }
 /**
  * Determines if the function is worth inlining and potentially trims references that increase the
  * cost.
  *
  * @return Whether inlining the references lowers the overall cost.
  */
 private boolean mimimizeCost(FunctionState fs) {
   if (!inliningLowersCost(fs)) {
     // Try again without Block inlining references
     if (fs.hasBlockInliningReferences()) {
       fs.setRemove(false);
       fs.removeBlockInliningReferences();
       if (!fs.hasReferences() || !inliningLowersCost(fs)) {
         return false;
       }
     } else {
       return false;
     }
   }
   return true;
 }
    /** Find functions that can be inlined. */
    private void checkNameUsage(Node n, Node parent) {
      Preconditions.checkState(n.isName());

      if (isCandidateUsage(n)) {
        return;
      }

      // Other refs to a function name remove its candidacy for inlining
      String name = n.getString();
      FunctionState fs = fns.get(name);
      if (fs == null) {
        return;
      }

      // Unlike normal call/new parameters, references passed to
      // JSCompiler_ObjectPropertyString are not aliases of a value, but
      // a reference to the name itself, as such the value of the name is
      // unknown and can not be inlined.
      if (parent.isNew()) {
        Node target = parent.getFirstChild();
        if (target.isName()
            && target
                .getString()
                .equals(ObjectPropertyStringPreprocess.EXTERN_OBJECT_PROPERTY_STRING)) {
          // This method is going to be replaced so don't inline it anywhere.
          fs.setInline(false);
        }
      }

      // If the name is being assigned to it can not be inlined.
      if (parent.isAssign() && parent.getFirstChild() == n) {
        // e.g. bar = something; <== we can't inline "bar"
        // so mark the function as uninlinable.
        // TODO(johnlenz): Should we just remove it from fns here?
        fs.setInline(false);
      } else {
        // e.g. var fn = bar; <== we can't inline "bar"
        // As this reference can't be inlined mark the function as
        // unremovable.
        fs.setRemove(false);
      }
    }
    void maybeAddReference(NodeTraversal t, FunctionState fs, Node callNode, JSModule module) {
      if (!fs.canInline()) {
        return;
      }

      InliningMode mode = fs.canInlineDirectly() ? InliningMode.DIRECT : InliningMode.BLOCK;
      boolean referenceAdded = maybeAddReferenceUsingMode(t, fs, callNode, module, mode);
      if (!referenceAdded && mode == InliningMode.DIRECT && blockFunctionInliningEnabled) {
        // This reference can not be directly inlined, see if
        // block replacement inlining is possible.
        mode = InliningMode.BLOCK;
        referenceAdded = maybeAddReferenceUsingMode(t, fs, callNode, module, mode);
      }

      if (!referenceAdded) {
        // Don't try to remove a function if we can't inline all
        // the references.
        fs.setRemove(false);
      }
    }