Source code for jasy.script.clean.Unused

#
# Jasy - Web Tooling Framework
# Copyright 2010-2012 Zynga Inc.
# Copyright 2013-2014 Sebastian Werner
#

import jasy.script.parse.Node as Node
import jasy.script.parse.ScopeScanner as ScopeScanner

import jasy.core.Console as Console


#
# Public API
#

[docs]class Error(Exception): def __init__(self, name, line): self.__name = name self.__line = line def __str__(self): return "Unallowed private field access to %s at line %s!" % (self.__name, self.__line)
[docs]def cleanup(node): """""" if not hasattr(node, "variables"): ScopeScanner.scan(node) # Re cleanup until nothing to remove is found x = 0 cleaned = False Console.debug("Removing unused variables...") while True: x = x + 1 #debug("Removing unused variables [Iteration: %s]...", x) Console.indent() if __cleanup(node): ScopeScanner.scan(node) cleaned = True Console.outdent() else: Console.outdent() break return cleaned
# # Implementation # def __cleanup(node): """The scanner part which looks for scopes with unused variables/params.""" cleaned = False for child in list(node): if child is not None and __cleanup(child): cleaned = True if node.type == "script" and node.scope.unused and hasattr(node, "parent"): if __recurser(node, node.scope.unused): cleaned = True return cleaned def __recurser(node, unused): """The cleanup part which always processes one scope and cleans up params and variable definitions which are unused.""" retval = False # Process children if node.type != "function": for child in node: # None children are allowed sometimes e.g. during array_init like [1,2,,,7,8] if child is not None: if __recurser(child, unused): retval = True if node.type == "script" and hasattr(node, "parent"): # Remove unused parameters params = getattr(node.parent, "params", None) if params: # Start from back, as we can only remove params as long # as there is not a required one after the current one for identifier in reversed(params): if identifier.value in unused: Console.debug("Removing unused parameter '%s' in line %s", identifier.value, identifier.line) params.remove(identifier) retval = True else: break # Remove function names which are unused if node.parent.functionForm == "expressed_form": funcName = getattr(node.parent, "name", None) if funcName is not None and funcName in unused: Console.debug("Removing unused function name at line %s" % node.line) del node.parent.name retval = True elif node.type == "function": # Remove full unused functions (when not in top-level scope) if node.functionForm == "declared_form" and getattr(node, "parent", None) and node.parent.type != "call": funcName = getattr(node, "name", None) if funcName is not None and funcName in unused: Console.debug("Removing unused function declaration %s at line %s" % (funcName, node.line)) node.parent.remove(node) retval = True elif node.type == "var": for decl in reversed(node): if getattr(decl, "name", None) in unused: if hasattr(decl, "initializer"): init = decl.initializer if init.type in ("null", "this", "true", "false", "identifier", "number", "string", "regexp"): Console.debug("Removing unused primitive variable %s at line %s" % (decl.name, decl.line)) node.remove(decl) retval = True elif init.type == "function" and (not hasattr(init, "name") or init.name in unused): Console.debug("Removing unused function variable %s at line %s" % (decl.name, decl.line)) node.remove(decl) retval = True # If we have only one child, we replace the whole var statement with just the init block elif len(node) == 1: semicolon = Node.Node(init.tokenizer, "semicolon") semicolon.append(init, "expression") # Protect non-expressions with parens if init.type in ("array_init", "object_init"): init.parenthesized = True elif init.type == "call" and init[0].type == "function": init[0].parenthesized = True node.parent.replace(node, semicolon) retval = True # If we are the last declaration, move it out of node and append after var block elif node[-1] == decl or node[0] == decl: isFirst = node[0] == decl node.remove(decl) nodePos = node.parent.index(node) semicolon = Node.Node(init.tokenizer, "semicolon") semicolon.append(init, "expression") # Protect non-expressions with parens if init.type in ("array_init", "object_init"): init.parenthesized = True elif init.type == "call" and init[0].type == "function": init[0].parenthesized = True if isFirst: node.parent.insert(nodePos, semicolon) else: node.parent.insert(nodePos + 1, semicolon) retval = True else: Console.debug("Could not automatically remove unused variable %s at line %s without possible side-effects" % (decl.name, decl.line)) else: node.remove(decl) retval = True if len(node) == 0: Console.debug("Removing empty 'var' block at line %s" % node.line) node.parent.remove(node) return retval