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