def main():
    usage = 'Usage: dex.py [options] [dex file(s)]'
    parser = optparse.OptionParser(usage=usage, description='A script that parses DEX files.')
    parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
    parser.add_option('-C', '--color', action='store_true', dest='color', help='Enable colorized output', default=False)
    parser.add_option('-a', '--all', action='store_true', dest='dump_all', help='Dump all DEX sections.', default=False)
    parser.add_option('-H', '--header', action='store_true', dest='dump_header', help='Dump the DEX file header.', default=False)
    parser.add_option('--map-list', action='store_true', dest='dump_map_list', help='Dump the DEX map list info.', default=False)
    parser.add_option('-s', '--strings', action='store_true', dest='dump_strings', help='Dump the DEX strings.', default=False)
    parser.add_option('-t', '--types', action='store_true', dest='dump_types', help='Dump the DEX types.', default=False)
    parser.add_option('-p', '--protos', action='store_true', dest='dump_protos', help='Dump the DEX protos.', default=False)
    parser.add_option('-f', '--fields', action='store_true', dest='dump_fields', help='Dump the DEX fields.', default=False)
    parser.add_option('-m', '--methods', action='store_true', dest='dump_methods', help='Dump the DEX methods.', default=False)
    parser.add_option('--method-handles', action='store_true', dest='dump_method_handles', help='Dump the DEX method handles.', default=False)
    parser.add_option('--classes', action='store_true', dest='dump_classes', help='Dump the DEX classes.', default=False)
    parser.add_option('--class', dest='class_filter', help=('Find a class by name. ' + 'Accepts `Lpath/to/Class;` or `path.to.Class`'), default=None)
    parser.add_option('--method', dest='method_filter', help='Find a method by name. Must be used with --class', default=None)
    parser.add_option('--call-sites', action='store_true', dest='dump_call_sites', help='Dump the DEX call sites.', default=False)
    parser.add_option('--code', action='store_true', dest='dump_code', help='Dump the DEX code in all class methods.', default=False)
    parser.add_option('--code-items', action='store_true', dest='dump_code_items', help='Dump the DEX code items.', default=False)
    parser.add_option('--code-duplication', action='store_true', dest='code_duplication', help='Dump any methods in the DEX file that have the same instructions.', default=False)
    parser.add_option('--debug', action='store_true', dest='debug', help='Dump the DEX debug info.', default=False)
    parser.add_option('-d', '--disassemble', action='store_true', dest='dump_disassembly', help='Dump the DEX code items instructions.', default=False)
    parser.add_option('--stats', action='store_true', dest='dump_stats', help='Dump the DEX opcode statistics.', default=False)
    parser.add_option('--check-encoding', action='store_true', dest='check_encoding', help='Verify opcodes are efficiently encoded.', default=False)
    parser.add_option('--new-encoding', action='store_true', dest='new_encoding', help='Report byte savings from potential new encodings.', default=False)
    parser.add_option('--proguard', dest='proguard', help='Specify a progard file to use for demangling.', default=None)
    (options, files) = parser.parse_args()
    total_code_bytes_inefficiently_encoded = 0
    total_debug_info_bytes_inefficiently_encoded = 0
    total_new_code_bytes_inefficiently_encoded = 0
    total_opcode_byte_size = 0
    total_file_size = 0
    op_name_to_size = {}
    string_counts = {}
    i = 0
    if (len(files) == 0):
        print('No input files. {}'.format(usage))
        return
    for (i, path) in enumerate(files):
        if (os.path.splitext(path)[1] == '.apk'):
            print('error: dex.py operates on dex files, please unpack your apk')
            return
        print(('Dex file: %s' % path))
        file_size = os.path.getsize(path)
        total_file_size += file_size
        dex = File(path, options.proguard)
        if options.class_filter:
            dex_class = dex.find_class(options.class_filter)
            if dex_class:
                if (options.method_filter is None):
                    dex_class.dump()
                for method in dex_class.get_methods():
                    method_name = method.get_name()
                    if options.method_filter:
                        if (options.method_filter != method_name):
                            continue
                    method.dump()
            else:
                print(('error: class definition not found for "%s"' % options.class_filter))
        if (options.dump_header or options.dump_all):
            dex.dump_header(options)
            print('')
        if (options.dump_map_list or options.dump_all):
            dex.dump_map_list(options)
        if (options.dump_strings or options.dump_all):
            dex.dump_string_ids(options)
        if (options.dump_types or options.dump_all):
            dex.dump_type_ids(options)
        if (options.dump_protos or options.dump_all):
            dex.dump_proto_ids(options)
        if (options.dump_fields or options.dump_all):
            dex.dump_field_ids(options)
        if (options.dump_methods or options.dump_all):
            dex.dump_method_ids(options)
        if (options.dump_classes or options.dump_all):
            dex.dump_class_defs(options)
        if (options.dump_call_sites or options.dump_all):
            dex.dump_call_site_ids(options)
        if (options.dump_method_handles or options.dump_all):
            dex.dump_method_handle_items(options)
        if (options.dump_code or options.debug or options.dump_all):
            dex.dump_code(options)
        if options.dump_code_items:
            dex.dump_code_items(options)
        if (options.dump_disassembly or options.dump_stats or options.check_encoding or options.new_encoding):
            if options.dump_stats:
                for string_item in dex.get_strings():
                    if (string_item.data not in string_counts):
                        string_counts[string_item.data] = 0
                    string_counts[string_item.data] += 1
            code_bytes_inefficiently_encoded = 0
            debug_info_bytes_inefficiently_encoded = 0
            new_code_bytes_inefficiently_encoded = 0
            file_opcodes_byte_size = 0
            classes = dex.get_classes()
            used_code_item_indexes = list()
            for cls in classes:
                methods = cls.get_methods()
                for method in methods:
                    if (options.dump_disassembly or options.debug):
                        method.dump(f=sys.stdout, dump_code=options.dump_disassembly, dump_debug_info=options.debug)
                    opcodes_bytes_size = method.get_code_byte_size()
                    file_opcodes_byte_size += opcodes_bytes_size
                    total_opcode_byte_size += opcodes_bytes_size
                    if (options.dump_stats or options.check_encoding or options.new_encoding):
                        for dex_inst in method.get_instructions():
                            if options.dump_stats:
                                op_name = dex_inst.get_name()
                                size = (dex_inst.get_num_code_units() * 2)
                                if (op_name not in op_name_to_size):
                                    op_name_to_size[op_name] = 0
                                op_name_to_size[op_name] += size
                            if options.check_encoding:
                                code_bytes_inefficiently_encoded += dex_inst.check_encoding()
                            if options.new_encoding:
                                new_code_bytes_inefficiently_encoded += dex_inst.new_encoding()
                        if options.check_encoding:
                            code_item_idx = method.get_code_item_index()
                            if (code_item_idx >= 0):
                                used_code_item_indexes.append(code_item_idx)
                            debug_info = method.get_debug_info()
                            if debug_info:
                                debug_info_bytes_inefficiently_encoded += method.check_debug_info_encoding()
            if options.check_encoding:
                efficiently_encoded = True
                if (code_bytes_inefficiently_encoded > 0):
                    efficiently_encoded = False
                    total_code_bytes_inefficiently_encoded += code_bytes_inefficiently_encoded
                    print_code_stats(code_bytes_inefficiently_encoded, file_opcodes_byte_size, file_size)
                if (debug_info_bytes_inefficiently_encoded > 0):
                    efficiently_encoded = False
                    total_debug_info_bytes_inefficiently_encoded += debug_info_bytes_inefficiently_encoded
                    print_debug_stats(debug_info_bytes_inefficiently_encoded, file_size)
                used_code_item_indexes.sort()
                prev_ci_idx = 0
                for ci_idx in used_code_item_indexes:
                    if (ci_idx != prev_ci_idx):
                        efficiently_encoded = False
                        for idx in range((prev_ci_idx + 1), ci_idx):
                            print(('code_item[%u] is not used and its code_item can be removed' % idx))
                    prev_ci_idx = ci_idx
                if efficiently_encoded:
                    print('file is efficiently encoded.')
            if options.new_encoding:
                if (new_code_bytes_inefficiently_encoded > 0):
                    total_new_code_bytes_inefficiently_encoded += new_code_bytes_inefficiently_encoded
                    print_encoding_stats(new_code_bytes_inefficiently_encoded, file_opcodes_byte_size, file_size)
                else:
                    print('file is efficiently encoded.')
        if options.code_duplication:
            dex.report_code_duplication()
    if options.dump_stats:
        duped_strings_byte_size = 0
        for s in string_counts:
            count = string_counts[s]
            if (count > 1):
                s_len = len(s)
                duped_strings_byte_size += (((count - 1) * s_len) + get_uleb128_byte_size(s_len))
        if (duped_strings_byte_size > 0):
            print(('%u bytes in duplicated strings across dex files.' % duped_strings_byte_size))
        print('BYTESIZE %AGE  OPCODE')
        print('======== ===== =================================')
        sorted_x = sorted(op_name_to_size.items(), key=operator.itemgetter(1))
        for (op_name, byte_size) in sorted_x:
            percentage = get_percentage(byte_size, total_opcode_byte_size)
            print(('%-8u %5.2f %s' % (byte_size, percentage, op_name)))
        print('-------- ----- ---------------------------------')
        print(('%-8u 100.0' % total_opcode_byte_size))
    if (i > 0):
        if options.check_encoding:
            if (total_code_bytes_inefficiently_encoded > 0):
                print_code_stats(total_code_bytes_inefficiently_encoded, total_opcode_byte_size, total_file_size)
            if (total_debug_info_bytes_inefficiently_encoded > 0):
                efficiently_encoded = False
                print_debug_stats(total_debug_info_bytes_inefficiently_encoded, total_file_size)
        if options.new_encoding:
            invoke_kind_percentage = get_percentage(can_use_new_encoding, (can_use_new_encoding + cant_use_new_encoding))
            print(('%u invoke-kind opcodes could use new encoding' % can_use_new_encoding), end='')
            print(('%u could not (%2.2f%%)' % (cant_use_new_encoding, invoke_kind_percentage)))
            if (total_new_code_bytes_inefficiently_encoded > 0):
                print_encoding_stats(total_new_code_bytes_inefficiently_encoded, total_opcode_byte_size, total_file_size)
