/** * Finds set of all defs in function given (defSites). Adds parameters of function to this set: * defSites. Finds all variables that are referenced in the function given (alf.names). * windowProps are all global properties available. Closures = alf.names - defSites - windowProps * * @param n root node of the function who's closures are to be found * @return set of closure names */ private Set<String> findClosures(Node n) { Set<String> closureVars = null; SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, n.getLastChild()); Collection<DefinitionSite> defSites = defFinder.getDefinitionSites(); Set<String> localDefs = new HashSet<String>(); for (DefinitionSite site : defSites) { if (site.node.getType() == Token.GETPROP) continue; String def = site.node.getString(); if (def.length() > 0) { localDefs.add(def); } } // adding params to function as defs Node origParamNode = n.getChildAtIndex(1); for (int i = 0; i < origParamNode.getChildCount(); i++) { String temp = origParamNode.getChildAtIndex(i).getString(); localDefs.add(temp); } // System.out.println("\nPrinting LOCAL def sites:" + defs); /*SimpleDefinitionFinder defFinder1 = new SimpleDefinitionFinder(compiler); defFinder1.process(externs, root); Collection<DefinitionSite> defSites1 = defFinder1.getDefinitionSites(); Set<String> defs1 = new HashSet<String>(); for(DefinitionSite site1: defSites1){ if (site1.node.getType() == Token.GETPROP) continue; String def = site1.node.getString(); if (def.length() > 0){ defs1.add(def); } } System.out.println("\nPrinting Global def sites:" + defs1);*/ AllNamesFinder alf = new AllNamesFinder(compiler); NodeTraversal.traverse(compiler, n.getLastChild(), alf); // System.out.println("all names: " + alf.names); closureVars = alf.names; closureVars.removeAll(localDefs); closureVars.removeAll(Props.windowProps); closureVars.remove( "this"); // since 'this' is later modified to $$_self we don't need to consider this as // closure return closureVars; }
@Override public void process(Node externs, Node root) { if (!passes.isEmpty()) { SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); compiler.setSimpleDefinitionFinder(defFinder); defFinder.process(externs, root); for (CallGraphCompilerPass pass : passes) { pass.process(externs, root, defFinder); } } }
/** * Traverses the root, removing all unused variables. Multiple traversals may occur to ensure all * unused variables are removed. */ public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); SimpleDefinitionFinder defFinder = null; if (modifyCallSites) { // For testing, allow the SimpleDefinitionFinder to be build now. defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, root); } process(externs, root, defFinder); }
/** * @return Whether the definitionSite represents a function whose call signature can be * modified. */ private boolean canChangeSignature(Node function) { Definition definition = getFunctionDefinition(function); CodingConvention convention = compiler.getCodingConvention(); Preconditions.checkState(!definition.isExtern()); Collection<UseSite> useSites = defFinder.getUseSites(definition); for (UseSite site : useSites) { Node parent = site.node.getParent(); // This was a use site removed by something else before we run. // 1. By another pass before us which means the definition graph is // no updated properly. // 2. By the continuations algorithm above. if (parent == null) { continue; // Ignore it. } // Ignore references within goog.inherits calls. if (NodeUtil.isCall(parent) && convention.getClassesDefinedByCall(parent) != null) { continue; } // Accessing the property directly prevents rewrite. if (!SimpleDefinitionFinder.isCallOrNewSite(site)) { if (!(NodeUtil.isGetProp(parent) && NodeUtil.isFunctionObjectCall(parent.getParent()))) { return false; } } if (NodeUtil.isFunctionObjectApply(parent)) { return false; } // TODO(johnlenz): support specialization // Multiple definitions prevent rewrite. // Attempt to validate the state of the simple definition finder. Node nameNode = site.node; Collection<Definition> singleSiteDefinitions = defFinder.getDefinitionsReferencedAt(nameNode); Preconditions.checkState(singleSiteDefinitions.size() == 1); Preconditions.checkState(singleSiteDefinitions.contains(definition)); } return true; }
/** * @param function * @return the Definition object for the function. */ private Definition getFunctionDefinition(Node function) { DefinitionSite definitionSite = defFinder.getDefinitionForFunction(function); Preconditions.checkNotNull(definitionSite); Definition definition = definitionSite.definition; Preconditions.checkState(!definitionSite.inExterns); Preconditions.checkState(definition.getRValue() == function); return definition; }
/** * @param function * @return Whether the callers to this function can be modified in any way. */ boolean canModifyCallers(Node function) { if (NodeUtil.isVarArgsFunction(function)) { return false; } DefinitionSite defSite = defFinder.getDefinitionForFunction(function); if (defSite == null) { return false; } Definition definition = defSite.definition; // Be conservative, don't try to optimize any declaration that isn't as // simple function declaration or assignment. if (!SimpleDefinitionFinder.isSimpleFunctionDeclaration(function)) { return false; } return defFinder.canModifyDefinition(definition); }
/** Remove all the following parameters without side-effects */ private void tryRemoveAllFollowingArgs(Node function, final int argIndex) { Definition definition = getFunctionDefinition(function); for (UseSite site : defFinder.getUseSites(definition)) { if (!isModifiableCallSite(site)) { continue; } Node arg = getArgumentForCallOrNewOrDotCall(site, argIndex + 1); while (arg != null) { if (!NodeUtil.mayHaveSideEffects(arg)) { toRemove.add(arg); } arg = arg.getNext(); } } }
/** * Remove all references to a parameter, otherwise simplify the known references. * * @return Whether all the references were removed. */ private boolean canRemoveArgFromCallSites(Node function, int argIndex) { Definition definition = getFunctionDefinition(function); // Check all the call sites. for (UseSite site : defFinder.getUseSites(definition)) { if (isModifiableCallSite(site)) { Node arg = getArgumentForCallOrNewOrDotCall(site, argIndex); // TODO(johnlenz): try to remove parameters with side-effects by // decomposing the call expression. if (arg != null && NodeUtil.mayHaveSideEffects(arg, compiler)) { return false; } } else { return false; } } return true; }
/** * Remove all references to a parameter if possible otherwise simplify the side-effect free * parameters. */ private void tryRemoveArgFromCallSites(Node function, int argIndex, boolean canModifyAllSites) { Definition definition = getFunctionDefinition(function); for (UseSite site : defFinder.getUseSites(definition)) { if (isModifiableCallSite(site)) { Node arg = getArgumentForCallOrNewOrDotCall(site, argIndex); if (arg != null) { Node argParent = arg.getParent(); // Even if we can't change the signature in general we can always // remove an unused value off the end of the parameter list. if (canModifyAllSites || (arg.getNext() == null && !NodeUtil.mayHaveSideEffects(arg, compiler))) { toRemove.add(arg); } else { // replace the node in the arg with 0 if (!NodeUtil.mayHaveSideEffects(arg, compiler) && (arg.getType() != Token.NUMBER || arg.getDouble() != 0)) { toReplaceWithZero.add(arg); } } } } } }
/** * @param site The site to inspect * @return Whether the call site is suitable for modification */ private static boolean isModifiableCallSite(UseSite site) { return SimpleDefinitionFinder.isCallOrNewSite(site) && !NodeUtil.isFunctionObjectApply(site.node.getParent()); }