diff options
Diffstat (limited to 'tools/deadcode.py')
-rwxr-xr-x | tools/deadcode.py | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/tools/deadcode.py b/tools/deadcode.py new file mode 100755 index 0000000..2ef8c70 --- /dev/null +++ b/tools/deadcode.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python + +import os +import re +import sys + +def SplitSections(buffer): + """Spin through the input buffer looking for section header lines. + When found, the name of the section is extracted. The entire contents + of that section is added to a result hashmap with the section name + as the key""" + + # Match lines like + # |section_name: + # capturing section_name + headerPattern = re.compile(r'^\s+\|([a-z _]+)\:$', re.MULTILINE) + + sections = {} + start = 0 + anchor = -1 + sectionName = '' + + while True: + # Look for a section header + result = headerPattern.search(buffer, start) + + # If there are no more, add a section from the last header to EOF + if result is None: + if anchor is not -1: + sections[sectionName] = buffer[anchor] + return sections + + # Add the lines from the last header, to this one to the sections + # map indexed by the section name + if anchor is not -1: + sections[sectionName] = buffer[anchor:result.start()] + + sectionName = result.group(1) + start = result.end() + anchor = start + + return sections + +def FindMethods(section): + """Spin through the 'method code index' section and extract all + method signatures. When found, they are added to a result list.""" + + # Match lines like: + # |[abcd] com/example/app/Class.method:(args)return + # capturing the method signature + methodPattern = re.compile(r'^\s+\|\[\w{4}\] (.*)$', re.MULTILINE) + + start = 0 + methods = [] + + while True: + # Look for a method name + result = methodPattern.search(section, start) + + if result is None: + return methods + + # Add the captured signature to the method list + methods.append(result.group(1)) + start = result.end() + +def CallsMethod(codes, method): + """Spin through all the input method signatures. For each one, return + whether or not there is method invokation line in the codes section that + lists the method as the target.""" + + start = 0 + + while True: + # Find the next reference to the method signature + match = codes.find(method, start) + + if match is -1: + break; + + # Find the beginning of the line the method reference is on + startOfLine = codes.rfind("\n", 0, match) + 1 + + # If the word 'invoke' comes between the beginning of the line + # and the method reference, then it is a call to that method rather + # than the beginning of the code section for that method. + if codes.find("invoke", startOfLine, match) is not -1: + return True + + start = match + len(method) + + return False + + + +def main(): + if len(sys.argv) is not 2 or not sys.argv[1].endswith(".jar"): + print "Usage:", sys.argv[0], "<filename.jar>" + sys.exit() + + command = 'dx --dex --dump-width=1000 --dump-to=-"" "%s"' % sys.argv[1] + + pipe = os.popen(command) + + # Read the whole dump file into memory + data = pipe.read() + sections = SplitSections(data) + + pipe.close() + del(data) + + methods = FindMethods(sections['method code index']) + codes = sections['codes'] + del(sections) + + print "Dead Methods:" + count = 0 + + for method in methods: + if not CallsMethod(codes, method): + print "\t", method + count += 1 + + if count is 0: + print "\tNone" + +if __name__ == '__main__': + main() |