def main():
    usage = 'Usage: dex.py [options] [dex file(s)]'
    parser = optparse.OptionParser(usage=usage, description='A script that parses DEX files.')
    parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
    parser.add_option('-C', '--color', action='store_true', dest='color', help='Enable colorized output', default=False)
    parser.add_option('-a', '--all', action='store_true', dest='dump_all', help='Dump all DEX sections.', default=False)
    parser.add_option('-H', '--header', action='store_true', dest='dump_header', help='Dump the DEX file header.', default=False)
    parser.add_option('--map-list', action='store_true', dest='dump_map_list', help='Dump the DEX map list info.', default=False)
    parser.add_option('-s', '--strings', action='store_true', dest='dump_strings', help='Dump the DEX strings.', default=False)
    parser.add_option('-t', '--types', action='store_true', dest='dump_types', help='Dump the DEX types.', default=False)
    parser.add_option('-p', '--protos', action='store_true', dest='dump_protos', help='Dump the DEX protos.', default=False)
    parser.add_option('-f', '--fields', action='store_true', dest='dump_fields', help='Dump the DEX fields.', default=False)
    parser.add_option('-m', '--methods', action='store_true', dest='dump_methods', help='Dump the DEX methods.', default=False)
    parser.add_option('--method-handles', action='store_true', dest='dump_method_handles', help='Dump the DEX method handles.', default=False)
    parser.add_option('--classes', action='store_true', dest='dump_classes', help='Dump the DEX classes.', default=False)
    parser.add_option('--class', dest='class_filter', help=('Find a class by name. ' + 'Accepts `Lpath/to/Class;` or `path.to.Class`'), default=None)
    parser.add_option('--method', dest='method_filter', help='Find a method by name. Must be used with --class', default=None)
    parser.add_option('--call-sites', action='store_true', dest='dump_call_sites', help='Dump the DEX call sites.', default=False)
    parser.add_option('--code', action='store_true', dest='dump_code', help='Dump the DEX code in all class methods.', default=False)
    parser.add_option('--code-items', action='store_true', dest='dump_code_items', help='Dump the DEX code items.', default=False)
    parser.add_option('--code-duplication', action='store_true', dest='code_duplication', help='Dump any methods in the DEX file that have the same instructions.', default=False)
    parser.add_option('--debug', action='store_true', dest='debug', help='Dump the DEX debug info.', default=False)
    parser.add_option('-d', '--disassemble', action='store_true', dest='dump_disassembly', help='Dump the DEX code items instructions.', default=False)
    parser.add_option('--stats', action='store_true', dest='dump_stats', help='Dump the DEX opcode statistics.', default=False)
    parser.add_option('--check-encoding', action='store_true', dest='check_encoding', help='Verify opcodes are efficiently encoded.', default=False)
    parser.add_option('--new-encoding', action='store_true', dest='new_encoding', help='Report byte savings from potential new encodings.', default=False)
    parser.add_option('--proguard', dest='proguard', help='Specify a progard file to use for demangling.', default=None)
    (options, files) = parser.parse_args()
    total_code_bytes_inefficiently_encoded = 0
    total_debug_info_bytes_inefficiently_encoded = 0
    total_new_code_bytes_inefficiently_encoded = 0
    total_opcode_byte_size = 0
    total_file_size = 0
    op_name_to_size = {}
    string_counts = {}
    i = 0
    if (len(files) == 0):
        print('No input files. {}'.format(usage))
        return
    for (i, path) in enumerate(files):
        if (os.path.splitext(path)[1] == '.apk'):
            print('error: dex.py operates on dex files, please unpack your apk')
            return
        print(('Dex file: %s' % path))
        file_size = os.path.getsize(path)
        total_file_size += file_size
        dex = File(path, options.proguard)
        if options.class_filter:
            dex_class = dex.find_class(options.class_filter)
            if dex_class:
                if (options.method_filter is None):
                    dex_class.dump()
                for method in dex_class.get_methods():
                    method_name = method.get_name()
                    if options.method_filter:
                        if (options.method_filter != method_name):
                            continue
                    method.dump()
            else:
                print(('error: class definition not found for "%s"' % options.class_filter))
        if (options.dump_header or options.dump_all):
            dex.dump_header(options)
            print()
        if (options.dump_map_list or options.dump_all):
            dex.dump_map_list(options)
        if (options.dump_strings or options.dump_all):
            dex.dump_string_ids(options)
        if (options.dump_types or options.dump_all):
            dex.dump_type_ids(options)
        if (options.dump_protos or options.dump_all):
            dex.dump_proto_ids(options)
        if (options.dump_fields or options.dump_all):
            dex.dump_field_ids(options)
        if (options.dump_methods or options.dump_all):
            dex.dump_method_ids(options)
        if (options.dump_classes or options.dump_all):
            dex.dump_class_defs(options)
        if (options.dump_call_sites or options.dump_all):
            dex.dump_call_site_ids(options)
        if (options.dump_method_handles or options.dump_all):
            dex.dump_method_handle_items(options)
        if (options.dump_code or options.debug or options.dump_all):
            dex.dump_code(options)
        if options.dump_code_items:
            dex.dump_code_items(options)
        if (options.dump_disassembly or options.dump_stats or options.check_encoding or options.new_encoding):
            if options.dump_stats:
                for string_item in dex.get_strings():
                    if (string_item.data not in string_counts):
                        string_counts[string_item.data] = 0
                    string_counts[string_item.data] += 1
            code_bytes_inefficiently_encoded = 0
            debug_info_bytes_inefficiently_encoded = 0
            new_code_bytes_inefficiently_encoded = 0
            file_opcodes_byte_size = 0
            classes = dex.get_classes()
            used_code_item_indexes = list()
            for cls in classes:
                methods = cls.get_methods()
                for method in methods:
                    if (options.dump_disassembly or options.debug):
                        method.dump(f=sys.stdout, dump_code=options.dump_disassembly, dump_debug_info=options.debug)
                    opcodes_bytes_size = method.get_code_byte_size()
                    file_opcodes_byte_size += opcodes_bytes_size
                    total_opcode_byte_size += opcodes_bytes_size
                    if (options.dump_stats or options.check_encoding or options.new_encoding):
                        for dex_inst in method.get_instructions():
                            if options.dump_stats:
                                op_name = dex_inst.get_name()
                                size = (dex_inst.get_num_code_units() * 2)
                                if (op_name not in op_name_to_size):
                                    op_name_to_size[op_name] = 0
                                op_name_to_size[op_name] += size
                            if options.check_encoding:
                                code_bytes_inefficiently_encoded += dex_inst.check_encoding()
                            if options.new_encoding:
                                new_code_bytes_inefficiently_encoded += dex_inst.new_encoding()
                        if options.check_encoding:
                            code_item_idx = method.get_code_item_index()
                            if (code_item_idx >= 0):
                                used_code_item_indexes.append(code_item_idx)
                            debug_info = method.get_debug_info()
                            if debug_info:
                                debug_info_bytes_inefficiently_encoded += method.check_debug_info_encoding()
            if options.check_encoding:
                efficiently_encoded = True
                if (code_bytes_inefficiently_encoded > 0):
                    efficiently_encoded = False
                    total_code_bytes_inefficiently_encoded += code_bytes_inefficiently_encoded
                    print_code_stats(code_bytes_inefficiently_encoded, file_opcodes_byte_size, file_size)
                if (debug_info_bytes_inefficiently_encoded > 0):
                    efficiently_encoded = False
                    total_debug_info_bytes_inefficiently_encoded += debug_info_bytes_inefficiently_encoded
                    print_debug_stats(debug_info_bytes_inefficiently_encoded, file_size)
                used_code_item_indexes.sort()
                prev_ci_idx = 0
                for ci_idx in used_code_item_indexes:
                    if (ci_idx != prev_ci_idx):
                        efficiently_encoded = False
                        for idx in range((prev_ci_idx + 1), ci_idx):
                            print(('code_item[%u] is not used and its code_item can be removed' % idx))
                    prev_ci_idx = ci_idx
                if efficiently_encoded:
                    print('file is efficiently encoded.')
            if options.new_encoding:
                if (new_code_bytes_inefficiently_encoded > 0):
                    total_new_code_bytes_inefficiently_encoded += new_code_bytes_inefficiently_encoded
                    print_encoding_stats(new_code_bytes_inefficiently_encoded, file_opcodes_byte_size, file_size)
                else:
                    print('file is efficiently encoded.')
        if options.code_duplication:
            dex.report_code_duplication()
    if options.dump_stats:
        duped_strings_byte_size = 0
        for s in string_counts:
            count = string_counts[s]
            if (count > 1):
                s_len = len(s)
                duped_strings_byte_size += (((count - 1) * s_len) + get_uleb128_byte_size(s_len))
        if (duped_strings_byte_size > 0):
            print(('%u bytes in duplicated strings across dex files.' % duped_strings_byte_size))
        print('BYTESIZE %AGE  OPCODE')
        print('======== ===== =================================')
        sorted_x = sorted(op_name_to_size.items(), key=operator.itemgetter(1))
        for (op_name, byte_size) in sorted_x:
            percentage = get_percentage(byte_size, total_opcode_byte_size)
            print(('%-8u %5.2f %s' % (byte_size, percentage, op_name)))
        print('-------- ----- ---------------------------------')
        print(('%-8u 100.0' % total_opcode_byte_size))
    if (i > 0):
        if options.check_encoding:
            if (total_code_bytes_inefficiently_encoded > 0):
                print_code_stats(total_code_bytes_inefficiently_encoded, total_opcode_byte_size, total_file_size)
            if (total_debug_info_bytes_inefficiently_encoded > 0):
                efficiently_encoded = False
                print_debug_stats(total_debug_info_bytes_inefficiently_encoded, total_file_size)
        if options.new_encoding:
            invoke_kind_percentage = get_percentage(can_use_new_encoding, (can_use_new_encoding + cant_use_new_encoding))
            print(('%u invoke-kind opcodes could use new encoding' % can_use_new_encoding), end=)
            print(('%u could not (%2.2f%%)' % (cant_use_new_encoding, invoke_kind_percentage)))
            if (total_new_code_bytes_inefficiently_encoded > 0):
                print_encoding_stats(total_new_code_bytes_inefficiently_encoded, total_opcode_byte_size, total_file_size)