view paraspace/tests/injection_test.py @ 142:50d09eba5166

Fix the issue of getting negative indices for _DEX_ClassData. - It caused by calling _optimize_classdata() at end of restore_dependencies() while the values cames from depend_idx_rel.get_value(). The values had been optimized. - Solution is to get value from <child>.data_idx while unlinking.
author Thinker K.F. Li <thinker@codemud.net>
date Mon, 15 Aug 2011 10:04:12 +0800
parents 90690a001172
children c56c7cf32b88
line wrap: on
line source

from paraspace import dexfile
import os

def _install_dexfile_4_deptracker():
    global dexfile
    import imp, sys
    from paraspace import dex_deptracker
    from paraspace import injection
    
    try:
        new_dexfile = imp.load_compiled('dexfile', dexfile.__file__)
    except ImportError:
        new_dexfile = imp.load_source('dexfile', dexfile.__file__)
        pass
    dex_deptracker.dexfile = new_dexfile
    dexfile = new_dexfile
    dex_deptracker._nest_types = (dexfile.array, dexfile.cond, dexfile.switch)

    injection.dex_type_2_array_attr_map = \
        injection._saved_dex_type_2_array_attr_map
    
    sys.modules['paraspace.dexfile'] = new_dexfile
    pass


def _find_map(dex, map_type):
    for map in dex.maps.items.items:
        if map.type == map_type:
            return map
        pass
    pass


def inject_fakefile_to_helloworld_test():
    from paraspace.dex_deptracker import prepare_dep_decls
    from paraspace.injection import dexfile_insert_class
    
    _install_dexfile_4_deptracker()
    
    all_dep_decls = prepare_dep_decls()
    
    srcdir = os.path.dirname(__file__)
    srcroot = os.path.join(srcdir, '..', '..')
    
    helloworld_fn = os.path.join(srcroot, 'data', 'helloworld.dex')
    helloworld_dex = dexfile.DEXFile.open(helloworld_fn)

    classdef_map = _find_map(helloworld_dex, 0x0006)
    saved_classdef_map_sz = classdef_map.size
    saved_methodids_sz = len(helloworld_dex.methodIds.items)
    
    codeitems_map = _find_map(helloworld_dex, 0x2001)
    saved_codeitems_sz = codeitems_map.size

    helloworld_linked = \
        dexfile.DEXFile_linked.build_dependencies(helloworld_dex,
                                                  all_dep_decls)

    fakefile_fn = os.path.join(srcroot, 'data', 'fakefile.dex')
    fakefile_dex = dexfile.DEXFile.open(fakefile_fn)
    fakefile_linked = \
        dexfile.DEXFile_linked. \
        build_dependencies(fakefile_dex, all_dep_decls)
    
    fakefile_def = fakefile_linked. \
        find_class_name('Lcom/codemud/fakefile/fakefile;')

    fakefile_data = fakefile_def.classDataOffRef.value
    assert len(fakefile_data.directMethods.items) == 1
    assert len(fakefile_data.virtualMethods.items) == 0
    fakefile_dataheader = fakefile_data.header
    assert fakefile_dataheader.directMethodsSize == 1
    assert fakefile_dataheader.virtualMethodsSize == 0

    clone = dexfile_insert_class(helloworld_linked, fakefile_def)
    assert clone
    assert clone != fakefile_def

    helloworld_unlinked = helloworld_linked.get_unlinked()
    assert helloworld_unlinked

    # map size for classdef must be increased by 1
    classdef_map = _find_map(helloworld_unlinked, 0x0006)
    assert classdef_map.size == saved_classdef_map_sz + 1

    classdata_map = _find_map(helloworld_unlinked, 0x2000)
    assert classdata_map.size == classdef_map.size

    # Check strings
    strdatas = helloworld_unlinked.stringDataItems.items
    strs = sorted([strdata.data.data for strdata in strdatas])
    assert len(strs) == len(set(strs)) # uniquely
    assert 'Lcom/codemud/fakefile/fakefile;' in strs

    # Check Method List
    methodids_map = _find_map(helloworld_unlinked, 0x0005) # method ids
    assert methodids_map.size == len(helloworld_unlinked.methodIds.items)
    assert methodids_map.size == saved_methodids_sz + 1

    # Check Code item List
    codeitems_map = _find_map(helloworld_unlinked, 0x2001)
    assert codeitems_map.size == saved_codeitems_sz + 1
    pass


