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