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