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