def redirect_types_test():
    from paraspace.dex_deptracker import prepare_dep_decls
    from paraspace.injection import dexfile_insert_class
    from paraspace.injection import dexfile_redirect_types
    from paraspace.injection import make_methodidxs_redir_map
    from paraspace import dalvik_opcodes
    
    _install_dexfile_4_deptracker()
    
    all_dep_decls = prepare_dep_decls()
    
    srcdir = os.path.dirname(__file__)
    srcroot = os.path.join(srcdir, '..', '..')
    
    helloworld_fn = os.path.join(srcroot, 'data', 'helloworld.dex')
    helloworld_dex = dexfile.DEXFile.open(helloworld_fn)

    classdef_map = _find_map(helloworld_dex, 0x0006)
    saved_classdef_map_sz = classdef_map.size
    saved_methodids_sz = len(helloworld_dex.methodIds.items)
    
    codeitems_map = _find_map(helloworld_dex, 0x2001)
    saved_codeitems_sz = codeitems_map.size

    helloworld_linked = \
        dexfile.DEXFile_linked.build_dependencies(helloworld_dex,
                                                  all_dep_decls)

    fakefile_fn = os.path.join(srcroot, 'data', 'fakefile.dex')
    fakefile_dex = dexfile.DEXFile.open(fakefile_fn)
    fakefile_linked = \
        dexfile.DEXFile_linked. \
        build_dependencies(fakefile_dex, all_dep_decls)
    
    fakefile_def = \
        fakefile_linked.find_class_name('Lcom/codemud/fakefile/fakefile;')
    clone = dexfile_insert_class(helloworld_linked, fakefile_def)

    init_method = fakefile_linked.find_method_name('<init>', fakefile_def)
    init_protoid = init_method.methodIdx.protoIdx

    File_typeid = helloworld_linked.find_typeid_name('Ljava/io/File;')
    assert File_typeid
    File_typeidx = helloworld_linked.get_idx_typeid(File_typeid)

    clone_typeidx = helloworld_linked.get_idx_typeid(clone.classIdx)

    types_redir = {File_typeidx: clone_typeidx}
    excludes = set([clone_typeidx])
    
    fakefile_data = clone.classDataOffRef.value
    fakefile_methods = fakefile_data.directMethods.items
    fakefile_codes = [method.codeOffRef.value for method in fakefile_methods]
    
    methods_redir = make_methodidxs_redir_map(helloworld_linked,
                                              helloworld_linked,
                                              types_redir)
    dexfile_redirect_types(helloworld_linked, types_redir,
                           methods_redir, excludes)

    for code in helloworld_linked.codeItems.items:
        op_vectors = dalvik_opcodes.decode_insn_blk(code.insns.data)
        for opcode, args in op_vectors:
            if opcode == dalvik_opcodes.all_opcodes.OP_NEW_INSTANCE:
                assert args[1] != File_typeidx
                pass
            elif opcode == dalvik_opcodes.all_opcodes.OP_INVOKE_DIRECT:
                methodid = helloworld_linked.find_methodid_idx(args[2])
                assert methodid.classIdx != File_typeid or \
                    code in fakefile_codes
                pass
            pass
        pass
    pass


