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 // C Includes
11 // C++ Includes
12 // Other libraries and framework includes
13 // Project includes
14 #include "Cocoa.h"
15 
16 #include "lldb/Core/DataBufferHeap.h"
17 #include "lldb/Core/Error.h"
18 #include "lldb/Core/Mangled.h"
19 #include "lldb/Core/Stream.h"
20 #include "lldb/Core/ValueObject.h"
21 #include "lldb/Core/ValueObjectConstResult.h"
22 #include "lldb/DataFormatters/FormattersHelpers.h"
23 #include "lldb/DataFormatters/StringPrinter.h"
24 #include "lldb/DataFormatters/TypeSummary.h"
25 #include "lldb/Host/Endian.h"
26 #include "lldb/Symbol/ClangASTContext.h"
27 #include "lldb/Target/Language.h"
28 #include "lldb/Target/ObjCLanguageRuntime.h"
29 #include "lldb/Target/Target.h"
30 #include "lldb/Target/Process.h"
31 #include "lldb/Utility/ProcessStructReader.h"
32 
33 #include "NSString.h"
34 
35 using namespace lldb;
36 using namespace lldb_private;
37 using namespace lldb_private::formatters;
38 
39 bool
40 lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
41 {
42     ProcessSP process_sp = valobj.GetProcessSP();
43     if (!process_sp)
44         return false;
45 
46     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
47 
48     if (!runtime)
49         return false;
50 
51     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
52 
53     if (!descriptor || !descriptor->IsValid())
54         return false;
55 
56     uint32_t ptr_size = process_sp->GetAddressByteSize();
57 
58     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
59 
60     if (!valobj_addr)
61         return false;
62 
63     const char* class_name = descriptor->GetClassName().GetCString();
64 
65     if (!class_name || !*class_name)
66         return false;
67 
68     if (!strcmp(class_name,"NSBundle"))
69     {
70         uint64_t offset = 5 * ptr_size;
71         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), true));
72 
73         StreamString summary_stream;
74         bool was_nsstring_ok = NSStringSummaryProvider(*text, summary_stream, options);
75         if (was_nsstring_ok && summary_stream.GetSize() > 0)
76         {
77             stream.Printf("%s",summary_stream.GetData());
78             return true;
79         }
80     }
81     // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
82     // which is encoded differently and needs to be handled by running code
83     return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream, options.GetLanguage());
84 }
85 
86 bool
87 lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
88 {
89     ProcessSP process_sp = valobj.GetProcessSP();
90     if (!process_sp)
91         return false;
92 
93     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
94 
95     if (!runtime)
96         return false;
97 
98     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
99 
100     if (!descriptor || !descriptor->IsValid())
101         return false;
102 
103     uint32_t ptr_size = process_sp->GetAddressByteSize();
104 
105     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
106 
107     if (!valobj_addr)
108         return false;
109 
110     const char* class_name = descriptor->GetClassName().GetCString();
111 
112     if (!class_name || !*class_name)
113         return false;
114 
115     if (!strcmp(class_name,"__NSTimeZone"))
116     {
117         uint64_t offset = ptr_size;
118         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType(), true));
119         StreamString summary_stream;
120         bool was_nsstring_ok = NSStringSummaryProvider(*text, summary_stream, options);
121         if (was_nsstring_ok && summary_stream.GetSize() > 0)
122         {
123             stream.Printf("%s",summary_stream.GetData());
124             return true;
125         }
126     }
127     return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream, options.GetLanguage());
128 }
129 
130 bool
131 lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
132 {
133     ProcessSP process_sp = valobj.GetProcessSP();
134     if (!process_sp)
135         return false;
136 
137     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
138 
139     if (!runtime)
140         return false;
141 
142     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
143 
144     if (!descriptor || !descriptor->IsValid())
145         return false;
146 
147     uint32_t ptr_size = process_sp->GetAddressByteSize();
148 
149     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
150 
151     if (!valobj_addr)
152         return false;
153 
154     const char* class_name = descriptor->GetClassName().GetCString();
155 
156     if (!class_name || !*class_name)
157         return false;
158 
159     if (!strcmp(class_name,"NSConcreteNotification"))
160     {
161         uint64_t offset = ptr_size;
162         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType(), true));
163         StreamString summary_stream;
164         bool was_nsstring_ok = NSStringSummaryProvider(*text, summary_stream, options);
165         if (was_nsstring_ok && summary_stream.GetSize() > 0)
166         {
167             stream.Printf("%s",summary_stream.GetData());
168             return true;
169         }
170     }
171     // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle]
172     // which is encoded differently and needs to be handled by running code
173     return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream, options.GetLanguage());
174 }
175 
176 bool
177 lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
178 {
179     ProcessSP process_sp = valobj.GetProcessSP();
180     if (!process_sp)
181         return false;
182 
183     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
184 
185     if (!runtime)
186         return false;
187 
188     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
189 
190     if (!descriptor || !descriptor->IsValid())
191         return false;
192 
193     uint32_t ptr_size = process_sp->GetAddressByteSize();
194 
195     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
196 
197     if (!valobj_addr)
198         return false;
199 
200     const char* class_name = descriptor->GetClassName().GetCString();
201 
202     if (!class_name || !*class_name)
203         return false;
204 
205     uint64_t port_number = 0;
206 
207     do
208     {
209         if (!strcmp(class_name,"NSMachPort"))
210         {
211             uint64_t offset = (ptr_size == 4 ? 12 : 20);
212             Error error;
213             port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error);
214             if (error.Success())
215                 break;
216         }
217         if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number))
218             return false;
219     } while (false);
220 
221     stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF));
222     return true;
223 }
224 
225 bool
226 lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
227 {
228     ProcessSP process_sp = valobj.GetProcessSP();
229     if (!process_sp)
230         return false;
231 
232     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
233 
234     if (!runtime)
235         return false;
236 
237     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
238 
239     if (!descriptor || !descriptor->IsValid())
240         return false;
241 
242     uint32_t ptr_size = process_sp->GetAddressByteSize();
243 
244     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
245 
246     if (!valobj_addr)
247         return false;
248 
249     const char* class_name = descriptor->GetClassName().GetCString();
250 
251     if (!class_name || !*class_name)
252         return false;
253 
254     uint64_t count = 0;
255 
256     do {
257         if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet"))
258         {
259             Error error;
260             uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 4, 0, error);
261             if (error.Fail())
262                 return false;
263             // this means the set is empty - count = 0
264             if ((mode & 1) == 1)
265             {
266                 count = 0;
267                 break;
268             }
269             if ((mode & 2) == 2)
270                 mode = 1; // this means the set only has one range
271             else
272                 mode = 2; // this means the set has multiple ranges
273             if (mode == 1)
274             {
275                 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+3*ptr_size, ptr_size, 0, error);
276                 if (error.Fail())
277                     return false;
278             }
279             else
280             {
281                 // read a pointer to the data at 2*ptr_size
282                 count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error);
283                 if (error.Fail())
284                     return false;
285                 // read the data at 2*ptr_size from the first location
286                 count = process_sp->ReadUnsignedIntegerFromMemory(count+2*ptr_size, ptr_size, 0, error);
287                 if (error.Fail())
288                     return false;
289             }
290         }
291         else
292         {
293             if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count))
294                 return false;
295         }
296     }  while (false);
297     stream.Printf("%" PRIu64 " index%s",
298                   count,
299                   (count == 1 ? "" : "es"));
300     return true;
301 }
302 
303 static void
304 NSNumber_FormatChar (ValueObject& valobj,
305                      Stream& stream,
306                      char value,
307                      lldb::LanguageType lang)
308 {
309     static ConstString g_TypeHint("NSNumber:char");
310 
311     std::string prefix,suffix;
312     if (Language* language = Language::FindPlugin(lang))
313     {
314         if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix))
315         {
316             prefix.clear();
317             suffix.clear();
318         }
319     }
320 
321     stream.Printf("%s%hhd%s",
322                   prefix.c_str(),
323                   value,
324                   suffix.c_str());
325 }
326 
327 static void
328 NSNumber_FormatShort (ValueObject& valobj,
329                       Stream& stream,
330                       short value,
331                       lldb::LanguageType lang)
332 {
333     static ConstString g_TypeHint("NSNumber:short");
334 
335     std::string prefix,suffix;
336     if (Language* language = Language::FindPlugin(lang))
337     {
338         if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix))
339         {
340             prefix.clear();
341             suffix.clear();
342         }
343     }
344 
345     stream.Printf("%s%hd%s",
346                   prefix.c_str(),
347                   value,
348                   suffix.c_str());
349 }
350 
351 static void
352 NSNumber_FormatInt (ValueObject& valobj,
353                     Stream& stream,
354                     int value,
355                     lldb::LanguageType lang)
356 {
357     static ConstString g_TypeHint("NSNumber:int");
358 
359     std::string prefix,suffix;
360     if (Language* language = Language::FindPlugin(lang))
361     {
362         if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix))
363         {
364             prefix.clear();
365             suffix.clear();
366         }
367     }
368 
369     stream.Printf("%s%d%s",
370                   prefix.c_str(),
371                   value,
372                   suffix.c_str());
373 }
374 
375 static void
376 NSNumber_FormatLong (ValueObject& valobj,
377                      Stream& stream,
378                      uint64_t value,
379                      lldb::LanguageType lang)
380 {
381     static ConstString g_TypeHint("NSNumber:long");
382 
383     std::string prefix,suffix;
384     if (Language* language = Language::FindPlugin(lang))
385     {
386         if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix))
387         {
388             prefix.clear();
389             suffix.clear();
390         }
391     }
392 
393     stream.Printf("%s%" PRId64 "%s",
394                   prefix.c_str(),
395                   value,
396                   suffix.c_str());
397 }
398 
399 static void
400 NSNumber_FormatFloat (ValueObject& valobj,
401                       Stream& stream,
402                       float value,
403                       lldb::LanguageType lang)
404 {
405     static ConstString g_TypeHint("NSNumber:float");
406 
407     std::string prefix,suffix;
408     if (Language* language = Language::FindPlugin(lang))
409     {
410         if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix))
411         {
412             prefix.clear();
413             suffix.clear();
414         }
415     }
416 
417     stream.Printf("%s%f%s",
418                   prefix.c_str(),
419                   value,
420                   suffix.c_str());
421 }
422 
423 static void
424 NSNumber_FormatDouble (ValueObject& valobj,
425                        Stream& stream,
426                        double value,
427                        lldb::LanguageType lang)
428 {
429     static ConstString g_TypeHint("NSNumber:double");
430 
431     std::string prefix,suffix;
432     if (Language* language = Language::FindPlugin(lang))
433     {
434         if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix))
435         {
436             prefix.clear();
437             suffix.clear();
438         }
439     }
440 
441     stream.Printf("%s%g%s",
442                   prefix.c_str(),
443                   value,
444                   suffix.c_str());
445 }
446 
447 bool
448 lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
449 {
450     ProcessSP process_sp = valobj.GetProcessSP();
451     if (!process_sp)
452         return false;
453 
454     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
455 
456     if (!runtime)
457         return false;
458 
459     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
460 
461     if (!descriptor || !descriptor->IsValid())
462         return false;
463 
464     uint32_t ptr_size = process_sp->GetAddressByteSize();
465 
466     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
467 
468     if (!valobj_addr)
469         return false;
470 
471     const char* class_name = descriptor->GetClassName().GetCString();
472 
473     if (!class_name || !*class_name)
474         return false;
475 
476     if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber"))
477     {
478         uint64_t value = 0;
479         uint64_t i_bits = 0;
480         if (descriptor->GetTaggedPointerInfo(&i_bits,&value))
481         {
482             switch (i_bits)
483             {
484                 case 0:
485                     NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
486                     break;
487                 case 1:
488                 case 4:
489                     NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage());
490                     break;
491                 case 2:
492                 case 8:
493                     NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
494                     break;
495                 case 3:
496                 case 12:
497                     NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
498                     break;
499                 default:
500                     return false;
501             }
502             return true;
503         }
504         else
505         {
506             Error error;
507             uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 0, error) & 0x1F);
508             uint64_t data_location = valobj_addr + 2*ptr_size;
509             uint64_t value = 0;
510             if (error.Fail())
511                 return false;
512             switch (data_type)
513             {
514                 case 1: // 0B00001
515                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error);
516                     if (error.Fail())
517                         return false;
518                     NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
519                     break;
520                 case 2: // 0B0010
521                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error);
522                     if (error.Fail())
523                         return false;
524                     NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage());
525                     break;
526                 case 3: // 0B0011
527                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
528                     if (error.Fail())
529                         return false;
530                     NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
531                     break;
532                 case 17: // 0B10001
533                     data_location += 8;
534                     LLVM_FALLTHROUGH;
535                 case 4: // 0B0100
536                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
537                     if (error.Fail())
538                         return false;
539                     NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
540                     break;
541                 case 5: // 0B0101
542                 {
543                     uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
544                     if (error.Fail())
545                         return false;
546                     float flt_value = *((float*)&flt_as_int);
547                     NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());
548                     break;
549                 }
550                 case 6: // 0B0110
551                 {
552                     uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
553                     if (error.Fail())
554                         return false;
555                     double dbl_value = *((double*)&dbl_as_lng);
556                     NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());
557                     break;
558                 }
559                 default:
560                     return false;
561             }
562             return true;
563         }
564     }
565     else
566     {
567         return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream, options.GetLanguage());
568     }
569 }
570 
571 bool
572 lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
573 {
574     ProcessSP process_sp = valobj.GetProcessSP();
575     if (!process_sp)
576         return false;
577 
578     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
579 
580     if (!runtime)
581         return false;
582 
583     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
584 
585     if (!descriptor || !descriptor->IsValid())
586         return false;
587 
588     uint32_t ptr_size = process_sp->GetAddressByteSize();
589 
590     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
591 
592     if (!valobj_addr)
593         return false;
594 
595     const char* class_name = descriptor->GetClassName().GetCString();
596 
597     if (!class_name || !*class_name)
598         return false;
599 
600     if (strcmp(class_name, "NSURL") == 0)
601     {
602         uint64_t offset_text = ptr_size + ptr_size + 8; // ISA + pointer + 8 bytes of data (even on 32bit)
603         uint64_t offset_base = offset_text + ptr_size;
604         CompilerType type(valobj.GetCompilerType());
605         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
606         ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
607         if (!text)
608             return false;
609         if (text->GetValueAsUnsigned(0) == 0)
610             return false;
611         StreamString summary;
612         if (!NSStringSummaryProvider(*text, summary, options))
613             return false;
614         if (base && base->GetValueAsUnsigned(0))
615         {
616             if (summary.GetSize() > 0)
617                 summary.GetString().resize(summary.GetSize()-1);
618             summary.Printf(" -- ");
619             StreamString base_summary;
620             if (NSURLSummaryProvider(*base, base_summary, options) && base_summary.GetSize() > 0)
621                 summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData());
622         }
623         if (summary.GetSize())
624         {
625             stream.Printf("%s",summary.GetData());
626             return true;
627         }
628     }
629     else
630     {
631         return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream, options.GetLanguage());
632     }
633     return false;
634 }
635 
636 bool
637 lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
638 {
639     ProcessSP process_sp = valobj.GetProcessSP();
640     if (!process_sp)
641         return false;
642 
643     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
644 
645     if (!runtime)
646         return false;
647 
648     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
649 
650     if (!descriptor || !descriptor->IsValid())
651         return false;
652 
653     uint32_t ptr_size = process_sp->GetAddressByteSize();
654 
655     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
656 
657     if (!valobj_addr)
658         return false;
659 
660     uint64_t date_value_bits = 0;
661     double date_value = 0.0;
662 
663     const char* class_name = descriptor->GetClassName().GetCString();
664 
665     if (!class_name || !*class_name)
666         return false;
667 
668     if (strcmp(class_name,"NSDate") == 0 ||
669         strcmp(class_name,"__NSDate") == 0 ||
670         strcmp(class_name,"__NSTaggedDate") == 0)
671     {
672         uint64_t info_bits=0,value_bits = 0;
673         if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits))
674         {
675             date_value_bits = ((value_bits << 8) | (info_bits << 4));
676             date_value = *((double*)&date_value_bits);
677         }
678         else
679         {
680             Error error;
681             date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error);
682             date_value = *((double*)&date_value_bits);
683             if (error.Fail())
684                 return false;
685         }
686     }
687     else if (!strcmp(class_name,"NSCalendarDate"))
688     {
689         Error error;
690         date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error);
691         date_value = *((double*)&date_value_bits);
692         if (error.Fail())
693             return false;
694     }
695     else
696     {
697         if (!ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits))
698             return false;
699         date_value = *((double*)&date_value_bits);
700     }
701     if (date_value == -63114076800)
702     {
703         stream.Printf("0001-12-30 00:00:00 +0000");
704         return true;
705     }
706     // this snippet of code assumes that time_t == seconds since Jan-1-1970
707     // this is generally true and POSIXly happy, but might break if a library
708     // vendor decides to get creative
709     time_t epoch = GetOSXEpoch();
710     epoch = epoch + (time_t)date_value;
711     tm *tm_date = gmtime(&epoch);
712     if (!tm_date)
713         return false;
714     std::string buffer(1024,0);
715     if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0)
716         return false;
717     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());
718     return true;
719 }
720 
721 bool
722 lldb_private::formatters::ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
723 {
724     ProcessSP process_sp = valobj.GetProcessSP();
725     if (!process_sp)
726         return false;
727 
728     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
729 
730     if (!runtime)
731         return false;
732 
733     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0)));
734 
735     if (!descriptor || !descriptor->IsValid())
736         return false;
737 
738     ConstString class_name = descriptor->GetClassName();
739 
740     if (class_name.IsEmpty())
741         return false;
742 
743     if (ConstString cs = Mangled(class_name).GetDemangledName(lldb::eLanguageTypeUnknown))
744         class_name = cs;
745 
746     stream.Printf("%s",class_name.AsCString("<unknown class>"));
747     return true;
748 }
749 
750 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd
751 {
752 public:
753     ObjCClassSyntheticChildrenFrontEnd (lldb::ValueObjectSP valobj_sp) :
754         SyntheticChildrenFrontEnd(*valobj_sp)
755     {
756     }
757 
758     ~ObjCClassSyntheticChildrenFrontEnd() override = default;
759 
760     size_t
761     CalculateNumChildren() override
762     {
763         return 0;
764     }
765 
766     lldb::ValueObjectSP
767     GetChildAtIndex(size_t idx) override
768     {
769         return lldb::ValueObjectSP();
770     }
771 
772     bool
773     Update() override
774     {
775         return false;
776     }
777 
778     bool
779     MightHaveChildren() override
780     {
781         return false;
782     }
783 
784     size_t
785     GetIndexOfChildWithName(const ConstString &name) override
786     {
787         return UINT32_MAX;
788     }
789 };
790 
791 SyntheticChildrenFrontEnd*
792 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
793 {
794     return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp);
795 }
796 
797 template<bool needs_at>
798 bool
799 lldb_private::formatters::NSDataSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
800 {
801     ProcessSP process_sp = valobj.GetProcessSP();
802     if (!process_sp)
803         return false;
804 
805     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
806 
807     if (!runtime)
808         return false;
809 
810     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
811 
812     if (!descriptor || !descriptor->IsValid())
813         return false;
814 
815     bool is_64bit = (process_sp->GetAddressByteSize() == 8);
816     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
817 
818     if (!valobj_addr)
819         return false;
820 
821     uint64_t value = 0;
822 
823     const char* class_name = descriptor->GetClassName().GetCString();
824 
825     if (!class_name || !*class_name)
826         return false;
827 
828     if (!strcmp(class_name,"NSConcreteData") ||
829         !strcmp(class_name,"NSConcreteMutableData") ||
830         !strcmp(class_name,"__NSCFData"))
831     {
832         uint32_t offset = (is_64bit ? 16 : 8);
833         Error error;
834         value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, is_64bit ? 8 : 4, 0, error);
835         if (error.Fail())
836             return false;
837     }
838     else
839     {
840         if (!ExtractValueFromObjCExpression(valobj, "int", "length", value))
841             return false;
842     }
843 
844     stream.Printf("%s%" PRIu64 " byte%s%s",
845                   (needs_at ? "@\"" : ""),
846                   value,
847                   (value != 1 ? "s" : ""),
848                   (needs_at ? "\"" : ""));
849 
850     return true;
851 }
852 
853 bool
854 lldb_private::formatters::ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
855 {
856     const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo();
857 
858     ValueObjectSP real_guy_sp = valobj.GetSP();
859 
860     if (type_info & eTypeIsPointer)
861     {
862         Error err;
863         real_guy_sp = valobj.Dereference(err);
864         if (err.Fail() || !real_guy_sp)
865             return false;
866     }
867     else if (type_info & eTypeIsReference)
868     {
869         real_guy_sp =  valobj.GetChildAtIndex(0, true);
870         if (!real_guy_sp)
871             return false;
872     }
873     uint8_t value = (real_guy_sp->GetValueAsUnsigned(0) & 0xFF);
874     switch (value)
875     {
876         case 0:
877             stream.Printf("NO");
878             break;
879         case 1:
880             stream.Printf("YES");
881             break;
882         default:
883             stream.Printf("%u",value);
884             break;
885     }
886     return true;
887 }
888 
889 template <bool is_sel_ptr>
890 bool
891 lldb_private::formatters::ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
892 {
893     lldb::ValueObjectSP valobj_sp;
894 
895     CompilerType charstar (valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeChar).GetPointerType());
896 
897     if (!charstar)
898         return false;
899 
900     ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
901 
902     if (is_sel_ptr)
903     {
904         lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
905         if (data_address == LLDB_INVALID_ADDRESS)
906             return false;
907         valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, exe_ctx, charstar);
908     }
909     else
910     {
911         DataExtractor data;
912         Error error;
913         valobj.GetData(data, error);
914         if (error.Fail())
915             return false;
916         valobj_sp = ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar);
917     }
918 
919     if (!valobj_sp)
920         return false;
921 
922     stream.Printf("%s",valobj_sp->GetSummaryAsCString());
923     return true;
924 }
925 
926 // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001
927 // this call gives the POSIX equivalent of the Cocoa epoch
928 time_t
929 lldb_private::formatters::GetOSXEpoch ()
930 {
931     static time_t epoch = 0;
932     if (!epoch)
933     {
934 #ifndef _WIN32
935         tzset();
936         tm tm_epoch;
937         tm_epoch.tm_sec = 0;
938         tm_epoch.tm_hour = 0;
939         tm_epoch.tm_min = 0;
940         tm_epoch.tm_mon = 0;
941         tm_epoch.tm_mday = 1;
942         tm_epoch.tm_year = 2001-1900; // for some reason, we need to subtract 1900 from this field. not sure why.
943         tm_epoch.tm_isdst = -1;
944         tm_epoch.tm_gmtoff = 0;
945         tm_epoch.tm_zone = nullptr;
946         epoch = timegm(&tm_epoch);
947 #endif
948     }
949     return epoch;
950 }
951 
952 bool
953 lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
954 {
955     if (const char* description = valobj.GetObjectDescription())
956     {
957         stream.Printf("%s", description);
958         return true;
959     }
960     else
961         return false;
962 }
963 
964 template bool
965 lldb_private::formatters::NSDataSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&);
966 
967 template bool
968 lldb_private::formatters::NSDataSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&);
969 
970 template bool
971 lldb_private::formatters::ObjCSELSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&);
972 
973 template bool
974 lldb_private::formatters::ObjCSELSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&);
975