blob: afc7fe21166098eff21ec3e0ac7152ddb776835b [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));
Enrico Granatabdbda932013-03-20 19:04:28 +000060 valobj_addr = text->GetValueAsUnsigned(0);
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 {
324 if (descriptor->IsTagged())
325 {
326 // we have a call to get info and value bits in the tagged descriptor. but we prefer not to cast and replicate them
327 int64_t value = (valobj_addr & ~0x0000000000000000FFL) >> 8;
328 uint64_t i_bits = (valobj_addr & 0xF0) >> 4;
329
330 switch (i_bits)
331 {
332 case 0:
333 stream.Printf("(char)%hhd",(char)value);
334 break;
335 case 4:
336 stream.Printf("(short)%hd",(short)value);
337 break;
338 case 8:
339 stream.Printf("(int)%d",(int)value);
340 break;
341 case 12:
342 stream.Printf("(long)%" PRId64,value);
343 break;
344 default:
345 stream.Printf("unexpected value:(info=%" PRIu64 ", value=%" PRIu64,i_bits,value);
346 break;
347 }
348 return true;
349 }
350 else
351 {
352 Error error;
353 uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 0, error) & 0x1F);
354 uint64_t data_location = valobj_addr + 2*ptr_size;
355 uint64_t value = 0;
356 if (error.Fail())
357 return false;
358 switch (data_type)
359 {
360 case 1: // 0B00001
361 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error);
362 if (error.Fail())
363 return false;
364 stream.Printf("(char)%hhd",(char)value);
365 break;
366 case 2: // 0B0010
367 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error);
368 if (error.Fail())
369 return false;
370 stream.Printf("(short)%hd",(short)value);
371 break;
372 case 3: // 0B0011
373 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
374 if (error.Fail())
375 return false;
376 stream.Printf("(int)%d",(int)value);
377 break;
378 case 17: // 0B10001
379 data_location += 8;
380 case 4: // 0B0100
381 value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
382 if (error.Fail())
383 return false;
384 stream.Printf("(long)%" PRId64,value);
385 break;
386 case 5: // 0B0101
387 {
388 uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
389 if (error.Fail())
390 return false;
391 float flt_value = *((float*)&flt_as_int);
392 stream.Printf("(float)%f",flt_value);
393 break;
394 }
395 case 6: // 0B0110
396 {
397 uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
398 if (error.Fail())
399 return false;
400 double dbl_value = *((double*)&dbl_as_lng);
401 stream.Printf("(double)%g",dbl_value);
402 break;
403 }
404 default:
405 stream.Printf("absurd: dt=%d",data_type);
406 break;
407 }
408 return true;
409 }
410 }
411 else
412 {
413 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream);
414 }
415}
416
417bool
418lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream)
419{
420 ProcessSP process_sp = valobj.GetProcessSP();
421 if (!process_sp)
422 return false;
423
424 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
425
426 if (!runtime)
427 return false;
428
429 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
430
431 if (!descriptor.get() || !descriptor->IsValid())
432 return false;
433
434 uint32_t ptr_size = process_sp->GetAddressByteSize();
435
436 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
437
438 if (!valobj_addr)
439 return false;
440
441 const char* class_name = descriptor->GetClassName().GetCString();
442
443 if (!class_name || !*class_name)
444 return false;
445
446 if (strcmp(class_name, "NSURL") == 0)
447 {
448 uint64_t offset_text = ptr_size + ptr_size + 8; // ISA + pointer + 8 bytes of data (even on 32bit)
449 uint64_t offset_base = offset_text + ptr_size;
450 ClangASTType type(valobj.GetClangAST(),valobj.GetClangType());
451 ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
452 ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
453 if (!text)
454 return false;
455 if (text->GetValueAsUnsigned(0) == 0)
456 return false;
457 StreamString summary;
458 if (!NSStringSummaryProvider(*text, summary))
459 return false;
460 if (base && base->GetValueAsUnsigned(0))
461 {
462 if (summary.GetSize() > 0)
463 summary.GetString().resize(summary.GetSize()-1);
464 summary.Printf(" -- ");
465 StreamString base_summary;
466 if (NSURLSummaryProvider(*base, base_summary) && base_summary.GetSize() > 0)
467 summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData());
468 }
469 if (summary.GetSize())
470 {
471 stream.Printf("%s",summary.GetData());
472 return true;
473 }
474 }
475 else
476 {
477 return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream);
478 }
479 return false;
480}
481
482bool
483lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream)
484{
485 ProcessSP process_sp = valobj.GetProcessSP();
486 if (!process_sp)
487 return false;
488
489 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
490
491 if (!runtime)
492 return false;
493
494 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
495
496 if (!descriptor.get() || !descriptor->IsValid())
497 return false;
498
499 uint32_t ptr_size = process_sp->GetAddressByteSize();
500
501 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
502
503 if (!valobj_addr)
504 return false;
505
506 uint64_t date_value_bits = 0;
507 double date_value = 0.0;
508
509 const char* class_name = descriptor->GetClassName().GetCString();
510
511 if (!class_name || !*class_name)
512 return false;
513
514 if (strcmp(class_name,"NSDate") == 0 ||
515 strcmp(class_name,"__NSDate") == 0 ||
516 strcmp(class_name,"__NSTaggedDate") == 0)
517 {
518 if (descriptor->IsTagged())
519 {
520 uint64_t info_bits = (valobj_addr & 0xF0ULL) >> 4;
521 uint64_t value_bits = (valobj_addr & ~0x0000000000000000FFULL) >> 8;
522 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}