blob: 0d7bcc9d90e70fcd25fa5b804af5f4f3f2ca9766 [file] [log] [blame]
Enrico Granata92373532013-03-19 22:58:48 +00001//===-- Cocoa.cpp -------------------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "lldb/DataFormatters/CXXFormatterFunctions.h"
11
12#include "lldb/Core/DataBufferHeap.h"
13#include "lldb/Core/Error.h"
14#include "lldb/Core/Stream.h"
15#include "lldb/Core/ValueObject.h"
16#include "lldb/Core/ValueObjectConstResult.h"
17#include "lldb/Host/Endian.h"
18#include "lldb/Symbol/ClangASTContext.h"
19#include "lldb/Target/ObjCLanguageRuntime.h"
20#include "lldb/Target/Target.h"
21
22using namespace lldb;
23using namespace lldb_private;
24using namespace lldb_private::formatters;
25
26bool
27lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream)
28{
29 ProcessSP process_sp = valobj.GetProcessSP();
30 if (!process_sp)
31 return false;
32
33 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
34
35 if (!runtime)
36 return false;
37
38 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
39
40 if (!descriptor.get() || !descriptor->IsValid())
41 return false;
42
43 uint32_t ptr_size = process_sp->GetAddressByteSize();
44
45 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
46
47 if (!valobj_addr)
48 return false;
49
50 const char* class_name = descriptor->GetClassName().GetCString();
51
52 if (!class_name || !*class_name)
53 return false;
54
55 if (!strcmp(class_name,"NSBundle"))
56 {
57 uint64_t offset = 5 * ptr_size;
Enrico Granatabdbda932013-03-20 19:04:28 +000058 ClangASTType type(valobj.GetClangAST(),ClangASTContext::GetBuiltInType_objc_id(valobj.GetClangAST()));
Enrico Granata92373532013-03-19 22:58:48 +000059 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, type, true));
Jim Ingham5c42d8a2013-05-15 18:27:08 +000060
Enrico Granata92373532013-03-19 22:58:48 +000061 StreamString summary_stream;
62 bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
63 if (was_nsstring_ok && summary_stream.GetSize() > 0)
64 {
65 stream.Printf("%s",summary_stream.GetData());
66 return true;
67 }
68 }
69 // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
70 // which is encoded differently and needs to be handled by running code
71 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream);
72}
73
74bool
75lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream)
76{
77 ProcessSP process_sp = valobj.GetProcessSP();
78 if (!process_sp)
79 return false;
80
81 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
82
83 if (!runtime)
84 return false;
85
86 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
87
88 if (!descriptor.get() || !descriptor->IsValid())
89 return false;
90
91 uint32_t ptr_size = process_sp->GetAddressByteSize();
92
93 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
94
95 if (!valobj_addr)
96 return false;
97
98 const char* class_name = descriptor->GetClassName().GetCString();
99
100 if (!class_name || !*class_name)
101 return false;
102
103 if (!strcmp(class_name,"__NSTimeZone"))
104 {
105 uint64_t offset = ptr_size;
106 ClangASTType type(valobj.GetClangAST(),valobj.GetClangType());
107 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, type, true));
108 StreamString summary_stream;
109 bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
110 if (was_nsstring_ok && summary_stream.GetSize() > 0)
111 {
112 stream.Printf("%s",summary_stream.GetData());
113 return true;
114 }
115 }
116 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
117}
118
119bool
120lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream)
121{
122 ProcessSP process_sp = valobj.GetProcessSP();
123 if (!process_sp)
124 return false;
125
126 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
127
128 if (!runtime)
129 return false;
130
131 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
132
133 if (!descriptor.get() || !descriptor->IsValid())
134 return false;
135
136 uint32_t ptr_size = process_sp->GetAddressByteSize();
137
138 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
139
140 if (!valobj_addr)
141 return false;
142
143 const char* class_name = descriptor->GetClassName().GetCString();
144
145 if (!class_name || !*class_name)
146 return false;
147
148 if (!strcmp(class_name,"NSConcreteNotification"))
149 {
150 uint64_t offset = ptr_size;
151 ClangASTType type(valobj.GetClangAST(),valobj.GetClangType());
152 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, type, true));
153 StreamString summary_stream;
154 bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
155 if (was_nsstring_ok && summary_stream.GetSize() > 0)
156 {
157 stream.Printf("%s",summary_stream.GetData());
158 return true;
159 }
160 }
161 // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
162 // which is encoded differently and needs to be handled by running code
163 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
164}
165
166bool
167lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream)
168{
169 ProcessSP process_sp = valobj.GetProcessSP();
170 if (!process_sp)
171 return false;
172
173 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
174
175 if (!runtime)
176 return false;
177
178 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
179
180 if (!descriptor.get() || !descriptor->IsValid())
181 return false;
182
183 uint32_t ptr_size = process_sp->GetAddressByteSize();
184
185 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
186
187 if (!valobj_addr)
188 return false;
189
190 const char* class_name = descriptor->GetClassName().GetCString();
191
192 if (!class_name || !*class_name)
193 return false;
194
195 uint64_t port_number = 0;
196
197 do
198 {
199 if (!strcmp(class_name,"NSMachPort"))
200 {
201 uint64_t offset = (ptr_size == 4 ? 12 : 20);
202 Error error;
203 port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error);
204 if (error.Success())
205 break;
206 }
207 if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number))
208 return false;
209 } while (false);
210
211 stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF));
212 return true;
213}
214
215bool
216lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream)
217{
218 ProcessSP process_sp = valobj.GetProcessSP();
219 if (!process_sp)
220 return false;
221
222 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
223
224 if (!runtime)
225 return false;
226
227 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
228
229 if (!descriptor.get() || !descriptor->IsValid())
230 return false;
231
232 uint32_t ptr_size = process_sp->GetAddressByteSize();
233
234 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
235
236 if (!valobj_addr)
237 return false;
238
239 const char* class_name = descriptor->GetClassName().GetCString();
240
241 if (!class_name || !*class_name)
242 return false;
243
244 uint64_t count = 0;
245
246 do {
247 if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet"))
248 {
249 Error error;
250 uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 4, 0, error);
251 if (error.Fail())
252 return false;
253 // this means the set is empty - count = 0
254 if ((mode & 1) == 1)
255 {
256 count = 0;
257 break;
258 }
259 if ((mode & 2) == 2)
260 mode = 1; // this means the set only has one range
261 else
262 mode = 2; // this means the set has multiple ranges
263 if (mode == 1)
264 {
265 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+3*ptr_size, ptr_size, 0, error);
266 if (error.Fail())
267 return false;
268 }
269 else
270 {
271 // read a pointer to the data at 2*ptr_size
272 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error);
273 if (error.Fail())
274 return false;
275 // read the data at 2*ptr_size from the first location
276 count = process_sp->ReadUnsignedIntegerFromMemory(count+2*ptr_size, ptr_size, 0, error);
277 if (error.Fail())
278 return false;
279 }
280 }
281 else
282 {
283 if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count))
284 return false;
285 }
286 } while (false);
287 stream.Printf("%llu index%s",
288 count,
289 (count == 1 ? "" : "es"));
290 return true;
291}
292
293bool
294lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream)
295{
296 ProcessSP process_sp = valobj.GetProcessSP();
297 if (!process_sp)
298 return false;
299
300 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
301
302 if (!runtime)
303 return false;
304
305 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
306
307 if (!descriptor.get() || !descriptor->IsValid())
308 return false;
309
310 uint32_t ptr_size = process_sp->GetAddressByteSize();
311
312 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
313
314 if (!valobj_addr)
315 return false;
316
317 const char* class_name = descriptor->GetClassName().GetCString();
318
319 if (!class_name || !*class_name)
320 return false;
321
322 if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber"))
323 {
Enrico Granataf15ee4e2013-04-05 18:49:06 +0000324 uint64_t value = 0;
325 uint64_t i_bits = 0;
326 if (descriptor->GetTaggedPointerInfo(&i_bits,&value))
Enrico Granata92373532013-03-19 22:58:48 +0000327 {
Enrico Granata92373532013-03-19 22:58:48 +0000328 switch (i_bits)
329 {
330 case 0:
331 stream.Printf("(char)%hhd",(char)value);
332 break;
Enrico Granatab636be72013-04-24 17:49:08 +0000333 case 1:
Enrico Granata92373532013-03-19 22:58:48 +0000334 case 4:
335 stream.Printf("(short)%hd",(short)value);
336 break;
Enrico Granatab636be72013-04-24 17:49:08 +0000337 case 2:
Enrico Granata92373532013-03-19 22:58:48 +0000338 case 8:
339 stream.Printf("(int)%d",(int)value);
340 break;
Enrico Granatab636be72013-04-24 17:49:08 +0000341 case 3:
Enrico Granata92373532013-03-19 22:58:48 +0000342 case 12:
343 stream.Printf("(long)%" PRId64,value);
344 break;
345 default:
Enrico Granatab636be72013-04-24 17:49:08 +0000346 stream.Printf("unexpected value:(info=%" PRIu64 ", value=%" PRIu64,i_bits,value);
Enrico Granata92373532013-03-19 22:58:48 +0000347 break;
348 }
349 return true;
350 }
351 else
352 {
353 Error error;
354 uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 0, error) & 0x1F);
355 uint64_t data_location = valobj_addr + 2*ptr_size;
356 uint64_t value = 0;
357 if (error.Fail())
358 return false;
359 switch (data_type)
360 {
361 case 1: // 0B00001
362 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error);
363 if (error.Fail())
364 return false;
365 stream.Printf("(char)%hhd",(char)value);
366 break;
367 case 2: // 0B0010
368 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error);
369 if (error.Fail())
370 return false;
371 stream.Printf("(short)%hd",(short)value);
372 break;
373 case 3: // 0B0011
374 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
375 if (error.Fail())
376 return false;
377 stream.Printf("(int)%d",(int)value);
378 break;
379 case 17: // 0B10001
380 data_location += 8;
381 case 4: // 0B0100
382 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
383 if (error.Fail())
384 return false;
385 stream.Printf("(long)%" PRId64,value);
386 break;
387 case 5: // 0B0101
388 {
389 uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
390 if (error.Fail())
391 return false;
392 float flt_value = *((float*)&flt_as_int);
393 stream.Printf("(float)%f",flt_value);
394 break;
395 }
396 case 6: // 0B0110
397 {
398 uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
399 if (error.Fail())
400 return false;
401 double dbl_value = *((double*)&dbl_as_lng);
402 stream.Printf("(double)%g",dbl_value);
403 break;
404 }
405 default:
Enrico Granata7c0788b2013-03-26 18:55:08 +0000406 stream.Printf("unexpected value: dt=%d",data_type);
Enrico Granata92373532013-03-19 22:58:48 +0000407 break;
408 }
409 return true;
410 }
411 }
412 else
413 {
414 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream);
415 }
416}
417
418bool
419lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream)
420{
421 ProcessSP process_sp = valobj.GetProcessSP();
422 if (!process_sp)
423 return false;
424
425 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
426
427 if (!runtime)
428 return false;
429
430 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
431
432 if (!descriptor.get() || !descriptor->IsValid())
433 return false;
434
435 uint32_t ptr_size = process_sp->GetAddressByteSize();
436
437 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
438
439 if (!valobj_addr)
440 return false;
441
442 const char* class_name = descriptor->GetClassName().GetCString();
443
444 if (!class_name || !*class_name)
445 return false;
446
447 if (strcmp(class_name, "NSURL") == 0)
448 {
449 uint64_t offset_text = ptr_size + ptr_size + 8; // ISA + pointer + 8 bytes of data (even on 32bit)
450 uint64_t offset_base = offset_text + ptr_size;
451 ClangASTType type(valobj.GetClangAST(),valobj.GetClangType());
452 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
453 ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
454 if (!text)
455 return false;
456 if (text->GetValueAsUnsigned(0) == 0)
457 return false;
458 StreamString summary;
459 if (!NSStringSummaryProvider(*text, summary))
460 return false;
461 if (base && base->GetValueAsUnsigned(0))
462 {
463 if (summary.GetSize() > 0)
464 summary.GetString().resize(summary.GetSize()-1);
465 summary.Printf(" -- ");
466 StreamString base_summary;
467 if (NSURLSummaryProvider(*base, base_summary) && base_summary.GetSize() > 0)
468 summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData());
469 }
470 if (summary.GetSize())
471 {
472 stream.Printf("%s",summary.GetData());
473 return true;
474 }
475 }
476 else
477 {
478 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream);
479 }
480 return false;
481}
482
483bool
484lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream)
485{
486 ProcessSP process_sp = valobj.GetProcessSP();
487 if (!process_sp)
488 return false;
489
490 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
491
492 if (!runtime)
493 return false;
494
495 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
496
497 if (!descriptor.get() || !descriptor->IsValid())
498 return false;
499
500 uint32_t ptr_size = process_sp->GetAddressByteSize();
501
502 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
503
504 if (!valobj_addr)
505 return false;
506
507 uint64_t date_value_bits = 0;
508 double date_value = 0.0;
509
510 const char* class_name = descriptor->GetClassName().GetCString();
511
512 if (!class_name || !*class_name)
513 return false;
514
515 if (strcmp(class_name,"NSDate") == 0 ||
516 strcmp(class_name,"__NSDate") == 0 ||
517 strcmp(class_name,"__NSTaggedDate") == 0)
518 {
Enrico Granataf15ee4e2013-04-05 18:49:06 +0000519 uint64_t info_bits=0,value_bits = 0;
520 if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits))
Enrico Granata92373532013-03-19 22:58:48 +0000521 {
Enrico Granata92373532013-03-19 22:58:48 +0000522 date_value_bits = ((value_bits << 8) | (info_bits << 4));
523 date_value = *((double*)&date_value_bits);
524 }
525 else
526 {
527 Error error;
528 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error);
529 date_value = *((double*)&date_value_bits);
530 if (error.Fail())
531 return false;
532 }
533 }
534 else if (!strcmp(class_name,"NSCalendarDate"))
535 {
536 Error error;
537 date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error);
538 date_value = *((double*)&date_value_bits);
539 if (error.Fail())
540 return false;
541 }
542 else
543 {
544 if (ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits) == false)
545 return false;
546 date_value = *((double*)&date_value_bits);
547 }
548 if (date_value == -63114076800)
549 {
550 stream.Printf("0001-12-30 00:00:00 +0000");
551 return true;
552 }
553 // this snippet of code assumes that time_t == seconds since Jan-1-1970
554 // this is generally true and POSIXly happy, but might break if a library
555 // vendor decides to get creative
556 time_t epoch = GetOSXEpoch();
557 epoch = epoch + (time_t)date_value;
558 tm *tm_date = localtime(&epoch);
559 if (!tm_date)
560 return false;
561 std::string buffer(1024,0);
562 if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0)
563 return false;
564 stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year+1900, tm_date->tm_mon+1, tm_date->tm_mday, tm_date->tm_hour, tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
565 return true;
566}