1 //===-- 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/lldb-python.h"
11 
12 #include "lldb/DataFormatters/CXXFormatterFunctions.h"
13 
14 #include "lldb/Core/DataBufferHeap.h"
15 #include "lldb/Core/Error.h"
16 #include "lldb/Core/Stream.h"
17 #include "lldb/Core/ValueObject.h"
18 #include "lldb/Core/ValueObjectConstResult.h"
19 #include "lldb/Host/Endian.h"
20 #include "lldb/Symbol/ClangASTContext.h"
21 #include "lldb/Target/ObjCLanguageRuntime.h"
22 #include "lldb/Target/Target.h"
23 
24 using namespace lldb;
25 using namespace lldb_private;
26 using namespace lldb_private::formatters;
27 
28 bool
NSBundleSummaryProvider(ValueObject & valobj,Stream & stream)29 lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream)
30 {
31     ProcessSP process_sp = valobj.GetProcessSP();
32     if (!process_sp)
33         return false;
34 
35     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
36 
37     if (!runtime)
38         return false;
39 
40     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
41 
42     if (!descriptor.get() || !descriptor->IsValid())
43         return false;
44 
45     uint32_t ptr_size = process_sp->GetAddressByteSize();
46 
47     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
48 
49     if (!valobj_addr)
50         return false;
51 
52     const char* class_name = descriptor->GetClassName().GetCString();
53 
54     if (!class_name || !*class_name)
55         return false;
56 
57     if (!strcmp(class_name,"NSBundle"))
58     {
59         uint64_t offset = 5 * ptr_size;
60         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), true));
61 
62         StreamString summary_stream;
63         bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
64         if (was_nsstring_ok && summary_stream.GetSize() > 0)
65         {
66             stream.Printf("%s",summary_stream.GetData());
67             return true;
68         }
69     }
70     // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
71     // which is encoded differently and needs to be handled by running code
72     return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream);
73 }
74 
75 bool
NSTimeZoneSummaryProvider(ValueObject & valobj,Stream & stream)76 lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream)
77 {
78     ProcessSP process_sp = valobj.GetProcessSP();
79     if (!process_sp)
80         return false;
81 
82     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
83 
84     if (!runtime)
85         return false;
86 
87     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
88 
89     if (!descriptor.get() || !descriptor->IsValid())
90         return false;
91 
92     uint32_t ptr_size = process_sp->GetAddressByteSize();
93 
94     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
95 
96     if (!valobj_addr)
97         return false;
98 
99     const char* class_name = descriptor->GetClassName().GetCString();
100 
101     if (!class_name || !*class_name)
102         return false;
103 
104     if (!strcmp(class_name,"__NSTimeZone"))
105     {
106         uint64_t offset = ptr_size;
107         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), 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 
119 bool
NSNotificationSummaryProvider(ValueObject & valobj,Stream & stream)120 lldb_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         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true));
152         StreamString summary_stream;
153         bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream);
154         if (was_nsstring_ok && summary_stream.GetSize() > 0)
155         {
156             stream.Printf("%s",summary_stream.GetData());
157             return true;
158         }
159     }
160     // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
161     // which is encoded differently and needs to be handled by running code
162     return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream);
163 }
164 
165 bool
NSMachPortSummaryProvider(ValueObject & valobj,Stream & stream)166 lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream)
167 {
168     ProcessSP process_sp = valobj.GetProcessSP();
169     if (!process_sp)
170         return false;
171 
172     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
173 
174     if (!runtime)
175         return false;
176 
177     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
178 
179     if (!descriptor.get() || !descriptor->IsValid())
180         return false;
181 
182     uint32_t ptr_size = process_sp->GetAddressByteSize();
183 
184     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
185 
186     if (!valobj_addr)
187         return false;
188 
189     const char* class_name = descriptor->GetClassName().GetCString();
190 
191     if (!class_name || !*class_name)
192         return false;
193 
194     uint64_t port_number = 0;
195 
196     do
197     {
198         if (!strcmp(class_name,"NSMachPort"))
199         {
200             uint64_t offset = (ptr_size == 4 ? 12 : 20);
201             Error error;
202             port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error);
203             if (error.Success())
204                 break;
205         }
206         if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number))
207             return false;
208     } while (false);
209 
210     stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF));
211     return true;
212 }
213 
214 bool
NSIndexSetSummaryProvider(ValueObject & valobj,Stream & stream)215 lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream)
216 {
217     ProcessSP process_sp = valobj.GetProcessSP();
218     if (!process_sp)
219         return false;
220 
221     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
222 
223     if (!runtime)
224         return false;
225 
226     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
227 
228     if (!descriptor.get() || !descriptor->IsValid())
229         return false;
230 
231     uint32_t ptr_size = process_sp->GetAddressByteSize();
232 
233     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
234 
235     if (!valobj_addr)
236         return false;
237 
238     const char* class_name = descriptor->GetClassName().GetCString();
239 
240     if (!class_name || !*class_name)
241         return false;
242 
243     uint64_t count = 0;
244 
245     do {
246         if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet"))
247         {
248             Error error;
249             uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 4, 0, error);
250             if (error.Fail())
251                 return false;
252             // this means the set is empty - count = 0
253             if ((mode & 1) == 1)
254             {
255                 count = 0;
256                 break;
257             }
258             if ((mode & 2) == 2)
259                 mode = 1; // this means the set only has one range
260             else
261                 mode = 2; // this means the set has multiple ranges
262             if (mode == 1)
263             {
264                 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+3*ptr_size, ptr_size, 0, error);
265                 if (error.Fail())
266                     return false;
267             }
268             else
269             {
270                 // read a pointer to the data at 2*ptr_size
271                 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error);
272                 if (error.Fail())
273                     return false;
274                 // read the data at 2*ptr_size from the first location
275                 count = process_sp->ReadUnsignedIntegerFromMemory(count+2*ptr_size, ptr_size, 0, error);
276                 if (error.Fail())
277                     return false;
278             }
279         }
280         else
281         {
282             if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count))
283                 return false;
284         }
285     }  while (false);
286     stream.Printf("%" PRIu64 " index%s",
287                   count,
288                   (count == 1 ? "" : "es"));
289     return true;
290 }
291 
292 bool
NSNumberSummaryProvider(ValueObject & valobj,Stream & stream)293 lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream)
294 {
295     ProcessSP process_sp = valobj.GetProcessSP();
296     if (!process_sp)
297         return false;
298 
299     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
300 
301     if (!runtime)
302         return false;
303 
304     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
305 
306     if (!descriptor.get() || !descriptor->IsValid())
307         return false;
308 
309     uint32_t ptr_size = process_sp->GetAddressByteSize();
310 
311     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
312 
313     if (!valobj_addr)
314         return false;
315 
316     const char* class_name = descriptor->GetClassName().GetCString();
317 
318     if (!class_name || !*class_name)
319         return false;
320 
321     if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber"))
322     {
323         uint64_t value = 0;
324         uint64_t i_bits = 0;
325         if (descriptor->GetTaggedPointerInfo(&i_bits,&value))
326         {
327             switch (i_bits)
328             {
329                 case 0:
330                     stream.Printf("(char)%hhd",(char)value);
331                     break;
332                 case 1:
333                 case 4:
334                     stream.Printf("(short)%hd",(short)value);
335                     break;
336                 case 2:
337                 case 8:
338                     stream.Printf("(int)%d",(int)value);
339                     break;
340                 case 3:
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("unexpected value: 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 
417 bool
NSURLSummaryProvider(ValueObject & valobj,Stream & stream)418 lldb_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.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 
482 bool
NSDateSummaryProvider(ValueObject & valobj,Stream & stream)483 lldb_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         uint64_t info_bits=0,value_bits = 0;
519         if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits))
520         {
521             date_value_bits = ((value_bits << 8) | (info_bits << 4));
522             date_value = *((double*)&date_value_bits);
523         }
524         else
525         {
526             Error error;
527             date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error);
528             date_value = *((double*)&date_value_bits);
529             if (error.Fail())
530                 return false;
531         }
532     }
533     else if (!strcmp(class_name,"NSCalendarDate"))
534     {
535         Error error;
536         date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error);
537         date_value = *((double*)&date_value_bits);
538         if (error.Fail())
539             return false;
540     }
541     else
542     {
543         if (ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits) == false)
544             return false;
545         date_value = *((double*)&date_value_bits);
546     }
547     if (date_value == -63114076800)
548     {
549         stream.Printf("0001-12-30 00:00:00 +0000");
550         return true;
551     }
552     // this snippet of code assumes that time_t == seconds since Jan-1-1970
553     // this is generally true and POSIXly happy, but might break if a library
554     // vendor decides to get creative
555     time_t epoch = GetOSXEpoch();
556     epoch = epoch + (time_t)date_value;
557     tm *tm_date = localtime(&epoch);
558     if (!tm_date)
559         return false;
560     std::string buffer(1024,0);
561     if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0)
562         return false;
563     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());
564     return true;
565 }
566