def collect_types_in_method_test():
    from paraspace.dex_deptracker import prepare_dep_decls
    from paraspace.injection import collect_typeidxs_in_method
    from paraspace.injection import collect_typeidxs_mentioned_by_class
    from paraspace.dexfile import DEXFile_linked
    
    _install_dexfile_4_deptracker()
    
    all_dep_decls = prepare_dep_decls()
    
    srcdir = os.path.dirname(__file__)
    srcroot = os.path.join(srcdir, '..', '..')
    
    fakefile_fn = os.path.join(srcroot, 'data', 'fakefile.dex')
    fakefile_dex = dexfile.DEXFile.open(fakefile_fn)
    fakefile_linked = \
        dexfile.DEXFile_linked. \
        build_dependencies(fakefile_dex, all_dep_decls)
    
    fakefile_def = \
        fakefile_linked.find_class_name('Lcom/codemud/fakefile/fakefile;')

    init_method = fakefile_linked.find_method_name('<init>', fakefile_def)

    typeidxs = collect_typeidxs_in_method(fakefile_linked, init_method)
    assert len(typeidxs) == 1

    typeid = fakefile_linked.find_typeid_idx(typeidxs[0])
    typeid_name = DEXFile_linked.get_typeid_name(typeid)
    assert typeid_name == 'Ljava/io/File;'
    
    typeidxs = collect_typeidxs_mentioned_by_class(fakefile_linked,
                                                   fakefile_def)
    assert len(typeidxs) == 1

    typeid = fakefile_linked.find_typeid_idx(typeidxs[0])
    typeid_name = DEXFile_linked.get_typeid_name(typeid)
    assert typeid_name == 'Ljava/io/File;'
    pass


def dexfile_insert_classdefs_relative_test():
    from paraspace.dex_deptracker import prepare_dep_decls
    from paraspace.injection import dexfile_insert_classdefs_relative
    
    _install_dexfile_4_deptracker()
    
    all_dep_decls = prepare_dep_decls()
    
    srcdir = os.path.dirname(__file__)
    srcroot = os.path.join(srcdir, '..', '..')
    
    helloworld_fn = os.path.join(srcroot, 'data', 'helloworld.dex')
    helloworld_dex = dexfile.DEXFile.open(helloworld_fn)

    classdef_map = _find_map(helloworld_dex, 0x0006)
    saved_classdef_map_sz = classdef_map.size
    saved_methodids_sz = len(helloworld_dex.methodIds.items)
    
    codeitems_map = _find_map(helloworld_dex, 0x2001)
    saved_codeitems_sz = codeitems_map.size

    helloworld_linked = \
        dexfile.DEXFile_linked.build_dependencies(helloworld_dex,
                                                  all_dep_decls)

    fakefile_fn = os.path.join(srcroot, 'data', 'fakefile.dex')
    fakefile_dex = dexfile.DEXFile.open(fakefile_fn)
    fakefile_linked = \
        dexfile.DEXFile_linked. \
        build_dependencies(fakefile_dex, all_dep_decls)
    
    fakefile_def = fakefile_linked. \
        find_class_name('Lcom/codemud/fakefile/fakefile;')

    fakefile_data = fakefile_def.classDataOffRef.value
    assert len(fakefile_data.directMethods.items) == 1
    assert len(fakefile_data.virtualMethods.items) == 0
    fakefile_dataheader = fakefile_data.header
    assert fakefile_dataheader.directMethodsSize == 1
    assert fakefile_dataheader.virtualMethodsSize == 0

    classdefs, typeids = dexfile_insert_classdefs_relative(helloworld_linked,
                                                           fakefile_linked,
                                                           [fakefile_def])
    assert classdefs
    assert len(classdefs) == 1
    assert classdefs[0] != fakefile_def

    helloworld_unlinked = helloworld_linked.get_unlinked()
    assert helloworld_unlinked

    # map size for classdef must be increased by 1
    classdef_map = _find_map(helloworld_unlinked, 0x0006)
    assert classdef_map.size == saved_classdef_map_sz + 1

    classdata_map = _find_map(helloworld_unlinked, 0x2000)
    assert classdata_map.size == classdef_map.size

    # Check strings
    strdatas = helloworld_unlinked.stringDataItems.items
    strs = sorted([strdata.data.data for strdata in strdatas])
    assert len(strs) == len(set(strs)) # uniquely
    assert 'Lcom/codemud/fakefile/fakefile;' in strs

    # Check Method List
    methodids_map = _find_map(helloworld_unlinked, 0x0005) # method ids
    assert methodids_map.size == len(helloworld_unlinked.methodIds.items)
    assert methodids_map.size == saved_methodids_sz + 1

    # Check Code item List
    codeitems_map = _find_map(helloworld_unlinked, 0x2001)
    assert codeitems_map.size == saved_codeitems_sz + 1
    pass


