blob: a3a07a0e83ad31d2a775bcb3dcc071eb57c6f20e [file] [log] [blame]
Enrico Granata3f1052b2012-03-13 21:52:00 +00001"""
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"""
Enrico Granataeb4a4792012-02-23 23:10:27 +00008# synthetic children and summary provider for CFString
9# (and related NSString class)
10import lldb
Enrico Granata896cd1d2012-03-01 19:32:33 +000011import objc_runtime
Enrico Granata247bd412012-04-02 16:39:29 +000012import Logger
Enrico Granataeb4a4792012-02-23 23:10:27 +000013
14def CFString_SummaryProvider (valobj,dict):
Enrico Granata247bd412012-04-02 16:39:29 +000015 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +000016 provider = CFStringSynthProvider(valobj,dict);
17 if provider.invalid == False:
Enrico Granata3f1052b2012-03-13 21:52:00 +000018 try:
Enrico Granata86ea8d82012-03-29 01:34:34 +000019 summary = provider.get_child_at_index(provider.get_child_index("content"))
20 if type(summary) == lldb.SBValue:
21 summary = summary.GetSummary()
22 else:
23 summary = '"' + summary + '"'
Enrico Granata3f1052b2012-03-13 21:52:00 +000024 except:
25 summary = None
26 if summary == None:
27 summary = '<variable is not NSString>'
28 return '@'+summary
Enrico Granataeb4a4792012-02-23 23:10:27 +000029 return ''
30
31def CFAttributedString_SummaryProvider (valobj,dict):
Enrico Granata247bd412012-04-02 16:39:29 +000032 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +000033 offset = valobj.GetTarget().GetProcess().GetAddressByteSize()
34 pointee = valobj.GetValueAsUnsigned(0)
Enrico Granata3f1052b2012-03-13 21:52:00 +000035 summary = '<variable is not NSAttributedString>'
Enrico Granataeb4a4792012-02-23 23:10:27 +000036 if pointee != None and pointee != 0:
37 pointee = pointee + offset
38 child_ptr = valobj.CreateValueFromAddress("string_ptr",pointee,valobj.GetType())
39 child = child_ptr.CreateValueFromAddress("string_data",child_ptr.GetValueAsUnsigned(),valobj.GetType()).AddressOf()
40 provider = CFStringSynthProvider(child,dict);
41 if provider.invalid == False:
42 try:
43 summary = provider.get_child_at_index(provider.get_child_index("content")).GetSummary();
44 except:
Enrico Granata3f1052b2012-03-13 21:52:00 +000045 summary = '<variable is not NSAttributedString>'
Enrico Granataeb4a4792012-02-23 23:10:27 +000046 if summary == None:
Enrico Granata3f1052b2012-03-13 21:52:00 +000047 summary = '<variable is not NSAttributedString>'
Enrico Granataeb4a4792012-02-23 23:10:27 +000048 return '@'+summary
49
50
51def __lldb_init_module(debugger,dict):
52 debugger.HandleCommand("type summary add -F CFString.CFString_SummaryProvider NSString CFStringRef CFMutableStringRef")
53 debugger.HandleCommand("type summary add -F CFString.CFAttributedString_SummaryProvider NSAttributedString")
54
55class CFStringSynthProvider:
56 def __init__(self,valobj,dict):
Enrico Granata247bd412012-04-02 16:39:29 +000057 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +000058 self.valobj = valobj;
59 self.update()
60
61 # children other than "content" are for debugging only and must not be used in production code
62 def num_children(self):
Enrico Granata247bd412012-04-02 16:39:29 +000063 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +000064 if self.invalid:
65 return 0;
66 return 6;
67
68 def read_unicode(self, pointer):
Enrico Granata247bd412012-04-02 16:39:29 +000069 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +000070 process = self.valobj.GetTarget().GetProcess()
71 error = lldb.SBError()
72 pystr = u''
73 # cannot do the read at once because the length value has
74 # a weird encoding. better play it safe here
75 while True:
76 content = process.ReadMemory(pointer, 2, error)
77 new_bytes = bytearray(content)
78 b0 = new_bytes[0]
79 b1 = new_bytes[1]
80 pointer = pointer + 2
81 if b0 == 0 and b1 == 0:
82 break
83 # rearrange bytes depending on endianness
84 # (do we really need this or is Cocoa going to
85 # use Windows-compatible little-endian even
86 # if the target is big endian?)
87 if self.is_little:
88 value = b1 * 256 + b0
89 else:
90 value = b0 * 256 + b1
91 pystr = pystr + unichr(value)
92 return pystr
93
94 # handle the special case strings
95 # only use the custom code for the tested LP64 case
96 def handle_special(self):
Enrico Granata247bd412012-04-02 16:39:29 +000097 logger = Logger.Logger()
Enrico Granata7bc0ec32012-02-29 03:28:49 +000098 if self.is_64_bit == False:
Enrico Granataeb4a4792012-02-23 23:10:27 +000099 # for 32bit targets, use safe ObjC code
100 return self.handle_unicode_string_safe()
101 offset = 12
102 pointer = self.valobj.GetValueAsUnsigned(0) + offset
103 pystr = self.read_unicode(pointer)
104 return self.valobj.CreateValueFromExpression("content",
105 "(char*)\"" + pystr.encode('utf-8') + "\"")
106
107 # last resort call, use ObjC code to read; the final aim is to
108 # be able to strip this call away entirely and only do the read
109 # ourselves
110 def handle_unicode_string_safe(self):
111 return self.valobj.CreateValueFromExpression("content",
112 "(char*)\"" + self.valobj.GetObjectDescription() + "\"");
113
114 def handle_unicode_string(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000115 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000116 # step 1: find offset
117 if self.inline:
118 pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base();
119 if self.explicit == False:
120 # untested, use the safe code path
121 return self.handle_unicode_string_safe();
122 else:
Enrico Granata74ec8f92012-03-01 19:48:54 +0000123 # a full pointer is skipped here before getting to the live data
Enrico Granatacfdafa32012-03-05 19:56:33 +0000124 pointer = pointer + self.pointer_size
Enrico Granataeb4a4792012-02-23 23:10:27 +0000125 else:
Enrico Granata86ea8d82012-03-29 01:34:34 +0000126 pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000127 # read 8 bytes here and make an address out of them
128 try:
Enrico Granata86ea8d82012-03-29 01:34:34 +0000129 char_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()
130 vopointer = self.valobj.CreateValueFromAddress("dummy",pointer,char_type);
131 pointer = vopointer.GetValueAsUnsigned(0)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000132 except:
Enrico Granata86ea8d82012-03-29 01:34:34 +0000133 return self.valobj.CreateValueFromExpression("content",
Enrico Granataeb4a4792012-02-23 23:10:27 +0000134 '(char*)"@\"invalid NSString\""')
135 # step 2: read Unicode data at pointer
136 pystr = self.read_unicode(pointer)
137 # step 3: return it
Enrico Granata86ea8d82012-03-29 01:34:34 +0000138 return pystr.encode('utf-8')
Enrico Granataeb4a4792012-02-23 23:10:27 +0000139
140 def handle_inline_explicit(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000141 logger = Logger.Logger()
Enrico Granatacfdafa32012-03-05 19:56:33 +0000142 offset = 3*self.pointer_size
Enrico Granataeb4a4792012-02-23 23:10:27 +0000143 offset = offset + self.valobj.GetValueAsUnsigned(0)
144 return self.valobj.CreateValueFromExpression("content",
145 "(char*)(" + str(offset) + ")")
146
147 def handle_mutable_string(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000148 logger = Logger.Logger()
Enrico Granatacfdafa32012-03-05 19:56:33 +0000149 offset = 2 * self.pointer_size
Enrico Granataeb4a4792012-02-23 23:10:27 +0000150 data = self.valobj.CreateChildAtOffset("content",
151 offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType());
152 data_value = data.GetValueAsUnsigned(0)
153 data_value = data_value + 1
154 return self.valobj.CreateValueFromExpression("content", "(char*)(" + str(data_value) + ")")
155
156 def handle_UTF8_inline(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000157 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000158 offset = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base();
159 if self.explicit == False:
160 offset = offset + 1;
161 return self.valobj.CreateValueFromAddress("content",
162 offset, self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)).AddressOf();
163
164 def handle_UTF8_not_inline(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000165 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000166 offset = self.size_of_cfruntime_base();
167 return self.valobj.CreateChildAtOffset("content",
168 offset,self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType());
169
170 def get_child_at_index(self,index):
Enrico Granata247bd412012-04-02 16:39:29 +0000171 logger = Logger.Logger()
172 logger >> "Querying for child [" + str(index) + "]"
Enrico Granataeb4a4792012-02-23 23:10:27 +0000173 if index == 0:
174 return self.valobj.CreateValueFromExpression("mutable",
175 str(int(self.mutable)));
176 if index == 1:
177 return self.valobj.CreateValueFromExpression("inline",
178 str(int(self.inline)));
179 if index == 2:
180 return self.valobj.CreateValueFromExpression("explicit",
181 str(int(self.explicit)));
182 if index == 3:
183 return self.valobj.CreateValueFromExpression("unicode",
184 str(int(self.unicode)));
185 if index == 4:
186 return self.valobj.CreateValueFromExpression("special",
187 str(int(self.special)));
188 if index == 5:
189 # we are handling the several possible combinations of flags.
190 # for each known combination we have a function that knows how to
191 # go fetch the data from memory instead of running code. if a string is not
192 # correctly displayed, one should start by finding a combination of flags that
193 # makes it different from these known cases, and provide a new reader function
194 # if this is not possible, a new flag might have to be made up (like the "special" flag
195 # below, which is not a real flag in CFString), or alternatively one might need to use
196 # the ObjC runtime helper to detect the new class and deal with it accordingly
Enrico Granata86ea8d82012-03-29 01:34:34 +0000197 #print 'mutable = ' + str(self.mutable)
198 #print 'inline = ' + str(self.inline)
199 #print 'explicit = ' + str(self.explicit)
200 #print 'unicode = ' + str(self.unicode)
201 #print 'special = ' + str(self.special)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000202 if self.mutable == True:
203 return self.handle_mutable_string()
204 elif self.inline == True and self.explicit == True and \
205 self.unicode == False and self.special == False and \
206 self.mutable == False:
207 return self.handle_inline_explicit()
208 elif self.unicode == True:
209 return self.handle_unicode_string();
210 elif self.special == True:
211 return self.handle_special();
212 elif self.inline == True:
213 return self.handle_UTF8_inline();
214 else:
215 return self.handle_UTF8_not_inline();
216
217 def get_child_index(self,name):
Enrico Granata247bd412012-04-02 16:39:29 +0000218 logger = Logger.Logger()
219 logger >> "Querying for child ['" + str(name) + "']"
Enrico Granataeb4a4792012-02-23 23:10:27 +0000220 if name == "content":
221 return self.num_children() - 1;
222 if name == "mutable":
223 return 0;
224 if name == "inline":
225 return 1;
226 if name == "explicit":
227 return 2;
228 if name == "unicode":
229 return 3;
230 if name == "special":
231 return 4;
232
Enrico Granataeb4a4792012-02-23 23:10:27 +0000233 # CFRuntimeBase is defined as having an additional
234 # 4 bytes (padding?) on LP64 architectures
235 # to get its size we add up sizeof(pointer)+4
236 # and then add 4 more bytes if we are on a 64bit system
237 def size_of_cfruntime_base(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000238 logger = Logger.Logger()
Enrico Granatacfdafa32012-03-05 19:56:33 +0000239 return self.pointer_size+4+(4 if self.is_64_bit else 0)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000240
241 # the info bits are part of the CFRuntimeBase structure
242 # to get at them we have to skip a uintptr_t and then get
243 # at the least-significant byte of a 4 byte array. If we are
244 # on big-endian this means going to byte 3, if we are on
245 # little endian (OSX & iOS), this means reading byte 0
246 def offset_of_info_bits(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000247 logger = Logger.Logger()
Enrico Granatacfdafa32012-03-05 19:56:33 +0000248 offset = self.pointer_size
Enrico Granataeb4a4792012-02-23 23:10:27 +0000249 if self.is_little == False:
250 offset = offset + 3;
251 return offset;
252
253 def read_info_bits(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000254 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000255 cfinfo = self.valobj.CreateChildAtOffset("cfinfo",
256 self.offset_of_info_bits(),
257 self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar));
258 cfinfo.SetFormat(11)
259 info = cfinfo.GetValue();
260 if info != None:
261 self.invalid = False;
262 return int(info,0);
263 else:
264 self.invalid = True;
265 return None;
266
267 # calculating internal flag bits of the CFString object
268 # this stuff is defined and discussed in CFString.c
269 def is_mutable(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000270 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000271 return (self.info_bits & 1) == 1;
272
273 def is_inline(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000274 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000275 return (self.info_bits & 0x60) == 0;
276
277 # this flag's name is ambiguous, it turns out
278 # we must skip a length byte to get at the data
279 # when this flag is False
280 def has_explicit_length(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000281 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000282 return (self.info_bits & (1 | 4)) != 4;
283
284 # probably a subclass of NSString. obtained this from [str pathExtension]
285 # here info_bits = 0 and Unicode data at the start of the padding word
286 # in the long run using the isa value might be safer as a way to identify this
287 # instead of reading the info_bits
288 def is_special_case(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000289 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000290 return self.info_bits == 0;
291
292 def is_unicode(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000293 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000294 return (self.info_bits & 0x10) == 0x10;
295
296 # preparing ourselves to read into memory
297 # by adjusting architecture-specific info
298 def adjust_for_architecture(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000299 logger = Logger.Logger()
Enrico Granatacfdafa32012-03-05 19:56:33 +0000300 self.pointer_size = self.valobj.GetTarget().GetProcess().GetAddressByteSize()
301 self.is_64_bit = self.pointer_size == 8
302 self.is_little = self.valobj.GetTarget().GetProcess().GetByteOrder() == lldb.eByteOrderLittle
Enrico Granataeb4a4792012-02-23 23:10:27 +0000303
304 # reading info bits out of the CFString and computing
305 # useful values to get at the real data
306 def compute_flags(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000307 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000308 self.info_bits = self.read_info_bits();
309 if self.info_bits == None:
310 return;
311 self.mutable = self.is_mutable();
312 self.inline = self.is_inline();
313 self.explicit = self.has_explicit_length();
314 self.unicode = self.is_unicode();
315 self.special = self.is_special_case();
316
317 def update(self):
Enrico Granata247bd412012-04-02 16:39:29 +0000318 logger = Logger.Logger()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000319 self.adjust_for_architecture();
Enrico Granata247bd412012-04-02 16:39:29 +0000320 self.compute_flags();