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