def inject_classdefs_test():
    from paraspace.dex_deptracker import prepare_dep_decls
    from paraspace.injection import inject_classdefs
    from paraspace.injection import make_methodidxs_redir_map
    from paraspace.injection import dexfile_redirect_types
    from paraspace import dalvik_opcodes
    from paraspace.dexfile import DEXFile_linked
    
    _install_dexfile_4_deptracker()
    
    all_dep_decls = prepare_dep_decls()
    
    srcdir = os.path.dirname(__file__)
    srcroot = os.path.join(srcdir, '..', '..')
    
    helloworld_fn = os.path.join(srcroot, 'data', 'helloworld.dex')
    helloworld_dex = dexfile.DEXFile.open(helloworld_fn)

    classdef_map = _find_map(helloworld_dex, 0x0006)
    saved_classdef_map_sz = classdef_map.size
    saved_methodids_sz = len(helloworld_dex.methodIds.items)
    
    codeitems_map = _find_map(helloworld_dex, 0x2001)
    saved_codeitems_sz = codeitems_map.size

    helloworld_linked = \
        dexfile.DEXFile_linked.build_dependencies(helloworld_dex,
                                                  all_dep_decls)

    fakefile_fn = os.path.join(srcroot, 'data', 'fakefile.dex')
    fakefile_dex = dexfile.DEXFile.open(fakefile_fn)
    fakefile_linked = \
        dexfile.DEXFile_linked. \
        build_dependencies(fakefile_dex, all_dep_decls)
    
    fakefile_def = \
        fakefile_linked.find_class_name('Lcom/codemud/fakefile/fakefile;')
    clonings = inject_classdefs(helloworld_linked, fakefile_linked,
                                [fakefile_def])
    assert clonings
    assert len(clonings) == 1

    fakefile_cloning = clonings[0]
    method = helloworld_linked.find_method_name('<init>', fakefile_cloning)
    assert isinstance(method, dexfile._DEX_Method)

    V_typeid = helloworld_linked.find_typeid_name('V')
    file_typeid = helloworld_linked.find_typeid_name('Ljava/io/File;')
    file_typeidx = helloworld_linked.get_idx_typeid(file_typeid)
    str_typeid = helloworld_linked.find_typeid_name('Ljava/lang/String;')
    protoid = DEXFile_linked.make_protoid(V_typeid,
                                          (file_typeid, str_typeid))
    initid = helloworld_linked.find_methodid_name_proto('<init>',
                                                        protoid,
                                                        file_typeid)
    initidx = helloworld_linked.get_idx_methodid(initid)

    insns_block = DEXFile_linked.get_code_block_method(method)
    opvectors = dalvik_opcodes.decode_insn_blk(insns_block)
    assert len(opvectors) == 2
    opvector = opvectors[0]
    args = opvector[1]
    assert args[2] == initidx

    fakefile_typeidx = \
        helloworld_linked.get_idx_typeid(fakefile_cloning.classIdx)
    typeidxs_redir_map = {file_typeidx: fakefile_typeidx}
    methodidxs_redir_map = \
        make_methodidxs_redir_map(helloworld_linked, \
                                      helloworld_linked, \
                                      typeidxs_redir_map)
    dexfile_redirect_types(helloworld_linked, typeidxs_redir_map,
                           methodidxs_redir_map, excludes=[fakefile_typeidx])

    cloning_initdef = \
        helloworld_linked.find_method_name_proto('<init>', \
                                                     protoid, \
                                                     fakefile_cloning)
    fakefile_codes = [cloning_initdef.codeOffRef.value]
    for code in helloworld_linked.codeItems.items:
        op_vectors = dalvik_opcodes.decode_insn_blk(code.insns.data)
        for opcode, args in op_vectors:
            if opcode == dalvik_opcodes.all_opcodes.OP_NEW_INSTANCE:
                assert args[1] != file_typeidx
                pass
            elif opcode == dalvik_opcodes.all_opcodes.OP_INVOKE_DIRECT:
                methodid = helloworld_linked.find_methodid_idx(args[2])
                assert methodid.classIdx != file_typeid or \
                    code in fakefile_codes
                pass
            pass
        pass
    pass


