changeset 40:0c0a659187c2

Use _objs_asso to define association between two set of items. Derivation of _objs_asso define a rule to assocate elements from first set to another element in second set.
author Thinker K.F. Li <thinker@codemud.net>
date Sat, 18 Jun 2011 23:59:37 +0800
parents aa05cc7ccd0d
children c5cfc796af8b
files paraspace/dex_deptracker.py paraspace/dexfile.py paraspace/tests/dexfile_test.py
diffstat 3 files changed, 166 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/paraspace/dex_deptracker.py	Thu Jun 16 08:03:50 2011 +0800
+++ b/paraspace/dex_deptracker.py	Sat Jun 18 23:59:37 2011 +0800
@@ -157,15 +157,21 @@
 
 
 def _dex_tree_get_child(obj, child_name):
-    if isinstance(obj, list):
-        idx = int(child_name)
-        return obj[idx]
-
-    if isinstance(obj, dexfile.switch):
-        assert obj.map[eval(child_name)] == obj.child_type
-        return obj.value
-
-    return getattr(obj, child_name)
+    child_parts = child_name.split('.')
+    for child_part in child_parts:
+        if isinstance(obj, list):
+            idx = int(child_part)
+            obj = obj[idx]
+            continue
+        
+        if isinstance(obj, dexfile.switch):
+            assert obj.map[eval(child_part)] == obj.child_type
+            obj = obj.value
+            continue
+        
+        obj = getattr(obj, child_part)
+        pass
+    return obj
 
 
 def _dex_tree_set_child(obj, child_name, value):
@@ -330,17 +336,26 @@
 
     @staticmethod
     def find_depon(name_path, parents):
+        name_parts = name_path.split('.')
+        dex_types = _all_dex_types()
+        comp_type_name = name_parts[0]
+        comp_type = dex_types[comp_type_name]
+        
         rev_parents = list(parents)
         rev_parents.reverse()
         
         for parent in rev_parents:
+            if isinstance(parent, comp_type):
+                attr_name = '.'.join(name_parts[1:])
+                depon = _dex_tree_get_child(parent, attr_name)
+                return depon
+
             try:
                 rel_marker_info = parent.rel_marker_info
             except:
                 continue
             if name_path in rel_marker_info:
                 depons = rel_marker_info[name_path]
-                print parent, depons
                 assert len(depons) == 1
                 depon = depons[0]
                 return depon
@@ -361,12 +376,24 @@
         return array
     
     def link_prepare(self, obj, name_path, parents, markers_info):
+        try:
+            id_item_map = markers_info[name_path]
+        except KeyError:
+            id_item_map = []
+            markers_info[name_path] = id_item_map
+            pass
+        for idx, item in enumerate(obj.items):
+            id_item_map[idx] = item
+            pass
         pass
     pass
 
 
 def _install_offset_marker(name_path):
     obj, parent = _resolve_name_path(name_path)
+    while isinstance(parent, dexfile.ref):
+        obj, parent = _resolve_name_path(parent.target_path)
+        pass
     marker = _offset_marker(obj, name_path)
     name = name_path.split('.')[-1]
     _dex_tree_set_child(parent, name, marker)
@@ -375,6 +402,9 @@
 
 def _install_rel_offset_marker(name_path):
     obj, parent = _resolve_name_path(name_path)
+    while isinstance(parent, dexfile.ref):
+        obj, parent = _resolve_name_path(parent.target_path)
+        pass
     marker = _rel_offset_marker(obj, name_path)
     name = name_path.split('.')[-1]
     _dex_tree_set_child(parent, name, marker)
@@ -383,6 +413,9 @@
 
 def _install_uid_marker(name_path):
     obj, parent = _resolve_name_path(name_path)
+    while isinstance(parent, dexfile.ref):
+        obj, parent = _resolve_name_path(parent.target_path)
+        pass
     marker = _uid_marker(obj, name_path)
     name = name_path.split('.')[-1]
     _dex_tree_set_child(parent, name, marker)
@@ -391,6 +424,9 @@
 
 def _install_idx_marker(name_path):
     obj, parent = _resolve_name_path(name_path)
+    while isinstance(parent, dexfile.ref):
+        obj, parent = _resolve_name_path(parent.target_path)
+        pass
     marker = _idx_marker(obj, name_path)
     name = name_path.split('.')[-1]
     _dex_tree_set_child(parent, name, marker)
@@ -479,6 +515,23 @@
     pass
 
 
