1 //===-- Cocoa.cpp ---------------------------------------------------------===//
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 #include "NSString.h"
11 #include "ObjCConstants.h"
12 
13 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
14 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
15 #include "lldb/Core/Mangled.h"
16 #include "lldb/Core/ValueObject.h"
17 #include "lldb/Core/ValueObjectConstResult.h"
18 #include "lldb/DataFormatters/FormattersHelpers.h"
19 #include "lldb/DataFormatters/StringPrinter.h"
20 #include "lldb/DataFormatters/TypeSummary.h"
21 #include "lldb/Host/Time.h"
22 #include "lldb/Target/Language.h"
23 #include "lldb/Target/Process.h"
24 #include "lldb/Target/ProcessStructReader.h"
25 #include "lldb/Target/Target.h"
26 #include "lldb/Utility/DataBufferHeap.h"
27 #include "lldb/Utility/Endian.h"
28 #include "lldb/Utility/Status.h"
29 #include "lldb/Utility/Stream.h"
30 
31 #include "llvm/ADT/APInt.h"
32 #include "llvm/ADT/bit.h"
33 
34 
35 using namespace lldb;
36 using namespace lldb_private;
37 using namespace lldb_private::formatters;
38 
39 bool lldb_private::formatters::NSBundleSummaryProvider(
40     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
41   ProcessSP process_sp = valobj.GetProcessSP();
42   if (!process_sp)
43     return false;
44 
45   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
46 
47   if (!runtime)
48     return false;
49 
50   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
51       runtime->GetClassDescriptor(valobj));
52 
53   if (!descriptor || !descriptor->IsValid())
54     return false;
55 
56   uint32_t ptr_size = process_sp->GetAddressByteSize();
57 
58   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
59 
60   if (!valobj_addr)
61     return false;
62 
63   llvm::StringRef class_name(descriptor->GetClassName().GetCString());
64 
65   if (class_name.empty())
66     return false;
67 
68   if (class_name == "NSBundle") {
69     uint64_t offset = 5 * ptr_size;
70     ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
71         offset,
72         valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID),
73         true));
74 
75     if (!text)
76       return false;
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 = ObjCLanguageRuntime::Get(*process_sp);
97 
98   if (!runtime)
99     return false;
100 
101   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
102       runtime->GetClassDescriptor(valobj));
103 
104   if (!descriptor || !descriptor->IsValid())
105     return false;
106 
107   uint32_t ptr_size = process_sp->GetAddressByteSize();
108 
109   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
110 
111   if (!valobj_addr)
112     return false;
113 
114   llvm::StringRef class_name(descriptor->GetClassName().GetCString());
115 
116   if (class_name.empty())
117     return false;
118 
119   if (class_name == "__NSTimeZone") {
120     uint64_t offset = ptr_size;
121     ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
122         offset, valobj.GetCompilerType(), true));
123 
124     if (!text)
125       return false;
126 
127     StreamString summary_stream;
128     bool was_nsstring_ok =
129         NSStringSummaryProvider(*text, summary_stream, options);
130     if (was_nsstring_ok && summary_stream.GetSize() > 0) {
131       stream.Printf("%s", summary_stream.GetData());
132       return true;
133     }
134   }
135 
136   return false;
137 }
138 
139 bool lldb_private::formatters::NSNotificationSummaryProvider(
140     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
141   ProcessSP process_sp = valobj.GetProcessSP();
142   if (!process_sp)
143     return false;
144 
145   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
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 
173     if (!text)
174       return false;
175 
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 = ObjCLanguageRuntime::Get(*process_sp);
195 
196   if (!runtime)
197     return false;
198 
199   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
200       runtime->GetClassDescriptor(valobj));
201 
202   if (!descriptor || !descriptor->IsValid())
203     return false;
204 
205   uint32_t ptr_size = process_sp->GetAddressByteSize();
206 
207   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
208 
209   if (!valobj_addr)
210     return false;
211 
212   llvm::StringRef class_name(descriptor->GetClassName().GetCString());
213 
214   if (class_name.empty())
215     return false;
216 
217   uint64_t port_number = 0;
218 
219   if (class_name == "NSMachPort") {
220     uint64_t offset = (ptr_size == 4 ? 12 : 20);
221     Status error;
222     port_number = process_sp->ReadUnsignedIntegerFromMemory(
223         offset + valobj_addr, 4, 0, error);
224     if (error.Success()) {
225       stream.Printf("mach port: %u",
226                     (uint32_t)(port_number & 0x00000000FFFFFFFF));
227       return true;
228     }
229   }
230 
231   return false;
232 }
233 
234 bool lldb_private::formatters::NSIndexSetSummaryProvider(
235     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
236   ProcessSP process_sp = valobj.GetProcessSP();
237   if (!process_sp)
238     return false;
239 
240   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
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                                 int64_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 = llvm::toString(value, 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   Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS);
430   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
431 
432   if (!runtime)
433     return false;
434 
435   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
436       runtime->GetClassDescriptor(valobj));
437 
438   if (!descriptor || !descriptor->IsValid())
439     return false;
440 
441   uint32_t ptr_size = process_sp->GetAddressByteSize();
442 
443   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
444 
445   if (!valobj_addr)
446     return false;
447 
448   llvm::StringRef class_name(descriptor->GetClassName().GetCString());
449 
450   if (class_name.empty())
451     return false;
452 
453   if (class_name == "__NSCFBoolean")
454     return ObjCBooleanSummaryProvider(valobj, stream, options);
455 
456   if (class_name == "NSDecimalNumber")
457     return NSDecimalNumberSummaryProvider(valobj, stream, options);
458 
459   if (class_name == "NSConstantIntegerNumber") {
460     Status error;
461     int64_t value = process_sp->ReadSignedIntegerFromMemory(
462         valobj_addr + 2 * ptr_size, 8, 0, error);
463     if (error.Fail())
464       return false;
465     uint64_t encoding_addr = process_sp->ReadUnsignedIntegerFromMemory(
466         valobj_addr + ptr_size, ptr_size, 0, error);
467     if (error.Fail())
468       return false;
469     char encoding =
470         process_sp->ReadUnsignedIntegerFromMemory(encoding_addr, 1, 0, error);
471     if (error.Fail())
472       return false;
473 
474     switch (encoding) {
475     case _C_CHR:
476       NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
477       return true;
478     case _C_SHT:
479       NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage());
480       return true;
481     case _C_INT:
482       NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
483       return true;
484     case _C_LNG:
485     case _C_LNG_LNG:
486       NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
487       return true;
488 
489     case _C_UCHR:
490     case _C_USHT:
491     case _C_UINT:
492     case _C_ULNG:
493     case _C_ULNG_LNG:
494       stream.Printf("%" PRIu64, value);
495       return true;
496     }
497 
498     return false;
499   }
500 
501   if (class_name == "NSConstantFloatNumber") {
502     Status error;
503     uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(
504         valobj_addr + ptr_size, 4, 0, error);
505     if (error.Fail())
506       return false;
507     float flt_value = 0.0f;
508     memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int));
509     NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());
510     return true;
511   }
512 
513   if (class_name == "NSConstantDoubleNumber") {
514     Status error;
515     uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(
516         valobj_addr + ptr_size, 8, 0, error);
517     if (error.Fail())
518       return false;
519     double dbl_value = 0.0;
520     memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng));
521     NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());
522     return true;
523   }
524 
525   if (class_name == "NSNumber" || class_name == "__NSCFNumber") {
526     int64_t value = 0;
527     uint64_t i_bits = 0;
528     if (descriptor->GetTaggedPointerInfoSigned(&i_bits, &value)) {
529       // Check for "preserved" numbers.  We still don't support them yet.
530       if (i_bits & 0x8) {
531         if (log)
532           log->Printf(
533               "Unsupported (preserved) NSNumber tagged pointer 0x%" PRIu64,
534               valobj_addr);
535         return false;
536       }
537 
538       switch (i_bits) {
539       case 0:
540         NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
541         break;
542       case 1:
543       case 4:
544         NSNumber_FormatShort(valobj, stream, (short)value,
545                              options.GetLanguage());
546         break;
547       case 2:
548       case 8:
549         NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
550         break;
551       case 3:
552       case 12:
553         NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
554         break;
555       default:
556         return false;
557       }
558       return true;
559     } else {
560       Status error;
561 
562       AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
563           ObjCLanguageRuntime::Get(*process_sp));
564 
565       const bool new_format =
566           (runtime && runtime->GetFoundationVersion() >= 1400);
567 
568       enum class TypeCodes : int {
569         sint8 = 0x0,
570         sint16 = 0x1,
571         sint32 = 0x2,
572         sint64 = 0x3,
573         f32 = 0x4,
574         f64 = 0x5,
575         sint128 = 0x6
576       };
577 
578       uint64_t data_location = valobj_addr + 2 * ptr_size;
579       TypeCodes type_code;
580 
581       if (new_format) {
582         uint64_t cfinfoa = process_sp->ReadUnsignedIntegerFromMemory(
583             valobj_addr + ptr_size, ptr_size, 0, error);
584 
585         if (error.Fail())
586           return false;
587 
588         bool is_preserved_number = cfinfoa & 0x8;
589         if (is_preserved_number) {
590           if (log)
591             log->Printf(
592                 "Unsupported preserved NSNumber tagged pointer 0x%" PRIu64,
593                 valobj_addr);
594           return false;
595         }
596 
597         type_code = static_cast<TypeCodes>(cfinfoa & 0x7);
598       } else {
599         uint8_t data_type = process_sp->ReadUnsignedIntegerFromMemory(
600                                 valobj_addr + ptr_size, 1, 0, error) &
601                             0x1F;
602 
603         if (error.Fail())
604           return false;
605 
606         switch (data_type) {
607         case 1:
608           type_code = TypeCodes::sint8;
609           break;
610         case 2:
611           type_code = TypeCodes::sint16;
612           break;
613         case 3:
614           type_code = TypeCodes::sint32;
615           break;
616         case 17:
617           data_location += 8;
618           LLVM_FALLTHROUGH;
619         case 4:
620           type_code = TypeCodes::sint64;
621           break;
622         case 5:
623           type_code = TypeCodes::f32;
624           break;
625         case 6:
626           type_code = TypeCodes::f64;
627           break;
628         default:
629           return false;
630         }
631       }
632 
633       uint64_t value = 0;
634       bool success = false;
635       switch (type_code) {
636       case TypeCodes::sint8:
637         value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0,
638                                                           error);
639         if (error.Fail())
640           return false;
641         NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
642         success = true;
643         break;
644       case TypeCodes::sint16:
645         value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0,
646                                                           error);
647         if (error.Fail())
648           return false;
649         NSNumber_FormatShort(valobj, stream, (short)value,
650                              options.GetLanguage());
651         success = true;
652         break;
653       case TypeCodes::sint32:
654         value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0,
655                                                           error);
656         if (error.Fail())
657           return false;
658         NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
659         success = true;
660         break;
661       case TypeCodes::sint64:
662         value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0,
663                                                           error);
664         if (error.Fail())
665           return false;
666         NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
667         success = true;
668         break;
669       case TypeCodes::f32: {
670         uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(
671             data_location, 4, 0, error);
672         if (error.Fail())
673           return false;
674         float flt_value = 0.0f;
675         memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int));
676         NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());
677         success = true;
678         break;
679       }
680       case TypeCodes::f64: {
681         uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(
682             data_location, 8, 0, error);
683         if (error.Fail())
684           return false;
685         double dbl_value = 0.0;
686         memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng));
687         NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());
688         success = true;
689         break;
690       }
691       case TypeCodes::sint128: // internally, this is the same
692       {
693         uint64_t words[2];
694         words[1] = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8,
695                                                              0, error);
696         if (error.Fail())
697           return false;
698         words[0] = process_sp->ReadUnsignedIntegerFromMemory(data_location + 8,
699                                                              8, 0, error);
700         if (error.Fail())
701           return false;
702         llvm::APInt i128_value(128, words);
703         NSNumber_FormatInt128(valobj, stream, i128_value,
704                               options.GetLanguage());
705         success = true;
706         break;
707       }
708       }
709       return success;
710     }
711   }
712 
713   return false;
714 }
715 
716 bool lldb_private::formatters::NSDecimalNumberSummaryProvider(
717     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
718   ProcessSP process_sp = valobj.GetProcessSP();
719   if (!process_sp)
720     return false;
721 
722   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
723   uint32_t ptr_size = process_sp->GetAddressByteSize();
724 
725   Status error;
726   int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory(
727       valobj_addr + ptr_size, 1, 0, error);
728   if (error.Fail())
729     return false;
730 
731   uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory(
732       valobj_addr + ptr_size + 1, 1, 0, error);
733   if (error.Fail())
734     return false;
735 
736   // Fifth bit marks negativity.
737   const bool is_negative = (length_and_negative >> 4) & 1;
738 
739   // Zero length and negative means NaN.
740   uint8_t length = length_and_negative & 0xf;
741   const bool is_nan = is_negative && (length == 0);
742 
743   if (is_nan) {
744     stream.Printf("NaN");
745     return true;
746   }
747 
748   if (length == 0) {
749     stream.Printf("0");
750     return true;
751   }
752 
753   uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory(
754       valobj_addr + ptr_size + 4, 8, 0, error);
755   if (error.Fail())
756     return false;
757 
758   if (is_negative)
759     stream.Printf("-");
760 
761   stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent);
762   return true;
763 }
764 
765 bool lldb_private::formatters::NSURLSummaryProvider(
766     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
767   ProcessSP process_sp = valobj.GetProcessSP();
768   if (!process_sp)
769     return false;
770 
771   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
772 
773   if (!runtime)
774     return false;
775 
776   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
777       runtime->GetClassDescriptor(valobj));
778 
779   if (!descriptor || !descriptor->IsValid())
780     return false;
781 
782   uint32_t ptr_size = process_sp->GetAddressByteSize();
783 
784   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
785 
786   if (!valobj_addr)
787     return false;
788 
789   llvm::StringRef class_name = descriptor->GetClassName().GetStringRef();
790 
791   if (!class_name.equals("NSURL"))
792     return false;
793 
794   uint64_t offset_text = ptr_size + ptr_size +
795                          8; // ISA + pointer + 8 bytes of data (even on 32bit)
796   uint64_t offset_base = offset_text + ptr_size;
797   CompilerType type(valobj.GetCompilerType());
798   ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
799   ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
800   if (!text || text->GetValueAsUnsigned(0) == 0)
801     return false;
802 
803   StreamString base_summary;
804   if (base && base->GetValueAsUnsigned(0)) {
805     if (!NSURLSummaryProvider(*base, base_summary, options))
806       base_summary.Clear();
807   }
808   if (base_summary.Empty())
809     return NSStringSummaryProvider(*text, stream, options);
810 
811   StreamString summary;
812   if (!NSStringSummaryProvider(*text, summary, options) || summary.Empty())
813     return false;
814 
815   const char quote_char = '"';
816   std::string prefix, suffix;
817   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
818     if (!language->GetFormatterPrefixSuffix(*text, ConstString("NSString"),
819                                             prefix, suffix)) {
820       prefix.clear();
821       suffix.clear();
822     }
823   }
824   // @"A" -> @"A
825   llvm::StringRef summary_str = summary.GetString();
826   bool back_consumed = summary_str.consume_back(quote_char + suffix);
827   assert(back_consumed);
828   UNUSED_IF_ASSERT_DISABLED(back_consumed);
829   // @"B" -> B"
830   llvm::StringRef base_summary_str = base_summary.GetString();
831   bool front_consumed = base_summary_str.consume_front(prefix + quote_char);
832   assert(front_consumed);
833   UNUSED_IF_ASSERT_DISABLED(front_consumed);
834   // @"A -- B"
835   if (!summary_str.empty() && !base_summary_str.empty()) {
836     stream.Printf("%s -- %s", summary_str.str().c_str(),
837                   base_summary_str.str().c_str());
838     return true;
839   }
840 
841   return false;
842 }
843 
844 /// Bias value for tagged pointer exponents.
845 /// Recommended values:
846 /// 0x3e3: encodes all dates between distantPast and distantFuture
847 ///   except for the range within about 1e-28 second of the reference date.
848 /// 0x3ef: encodes all dates for a few million years beyond distantPast and
849 ///   distantFuture, except within about 1e-25 second of the reference date.
850 const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef;
851 
852 struct DoubleBits {
853   uint64_t fraction : 52; // unsigned
854   uint64_t exponent : 11; // signed
855   uint64_t sign : 1;
856 };
857 
858 struct TaggedDoubleBits {
859   uint64_t fraction : 52; // unsigned
860   uint64_t exponent : 7;  // signed
861   uint64_t sign : 1;
862   uint64_t unused : 4; // placeholder for pointer tag bits
863 };
864 
865 static uint64_t decodeExponent(uint64_t exp) {
866   // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits
867   // before performing arithmetic.
868   return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS;
869 }
870 
871 static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) {
872   if (encodedTimeInterval == 0)
873     return 0.0;
874   if (encodedTimeInterval == std::numeric_limits<uint64_t>::max())
875     return (uint64_t)-0.0;
876 
877   TaggedDoubleBits encodedBits =
878       llvm::bit_cast<TaggedDoubleBits>(encodedTimeInterval);
879   assert(encodedBits.unused == 0);
880 
881   // Sign and fraction are represented exactly.
882   // Exponent is encoded.
883   DoubleBits decodedBits;
884   decodedBits.sign = encodedBits.sign;
885   decodedBits.fraction = encodedBits.fraction;
886   decodedBits.exponent = decodeExponent(encodedBits.exponent);
887 
888   return llvm::bit_cast<double>(decodedBits);
889 }
890 
891 bool lldb_private::formatters::NSDateSummaryProvider(
892     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
893   ProcessSP process_sp = valobj.GetProcessSP();
894   if (!process_sp)
895     return false;
896 
897   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
898 
899   if (!runtime)
900     return false;
901 
902   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
903       runtime->GetClassDescriptor(valobj));
904 
905   if (!descriptor || !descriptor->IsValid())
906     return false;
907 
908   uint32_t ptr_size = process_sp->GetAddressByteSize();
909 
910   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
911 
912   if (!valobj_addr)
913     return false;
914 
915   uint64_t date_value_bits = 0;
916   double date_value = 0.0;
917 
918   ConstString class_name = descriptor->GetClassName();
919 
920   static const ConstString g_NSDate("NSDate");
921   static const ConstString g___NSDate("__NSDate");
922   static const ConstString g___NSTaggedDate("__NSTaggedDate");
923   static const ConstString g_NSCalendarDate("NSCalendarDate");
924   static const ConstString g_NSConstantDate("NSConstantDate");
925 
926   if (class_name.IsEmpty())
927     return false;
928 
929   uint64_t info_bits = 0, value_bits = 0;
930   if ((class_name == g_NSDate) || (class_name == g___NSDate) ||
931       (class_name == g___NSTaggedDate) || (class_name == g_NSConstantDate)) {
932     if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) {
933       date_value_bits = ((value_bits << 8) | (info_bits << 4));
934       memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
935     } else {
936       llvm::Triple triple(
937           process_sp->GetTarget().GetArchitecture().GetTriple());
938       uint32_t delta =
939           (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size;
940       Status error;
941       date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(
942           valobj_addr + delta, 8, 0, error);
943       memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
944       if (error.Fail())
945         return false;
946     }
947   } else if (class_name == g_NSCalendarDate) {
948     Status error;
949     date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(
950         valobj_addr + 2 * ptr_size, 8, 0, error);
951     memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
952     if (error.Fail())
953       return false;
954   } else
955     return false;
956 
957   // FIXME: It seems old dates are not formatted according to NSDate's calendar
958   // so we hardcode distantPast's value so that it looks like LLDB is doing
959   // the right thing.
960 
961   // The relative time in seconds from Cocoa Epoch to [NSDate distantPast].
962   const double RelSecondsFromCocoaEpochToNSDateDistantPast = -63114076800;
963   if (date_value == RelSecondsFromCocoaEpochToNSDateDistantPast) {
964     stream.Printf("0001-01-01 00:00:00 UTC");
965     return true;
966   }
967 
968   // Accomodate for the __NSTaggedDate format introduced in Foundation 1600.
969   if (class_name == g___NSTaggedDate) {
970     auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
971         ObjCLanguageRuntime::Get(*process_sp));
972     if (runtime && runtime->GetFoundationVersion() >= 1600)
973       date_value = decodeTaggedTimeInterval(value_bits << 4);
974   }
975 
976   // this snippet of code assumes that time_t == seconds since Jan-1-1970 this
977   // is generally true and POSIXly happy, but might break if a library vendor
978   // decides to get creative
979   time_t epoch = GetOSXEpoch();
980   epoch = epoch + static_cast<time_t>(std::floor(date_value));
981   tm *tm_date = gmtime(&epoch);
982   if (!tm_date)
983     return false;
984   std::string buffer(1024, 0);
985   if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)
986     return false;
987   stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,
988                 tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,
989                 tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
990   return true;
991 }
992 
993 bool lldb_private::formatters::ObjCClassSummaryProvider(
994     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
995   ProcessSP process_sp = valobj.GetProcessSP();
996   if (!process_sp)
997     return false;
998 
999   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
1000 
1001   if (!runtime)
1002     return false;
1003 
1004   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
1005       runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0)));
1006 
1007   if (!descriptor || !descriptor->IsValid())
1008     return false;
1009 
1010   ConstString class_name = descriptor->GetClassName();
1011 
1012   if (class_name.IsEmpty())
1013     return false;
1014 
1015   if (ConstString cs = Mangled(class_name).GetDemangledName())
1016     class_name = cs;
1017 
1018   stream.Printf("%s", class_name.AsCString("<unknown class>"));
1019   return true;
1020 }
1021 
1022 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd {
1023 public:
1024   ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp)
1025       : SyntheticChildrenFrontEnd(*valobj_sp) {}
1026 
1027   ~ObjCClassSyntheticChildrenFrontEnd() override = default;
1028 
1029   size_t CalculateNumChildren() override { return 0; }
1030 
1031   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
1032     return lldb::ValueObjectSP();
1033   }
1034 
1035   bool Update() override { return false; }
1036 
1037   bool MightHaveChildren() override { return false; }
1038 
1039   size_t GetIndexOfChildWithName(ConstString name) override {
1040     return UINT32_MAX;
1041   }
1042 };
1043 
1044 SyntheticChildrenFrontEnd *
1045 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator(
1046     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
1047   return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp);
1048 }
1049 
1050 template <bool needs_at>
1051 bool lldb_private::formatters::NSDataSummaryProvider(
1052     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1053   ProcessSP process_sp = valobj.GetProcessSP();
1054   if (!process_sp)
1055     return false;
1056 
1057   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
1058 
1059   if (!runtime)
1060     return false;
1061 
1062   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
1063       runtime->GetClassDescriptor(valobj));
1064 
1065   if (!descriptor || !descriptor->IsValid())
1066     return false;
1067 
1068   bool is_64bit = (process_sp->GetAddressByteSize() == 8);
1069   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
1070 
1071   if (!valobj_addr)
1072     return false;
1073 
1074   uint64_t value = 0;
1075 
1076   llvm::StringRef class_name = descriptor->GetClassName().GetCString();
1077 
1078   if (class_name.empty())
1079     return false;
1080 
1081   bool isNSConcreteData = class_name == "NSConcreteData";
1082   bool isNSConcreteMutableData = class_name == "NSConcreteMutableData";
1083   bool isNSCFData = class_name == "__NSCFData";
1084   if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) {
1085     uint32_t offset;
1086     if (isNSConcreteData)
1087       offset = is_64bit ? 8 : 4;
1088     else
1089       offset = is_64bit ? 16 : 8;
1090 
1091     Status error;
1092     value = process_sp->ReadUnsignedIntegerFromMemory(
1093         valobj_addr + offset, is_64bit ? 8 : 4, 0, error);
1094     if (error.Fail())
1095       return false;
1096   } else if (class_name == "_NSInlineData") {
1097     uint32_t offset = (is_64bit ? 8 : 4);
1098     Status error;
1099     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2,
1100                                                       0, error);
1101     if (error.Fail())
1102       return false;
1103   } else if (class_name == "_NSZeroData") {
1104     value = 0;
1105   } else
1106     return false;
1107 
1108   stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value,
1109                 (value != 1 ? "s" : ""), (needs_at ? "\"" : ""));
1110 
1111   return true;
1112 }
1113 
1114 bool lldb_private::formatters::ObjCBOOLSummaryProvider(
1115     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1116   const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo();
1117 
1118   ValueObjectSP real_guy_sp = valobj.GetSP();
1119 
1120   if (type_info & eTypeIsPointer) {
1121     Status err;
1122     real_guy_sp = valobj.Dereference(err);
1123     if (err.Fail() || !real_guy_sp)
1124       return false;
1125   } else if (type_info & eTypeIsReference) {
1126     real_guy_sp = valobj.GetChildAtIndex(0, true);
1127     if (!real_guy_sp)
1128       return false;
1129   }
1130   int8_t value = (real_guy_sp->GetValueAsSigned(0) & 0xFF);
1131   switch (value) {
1132   case 0:
1133     stream.Printf("NO");
1134     break;
1135   case 1:
1136     stream.Printf("YES");
1137     break;
1138   default:
1139     stream.Printf("%d", value);
1140     break;
1141   }
1142   return true;
1143 }
1144 
1145 bool lldb_private::formatters::ObjCBooleanSummaryProvider(
1146     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1147   lldb::addr_t valobj_ptr_value =
1148       valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
1149   if (valobj_ptr_value == LLDB_INVALID_ADDRESS)
1150     return false;
1151 
1152   ProcessSP process_sp(valobj.GetProcessSP());
1153   if (!process_sp)
1154     return false;
1155 
1156   if (AppleObjCRuntime *objc_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
1157           ObjCLanguageRuntime::Get(*process_sp))) {
1158     lldb::addr_t cf_true = LLDB_INVALID_ADDRESS,
1159                  cf_false = LLDB_INVALID_ADDRESS;
1160     objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false);
1161     if (valobj_ptr_value == cf_true) {
1162       stream.PutCString("YES");
1163       return true;
1164     }
1165     if (valobj_ptr_value == cf_false) {
1166       stream.PutCString("NO");
1167       return true;
1168     }
1169   }
1170 
1171   return false;
1172 }
1173 
1174 template <bool is_sel_ptr>
1175 bool lldb_private::formatters::ObjCSELSummaryProvider(
1176     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1177   lldb::ValueObjectSP valobj_sp;
1178 
1179   CompilerType charstar(valobj.GetCompilerType()
1180                             .GetBasicTypeFromAST(eBasicTypeChar)
1181                             .GetPointerType());
1182 
1183   if (!charstar)
1184     return false;
1185 
1186   ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
1187 
1188   if (is_sel_ptr) {
1189     lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
1190     if (data_address == LLDB_INVALID_ADDRESS)
1191       return false;
1192     valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address,
1193                                                           exe_ctx, charstar);
1194   } else {
1195     DataExtractor data;
1196     Status error;
1197     valobj.GetData(data, error);
1198     if (error.Fail())
1199       return false;
1200     valobj_sp =
1201         ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar);
1202   }
1203 
1204   if (!valobj_sp)
1205     return false;
1206 
1207   stream.Printf("%s", valobj_sp->GetSummaryAsCString());
1208   return true;
1209 }
1210 
1211 // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001
1212 // this call gives the POSIX equivalent of the Cocoa epoch
1213 time_t lldb_private::formatters::GetOSXEpoch() {
1214   static time_t epoch = 0;
1215   if (!epoch) {
1216 #ifndef _WIN32
1217     tzset();
1218     tm tm_epoch;
1219     tm_epoch.tm_sec = 0;
1220     tm_epoch.tm_hour = 0;
1221     tm_epoch.tm_min = 0;
1222     tm_epoch.tm_mon = 0;
1223     tm_epoch.tm_mday = 1;
1224     tm_epoch.tm_year = 2001 - 1900;
1225     tm_epoch.tm_isdst = -1;
1226     tm_epoch.tm_gmtoff = 0;
1227     tm_epoch.tm_zone = nullptr;
1228     epoch = timegm(&tm_epoch);
1229 #endif
1230   }
1231   return epoch;
1232 }
1233 
1234 template bool lldb_private::formatters::NSDataSummaryProvider<true>(
1235     ValueObject &, Stream &, const TypeSummaryOptions &);
1236 
1237 template bool lldb_private::formatters::NSDataSummaryProvider<false>(
1238     ValueObject &, Stream &, const TypeSummaryOptions &);
1239 
1240 template bool lldb_private::formatters::ObjCSELSummaryProvider<true>(
1241     ValueObject &, Stream &, const TypeSummaryOptions &);
1242 
1243 template bool lldb_private::formatters::ObjCSELSummaryProvider<false>(
1244     ValueObject &, Stream &, const TypeSummaryOptions &);
1245