## \brief Test case for error of verifing data map.
#
# The output of inject_redir.py is not accepted by dalvik with following
# error messages.
# \pre
#   E/dalvikvm(  302): Unexpected data map entry @ 0x930: expected 1001, \
#   found 2006
#   E/dalvikvm(  302): Trouble with item 0 @ offset 0x25c
#   E/dalvikvm(  302): Cross-item verify of section type 0003 failed
#   E/dalvikvm(  302): ERROR: Byte swap + verify failed
#   E/dalvikvm(  302): Optimization failed
#
# The data map printed by paraspace.dexfile is
# \pre
#        0x0000(kDexTypeHeaderItem) size=1 offset=0x00000000
#        0x0001(kDexTypeStringIdItem) size=92 offset=0x00000070
#        0x0002(kDexTypeTypeIdItem) size=31 offset=0x000001e0
#        0x0003(kDexTypeProtoIdItem) size=18 offset=0x0000025c
#        0x0004(kDexTypeFieldIdItem) size=7 offset=0x00000334
#        0x0005(kDexTypeMethodIdItem) size=36 offset=0x0000036c
#        0x0006(kDexTypeClassDefItem) size=10 offset=0x0000048c
#        0x1003(kDexTypeAnnotationSetItem) size=7 offset=0x000005cc
#        0x2001(kDexTypeCodeItem) size=17 offset=0x0000061c
#        0x2006(kDexTypeAnnotationsDirectoryItem) size=7 offset=0x00000910
#        0x1001(kDexTypeTypeList) size=12 offset=0x00000980
#        0x2002(kDexTypeStringDataItem) size=92 offset=0x000009e2
#        0x2003(kDexTypeDebugInfoItem) size=17 offset=0x00000f05
#        0x2004(kDexTypeAnnotationItem) size=9 offset=0x00000fc1
#        0x2005(kDexTypeEncodedArrayItem) size=5 offset=0x00001012
#        0x2000(kDexTypeClassDataItem) size=10 offset=0x0000102d
#        0x1000(kDexTypeMapList) size=1 offset=0x000010c0
#
# It is possible restore_dependencies() does not following \ref ref
# object to set offset value on referenced attribute.
#
# It is also possible dexfile_insert_classdefs_relative() does not
# put injected objects into lists properly.
#
def map_verify_error_test():
    from paraspace.dex_deptracker import prepare_dep_decls
    from paraspace.injection import inject_redir
    
    _install_dexfile_4_deptracker()
    
    all_dep_decls = prepare_dep_decls()
    
    srcdir = os.path.dirname(__file__)
    datapath = os.path.join(srcdir, '..', '..', 'data')

    fake_path = os.path.join(datapath, 'fakefile.dex')
    hello_path = os.path.join(datapath, 'helloworld.dex')
    
    fake_dex = dexfile.DEXFile.open(fake_path)
    fake_linked = dexfile.DEXFile_linked.build_dependencies(fake_dex,
                                                            all_dep_decls)

    hello_dex = dexfile.DEXFile.open(hello_path)
    hello_linked = dexfile.DEXFile_linked.build_dependencies(hello_dex,
                                                             all_dep_decls)

    inject_redir(fake_linked, 'Lcom/codemud/fakefile/fakefile;',
                 hello_linked, 'Ljava/io/File;', all_dep_decls)

    for idx, item in enumerate(hello_linked.maps.items.items):
        if item.type == dexfile._DEX_MapItem.name_to_types['kDexTypeTypeList']:
            break
        pass
    else:
        assert False
        pass
    typelist_start = item.offset
    next_item = hello_linked.maps.items.items[idx + 1]
    typelist_stop = next_item.offset
    
    for idx, protoid in enumerate(hello_linked.protoIds.items):
        off = protoid.parametersOff
        try:
            if off != 0:
                assert off >= typelist_start
                assert off < typelist_stop
                pass
            pass
        except:
            print 'ERROR: protoid idx@%d %s 0x%x' % (idx, repr(protoid), off)
            print 'start 0x%x stop 0x%x' % (typelist_start, typelist_stop)
            raise
        pass

    for idx, classdef in enumerate(hello_linked.classDefs.items):
        off = classdef.interfacesOff
        try:
            if off != 0:
                assert off >= typelist_start
                assert off < typelist_stop
                pass
            pass
        except:
            print 'ERROR: classdef idx@%d %s 0x%x' % (idx, repr(classdef), off)
            print 'start 0x%x stop 0x%x' % (typelist_start, typelist_stop)
            raise
        pass
    pass