+def _build_associations(root_obj):
+    for obj, parents, name_path in \
+            _travel_dex_relocatable(root_obj):
+        if isinstance(obj, dexfile._objs_asso):
+            for parent in parents:
+                if isinstance(parent, dexfile.composite):
+                    break
+                pass
+            
+            left_elts = _dex_tree_get_child(parent, obj.left)
+            right_elts = _dex_tree_get_child(parent, obj.right)
+            obj.build_associations()
+            pass
+        pass
+    pass
+
+
 def _link_dependencies(root_obj, all_dep_decls):
     markers_info = {}
     depon_src_map = {}
@@ -520,13 +573,13 @@
             _dex_tree_set_child(parent, name, (depon1, depon2))
         elif dep_type == dexfile.depend_off:
             depon_name_path = dep[1]
-            depon = markers_info[depon_name_path]
+            depon = markers_info[depon_name_path][obj]
             parent = parents[-1]
             name = name_path.split('.')[-1]
             _dex_tree_set_child(parent, name, depon)
         elif dep_type == dexfile.depend_idx:
             depon_name_path = dep[1]
-            depon = markers_info[depon_name_path]
+            depon = markers_info[depon_name_path][obj]
             parent = parents[-1]
             name = name_path.split('.')[-1]
             _dex_tree_set_child(parent, name, depon)
--- a/paraspace/dexfile.py	Thu Jun 16 08:03:50 2011 +0800
+++ b/paraspace/dexfile.py	Sat Jun 18 23:59:37 2011 +0800
@@ -175,7 +175,7 @@
 class tap(_dex_type):
     @staticmethod
     def parse(parent, data, off):
-        pass
+        return tap()
 
     @staticmethod
     def sizeof(v):
@@ -329,6 +329,84 @@
     return o.__class__.sizeof(o)
 
 
+## \biref Associate objects from two set of objects.
+#
+class _objs_asso(_dex_type):
+    left = None
+    left_ref = None
+    right = None
+    right_ref = None
+    
+    ## \brief Update references for a element pair from left and right.
+    #
+    # This method must be called by derivation to associate a pair of
+    # elements.
+    #
+    def _update_refs(self, left_elt, right_elt):
+        lref = getattr(left_elt, self.left_ref)
+        if not isinstance(right_elt, lref.target_path):
+            raise TypeError, 'invalid target_path in left %s' % (repr(le))
+        
+        rref = getattr(right_elt, self.right_ref)
+        if not isinstance(left_elt, rref.target_path):
+            raise TypeError, 'invalid target_path in right %s' % (repr(re))
+        
+        new_lref = ref(lref.target_path)
+        new_lref.target = right_elt
+        setattr(left_elt, self.left_ref, new_lref)
+
+        new_rref = ref(rref.target_path)
+        new_rref.target = left_elt
+        setattr(right_elt, self.right_ref, new_rref)
+        pass
+    
+    ## \brief Assocate elements from left list to a respective right element.
+    #
+    # This method must be called before linking dependencies.
+    #
+    def build_associations(self, left, right):
+        raise NotImplementedError, 'build_associations is not implemented'
+    pass
+
+
+## \brief One to one association.
+#
+# Associate nth element from left sequence to nth element in right
+# sequence.
+#
+class one2one(_objs_asso):
+    def __init__(self, left, left_ref, right, right_ref):
+        self.left = left
+        self.left_ref = left_ref
+        self.right = right
+        self.right_ref = right_ref
+        pass
+
+    ## \brief Associate elements from left list to elements from right list
+    #
+    def build_associations(self, left, right):
+        assert len(left) == len(right)
+        for le, re in map(None, left, right):
+            self._update_refs(le, re)
+            pass
+        pass
+    pass
+
+
+## \brief Implicit reference to a target.
+#
+# It is a placeholder for storing relationship defined by an association.
+#
+class ref(_dex_type):
+    target_path = None
+    target = None
+
+    def __init__(self, target_path=None):
+        self.target_path = target_path
+        pass
+    pass
+
+
 class relocatable(_dex_type):
     data_size = None
 
@@ -832,11 +910,18 @@
 
 
 class _DEX_TypeList(composite):
-    padding = auto_align(2)     # 2 bits alignment
     num = uint32
     typeItems = array('num', uint16)
 
-    child_names = 'padding num typeItems'.split()
+    child_names = 'num typeItems'.split()
+    pass
+
+
+class _DEX_TypeList_align(composite):
+    padding = auto_align(2)     # 2 bits alignment
+    value = _DEX_TypeList
+    
+    child_names = 'padding value'.split()
     pass
 
 
