Enrico Granata | 3f1052b | 2012-03-13 21:52:00 +0000 | [diff] [blame] | 1 | """ |
| 2 | LLDB AppKit formatters |
| 3 | |
| 4 | part of The LLVM Compiler Infrastructure |
| 5 | This file is distributed under the University of Illinois Open Source |
| 6 | License. See LICENSE.TXT for details. |
| 7 | """ |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 8 | # synthetic children and summary provider for CFString |
| 9 | # (and related NSString class) |
| 10 | import lldb |
Enrico Granata | 896cd1d | 2012-03-01 19:32:33 +0000 | [diff] [blame] | 11 | import objc_runtime |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 12 | |
| 13 | def CFString_SummaryProvider (valobj,dict): |
| 14 | provider = CFStringSynthProvider(valobj,dict); |
| 15 | if provider.invalid == False: |
Enrico Granata | 3f1052b | 2012-03-13 21:52:00 +0000 | [diff] [blame] | 16 | try: |
Enrico Granata | 86ea8d8 | 2012-03-29 01:34:34 +0000 | [diff] [blame] | 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 + '"' |
Enrico Granata | 3f1052b | 2012-03-13 21:52:00 +0000 | [diff] [blame] | 22 | except: |
| 23 | summary = None |
| 24 | if summary == None: |
| 25 | summary = '<variable is not NSString>' |
| 26 | return '@'+summary |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 27 | return '' |
| 28 | |
| 29 | def CFAttributedString_SummaryProvider (valobj,dict): |
| 30 | offset = valobj.GetTarget().GetProcess().GetAddressByteSize() |
| 31 | pointee = valobj.GetValueAsUnsigned(0) |
Enrico Granata | 3f1052b | 2012-03-13 21:52:00 +0000 | [diff] [blame] | 32 | summary = '<variable is not NSAttributedString>' |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 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: |
Enrico Granata | 3f1052b | 2012-03-13 21:52:00 +0000 | [diff] [blame] | 42 | summary = '<variable is not NSAttributedString>' |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 43 | if summary == None: |
Enrico Granata | 3f1052b | 2012-03-13 21:52:00 +0000 | [diff] [blame] | 44 | summary = '<variable is not NSAttributedString>' |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 45 | return '@'+summary |
| 46 | |
| 47 | |
| 48 | def __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 | |
| 52 | class 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): |
Enrico Granata | 7bc0ec3 | 2012-02-29 03:28:49 +0000 | [diff] [blame] | 91 | if self.is_64_bit == False: |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 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: |
Enrico Granata | 74ec8f9 | 2012-03-01 19:48:54 +0000 | [diff] [blame] | 115 | # a full pointer is skipped here before getting to the live data |
Enrico Granata | cfdafa3 | 2012-03-05 19:56:33 +0000 | [diff] [blame] | 116 | pointer = pointer + self.pointer_size |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 117 | else: |
Enrico Granata | 86ea8d8 | 2012-03-29 01:34:34 +0000 | [diff] [blame] | 118 | pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base() |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 119 | # read 8 bytes here and make an address out of them |
| 120 | try: |
Enrico Granata | 86ea8d8 | 2012-03-29 01:34:34 +0000 | [diff] [blame] | 121 | char_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType() |
| 122 | vopointer = self.valobj.CreateValueFromAddress("dummy",pointer,char_type); |
| 123 | pointer = vopointer.GetValueAsUnsigned(0) |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 124 | except: |
Enrico Granata | 86ea8d8 | 2012-03-29 01:34:34 +0000 | [diff] [blame] | 125 | return self.valobj.CreateValueFromExpression("content", |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 126 | '(char*)"@\"invalid NSString\""') |
| 127 | # step 2: read Unicode data at pointer |
| 128 | pystr = self.read_unicode(pointer) |
| 129 | # step 3: return it |
Enrico Granata | 86ea8d8 | 2012-03-29 01:34:34 +0000 | [diff] [blame] | 130 | return pystr.encode('utf-8') |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 131 | |
| 132 | def handle_inline_explicit(self): |
Enrico Granata | cfdafa3 | 2012-03-05 19:56:33 +0000 | [diff] [blame] | 133 | offset = 3*self.pointer_size |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 134 | offset = offset + self.valobj.GetValueAsUnsigned(0) |
| 135 | return self.valobj.CreateValueFromExpression("content", |
| 136 | "(char*)(" + str(offset) + ")") |
| 137 | |
| 138 | def handle_mutable_string(self): |
Enrico Granata | cfdafa3 | 2012-03-05 19:56:33 +0000 | [diff] [blame] | 139 | offset = 2 * self.pointer_size |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 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 |
Enrico Granata | 86ea8d8 | 2012-03-29 01:34:34 +0000 | [diff] [blame] | 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) |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 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 | |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 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): |
Enrico Granata | cfdafa3 | 2012-03-05 19:56:33 +0000 | [diff] [blame] | 222 | return self.pointer_size+4+(4 if self.is_64_bit else 0) |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 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): |
Enrico Granata | cfdafa3 | 2012-03-05 19:56:33 +0000 | [diff] [blame] | 230 | offset = self.pointer_size |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 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): |
Enrico Granata | cfdafa3 | 2012-03-05 19:56:33 +0000 | [diff] [blame] | 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 |
Enrico Granata | eb4a479 | 2012-02-23 23:10:27 +0000 | [diff] [blame] | 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(); |