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/Target.h"
25 #include "lldb/Utility/DataBufferHeap.h"
26 #include "lldb/Utility/Endian.h"
27 #include "lldb/Utility/LLDBLog.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 constexpr llvm::StringLiteral g_TypeHint("NSNumber:char");
308 
309   llvm::StringRef prefix, suffix;
310   if (Language *language = Language::FindPlugin(lang))
311     std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
312 
313   stream << prefix;
314   stream.Printf("%hhd", value);
315   stream << suffix;
316 }
317 
318 static void NSNumber_FormatShort(ValueObject &valobj, Stream &stream,
319                                  short value, lldb::LanguageType lang) {
320   static constexpr llvm::StringLiteral g_TypeHint("NSNumber:short");
321 
322   llvm::StringRef prefix, suffix;
323   if (Language *language = Language::FindPlugin(lang))
324     std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
325 
326   stream << prefix;
327   stream.Printf("%hd", value);
328   stream << suffix;
329 }
330 
331 static void NSNumber_FormatInt(ValueObject &valobj, Stream &stream, int value,
332                                lldb::LanguageType lang) {
333   static constexpr llvm::StringLiteral g_TypeHint("NSNumber:int");
334 
335   llvm::StringRef prefix, suffix;
336   if (Language *language = Language::FindPlugin(lang))
337     std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
338 
339   stream << prefix;
340   stream.Printf("%d", value);
341   stream << suffix;
342 }
343 
344 static void NSNumber_FormatLong(ValueObject &valobj, Stream &stream,
345                                 int64_t value, lldb::LanguageType lang) {
346   static constexpr llvm::StringLiteral g_TypeHint("NSNumber:long");
347 
348   llvm::StringRef prefix, suffix;
349   if (Language *language = Language::FindPlugin(lang))
350     std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
351 
352   stream << prefix;
353   stream.Printf("%" PRId64 "", value);
354   stream << suffix;
355 }
356 
357 static void NSNumber_FormatInt128(ValueObject &valobj, Stream &stream,
358                                   const llvm::APInt &value,
359                                   lldb::LanguageType lang) {
360   static constexpr llvm::StringLiteral g_TypeHint("NSNumber:int128_t");
361 
362   llvm::StringRef prefix, suffix;
363   if (Language *language = Language::FindPlugin(lang))
364     std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
365 
366   stream << prefix;
367   const int radix = 10;
368   const bool isSigned = true;
369   std::string str = llvm::toString(value, radix, isSigned);
370   stream.PutCString(str.c_str());
371   stream << suffix;
372 }
373 
374 static void NSNumber_FormatFloat(ValueObject &valobj, Stream &stream,
375                                  float value, lldb::LanguageType lang) {
376   static constexpr llvm::StringLiteral g_TypeHint("NSNumber:float");
377 
378   llvm::StringRef prefix, suffix;
379   if (Language *language = Language::FindPlugin(lang))
380     std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
381 
382   stream << prefix;
383   stream.Printf("%f", value);
384   stream << suffix;
385 }
386 
387 static void NSNumber_FormatDouble(ValueObject &valobj, Stream &stream,
388                                   double value, lldb::LanguageType lang) {
389   static constexpr llvm::StringLiteral g_TypeHint("NSNumber:double");
390 
391   llvm::StringRef prefix, suffix;
392   if (Language *language = Language::FindPlugin(lang))
393     std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
394 
395   stream << prefix;
396   stream.Printf("%g", value);
397   stream << suffix;
398 }
399 
400 bool lldb_private::formatters::NSNumberSummaryProvider(
401     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
402   ProcessSP process_sp = valobj.GetProcessSP();
403   if (!process_sp)
404     return false;
405 
406   Log *log = GetLog(LLDBLog::DataFormatters);
407   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
408 
409   if (!runtime)
410     return false;
411 
412   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
413       runtime->GetClassDescriptor(valobj));
414 
415   if (!descriptor || !descriptor->IsValid())
416     return false;
417 
418   uint32_t ptr_size = process_sp->GetAddressByteSize();
419 
420   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
421 
422   if (!valobj_addr)
423     return false;
424 
425   llvm::StringRef class_name(descriptor->GetClassName().GetCString());
426 
427   if (class_name.empty())
428     return false;
429 
430   if (class_name == "__NSCFBoolean")
431     return ObjCBooleanSummaryProvider(valobj, stream, options);
432 
433   if (class_name == "NSDecimalNumber")
434     return NSDecimalNumberSummaryProvider(valobj, stream, options);
435 
436   if (class_name == "NSConstantIntegerNumber") {
437     Status error;
438     int64_t value = process_sp->ReadSignedIntegerFromMemory(
439         valobj_addr + 2 * ptr_size, 8, 0, error);
440     if (error.Fail())
441       return false;
442     uint64_t encoding_addr = process_sp->ReadUnsignedIntegerFromMemory(
443         valobj_addr + ptr_size, ptr_size, 0, error);
444     if (error.Fail())
445       return false;
446     char encoding =
447         process_sp->ReadUnsignedIntegerFromMemory(encoding_addr, 1, 0, error);
448     if (error.Fail())
449       return false;
450 
451     switch (encoding) {
452     case _C_CHR:
453       NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
454       return true;
455     case _C_SHT:
456       NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage());
457       return true;
458     case _C_INT:
459       NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
460       return true;
461     case _C_LNG:
462     case _C_LNG_LNG:
463       NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
464       return true;
465 
466     case _C_UCHR:
467     case _C_USHT:
468     case _C_UINT:
469     case _C_ULNG:
470     case _C_ULNG_LNG:
471       stream.Printf("%" PRIu64, value);
472       return true;
473     }
474 
475     return false;
476   }
477 
478   if (class_name == "NSConstantFloatNumber") {
479     Status error;
480     uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(
481         valobj_addr + ptr_size, 4, 0, error);
482     if (error.Fail())
483       return false;
484     float flt_value = 0.0f;
485     memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int));
486     NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());
487     return true;
488   }
489 
490   if (class_name == "NSConstantDoubleNumber") {
491     Status error;
492     uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(
493         valobj_addr + ptr_size, 8, 0, error);
494     if (error.Fail())
495       return false;
496     double dbl_value = 0.0;
497     memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng));
498     NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());
499     return true;
500   }
501 
502   if (class_name == "NSNumber" || class_name == "__NSCFNumber") {
503     int64_t value = 0;
504     uint64_t i_bits = 0;
505     if (descriptor->GetTaggedPointerInfoSigned(&i_bits, &value)) {
506       // Check for "preserved" numbers.  We still don't support them yet.
507       if (i_bits & 0x8) {
508         if (log)
509           log->Printf(
510               "Unsupported (preserved) NSNumber tagged pointer 0x%" PRIu64,
511               valobj_addr);
512         return false;
513       }
514 
515       switch (i_bits) {
516       case 0:
517         NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
518         break;
519       case 1:
520       case 4:
521         NSNumber_FormatShort(valobj, stream, (short)value,
522                              options.GetLanguage());
523         break;
524       case 2:
525       case 8:
526         NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
527         break;
528       case 3:
529       case 12:
530         NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
531         break;
532       default:
533         return false;
534       }
535       return true;
536     } else {
537       Status error;
538 
539       AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
540           ObjCLanguageRuntime::Get(*process_sp));
541 
542       const bool new_format =
543           (runtime && runtime->GetFoundationVersion() >= 1400);
544 
545       enum class TypeCodes : int {
546         sint8 = 0x0,
547         sint16 = 0x1,
548         sint32 = 0x2,
549         sint64 = 0x3,
550         f32 = 0x4,
551         f64 = 0x5,
552         sint128 = 0x6
553       };
554 
555       uint64_t data_location = valobj_addr + 2 * ptr_size;
556       TypeCodes type_code;
557 
558       if (new_format) {
559         uint64_t cfinfoa = process_sp->ReadUnsignedIntegerFromMemory(
560             valobj_addr + ptr_size, ptr_size, 0, error);
561 
562         if (error.Fail())
563           return false;
564 
565         bool is_preserved_number = cfinfoa & 0x8;
566         if (is_preserved_number) {
567           if (log)
568             log->Printf(
569                 "Unsupported preserved NSNumber tagged pointer 0x%" PRIu64,
570                 valobj_addr);
571           return false;
572         }
573 
574         type_code = static_cast<TypeCodes>(cfinfoa & 0x7);
575       } else {
576         uint8_t data_type = process_sp->ReadUnsignedIntegerFromMemory(
577                                 valobj_addr + ptr_size, 1, 0, error) &
578                             0x1F;
579 
580         if (error.Fail())
581           return false;
582 
583         switch (data_type) {
584         case 1:
585           type_code = TypeCodes::sint8;
586           break;
587         case 2:
588           type_code = TypeCodes::sint16;
589           break;
590         case 3:
591           type_code = TypeCodes::sint32;
592           break;
593         case 17:
594           data_location += 8;
595           [[fallthrough]];
596         case 4:
597           type_code = TypeCodes::sint64;
598           break;
599         case 5:
600           type_code = TypeCodes::f32;
601           break;
602         case 6:
603           type_code = TypeCodes::f64;
604           break;
605         default:
606           return false;
607         }
608       }
609 
610       uint64_t value = 0;
611       bool success = false;
612       switch (type_code) {
613       case TypeCodes::sint8:
614         value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0,
615                                                           error);
616         if (error.Fail())
617           return false;
618         NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
619         success = true;
620         break;
621       case TypeCodes::sint16:
622         value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0,
623                                                           error);
624         if (error.Fail())
625           return false;
626         NSNumber_FormatShort(valobj, stream, (short)value,
627                              options.GetLanguage());
628         success = true;
629         break;
630       case TypeCodes::sint32:
631         value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0,
632                                                           error);
633         if (error.Fail())
634           return false;
635         NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
636         success = true;
637         break;
638       case TypeCodes::sint64:
639         value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0,
640                                                           error);
641         if (error.Fail())
642           return false;
643         NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
644         success = true;
645         break;
646       case TypeCodes::f32: {
647         uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(
648             data_location, 4, 0, error);
649         if (error.Fail())
650           return false;
651         float flt_value = 0.0f;
652         memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int));
653         NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());
654         success = true;
655         break;
656       }
657       case TypeCodes::f64: {
658         uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(
659             data_location, 8, 0, error);
660         if (error.Fail())
661           return false;
662         double dbl_value = 0.0;
663         memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng));
664         NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());
665         success = true;
666         break;
667       }
668       case TypeCodes::sint128: // internally, this is the same
669       {
670         uint64_t words[2];
671         words[1] = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8,
672                                                              0, error);
673         if (error.Fail())
674           return false;
675         words[0] = process_sp->ReadUnsignedIntegerFromMemory(data_location + 8,
676                                                              8, 0, error);
677         if (error.Fail())
678           return false;
679         llvm::APInt i128_value(128, words);
680         NSNumber_FormatInt128(valobj, stream, i128_value,
681                               options.GetLanguage());
682         success = true;
683         break;
684       }
685       }
686       return success;
687     }
688   }
689 
690   return false;
691 }
692 
693 bool lldb_private::formatters::NSDecimalNumberSummaryProvider(
694     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
695   ProcessSP process_sp = valobj.GetProcessSP();
696   if (!process_sp)
697     return false;
698 
699   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
700   uint32_t ptr_size = process_sp->GetAddressByteSize();
701 
702   Status error;
703   int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory(
704       valobj_addr + ptr_size, 1, 0, error);
705   if (error.Fail())
706     return false;
707 
708   uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory(
709       valobj_addr + ptr_size + 1, 1, 0, error);
710   if (error.Fail())
711     return false;
712 
713   // Fifth bit marks negativity.
714   const bool is_negative = (length_and_negative >> 4) & 1;
715 
716   // Zero length and negative means NaN.
717   uint8_t length = length_and_negative & 0xf;
718   const bool is_nan = is_negative && (length == 0);
719 
720   if (is_nan) {
721     stream.Printf("NaN");
722     return true;
723   }
724 
725   if (length == 0) {
726     stream.Printf("0");
727     return true;
728   }
729 
730   uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory(
731       valobj_addr + ptr_size + 4, 8, 0, error);
732   if (error.Fail())
733     return false;
734 
735   if (is_negative)
736     stream.Printf("-");
737 
738   stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent);
739   return true;
740 }
741 
742 bool lldb_private::formatters::NSURLSummaryProvider(
743     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
744   ProcessSP process_sp = valobj.GetProcessSP();
745   if (!process_sp)
746     return false;
747 
748   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
749 
750   if (!runtime)
751     return false;
752 
753   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
754       runtime->GetClassDescriptor(valobj));
755 
756   if (!descriptor || !descriptor->IsValid())
757     return false;
758 
759   uint32_t ptr_size = process_sp->GetAddressByteSize();
760 
761   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
762 
763   if (!valobj_addr)
764     return false;
765 
766   llvm::StringRef class_name = descriptor->GetClassName().GetStringRef();
767 
768   if (!class_name.equals("NSURL"))
769     return false;
770 
771   uint64_t offset_text = ptr_size + ptr_size +
772                          8; // ISA + pointer + 8 bytes of data (even on 32bit)
773   uint64_t offset_base = offset_text + ptr_size;
774   CompilerType type(valobj.GetCompilerType());
775   ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
776   ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
777   if (!text || text->GetValueAsUnsigned(0) == 0)
778     return false;
779 
780   StreamString base_summary;
781   if (base && base->GetValueAsUnsigned(0)) {
782     if (!NSURLSummaryProvider(*base, base_summary, options))
783       base_summary.Clear();
784   }
785   if (base_summary.Empty())
786     return NSStringSummaryProvider(*text, stream, options);
787 
788   StreamString summary;
789   if (!NSStringSummaryProvider(*text, summary, options) || summary.Empty())
790     return false;
791 
792   static constexpr llvm::StringLiteral quote_char("\"");
793   static constexpr llvm::StringLiteral g_TypeHint("NSString");
794   llvm::StringRef prefix, suffix;
795   if (Language *language = Language::FindPlugin(options.GetLanguage()))
796     std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);
797 
798   // @"A" -> @"A
799   llvm::StringRef summary_str = summary.GetString();
800   bool back_consumed =
801       summary_str.consume_back(suffix) && summary_str.consume_back(quote_char);
802   assert(back_consumed);
803   UNUSED_IF_ASSERT_DISABLED(back_consumed);
804   // @"B" -> B"
805   llvm::StringRef base_summary_str = base_summary.GetString();
806   bool front_consumed = base_summary_str.consume_front(prefix) &&
807                         base_summary_str.consume_front(quote_char);
808   assert(front_consumed);
809   UNUSED_IF_ASSERT_DISABLED(front_consumed);
810   // @"A -- B"
811   if (!summary_str.empty() && !base_summary_str.empty()) {
812     stream << summary_str << " -- " << base_summary_str;
813     return true;
814   }
815 
816   return false;
817 }
818 
819 /// Bias value for tagged pointer exponents.
820 /// Recommended values:
821 /// 0x3e3: encodes all dates between distantPast and distantFuture
822 ///   except for the range within about 1e-28 second of the reference date.
823 /// 0x3ef: encodes all dates for a few million years beyond distantPast and
824 ///   distantFuture, except within about 1e-25 second of the reference date.
825 const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef;
826 
827 struct DoubleBits {
828   uint64_t fraction : 52; // unsigned
829   uint64_t exponent : 11; // signed
830   uint64_t sign : 1;
831 };
832 
833 struct TaggedDoubleBits {
834   uint64_t fraction : 52; // unsigned
835   uint64_t exponent : 7;  // signed
836   uint64_t sign : 1;
837   uint64_t unused : 4; // placeholder for pointer tag bits
838 };
839 
840 static uint64_t decodeExponent(uint64_t exp) {
841   // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits
842   // before performing arithmetic.
843   return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS;
844 }
845 
846 static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) {
847   if (encodedTimeInterval == 0)
848     return 0.0;
849   if (encodedTimeInterval == std::numeric_limits<uint64_t>::max())
850     return (uint64_t)-0.0;
851 
852   TaggedDoubleBits encodedBits =
853       llvm::bit_cast<TaggedDoubleBits>(encodedTimeInterval);
854   assert(encodedBits.unused == 0);
855 
856   // Sign and fraction are represented exactly.
857   // Exponent is encoded.
858   DoubleBits decodedBits;
859   decodedBits.sign = encodedBits.sign;
860   decodedBits.fraction = encodedBits.fraction;
861   decodedBits.exponent = decodeExponent(encodedBits.exponent);
862 
863   return llvm::bit_cast<double>(decodedBits);
864 }
865 
866 bool lldb_private::formatters::NSDateSummaryProvider(
867     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
868   ProcessSP process_sp = valobj.GetProcessSP();
869   if (!process_sp)
870     return false;
871 
872   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
873 
874   if (!runtime)
875     return false;
876 
877   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
878       runtime->GetClassDescriptor(valobj));
879 
880   if (!descriptor || !descriptor->IsValid())
881     return false;
882 
883   uint32_t ptr_size = process_sp->GetAddressByteSize();
884 
885   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
886 
887   if (!valobj_addr)
888     return false;
889 
890   uint64_t date_value_bits = 0;
891   double date_value = 0.0;
892 
893   ConstString class_name = descriptor->GetClassName();
894 
895   static const ConstString g_NSDate("NSDate");
896   static const ConstString g_dunder_NSDate("__NSDate");
897   static const ConstString g_NSTaggedDate("__NSTaggedDate");
898   static const ConstString g_NSCalendarDate("NSCalendarDate");
899   static const ConstString g_NSConstantDate("NSConstantDate");
900 
901   if (class_name.IsEmpty())
902     return false;
903 
904   uint64_t info_bits = 0, value_bits = 0;
905   if ((class_name == g_NSDate) || (class_name == g_dunder_NSDate) ||
906       (class_name == g_NSTaggedDate) || (class_name == g_NSConstantDate)) {
907     if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) {
908       date_value_bits = ((value_bits << 8) | (info_bits << 4));
909       memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
910     } else {
911       llvm::Triple triple(
912           process_sp->GetTarget().GetArchitecture().GetTriple());
913       uint32_t delta =
914           (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size;
915       Status error;
916       date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(
917           valobj_addr + delta, 8, 0, error);
918       memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
919       if (error.Fail())
920         return false;
921     }
922   } else if (class_name == g_NSCalendarDate) {
923     Status error;
924     date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(
925         valobj_addr + 2 * ptr_size, 8, 0, error);
926     memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
927     if (error.Fail())
928       return false;
929   } else
930     return false;
931 
932   // FIXME: It seems old dates are not formatted according to NSDate's calendar
933   // so we hardcode distantPast's value so that it looks like LLDB is doing
934   // the right thing.
935 
936   // The relative time in seconds from Cocoa Epoch to [NSDate distantPast].
937   const double RelSecondsFromCocoaEpochToNSDateDistantPast = -63114076800;
938   if (date_value == RelSecondsFromCocoaEpochToNSDateDistantPast) {
939     stream.Printf("0001-01-01 00:00:00 UTC");
940     return true;
941   }
942 
943   // Accomodate for the __NSTaggedDate format introduced in Foundation 1600.
944   if (class_name == g_NSTaggedDate) {
945     auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
946         ObjCLanguageRuntime::Get(*process_sp));
947     if (runtime && runtime->GetFoundationVersion() >= 1600)
948       date_value = decodeTaggedTimeInterval(value_bits << 4);
949   }
950 
951   // this snippet of code assumes that time_t == seconds since Jan-1-1970 this
952   // is generally true and POSIXly happy, but might break if a library vendor
953   // decides to get creative
954   time_t epoch = GetOSXEpoch();
955   epoch = epoch + static_cast<time_t>(std::floor(date_value));
956   tm *tm_date = gmtime(&epoch);
957   if (!tm_date)
958     return false;
959   std::string buffer(1024, 0);
960   if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)
961     return false;
962   stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,
963                 tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,
964                 tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
965   return true;
966 }
967 
968 bool lldb_private::formatters::ObjCClassSummaryProvider(
969     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
970   ProcessSP process_sp = valobj.GetProcessSP();
971   if (!process_sp)
972     return false;
973 
974   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
975 
976   if (!runtime)
977     return false;
978 
979   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
980       runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0)));
981 
982   if (!descriptor || !descriptor->IsValid())
983     return false;
984 
985   ConstString class_name = descriptor->GetClassName();
986 
987   if (class_name.IsEmpty())
988     return false;
989 
990   if (ConstString cs = Mangled(class_name).GetDemangledName())
991     class_name = cs;
992 
993   stream.Printf("%s", class_name.AsCString("<unknown class>"));
994   return true;
995 }
996 
997 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd {
998 public:
999   ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp)
1000       : SyntheticChildrenFrontEnd(*valobj_sp) {}
1001 
1002   ~ObjCClassSyntheticChildrenFrontEnd() override = default;
1003 
1004   size_t CalculateNumChildren() override { return 0; }
1005 
1006   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
1007     return lldb::ValueObjectSP();
1008   }
1009 
1010   bool Update() override { return false; }
1011 
1012   bool MightHaveChildren() override { return false; }
1013 
1014   size_t GetIndexOfChildWithName(ConstString name) override {
1015     return UINT32_MAX;
1016   }
1017 };
1018 
1019 SyntheticChildrenFrontEnd *
1020 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator(
1021     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
1022   return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp);
1023 }
1024 
1025 template <bool needs_at>
1026 bool lldb_private::formatters::NSDataSummaryProvider(
1027     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1028   ProcessSP process_sp = valobj.GetProcessSP();
1029   if (!process_sp)
1030     return false;
1031 
1032   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
1033 
1034   if (!runtime)
1035     return false;
1036 
1037   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
1038       runtime->GetClassDescriptor(valobj));
1039 
1040   if (!descriptor || !descriptor->IsValid())
1041     return false;
1042 
1043   bool is_64bit = (process_sp->GetAddressByteSize() == 8);
1044   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
1045 
1046   if (!valobj_addr)
1047     return false;
1048 
1049   uint64_t value = 0;
1050 
1051   llvm::StringRef class_name = descriptor->GetClassName().GetCString();
1052 
1053   if (class_name.empty())
1054     return false;
1055 
1056   bool isNSConcreteData = class_name == "NSConcreteData";
1057   bool isNSConcreteMutableData = class_name == "NSConcreteMutableData";
1058   bool isNSCFData = class_name == "__NSCFData";
1059   if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) {
1060     uint32_t offset;
1061     if (isNSConcreteData)
1062       offset = is_64bit ? 8 : 4;
1063     else
1064       offset = is_64bit ? 16 : 8;
1065 
1066     Status error;
1067     value = process_sp->ReadUnsignedIntegerFromMemory(
1068         valobj_addr + offset, is_64bit ? 8 : 4, 0, error);
1069     if (error.Fail())
1070       return false;
1071   } else if (class_name == "_NSInlineData") {
1072     uint32_t offset = (is_64bit ? 8 : 4);
1073     Status error;
1074     value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2,
1075                                                       0, error);
1076     if (error.Fail())
1077       return false;
1078   } else if (class_name == "_NSZeroData") {
1079     value = 0;
1080   } else
1081     return false;
1082 
1083   stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value,
1084                 (value != 1 ? "s" : ""), (needs_at ? "\"" : ""));
1085 
1086   return true;
1087 }
1088 
1089 bool lldb_private::formatters::ObjCBOOLSummaryProvider(
1090     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1091   const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo();
1092 
1093   ValueObjectSP real_guy_sp = valobj.GetSP();
1094 
1095   if (type_info & eTypeIsPointer) {
1096     Status err;
1097     real_guy_sp = valobj.Dereference(err);
1098     if (err.Fail() || !real_guy_sp)
1099       return false;
1100   } else if (type_info & eTypeIsReference) {
1101     real_guy_sp = valobj.GetChildAtIndex(0);
1102     if (!real_guy_sp)
1103       return false;
1104   }
1105   int8_t value = (real_guy_sp->GetValueAsSigned(0) & 0xFF);
1106   switch (value) {
1107   case 0:
1108     stream.Printf("NO");
1109     break;
1110   case 1:
1111     stream.Printf("YES");
1112     break;
1113   default:
1114     stream.Printf("%d", value);
1115     break;
1116   }
1117   return true;
1118 }
1119 
1120 bool lldb_private::formatters::ObjCBooleanSummaryProvider(
1121     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1122   lldb::addr_t valobj_ptr_value =
1123       valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
1124   if (valobj_ptr_value == LLDB_INVALID_ADDRESS)
1125     return false;
1126 
1127   ProcessSP process_sp(valobj.GetProcessSP());
1128   if (!process_sp)
1129     return false;
1130 
1131   if (AppleObjCRuntime *objc_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
1132           ObjCLanguageRuntime::Get(*process_sp))) {
1133     lldb::addr_t cf_true = LLDB_INVALID_ADDRESS,
1134                  cf_false = LLDB_INVALID_ADDRESS;
1135     objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false);
1136     if (valobj_ptr_value == cf_true) {
1137       stream.PutCString("YES");
1138       return true;
1139     }
1140     if (valobj_ptr_value == cf_false) {
1141       stream.PutCString("NO");
1142       return true;
1143     }
1144   }
1145 
1146   return false;
1147 }
1148 
1149 template <bool is_sel_ptr>
1150 bool lldb_private::formatters::ObjCSELSummaryProvider(
1151     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
1152   lldb::ValueObjectSP valobj_sp;
1153 
1154   CompilerType charstar(valobj.GetCompilerType()
1155                             .GetBasicTypeFromAST(eBasicTypeChar)
1156                             .GetPointerType());
1157 
1158   if (!charstar)
1159     return false;
1160 
1161   ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
1162 
1163   if (is_sel_ptr) {
1164     lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
1165     if (data_address == LLDB_INVALID_ADDRESS)
1166       return false;
1167     valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address,
1168                                                           exe_ctx, charstar);
1169   } else {
1170     DataExtractor data;
1171     Status error;
1172     valobj.GetData(data, error);
1173     if (error.Fail())
1174       return false;
1175     valobj_sp =
1176         ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar);
1177   }
1178 
1179   if (!valobj_sp)
1180     return false;
1181 
1182   stream.Printf("%s", valobj_sp->GetSummaryAsCString());
1183   return true;
1184 }
1185 
1186 // POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001
1187 // this call gives the POSIX equivalent of the Cocoa epoch
1188 time_t lldb_private::formatters::GetOSXEpoch() {
1189   static time_t epoch = 0;
1190   if (!epoch) {
1191 #ifndef _WIN32
1192     tzset();
1193     tm tm_epoch;
1194     tm_epoch.tm_sec = 0;
1195     tm_epoch.tm_hour = 0;
1196     tm_epoch.tm_min = 0;
1197     tm_epoch.tm_mon = 0;
1198     tm_epoch.tm_mday = 1;
1199     tm_epoch.tm_year = 2001 - 1900;
1200     tm_epoch.tm_isdst = -1;
1201     tm_epoch.tm_gmtoff = 0;
1202     tm_epoch.tm_zone = nullptr;
1203     epoch = timegm(&tm_epoch);
1204 #endif
1205   }
1206   return epoch;
1207 }
1208 
1209 template bool lldb_private::formatters::NSDataSummaryProvider<true>(
1210     ValueObject &, Stream &, const TypeSummaryOptions &);
1211 
1212 template bool lldb_private::formatters::NSDataSummaryProvider<false>(
1213     ValueObject &, Stream &, const TypeSummaryOptions &);
1214 
1215 template bool lldb_private::formatters::ObjCSELSummaryProvider<true>(
1216     ValueObject &, Stream &, const TypeSummaryOptions &);
1217 
1218 template bool lldb_private::formatters::ObjCSELSummaryProvider<false>(
1219     ValueObject &, Stream &, const TypeSummaryOptions &);
1220