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.get() || !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.get(), 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.get() || !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.get(), 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.get() || !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.get(), 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.get() || !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.get() || !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.get() || !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                 case 4: // 0B0100
535                     value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
536                     if (error.Fail())
537                         return false;
538                     NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
539                     break;
540                 case 5: // 0B0101
541                 {
542                     uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error);
543                     if (error.Fail())
544                         return false;
545                     float flt_value = *((float*)&flt_as_int);
546                     NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());
547                     break;
548                 }
549                 case 6: // 0B0110
550                 {
551                     uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error);
552                     if (error.Fail())
553                         return false;
554                     double dbl_value = *((double*)&dbl_as_lng);
555                     NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());
556                     break;
557                 }
558                 default:
559                     return false;
560             }
561             return true;
562         }
563     }
564     else
565     {
566         return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream, options.GetLanguage());
567     }
568 }
569 
570 bool
571 lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
572 {
573     ProcessSP process_sp = valobj.GetProcessSP();
574     if (!process_sp)
575         return false;
576 
577     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
578 
579     if (!runtime)
580         return false;
581 
582     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
583 
584     if (!descriptor.get() || !descriptor->IsValid())
585         return false;
586 
587     uint32_t ptr_size = process_sp->GetAddressByteSize();
588 
589     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
590 
591     if (!valobj_addr)
592         return false;
593 
594     const char* class_name = descriptor->GetClassName().GetCString();
595 
596     if (!class_name || !*class_name)
597         return false;
598 
599     if (strcmp(class_name, "NSURL") == 0)
600     {
601         uint64_t offset_text = ptr_size + ptr_size + 8; // ISA + pointer + 8 bytes of data (even on 32bit)
602         uint64_t offset_base = offset_text + ptr_size;
603         CompilerType type(valobj.GetCompilerType());
604         ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
605         ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
606         if (!text)
607             return false;
608         if (text->GetValueAsUnsigned(0) == 0)
609             return false;
610         StreamString summary;
611         if (!NSStringSummaryProvider(*text, summary, options))
612             return false;
613         if (base && base->GetValueAsUnsigned(0))
614         {
615             if (summary.GetSize() > 0)
616                 summary.GetString().resize(summary.GetSize()-1);
617             summary.Printf(" -- ");
618             StreamString base_summary;
619             if (NSURLSummaryProvider(*base, base_summary, options) && base_summary.GetSize() > 0)
620                 summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData());
621         }
622         if (summary.GetSize())
623         {
624             stream.Printf("%s",summary.GetData());
625             return true;
626         }
627     }
628     else
629     {
630         return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream, options.GetLanguage());
631     }
632     return false;
633 }
634 
635 bool
636 lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
637 {
638     ProcessSP process_sp = valobj.GetProcessSP();
639     if (!process_sp)
640         return false;
641 
642     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
643 
644     if (!runtime)
645         return false;
646 
647     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
648 
649     if (!descriptor.get() || !descriptor->IsValid())
650         return false;
651 
652     uint32_t ptr_size = process_sp->GetAddressByteSize();
653 
654     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
655 
656     if (!valobj_addr)
657         return false;
658 
659     uint64_t date_value_bits = 0;
660     double date_value = 0.0;
661 
662     const char* class_name = descriptor->GetClassName().GetCString();
663 
664     if (!class_name || !*class_name)
665         return false;
666 
667     if (strcmp(class_name,"NSDate") == 0 ||
668         strcmp(class_name,"__NSDate") == 0 ||
669         strcmp(class_name,"__NSTaggedDate") == 0)
670     {
671         uint64_t info_bits=0,value_bits = 0;
672         if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits))
673         {
674             date_value_bits = ((value_bits << 8) | (info_bits << 4));
675             date_value = *((double*)&date_value_bits);
676         }
677         else
678         {
679             Error error;
680             date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error);
681             date_value = *((double*)&date_value_bits);
682             if (error.Fail())
683                 return false;
684         }
685     }
686     else if (!strcmp(class_name,"NSCalendarDate"))
687     {
688         Error error;
689         date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error);
690         date_value = *((double*)&date_value_bits);
691         if (error.Fail())
692             return false;
693     }
694     else
695     {
696         if (ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits) == false)
697             return false;
698         date_value = *((double*)&date_value_bits);
699     }
700     if (date_value == -63114076800)
701     {
702         stream.Printf("0001-12-30 00:00:00 +0000");
703         return true;
704     }
705     // this snippet of code assumes that time_t == seconds since Jan-1-1970
706     // this is generally true and POSIXly happy, but might break if a library
707     // vendor decides to get creative
708     time_t epoch = GetOSXEpoch();
709     epoch = epoch + (time_t)date_value;
710     tm *tm_date = gmtime(&epoch);
711     if (!tm_date)
712         return false;
713     std::string buffer(1024,0);
714     if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0)
715         return false;
716     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());
717     return true;
718 }
719 
720 bool
721 lldb_private::formatters::ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
722 {
723     ProcessSP process_sp = valobj.GetProcessSP();
724     if (!process_sp)
725         return false;
726 
727     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
728 
729     if (!runtime)
730         return false;
731 
732     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0)));
733 
734     if (!descriptor.get() || !descriptor->IsValid())
735         return false;
736 
737     ConstString class_name = descriptor->GetClassName();
738 
739     if (class_name.IsEmpty())
740         return false;
741 
742     if (ConstString cs = Mangled(class_name).GetDemangledName(lldb::eLanguageTypeUnknown))
743         class_name = cs;
744 
745     stream.Printf("%s",class_name.AsCString("<unknown class>"));
746     return true;
747 }
748 
749 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd
750 {
751 public:
752     ObjCClassSyntheticChildrenFrontEnd (lldb::ValueObjectSP valobj_sp) :
753     SyntheticChildrenFrontEnd(*valobj_sp.get())
754     {
755     }
756 
757     ~ObjCClassSyntheticChildrenFrontEnd() override = default;
758 
759     size_t
760     CalculateNumChildren() override
761     {
762         return 0;
763     }
764 
765     lldb::ValueObjectSP
766     GetChildAtIndex(size_t idx) override
767     {
768         return lldb::ValueObjectSP();
769     }
770 
771     bool
772     Update() override
773     {
774         return false;
775     }
776 
777     bool
778     MightHaveChildren() override
779     {
780         return false;
781     }
782 
783     size_t
784     GetIndexOfChildWithName(const ConstString &name) override
785     {
786         return UINT32_MAX;
787     }
788 };
789 
790 SyntheticChildrenFrontEnd*
791 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
792 {
793     return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp);
794 }
795 
796 template<bool needs_at>
797 bool
798 lldb_private::formatters::NSDataSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
799 {
800     ProcessSP process_sp = valobj.GetProcessSP();
801     if (!process_sp)
802         return false;
803 
804     ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
805 
806     if (!runtime)
807         return false;
808 
809     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj));
810 
811     if (!descriptor.get() || !descriptor->IsValid())
812         return false;
813 
814     bool is_64bit = (process_sp->GetAddressByteSize() == 8);
815     lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
816 
817     if (!valobj_addr)
818         return false;
819 
820     uint64_t value = 0;
821 
822     const char* class_name = descriptor->GetClassName().GetCString();
823 
824     if (!class_name || !*class_name)
825         return false;
826 
827     if (!strcmp(class_name,"NSConcreteData") ||
828         !strcmp(class_name,"NSConcreteMutableData") ||
829         !strcmp(class_name,"__NSCFData"))
830     {
831         uint32_t offset = (is_64bit ? 16 : 8);
832         Error error;
833         value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, is_64bit ? 8 : 4, 0, error);
834         if (error.Fail())
835             return false;
836     }
837     else
838     {
839         if (!ExtractValueFromObjCExpression(valobj, "int", "length", value))
840             return false;
841     }
842 
843     stream.Printf("%s%" PRIu64 " byte%s%s",
844                   (needs_at ? "@\"" : ""),
845                   value,
846                   (value != 1 ? "s" : ""),
847                   (needs_at ? "\"" : ""));
848 
849     return true;
850 }
851 
852 bool
853 lldb_private::formatters::ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
854 {
855     const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo();
856 
857     ValueObjectSP real_guy_sp = valobj.GetSP();
858 
859     if (type_info & eTypeIsPointer)
860     {
861         Error err;
862         real_guy_sp = valobj.Dereference(err);
863         if (err.Fail() || !real_guy_sp)
864             return false;
865     }
866     else if (type_info & eTypeIsReference)
867     {
868         real_guy_sp =  valobj.GetChildAtIndex(0, true);
869         if (!real_guy_sp)
870             return false;
871     }
872     uint64_t value = real_guy_sp->GetValueAsUnsigned(0);
873     if (value == 0)
874     {
875         stream.Printf("NO");
876         return true;
877     }
878     stream.Printf("YES");
879     return true;
880 }
881 
882 template <bool is_sel_ptr>
883 bool
884 lldb_private::formatters::ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
885 {
886     lldb::ValueObjectSP valobj_sp;
887 
888     CompilerType charstar (valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeChar).GetPointerType());
889 
890     if (!charstar)
891         return false;
892 
893     ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
894 
895     if (is_sel_ptr)
896     {
897         lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
898         if (data_address == LLDB_INVALID_ADDRESS)
899             return false;
900         valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, exe_ctx, charstar);
901     }
902     else
903     {
904         DataExtractor data;
905         Error error;
906         valobj.GetData(data, error);
907         if (error.Fail())
908             return false;
909         valobj_sp = ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar);
910     }
911 
912     if (!valobj_sp)
913         return false;
914 
915     stream.Printf("%s",valobj_sp->GetSummaryAsCString());
916     return true;
917 }
918 
919 // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001
920 // this call gives the POSIX equivalent of the Cocoa epoch
921 time_t
922 lldb_private::formatters::GetOSXEpoch ()
923 {
924     static time_t epoch = 0;
925     if (!epoch)
926     {
927 #ifndef _WIN32
928         tzset();
929         tm tm_epoch;
930         tm_epoch.tm_sec = 0;
931         tm_epoch.tm_hour = 0;
932         tm_epoch.tm_min = 0;
933         tm_epoch.tm_mon = 0;
934         tm_epoch.tm_mday = 1;
935         tm_epoch.tm_year = 2001-1900; // for some reason, we need to subtract 1900 from this field. not sure why.
936         tm_epoch.tm_isdst = -1;
937         tm_epoch.tm_gmtoff = 0;
938         tm_epoch.tm_zone = NULL;
939         epoch = timegm(&tm_epoch);
940 #endif
941     }
942     return epoch;
943 }
944 
945 bool
946 lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
947 {
948     if (const char* description = valobj.GetObjectDescription())
949     {
950         stream.Printf("%s", description);
951         return true;
952     }
953     else
954         return false;
955 }
956 
957 template bool
958 lldb_private::formatters::NSDataSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&);
959 
960 template bool
961 lldb_private::formatters::NSDataSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&);
962 
963 template bool
964 lldb_private::formatters::ObjCSELSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&);
965 
966 template bool
967 lldb_private::formatters::ObjCSELSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&);
968