1""" 2LLDB AppKit formatters 3 4part of The LLVM Compiler Infrastructure 5This file is distributed under the University of Illinois Open Source 6License. See LICENSE.TXT for details. 7""" 8# synthetic children and summary provider for CFString 9# (and related NSString class) 10import lldb 11import objc_runtime 12 13def CFString_SummaryProvider (valobj,dict): 14 provider = CFStringSynthProvider(valobj,dict); 15 if provider.invalid == False: 16 try: 17 summary = provider.get_child_at_index(provider.get_child_index("content")) 18 if type(summary) == lldb.SBValue: 19 summary = summary.GetSummary() 20 else: 21 summary = '"' + summary + '"' 22 except: 23 summary = None 24 if summary == None: 25 summary = '<variable is not NSString>' 26 return '@'+summary 27 return '' 28 29def CFAttributedString_SummaryProvider (valobj,dict): 30 offset = valobj.GetTarget().GetProcess().GetAddressByteSize() 31 pointee = valobj.GetValueAsUnsigned(0) 32 summary = '<variable is not NSAttributedString>' 33 if pointee != None and pointee != 0: 34 pointee = pointee + offset 35 child_ptr = valobj.CreateValueFromAddress("string_ptr",pointee,valobj.GetType()) 36 child = child_ptr.CreateValueFromAddress("string_data",child_ptr.GetValueAsUnsigned(),valobj.GetType()).AddressOf() 37 provider = CFStringSynthProvider(child,dict); 38 if provider.invalid == False: 39 try: 40 summary = provider.get_child_at_index(provider.get_child_index("content")).GetSummary(); 41 except: 42 summary = '<variable is not NSAttributedString>' 43 if summary == None: 44 summary = '<variable is not NSAttributedString>' 45 return '@'+summary 46 47 48def __lldb_init_module(debugger,dict): 49 debugger.HandleCommand("type summary add -F CFString.CFString_SummaryProvider NSString CFStringRef CFMutableStringRef") 50 debugger.HandleCommand("type summary add -F CFString.CFAttributedString_SummaryProvider NSAttributedString") 51 52class CFStringSynthProvider: 53 def __init__(self,valobj,dict): 54 self.valobj = valobj; 55 self.update() 56 57 # children other than "content" are for debugging only and must not be used in production code 58 def num_children(self): 59 if self.invalid: 60 return 0; 61 return 6; 62 63 def read_unicode(self, pointer): 64 process = self.valobj.GetTarget().GetProcess() 65 error = lldb.SBError() 66 pystr = u'' 67 # cannot do the read at once because the length value has 68 # a weird encoding. better play it safe here 69 while True: 70 content = process.ReadMemory(pointer, 2, error) 71 new_bytes = bytearray(content) 72 b0 = new_bytes[0] 73 b1 = new_bytes[1] 74 pointer = pointer + 2 75 if b0 == 0 and b1 == 0: 76 break 77 # rearrange bytes depending on endianness 78 # (do we really need this or is Cocoa going to 79 # use Windows-compatible little-endian even 80 # if the target is big endian?) 81 if self.is_little: 82 value = b1 * 256 + b0 83 else: 84 value = b0 * 256 + b1 85 pystr = pystr + unichr(value) 86 return pystr 87 88 # handle the special case strings 89 # only use the custom code for the tested LP64 case 90 def handle_special(self): 91 if self.is_64_bit == False: 92 # for 32bit targets, use safe ObjC code 93 return self.handle_unicode_string_safe() 94 offset = 12 95 pointer = self.valobj.GetValueAsUnsigned(0) + offset 96 pystr = self.read_unicode(pointer) 97 return self.valobj.CreateValueFromExpression("content", 98 "(char*)\"" + pystr.encode('utf-8') + "\"") 99 100 # last resort call, use ObjC code to read; the final aim is to 101 # be able to strip this call away entirely and only do the read 102 # ourselves 103 def handle_unicode_string_safe(self): 104 return self.valobj.CreateValueFromExpression("content", 105 "(char*)\"" + self.valobj.GetObjectDescription() + "\""); 106 107 def handle_unicode_string(self): 108 # step 1: find offset 109 if self.inline: 110 pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base(); 111 if self.explicit == False: 112 # untested, use the safe code path 113 return self.handle_unicode_string_safe(); 114 else: 115 # a full pointer is skipped here before getting to the live data 116 pointer = pointer + self.pointer_size 117 else: 118 pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base() 119 # read 8 bytes here and make an address out of them 120 try: 121 char_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType() 122 vopointer = self.valobj.CreateValueFromAddress("dummy",pointer,char_type); 123 pointer = vopointer.GetValueAsUnsigned(0) 124 except: 125 return self.valobj.CreateValueFromExpression("content", 126 '(char*)"@\"invalid NSString\""') 127 # step 2: read Unicode data at pointer 128 pystr = self.read_unicode(pointer) 129 # step 3: return it 130 return pystr.encode('utf-8') 131 132 def handle_inline_explicit(self): 133 offset = 3*self.pointer_size 134 offset = offset + self.valobj.GetValueAsUnsigned(0) 135 return self.valobj.CreateValueFromExpression("content", 136 "(char*)(" + str(offset) + ")") 137 138 def handle_mutable_string(self): 139 offset = 2 * self.pointer_size 140 data = self.valobj.CreateChildAtOffset("content", 141 offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()); 142 data_value = data.GetValueAsUnsigned(0) 143 data_value = data_value + 1 144 return self.valobj.CreateValueFromExpression("content", "(char*)(" + str(data_value) + ")") 145 146 def handle_UTF8_inline(self): 147 offset = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base(); 148 if self.explicit == False: 149 offset = offset + 1; 150 return self.valobj.CreateValueFromAddress("content", 151 offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)).AddressOf(); 152 153 def handle_UTF8_not_inline(self): 154 offset = self.size_of_cfruntime_base(); 155 return self.valobj.CreateChildAtOffset("content", 156 offset,self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()); 157 158 def get_child_at_index(self,index): 159 if index == 0: 160 return self.valobj.CreateValueFromExpression("mutable", 161 str(int(self.mutable))); 162 if index == 1: 163 return self.valobj.CreateValueFromExpression("inline", 164 str(int(self.inline))); 165 if index == 2: 166 return self.valobj.CreateValueFromExpression("explicit", 167 str(int(self.explicit))); 168 if index == 3: 169 return self.valobj.CreateValueFromExpression("unicode", 170 str(int(self.unicode))); 171 if index == 4: 172 return self.valobj.CreateValueFromExpression("special", 173 str(int(self.special))); 174 if index == 5: 175 # we are handling the several possible combinations of flags. 176 # for each known combination we have a function that knows how to 177 # go fetch the data from memory instead of running code. if a string is not 178 # correctly displayed, one should start by finding a combination of flags that 179 # makes it different from these known cases, and provide a new reader function 180 # if this is not possible, a new flag might have to be made up (like the "special" flag 181 # below, which is not a real flag in CFString), or alternatively one might need to use 182 # the ObjC runtime helper to detect the new class and deal with it accordingly 183 #print 'mutable = ' + str(self.mutable) 184 #print 'inline = ' + str(self.inline) 185 #print 'explicit = ' + str(self.explicit) 186 #print 'unicode = ' + str(self.unicode) 187 #print 'special = ' + str(self.special) 188 if self.mutable == True: 189 return self.handle_mutable_string() 190 elif self.inline == True and self.explicit == True and \ 191 self.unicode == False and self.special == False and \ 192 self.mutable == False: 193 return self.handle_inline_explicit() 194 elif self.unicode == True: 195 return self.handle_unicode_string(); 196 elif self.special == True: 197 return self.handle_special(); 198 elif self.inline == True: 199 return self.handle_UTF8_inline(); 200 else: 201 return self.handle_UTF8_not_inline(); 202 203 def get_child_index(self,name): 204 if name == "content": 205 return self.num_children() - 1; 206 if name == "mutable": 207 return 0; 208 if name == "inline": 209 return 1; 210 if name == "explicit": 211 return 2; 212 if name == "unicode": 213 return 3; 214 if name == "special": 215 return 4; 216 217 # CFRuntimeBase is defined as having an additional 218 # 4 bytes (padding?) on LP64 architectures 219 # to get its size we add up sizeof(pointer)+4 220 # and then add 4 more bytes if we are on a 64bit system 221 def size_of_cfruntime_base(self): 222 return self.pointer_size+4+(4 if self.is_64_bit else 0) 223 224 # the info bits are part of the CFRuntimeBase structure 225 # to get at them we have to skip a uintptr_t and then get 226 # at the least-significant byte of a 4 byte array. If we are 227 # on big-endian this means going to byte 3, if we are on 228 # little endian (OSX & iOS), this means reading byte 0 229 def offset_of_info_bits(self): 230 offset = self.pointer_size 231 if self.is_little == False: 232 offset = offset + 3; 233 return offset; 234 235 def read_info_bits(self): 236 cfinfo = self.valobj.CreateChildAtOffset("cfinfo", 237 self.offset_of_info_bits(), 238 self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)); 239 cfinfo.SetFormat(11) 240 info = cfinfo.GetValue(); 241 if info != None: 242 self.invalid = False; 243 return int(info,0); 244 else: 245 self.invalid = True; 246 return None; 247 248 # calculating internal flag bits of the CFString object 249 # this stuff is defined and discussed in CFString.c 250 def is_mutable(self): 251 return (self.info_bits & 1) == 1; 252 253 def is_inline(self): 254 return (self.info_bits & 0x60) == 0; 255 256 # this flag's name is ambiguous, it turns out 257 # we must skip a length byte to get at the data 258 # when this flag is False 259 def has_explicit_length(self): 260 return (self.info_bits & (1 | 4)) != 4; 261 262 # probably a subclass of NSString. obtained this from [str pathExtension] 263 # here info_bits = 0 and Unicode data at the start of the padding word 264 # in the long run using the isa value might be safer as a way to identify this 265 # instead of reading the info_bits 266 def is_special_case(self): 267 return self.info_bits == 0; 268 269 def is_unicode(self): 270 return (self.info_bits & 0x10) == 0x10; 271 272 # preparing ourselves to read into memory 273 # by adjusting architecture-specific info 274 def adjust_for_architecture(self): 275 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize() 276 self.is_64_bit = self.pointer_size == 8 277 self.is_little = self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle 278 279 # reading info bits out of the CFString and computing 280 # useful values to get at the real data 281 def compute_flags(self): 282 self.info_bits = self.read_info_bits(); 283 if self.info_bits == None: 284 return; 285 self.mutable = self.is_mutable(); 286 self.inline = self.is_inline(); 287 self.explicit = self.has_explicit_length(); 288 self.unicode = self.is_unicode(); 289 self.special = self.is_special_case(); 290 291 def update(self): 292 self.adjust_for_architecture(); 293 self.compute_flags();