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