## \brief Test case for protoid out of order.
#
# Dalvik complaints that output of inject_redir() is out-of-order in
# DEXFile.protoIds list.
# \pre
#   E/dalvikvm(  298): Out-of-order proto_id arguments
#   E/dalvikvm(  298): Trouble with item 14 @ offset 0x304
#   E/dalvikvm(  298): Cross-item verify of section type 0003 failed
#   E/dalvikvm(  298): ERROR: Byte swap + verify failed
#   E/dalvikvm(  298): Optimization failed
#
def proto_id_order_verify_test():
    from paraspace.dex_deptracker import prepare_dep_decls
    from paraspace.dex_deptracker import dex_sort_sorted_arrays
    from paraspace.injection import inject_redir_no_restore
    
    _install_dexfile_4_deptracker()
    
    all_dep_decls = prepare_dep_decls()
    
    srcdir = os.path.dirname(__file__)
    datapath = os.path.join(srcdir, '..', '..', 'data')

    fake_path = os.path.join(datapath, 'fakefile.dex')
    hello_path = os.path.join(datapath, 'helloworld.dex')
    
    fake_dex = dexfile.DEXFile.open(fake_path)
    fake_linked = dexfile.DEXFile_linked.build_dependencies(fake_dex,
                                                            all_dep_decls)

    hello_dex = dexfile.DEXFile.open(hello_path)
    hello_linked = dexfile.DEXFile_linked.build_dependencies(hello_dex,
                                                             all_dep_decls)

    inject_redir_no_restore(fake_linked, 'Lcom/codemud/fakefile/fakefile;',
                            hello_linked, 'Ljava/io/File;', all_dep_decls)

    dex_sort_sorted_arrays(hello_linked)

    pairs = map(None, hello_linked.protoIds.items[:-1],
                hello_linked.protoIds.items[1:])
    for idx, (protoid0, protoid1) in enumerate(pairs):
        try:
            assert protoid0 < protoid1
        except:
            print 'ERROR: protoid %d is out of order' % (idx + 1)
            raise
        pass
    pass

## \brief Test case for issue of bad offset list in _DEX_AnnotationSetItem.
#
# Values of _DEX_AnnotationSetItem.annotationOffs is corrupted after
# injection.
# \pre
#    E/dalvikvm(  300): No data map entry found @ 0xf31; expected 2004
#    E/dalvikvm(  300): Trouble with item 0 @ offset 0x5c0
#    E/dalvikvm(  300): Cross-item verify of section type 1003 failed
#    E/dalvikvm(  300): ERROR: Byte swap + verify failed
#    E/dalvikvm(  300): Optimization failed
#
# It must be the issue that back_type of an array can not be a \ref depend.
#
def annotation_set_items_verify_test():
    from paraspace.dex_deptracker import prepare_dep_decls
    from paraspace.dex_deptracker import dex_sort_sorted_arrays
    from paraspace.injection import inject_redir_no_restore
    
    _install_dexfile_4_deptracker()
    
    all_dep_decls = prepare_dep_decls()
    
    srcdir = os.path.dirname(__file__)
    datapath = os.path.join(srcdir, '..', '..', 'data')

    fake_path = os.path.join(datapath, 'fakefile.dex')
    hello_path = os.path.join(datapath, 'helloworld.dex')
    
    fake_dex = dexfile.DEXFile.open(fake_path)
    fake_linked = dexfile.DEXFile_linked.build_dependencies(fake_dex,
                                                            all_dep_decls)

    hello_dex = dexfile.DEXFile.open(hello_path)
    hello_linked = dexfile.DEXFile_linked.build_dependencies(hello_dex,
                                                             all_dep_decls)

    inject_redir_no_restore(fake_linked, 'Lcom/codemud/fakefile/fakefile;',
                            hello_linked, 'Ljava/io/File;', all_dep_decls)

    dex_sort_sorted_arrays(hello_linked)

    for setitem in hello_linked.annotationSetItems.items:
        for anno in setitem.annotationOffs.items:
            assert anno.offset in hello_linked.annotationItems.items
            pass
        pass
    pass


