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