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