@@ -844,7 +929,8 @@
     startAddr = uint32
     insnCount = uint16
     handlerOff = depend_off_rel('_DEX_Code.handlers_size',
-                                '_DEX_Catch')(uint16)
+                                '_DEX_Try.catch_ref.target')(uint16)
+    catch_ref = ref('_DEX_Catch')
 
     child_names = 'startAddr insnCount handlerOff'.split()
     pass
@@ -870,6 +956,7 @@
     handlers = array('count', _DEX_CatchHandler)
     catchAllHandler = cond((lambda parent, data, off: parent.catchesAll),
                            _DEX_CatchAllHandler)
+    try_ref = ref('_DEX_Try')
 
     child_names = 'size handlers catchAllHandler'.split()
 
@@ -903,6 +990,8 @@
     handlers_size = cond(_has_tries, uleb128)
     catch_handler_items = cond(_has_tries,
                                array('handlers_size.value', _DEX_Catch))
+    try_catch_asso = one2one('try_items.value.items', 'catch_ref',
+                             'catch_handler_items.value.items', 'try_ref')
 
     padding2 = auto_align(2)
 
@@ -1326,7 +1415,7 @@
     methodIds = array(None, _DEX_MethodId)
     classDefs = array(None, _DEX_ClassDef)
     classDatas = array(None, _DEX_ClassData)
-    typeLists = array(None, _DEX_TypeList)
+    typeLists = array(None, _DEX_TypeList_align)
     codeItems = array(None, _DEX_Code)
     annotationSetItems = array(None, _DEX_AnnotationSetItem)
     annotationsDirectoryItems = array(None, _DEX_AnnotationsDirectoryItem)
--- a/paraspace/tests/dexfile_test.py	Thu Jun 16 08:03:50 2011 +0800
+++ b/paraspace/tests/dexfile_test.py	Sat Jun 18 23:59:37 2011 +0800
@@ -35,7 +35,7 @@
     assert deps['_DEX_ClassDef.staticValuesOff'][0] == dexfile.depend_off
     assert deps['_DEX_ClassDef.staticValuesOff'][1] == '_DEX_EncodedArrayItem'
     assert deps['_DEX_Try.handlerOff'][0] == dexfile.depend_off_rel
-    assert deps['_DEX_Try.handlerOff'][1] == '_DEX_Catch'
+    assert deps['_DEX_Try.handlerOff'][1] == '_DEX_Try.catch_ref.target'
     assert deps['_DEX_Try.handlerOff'][2] == '_DEX_Code.handlers_size'
     pass
 
@@ -101,13 +101,12 @@
     srcroot = os.path.join(srcdir, '..', '..')
     testdatapath = os.path.join(srcroot, 'data', 'testdata1.dex')
     dex = dexfile.DEXFile.open(testdatapath)
-    dexroot = dex.typeLists.items[0]
+    dexroot = dex.typeLists.items[0].value
 
     itr = _travel_dex_relocatable(dexroot)
     pathes = [v[2] for v in itr]
-    assert len(pathes) == 6
+    assert len(pathes) == 5
     assert '_DEX_TypeList' in pathes
-    assert '_DEX_TypeList.padding' in pathes
     assert '_DEX_TypeList.num' in pathes
     assert '_DEX_TypeList.typeItems' in pathes
     assert '_DEX_TypeList.typeItems.items' in pathes
@@ -192,12 +191,14 @@
 
     _patch_dex_type_markers(all_dep_decls)
     assert isinstance(dexfile._DEX_TypeList, _offset_marker)
-    assert isinstance(dexfile.DEXFile.typeLists.child_type, _offset_marker)
+    assert isinstance(dexfile.DEXFile.typeLists.child_type.value,
+                      _offset_marker)
     pass
 
 
 def link_dependencies_test():
     from paraspace.dex_deptracker import collect_all_dep_decls
+    from paraspace.dex_deptracker import _build_associations
     from paraspace.dex_deptracker import _link_dependencies
     from paraspace.dex_deptracker import _install_markers, _idx_marker
     from paraspace.dex_deptracker import _offset_marker, _rel_offset_marker
@@ -215,6 +216,7 @@
     testdatapath = os.path.join(srcroot, 'data', 'testdata1.dex')
     dex = dexfile.DEXFile.open(testdatapath)
 
+    _build_associations(dex)
     _link_dependencies(dex, all_dep_decls)
     
     code_item = dex.codeItems.items[0]