changeset 88:bbe8d5cbe368

Clone objects with meta info
author Thinker K.F. Li <thinker@codemud.net>
date Sun, 24 Jul 2011 19:15:04 +0800
parents cd1ee85853f4
children 7a059ab408f0
files paraspace/dex_deptracker.py paraspace/injection.py
diffstat 2 files changed, 172 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/paraspace/dex_deptracker.py	Sat Jul 23 23:02:39 2011 +0800
+++ b/paraspace/dex_deptracker.py	Sun Jul 24 19:15:04 2011 +0800
@@ -680,6 +680,7 @@
     pass
 
 
+## \brief Link objects to their dependencies .
 def _link_dependencies(root_obj, all_dep_decls):
     markers_info = {}
     depon_src_map = {}
--- a/paraspace/injection.py	Sat Jul 23 23:02:39 2011 +0800
+++ b/paraspace/injection.py	Sun Jul 24 19:15:04 2011 +0800
@@ -1,47 +1,183 @@
 
-def _deep_travel(obj, cloner, adjuster, visit_log=None):
-    from paraspace.dexfile import _dex_type
+def _relocatable_children(obj):
+    from paraspace.dexfile import relocatable
     
-    if not visit_log:
-        visit_log = {}
-        pass
+    attr_value_pairs = [(attr, getattr(obj, attr)) for attr in dir(obj)]
+    rel_children = [(attr, value) for attr, value in attr_value_pairs
+                    if isinstance(value, relocatable)]
+    return rel_children
+
 
+## \brief Travel relocatable descendants.
+#
+# \param cloner is the function to return a clone.
+# \param adjuster is called to adjust the clone.
+# \param visit_log is a dictionary to keep clones.
+#
+def _travel_desc_relocatable(obj, worker, visit_log):
     if id(obj) in visit_log:
         return visit_log[id(obj)]
 
-    attrs = [attr for attr in dir(obj)
-             if not attr.startswith('_') and \
-                 isinstance(getattr(obj, attr), _dex_type)]
+    result = worker(obj)
+    visit_log[id(obj)] = result
+    
+    rel_children = _relocatable_children(obj)
+    for attr, value in rel_children:
+        _travel_conn_objs(value, worker, visit_log)
+        pass
+    pass
+
+
+## \brief Return name string of a linked class definition item.
+def classdef_name(classdef):
+    return classdef.classIdx.descriptorIdx.stringDataOff.data
+
+
+## \brief Return a map that map type of a object to the list of a DEXFile.
+def dex_type_2_array_attr_map():
+    global dex_type_2_array_attr_map
+    from paraspace.dexfile import DEXFile, array
+    
+    attr_values = [(attr, getattr(DEXFile, attr))
+                   for attr in dir(DEXFile)]
+    type_2_attr = dict([(value.child_type, attr)
+                        for attr, value in attr_values
+                        if isinstance(value, array)])
+    
+    dex_type_2_array_attr_map = lambda: type_2_attr
+    
+    return type_2_attr
+
+
+## \brief Append a object to appropriate list of a DEXFile object.
+#
+# Skip the object if found no appropriate list.
+#
+def dex_append_obj_list(dex, obj):
+    from paraspace.dex_deptracker import _dex_tree_get_child
+    from paraspace.dex_deptracker import _dex_tree_set_child
+
+    type_2_attr = dex_type_2_array_attr_map()
+    try:
+        attr = type_2_attr[obj.__class__]
+    except KeyError:
+        return
+
+    array = getattr(dex, attr)
+    array.items.append(obj)
+    
+    count_name = array.count_name
+    count = _dex_tree_get_child(dex, count_name)
+    _dex_tree_set_child(dex, count_name, count + 1)
+    pass
+
+
+## \brief Clone a class definition item
+#
+# \param dex is the DEXFile that clazz is cloning for.
+# \param clazz is the class definition item that is cloning.
+#
+def _clone_classdef(dex, clazz):
+    from copy import copy
+    from paraspace.dexfile import _DEX_StringDataItem, _DEX_StringId
+    from paraspace.dexfile import _DEX_TypeId
+    
+    visit_log = {}
     
