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