## \brief Test case for dalvik complaining fail to verify code.
#
#    W/dalvikvm(  317): VFY: expected 1 args, found more (I)
#    W/dalvikvm(  317): VFY:  rejecting call to Landroid/app/Activity;\
#    .setContentView (I)V
#    W/dalvikvm(  317): VFY:  rejecting opcode 0x6e at 0x0006
#    W/dalvikvm(  317): VFY:  rejected Lcom/codemud/helloworld/helloworld;\
#    .onClick (Landroid/view/View;)V
#    W/dalvikvm(  317): Verifier rejected class Lcom/codemud/helloworld/\
#    helloworld;
#    W/dalvikvm(  317): Class init failed in newInstance call (Lcom/codemud/\
#    helloworl
#
# DEXFile.methodIds is a sorted array.  The indices of _DEX_MethodIds would
# be changed after injection and sorting, dex_sort_sorted_arrays().
# It means indices in code blocks are invalid.  So, code blocks must be
# rewrote with valid indices.
#
def code_verify_test():
    from paraspace.dex_deptracker import prepare_dep_decls
    from paraspace.injection import dex_sort_sorted_arrays_consistent
    from paraspace.injection import inject_redir_no_restore
    from paraspace.dalvik_opcodes import decode_insn_blk
    
    _install_dexfile_4_deptracker()
    
    all_dep_decls = prepare_dep_decls()
    
    srcdir = os.path.dirname(__file__)
    datapath = os.path.join(srcdir, '..', '..', 'data')

    fake_path = os.path.join(datapath, 'fakefile.dex')
    hello_path = os.path.join(datapath, 'helloworld.dex')
    
    fake_dex = dexfile.DEXFile.open(fake_path)
    fake_linked = dexfile.DEXFile_linked.build_dependencies(fake_dex,
                                                            all_dep_decls)

    hello_dex = dexfile.DEXFile.open(hello_path)
    hello_linked = dexfile.DEXFile_linked.build_dependencies(hello_dex,
                                                             all_dep_decls)

    helloworld_typeid = \
        hello_linked.find_typeid_name('Lcom/codemud/helloworld/helloworld;')
    helloworld_methodids = \
        hello_linked.find_methodids_typeid(helloworld_typeid)
    for methodid in helloworld_methodids:
        methodname = hello_linked.get_methodid_name(methodid)
        if methodname == 'onClick':
            methodidx = hello_linked.get_idx_methodid(methodid)
            method = hello_linked.find_method_idx(methodidx)
            blk = dexfile.DEXFile_linked.get_code_block_method(method)
            opvectors = decode_insn_blk(blk)
            methodidx = opvectors[4][1][2]
            saved_methodid0 = hello_linked.find_methodid_idx(methodidx)
            methodidx = opvectors[12][1][1]
            saved_methodid1 = hello_linked.find_methodid_idx(methodidx)
        elif methodname == 'write_file':
            assert methodid in hello_linked.methodIds.items
            methodidx = hello_linked.get_idx_methodid(methodid)
            method = hello_linked.find_method_idx(methodidx)
            blk = dexfile.DEXFile_linked.get_code_block_method(method)
            opvectors = decode_insn_blk(blk)
            pass
        pass

    inject_redir_no_restore(fake_linked, 'Lcom/codemud/fakefile/fakefile;',
                            hello_linked, 'Ljava/io/File;', all_dep_decls)
    dex_sort_sorted_arrays_consistent(hello_linked)

    for methodid in hello_linked.methodIds.items:
        methodname = hello_linked.get_methodid_name(methodid)
        if methodname == 'setContentView':
            protoid = methodid.protoIdx
            sig = hello_linked.dump_protoid(protoid)
            assert sig == '(I) --> V'
            pass
        pass

    helloworld_typeid = \
        hello_linked.find_typeid_name('Lcom/codemud/helloworld/helloworld;')
    helloworld_methodids = \
        hello_linked.find_methodids_typeid(helloworld_typeid)
    for methodid in helloworld_methodids:
        methodname = hello_linked.get_methodid_name(methodid)
        if methodname == 'onClick':
            methodidx = hello_linked.get_idx_methodid(methodid)
            method = hello_linked.find_method_idx(methodidx)
            blk = dexfile.DEXFile_linked.get_code_block_method(method)
            opvectors = decode_insn_blk(blk)
            methodidx = opvectors[4][1][2]
            new_methodid0 = hello_linked.find_methodid_idx(methodidx)
            assert new_methodid0 is saved_methodid0
            methodidx = opvectors[12][1][1]
            new_methodid1 = hello_linked.find_methodid_idx(methodidx)
            assert saved_methodid1 in hello_linked.methodIds.items
            assert new_methodid1 in hello_linked.methodIds.items
            assert new_methodid1 is saved_methodid1
            pass
        elif methodname == 'write_file':
            assert methodid in hello_linked.methodIds.items
            methodidx = hello_linked.get_idx_methodid(methodid)
            method = hello_linked.find_method_idx(methodidx)
            blk = dexfile.DEXFile_linked.get_code_block_method(method)
            opvectors = decode_insn_blk(blk)
            pass
        pass
    pass