+    def cloner(obj):
+        clone = copy(obj)
+        return clone
+
+    def relink_dependencies(clone):
+        rel_children = _relocatable_children(clone)
+        for attr, value in rel_children:
+            clone_value = visit_log[id(value)]
+            setattr(clone, attr, clone_value)
+            pass
+        pass
+
+    def merge_unique_strdata():
+        strdatas = [(obj_id, obj)
+                    for obj_id, obj in visit_log.items()
+                    if isinstance(obj, _DEX_StringDataItem)]
+        dex_str_2_strdata = dict([(strdata.data, strdata)
+                                  for strdata in dex.stringDataItems.items])
+        for obj_id, strdata in strdatas:
+            if strdata.data in dex_str_2_strdata:
+                visit_log[obj_id] = dex_str_2_strdata[strdata]
+            else:
+                dex_append_obj_list(dex, strdata)
+                pass
+            pass
+        pass
+
+    def merge_unique_strid():
+        strids = [(obj_id, obj)
+                  for obj_id, obj in visit_log.items()
+                  if isinstance(obj, _DEX_StringId)]
+        
+        for obj_id, strid in strids:
+            relink_dependencies(strid)
+            pass
+
+        strdata_2_strid = dict([(strid.stringDataOff, strid)
+                                for strid in dex.stringIds.items])
+        for obj_id, strid in strids:
+            if strid.stringDataOff in strdata_2_strid:
+                visit_log[obj_id] = strdata_2_strid[strid.stringDataOff]
+            else:
+                dex_append_obj_list(dex, strid)
+                pass
+            pass
+        pass
+
+    def merge_unique_typeid():
+        typeids = [(obj_id, obj)
+                   for obj_id, obj in visit_log.items()
+                   if isinstance(obj, _DEX_TypeId)]
+        
+        for obj_id, typeid in typeids:
+            relink_dependencies(typeid)
+            pass
+
+        strid_2_typeid = dict([(typeid.descriptorIdx, typeid)
+                               for typeid in dex.typeIds.items])
+        for obj_id, typeid in typeids:
+            if typeid.descriptorIdx in strid_2_typeid:
+                visit_log[obj_id] = strid_2_typeid[typeid.descriptorIdx]
+            else:
+                dex_append_obj_list(dex, typeid)
+                pass
+            pass
+        pass
+
+    def has_classdef(clazz):
+        class_typeIds = set([classdef.classIdx
+                             for classdef in dex.classDefs.items])
+        return dex.classIdx in class_typeIds
+
+    _travel_desc_relocatable(obj, cloner, visit_log)
+
+    merge_unique_strdata()
+    merge_unique_strid()
+    merge_unique_typeid()
+    
+    for obj in visit_log.values():
+        if isinstance(obj, (_DEX_StringDataItem,
+                            _DEX_StringId,
+                            _DEX_TypeId)):
+            continue
+        relink_dependencies(obj)
+        dex_append_obj_list(dex, obj)
+        pass
+    
+    if has_classef(clazz):
+        raise RuntimeError, \
+            'clone a class \'%s\'that is already in the DEXFile' % \
+            classdef_name(clazz)
     pass
 
 
 def dexfile_insert_class(dex, class_def):
-    classId_orig = class_def.classIdx
-    if dex_find_type(dex, class_orig):
-        raise RuntimeError, 'duplicated type'
-    classId = dex_dup_insert_type(dex, classId_orig)
-    
-    superclassId_orig = class_def.superclassId
-    superclassId = dex_find_type(dex, superclassId_orig) or \
-        dex_dup_insert_type(dex, superclassId_orig)
-    
-    if dex.interfacesOffRef.is_true:
-        interfaces_orig = dex.interfacesOffRef.value
-        interfaces = dex_dup_insert_typelist(dex, interfaces_org)
-        pass
-    
-    sourceFileIdx_orig = dex.sourceFileIdx
-    sourceFileIdx = dex_find_type(dex, sourceFileIdx_orig) or \
-        dex_dup_insert_type(dex, sourceFileIdx_orig)
-
-    if dex.annotationsOffRef.is_true:
-        annotations_orig = dex.annotationsOffRef.value
-        annotations = dex_dup_insert(dex, annotations_orig)
-        pass
-
-    if dex.classDataOffRef.is_true:
-        class_data_orig = dex.classDataOffRef.value
-        class_data = dex_dup_insert(dex, class_data_orig)
-        pass
+    _clone_classdef(dex, class_def)
     pass