blob: 6ffc7761a3070bb5a3e0ba0098428dd33eeadb3b [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 Granataeb4a4792012-02-23 23:10:27 +000012
13def CFString_SummaryProvider (valobj,dict):
14 provider = CFStringSynthProvider(valobj,dict);
15 if provider.invalid == False:
Enrico Granata3f1052b2012-03-13 21:52:00 +000016 try:
Enrico Granata86ea8d82012-03-29 01:34:34 +000017 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 Granata3f1052b2012-03-13 21:52:00 +000022 except:
23 summary = None
24 if summary == None:
25 summary = '<variable is not NSString>'
26 return '@'+summary
Enrico Granataeb4a4792012-02-23 23:10:27 +000027 return ''
28
29def CFAttributedString_SummaryProvider (valobj,dict):
30 offset = valobj.GetTarget().GetProcess().GetAddressByteSize()
31 pointee = valobj.GetValueAsUnsigned(0)
Enrico Granata3f1052b2012-03-13 21:52:00 +000032 summary = '<variable is not NSAttributedString>'
Enrico Granataeb4a4792012-02-23 23:10:27 +000033 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 Granata3f1052b2012-03-13 21:52:00 +000042 summary = '<variable is not NSAttributedString>'
Enrico Granataeb4a4792012-02-23 23:10:27 +000043 if summary == None:
Enrico Granata3f1052b2012-03-13 21:52:00 +000044 summary = '<variable is not NSAttributedString>'
Enrico Granataeb4a4792012-02-23 23:10:27 +000045 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):
Enrico Granata7bc0ec32012-02-29 03:28:49 +000091 if self.is_64_bit == False:
Enrico Granataeb4a4792012-02-23 23:10:27 +000092 # 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 Granata74ec8f92012-03-01 19:48:54 +0000115 # a full pointer is skipped here before getting to the live data
Enrico Granatacfdafa32012-03-05 19:56:33 +0000116 pointer = pointer + self.pointer_size
Enrico Granataeb4a4792012-02-23 23:10:27 +0000117 else:
Enrico Granata86ea8d82012-03-29 01:34:34 +0000118 pointer = self.valobj.GetValueAsUnsigned(0) + self.size_of_cfruntime_base()
Enrico Granataeb4a4792012-02-23 23:10:27 +0000119 # read 8 bytes here and make an address out of them
120 try:
Enrico Granata86ea8d82012-03-29 01:34:34 +0000121 char_type = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar).GetPointerType()
122 vopointer = self.valobj.CreateValueFromAddress("dummy",pointer,char_type);
123 pointer = vopointer.GetValueAsUnsigned(0)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000124 except:
Enrico Granata86ea8d82012-03-29 01:34:34 +0000125 return self.valobj.CreateValueFromExpression("content",
Enrico Granataeb4a4792012-02-23 23:10:27 +0000126 '(char*)"@\"invalid NSString\""')
127 # step 2: read Unicode data at pointer
128 pystr = self.read_unicode(pointer)
129 # step 3: return it
Enrico Granata86ea8d82012-03-29 01:34:34 +0000130 return pystr.encode('utf-8')
Enrico Granataeb4a4792012-02-23 23:10:27 +0000131
132 def handle_inline_explicit(self):
Enrico Granatacfdafa32012-03-05 19:56:33 +0000133 offset = 3*self.pointer_size
Enrico Granataeb4a4792012-02-23 23:10:27 +0000134 offset = offset + self.valobj.GetValueAsUnsigned(0)
135 return self.valobj.CreateValueFromExpression("content",
136 "(char*)(" + str(offset) + ")")
137
138 def handle_mutable_string(self):
Enrico Granatacfdafa32012-03-05 19:56:33 +0000139 offset = 2 * self.pointer_size
Enrico Granataeb4a4792012-02-23 23:10:27 +0000140 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 Granata86ea8d82012-03-29 01:34:34 +0000183 #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 Granataeb4a4792012-02-23 23:10:27 +0000188 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 Granataeb4a4792012-02-23 23:10:27 +0000217 # 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 Granatacfdafa32012-03-05 19:56:33 +0000222 return self.pointer_size+4+(4 if self.is_64_bit else 0)
Enrico Granataeb4a4792012-02-23 23:10:27 +0000223
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 Granatacfdafa32012-03-05 19:56:33 +0000230 offset = self.pointer_size
Enrico Granataeb4a4792012-02-23 23:10:27 +0000231 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 Granatacfdafa32012-03-05 19:56:33 +0000275 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 Granataeb4a4792012-02-23 23:10:27 +0000278
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();