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