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