## \brief Test case for checking methods of classdatas are consistent.
#
# Becase space optimization, DEX ecnode value for method indices
# in \ref _DEX_Method.  This test case make sure build_dependencies()
# decode indices correctly.
#
# \see _deoptimize_classdata
#
def class_methods_consistent_test():
    from paraspace.dex_deptracker import prepare_dep_decls
    
    _install_dexfile_4_deptracker()
    
    all_dep_decls = prepare_dep_decls()
    
    srcdir = os.path.dirname(__file__)
    datapath = os.path.join(srcdir, '..', '..', 'data')

    hello_path = os.path.join(datapath, 'helloworld.dex')
    
    hello_dex = dexfile.DEXFile.open(hello_path)
    
    hello_linked = dexfile.DEXFile_linked.build_dependencies(hello_dex,
                                                             all_dep_decls)

    for classdata in hello_linked.classDatas.items:
        allmethods = classdata.directMethods.items + \
            classdata.virtualMethods.items
        if len(allmethods) < 2:
            continue

        firstmethod = allmethods[0]
        firstmethodid = firstmethod.methodIdx
        for idx, method in enumerate(allmethods[1:]):
            try:
                assert method.methodIdx.classIdx == firstmethodid.classIdx
            except:
                print 'ERROR: method %d' % (idx + 1)
                raise
            pass
        pass
    pass


## \brief Test case for getting negative indices in _DEX_ClassDAta.
#
# It, possibly, caused by appling _optimize_classdata() at end of
# restore_dependencies().  But, value was comes from
# depend_idx_rel.get_value().  It already deoptimize the value.
#
def negative_depend_idx_rel_test():
    from paraspace.dex_deptracker import prepare_dep_decls
    from paraspace.dex_deptracker import dex_sort_sorted_arrays
    from paraspace.injection import inject_redir
    
    _install_dexfile_4_deptracker()
    
    all_dep_decls = prepare_dep_decls()
    
    srcdir = os.path.dirname(__file__)
    datapath = os.path.join(srcdir, '..', '..', 'data')

    fake_path = os.path.join(datapath, 'fakefile.dex')
    hello_path = os.path.join(datapath, 'helloworld.dex')
    
    fake_dex = dexfile.DEXFile.open(fake_path)
    fake_linked = dexfile.DEXFile_linked.build_dependencies(fake_dex,
                                                            all_dep_decls)

    hello_dex = dexfile.DEXFile.open(hello_path)
    hello_linked = dexfile.DEXFile_linked.build_dependencies(hello_dex,
                                                             all_dep_decls)

    inject_redir(fake_linked, 'Lcom/codemud/fakefile/fakefile;',
                 hello_linked, 'Ljava/io/File;', all_dep_decls)

    print len(hello_linked.classDatas.items)
    hello_linked.to_str()
    pass