| #!/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() |