1# synthetic children provider for NSArray 2import lldb 3import ctypes 4import objc_runtime 5import metrics 6 7statistics = metrics.Metrics() 8statistics.add_metric('invalid_isa') 9statistics.add_metric('invalid_pointer') 10statistics.add_metric('unknown_class') 11statistics.add_metric('code_notrun') 12 13# much less functional than the other two cases below 14# just runs code to get to the count and then returns 15# no children 16class NSArrayKVC_SynthProvider: 17 18 def adjust_for_architecture(self): 19 pass 20 21 def __init__(self, valobj, dict, params): 22 self.valobj = valobj; 23 self.update() 24 25 def update(self): 26 self.adjust_for_architecture(); 27 28 def num_children(self): 29 stream = lldb.SBStream() 30 self.valobj.GetExpressionPath(stream) 31 num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]"); 32 return num_children_vo.GetValueAsUnsigned(0) 33 34# much less functional than the other two cases below 35# just runs code to get to the count and then returns 36# no children 37class NSArrayCF_SynthProvider: 38 39 def adjust_for_architecture(self): 40 pass 41 42 def __init__(self, valobj, dict, params): 43 self.valobj = valobj; 44 self.sys_params = params 45 if not (self.sys_params.types_cache.ulong): 46 self.sys_params.types_cache.ulong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong) 47 self.update() 48 49 def update(self): 50 self.adjust_for_architecture(); 51 52 def num_children(self): 53 num_children_vo = self.valobj.CreateChildAtOffset("count", 54 self.sys_params.cfruntime_size, 55 self.sys_params.types_cache.ulong) 56 return num_children_vo.GetValueAsUnsigned(0) 57 58class NSArrayI_SynthProvider: 59 def adjust_for_architecture(self): 60 pass 61 62 def __init__(self, valobj, dict, params): 63 self.valobj = valobj; 64 self.sys_params = params 65 if not(self.sys_params.types_cache.long): 66 self.sys_params.types_cache.long = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong) 67 self.update() 68 69 def update(self): 70 self.adjust_for_architecture(); 71 72 # skip the isa pointer and get at the size 73 def num_children(self): 74 count = self.valobj.CreateChildAtOffset("count", 75 self.sys_params.pointer_size, 76 self.sys_params.types_cache.long); 77 return count.GetValueAsUnsigned(0) 78 79class NSArrayM_SynthProvider: 80 def adjust_for_architecture(self): 81 pass 82 83 def __init__(self, valobj, dict, params): 84 self.valobj = valobj; 85 self.sys_params = params 86 if not(self.sys_params.types_cache.long): 87 self.sys_params.types_cache.long = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong) 88 self.update() 89 90 def update(self): 91 self.adjust_for_architecture(); 92 93 # skip the isa pointer and get at the size 94 def num_children(self): 95 count = self.valobj.CreateChildAtOffset("count", 96 self.sys_params.pointer_size, 97 self.sys_params.types_cache.long); 98 return count.GetValueAsUnsigned(0) 99 100# this is the actual synth provider, but is just a wrapper that checks 101# whether valobj is an instance of __NSArrayI or __NSArrayM and sets up an 102# appropriate backend layer to do the computations 103class NSArray_SynthProvider: 104 def adjust_for_architecture(self): 105 pass 106 107 def __init__(self, valobj, dict): 108 self.valobj = valobj; 109 self.adjust_for_architecture() 110 self.wrapper = self.make_wrapper(valobj,dict) 111 self.invalid = (self.wrapper == None) 112 113 def num_children(self): 114 if self.wrapper == None: 115 return 0; 116 return self.wrapper.num_children() 117 118 def update(self): 119 if self.wrapper == None: 120 return 121 self.wrapper.update() 122 123 # this code acts as our defense against NULL and unitialized 124 # NSArray pointers, which makes it much longer than it would be otherwise 125 def make_wrapper(self,valobj,dict): 126 global statistics 127 class_data = objc_runtime.ObjCRuntime(valobj) 128 if class_data.is_valid() == False: 129 statistics.metric_hit('invalid_pointer',valobj) 130 wrapper = None 131 return 132 class_data = class_data.read_class_data() 133 if class_data.is_valid() == False: 134 statistics.metric_hit('invalid_isa',valobj) 135 wrapper = None 136 return 137 if class_data.is_kvo(): 138 class_data = class_data.get_superclass() 139 if class_data.is_valid() == False: 140 statistics.metric_hit('invalid_isa',valobj) 141 wrapper = None 142 return 143 144 name_string = class_data.class_name() 145 if name_string == '__NSArrayI': 146 wrapper = NSArrayI_SynthProvider(valobj, dict, class_data.sys_params) 147 statistics.metric_hit('code_notrun',valobj) 148 elif name_string == '__NSArrayM': 149 wrapper = NSArrayM_SynthProvider(valobj, dict, class_data.sys_params) 150 statistics.metric_hit('code_notrun',valobj) 151 elif name_string == '__NSCFArray': 152 wrapper = NSArrayCF_SynthProvider(valobj, dict, class_data.sys_params) 153 statistics.metric_hit('code_notrun',valobj) 154 else: 155 wrapper = NSArrayKVC_SynthProvider(valobj, dict, class_data.sys_params) 156 statistics.metric_hit('unknown_class',str(valobj) + " seen as " + name_string) 157 return wrapper; 158 159def CFArray_SummaryProvider (valobj,dict): 160 provider = NSArray_SynthProvider(valobj,dict); 161 if provider.invalid == False: 162 try: 163 summary = str(provider.num_children()); 164 except: 165 summary = None 166 if summary == None: 167 summary = 'no valid array here' 168 return summary + " objects" 169 return '' 170 171def __lldb_init_module(debugger,dict): 172 debugger.HandleCommand("type summary add -F CFArray.CFArray_SummaryProvider NSArray CFArrayRef CFMutableArrayRef") 173