• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- Cocoa.cpp ---------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Cocoa.h"
10 
11 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
12 #include "lldb/Core/Mangled.h"
13 #include "lldb/Core/ValueObject.h"
14 #include "lldb/Core/ValueObjectConstResult.h"
15 #include "lldb/DataFormatters/FormattersHelpers.h"
16 #include "lldb/DataFormatters/StringPrinter.h"
17 #include "lldb/DataFormatters/TypeSummary.h"
18 #include "lldb/Host/Time.h"
19 #include "lldb/Target/Language.h"
20 #include "lldb/Target/Process.h"
21 #include "lldb/Target/ProcessStructReader.h"
22 #include "lldb/Target/Target.h"
23 #include "lldb/Utility/DataBufferHeap.h"
24 #include "lldb/Utility/Endian.h"
25 #include "lldb/Utility/Status.h"
26 #include "lldb/Utility/Stream.h"
27 
28 #include "llvm/ADT/APInt.h"
29 #include "llvm/ADT/bit.h"
30 
31 #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
32 
33 #include "NSString.h"
34 
35 using namespace lldb;
36 using namespace lldb_private;
37 using namespace lldb_private::formatters;
38 
NSBundleSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)39 bool lldb_private::formatters::NSBundleSummaryProvider(
40     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
41   ProcessSP process_sp = valobj.GetProcessSP();
42   if (!process_sp)
43     return false;
44 
45   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
46 
47   if (!runtime)
48     return false;
49 
50   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
51       runtime->GetClassDescriptor(valobj));
52 
53   if (!descriptor || !descriptor->IsValid())
54     return false;
55 
56   uint32_t ptr_size = process_sp->GetAddressByteSize();
57 
58   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
59 
60   if (!valobj_addr)
61     return false;
62 
63   llvm::StringRef class_name(descriptor->GetClassName().GetCString());
64 
65   if (class_name.empty())
66     return false;
67 
68   if (class_name == "NSBundle") {
69     uint64_t offset = 5 * ptr_size;
70     ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
71         offset,
72         valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID),
73         true));
74 
75     if (!text)
76       return false;
77 
78     StreamString summary_stream;
79     bool was_nsstring_ok =
80         NSStringSummaryProvider(*text, summary_stream, options);
81     if (was_nsstring_ok && summary_stream.GetSize() > 0) {
82       stream.Printf("%s", summary_stream.GetData());
83       return true;
84     }
85   }
86 
87   return false;
88 }
89 
NSTimeZoneSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)90 bool lldb_private::formatters::NSTimeZoneSummaryProvider(
91     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
92   ProcessSP process_sp = valobj.GetProcessSP();
93   if (!process_sp)
94     return false;
95 
96   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
97 
98   if (!runtime)
99     return false;
100 
101   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
102       runtime->GetClassDescriptor(valobj));
103 
104   if (!descriptor || !descriptor->IsValid())
105     return false;
106 
107   uint32_t ptr_size = process_sp->GetAddressByteSize();
108 
109   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
110 
111   if (!valobj_addr)
112     return false;
113 
114   llvm::StringRef class_name(descriptor->GetClassName().GetCString());
115 
116   if (class_name.empty())
117     return false;
118 
119   if (class_name == "__NSTimeZone") {
120     uint64_t offset = ptr_size;
121     ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
122         offset, valobj.GetCompilerType(), true));
123 
124     if (!text)
125       return false;
126 
127     StreamString summary_stream;
128     bool was_nsstring_ok =
129         NSStringSummaryProvider(*text, summary_stream, options);
130     if (was_nsstring_ok && summary_stream.GetSize() > 0) {
131       stream.Printf("%s", summary_stream.GetData());
132       return true;
133     }
134   }
135 
136   return false;
137 }
138 
NSNotificationSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)139 bool lldb_private::formatters::NSNotificationSummaryProvider(
140     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
141   ProcessSP process_sp = valobj.GetProcessSP();
142   if (!process_sp)
143     return false;
144 
145   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
146 
147   if (!runtime)
148     return false;
149 
150   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
151       runtime->GetClassDescriptor(valobj));
152 
153   if (!descriptor || !descriptor->IsValid())
154     return false;
155 
156   uint32_t ptr_size = process_sp->GetAddressByteSize();
157 
158   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
159 
160   if (!valobj_addr)
161     return false;
162 
163   llvm::StringRef class_name(descriptor->GetClassName().GetCString());
164 
165   if (class_name.empty())
166     return false;
167 
168   if (class_name == "NSConcreteNotification") {
169     uint64_t offset = ptr_size;
170     ValueObjectSP text(valobj.GetSyntheticChildAtOffset(
171         offset, valobj.GetCompilerType(), true));
172 
173     if (!text)
174       return false;
175 
176     StreamString summary_stream;
177     bool was_nsstring_ok =
178         NSStringSummaryProvider(*text, summary_stream, options);
179     if (was_nsstring_ok && summary_stream.GetSize() > 0) {
180       stream.Printf("%s", summary_stream.GetData());
181       return true;
182     }
183   }
184 
185   return false;
186 }
187 
NSMachPortSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)188 bool lldb_private::formatters::NSMachPortSummaryProvider(
189     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
190   ProcessSP process_sp = valobj.GetProcessSP();
191   if (!process_sp)
192     return false;
193 
194   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
195 
196   if (!runtime)
197     return false;
198 
199   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
200       runtime->GetClassDescriptor(valobj));
201 
202   if (!descriptor || !descriptor->IsValid())
203     return false;
204 
205   uint32_t ptr_size = process_sp->GetAddressByteSize();
206 
207   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
208 
209   if (!valobj_addr)
210     return false;
211 
212   llvm::StringRef class_name(descriptor->GetClassName().GetCString());
213 
214   if (class_name.empty())
215     return false;
216 
217   uint64_t port_number = 0;
218 
219   if (class_name == "NSMachPort") {
220     uint64_t offset = (ptr_size == 4 ? 12 : 20);
221     Status error;
222     port_number = process_sp->ReadUnsignedIntegerFromMemory(
223         offset + valobj_addr, 4, 0, error);
224     if (error.Success()) {
225       stream.Printf("mach port: %u",
226                     (uint32_t)(port_number & 0x00000000FFFFFFFF));
227       return true;
228     }
229   }
230 
231   return false;
232 }
233 
NSIndexSetSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)234 bool lldb_private::formatters::NSIndexSetSummaryProvider(
235     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
236   ProcessSP process_sp = valobj.GetProcessSP();
237   if (!process_sp)
238     return false;
239 
240   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
241 
242   if (!runtime)
243     return false;
244 
245   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
246       runtime->GetClassDescriptor(valobj));
247 
248   if (!descriptor || !descriptor->IsValid())
249     return false;
250 
251   uint32_t ptr_size = process_sp->GetAddressByteSize();
252 
253   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
254 
255   if (!valobj_addr)
256     return false;
257 
258   llvm::StringRef class_name(descriptor->GetClassName().GetCString());
259 
260   if (class_name.empty())
261     return false;
262 
263   uint64_t count = 0;
264 
265   do {
266     if (class_name == "NSIndexSet" || class_name == "NSMutableIndexSet") {
267       Status error;
268       uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(
269           valobj_addr + ptr_size, 4, 0, error);
270       if (error.Fail())
271         return false;
272       // this means the set is empty - count = 0
273       if ((mode & 1) == 1) {
274         count = 0;
275         break;
276       }
277       if ((mode & 2) == 2)
278         mode = 1; // this means the set only has one range
279       else
280         mode = 2; // this means the set has multiple ranges
281       if (mode == 1) {
282         count = process_sp->ReadUnsignedIntegerFromMemory(
283             valobj_addr + 3 * ptr_size, ptr_size, 0, error);
284         if (error.Fail())
285           return false;
286       } else {
287         // read a pointer to the data at 2*ptr_size
288         count = process_sp->ReadUnsignedIntegerFromMemory(
289             valobj_addr + 2 * ptr_size, ptr_size, 0, error);
290         if (error.Fail())
291           return false;
292         // read the data at 2*ptr_size from the first location
293         count = process_sp->ReadUnsignedIntegerFromMemory(count + 2 * ptr_size,
294                                                           ptr_size, 0, error);
295         if (error.Fail())
296           return false;
297       }
298     } else
299       return false;
300   } while (false);
301   stream.Printf("%" PRIu64 " index%s", count, (count == 1 ? "" : "es"));
302   return true;
303 }
304 
NSNumber_FormatChar(ValueObject & valobj,Stream & stream,char value,lldb::LanguageType lang)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 
NSNumber_FormatShort(ValueObject & valobj,Stream & stream,short value,lldb::LanguageType lang)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 
NSNumber_FormatInt(ValueObject & valobj,Stream & stream,int value,lldb::LanguageType lang)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 
NSNumber_FormatLong(ValueObject & valobj,Stream & stream,uint64_t value,lldb::LanguageType lang)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 
NSNumber_FormatInt128(ValueObject & valobj,Stream & stream,const llvm::APInt & value,lldb::LanguageType lang)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 
NSNumber_FormatFloat(ValueObject & valobj,Stream & stream,float value,lldb::LanguageType lang)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 
NSNumber_FormatDouble(ValueObject & valobj,Stream & stream,double value,lldb::LanguageType lang)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 
NSNumberSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)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 = ObjCLanguageRuntime::Get(*process_sp);
430 
431   if (!runtime)
432     return false;
433 
434   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
435       runtime->GetClassDescriptor(valobj));
436 
437   if (!descriptor || !descriptor->IsValid())
438     return false;
439 
440   uint32_t ptr_size = process_sp->GetAddressByteSize();
441 
442   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
443 
444   if (!valobj_addr)
445     return false;
446 
447   llvm::StringRef class_name(descriptor->GetClassName().GetCString());
448 
449   if (class_name.empty())
450     return false;
451 
452   if (class_name == "__NSCFBoolean")
453     return ObjCBooleanSummaryProvider(valobj, stream, options);
454 
455   if (class_name == "NSDecimalNumber")
456     return NSDecimalNumberSummaryProvider(valobj, stream, options);
457 
458   if (class_name == "NSNumber" || class_name == "__NSCFNumber") {
459     uint64_t value = 0;
460     uint64_t i_bits = 0;
461     if (descriptor->GetTaggedPointerInfo(&i_bits, &value)) {
462       switch (i_bits) {
463       case 0:
464         NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
465         break;
466       case 1:
467       case 4:
468         NSNumber_FormatShort(valobj, stream, (short)value,
469                              options.GetLanguage());
470         break;
471       case 2:
472       case 8:
473         NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
474         break;
475       case 3:
476       case 12:
477         NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
478         break;
479       default:
480         return false;
481       }
482       return true;
483     } else {
484       Status error;
485 
486       AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
487           ObjCLanguageRuntime::Get(*process_sp));
488 
489       const bool new_format =
490           (runtime && runtime->GetFoundationVersion() >= 1400);
491 
492       enum class TypeCodes : int {
493         sint8 = 0x0,
494         sint16 = 0x1,
495         sint32 = 0x2,
496         sint64 = 0x3,
497         f32 = 0x4,
498         f64 = 0x5,
499         sint128 = 0x6
500       };
501 
502       uint64_t data_location = valobj_addr + 2 * ptr_size;
503       TypeCodes type_code;
504 
505       if (new_format) {
506         uint64_t cfinfoa =
507             process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
508                                                       ptr_size, 0, error);
509 
510         if (error.Fail())
511           return false;
512 
513         bool is_preserved_number = cfinfoa & 0x8;
514         if (is_preserved_number) {
515           lldbassert(!static_cast<bool>("We should handle preserved numbers!"));
516           return false;
517         }
518 
519         type_code = static_cast<TypeCodes>(cfinfoa & 0x7);
520       } else {
521         uint8_t data_type =
522         process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1,
523                                                   0, error) & 0x1F;
524 
525         if (error.Fail())
526           return false;
527 
528         switch (data_type) {
529           case 1: type_code = TypeCodes::sint8; break;
530           case 2: type_code = TypeCodes::sint16; break;
531           case 3: type_code = TypeCodes::sint32; break;
532           case 17: data_location += 8; LLVM_FALLTHROUGH;
533           case 4: type_code = TypeCodes::sint64; break;
534           case 5: type_code = TypeCodes::f32; break;
535           case 6: type_code = TypeCodes::f64; break;
536           default: return false;
537         }
538       }
539 
540       uint64_t value = 0;
541       bool success = false;
542       switch (type_code) {
543         case TypeCodes::sint8:
544         value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0,
545                                                           error);
546         if (error.Fail())
547           return false;
548         NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
549         success = true;
550         break;
551         case TypeCodes::sint16:
552         value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0,
553                                                           error);
554         if (error.Fail())
555           return false;
556         NSNumber_FormatShort(valobj, stream, (short)value,
557                              options.GetLanguage());
558         success = true;
559         break;
560       case TypeCodes::sint32:
561         value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0,
562                                                           error);
563         if (error.Fail())
564           return false;
565         NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
566         success = true;
567         break;
568       case TypeCodes::sint64:
569         value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0,
570                                                           error);
571         if (error.Fail())
572           return false;
573         NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
574         success = true;
575         break;
576       case TypeCodes::f32:
577       {
578         uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(
579             data_location, 4, 0, error);
580         if (error.Fail())
581           return false;
582         float flt_value = 0.0f;
583         memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int));
584         NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());
585         success = true;
586         break;
587       }
588       case TypeCodes::f64:
589       {
590         uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(
591             data_location, 8, 0, error);
592         if (error.Fail())
593           return false;
594         double dbl_value = 0.0;
595         memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng));
596         NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());
597         success = true;
598         break;
599       }
600       case TypeCodes::sint128: // internally, this is the same
601       {
602         uint64_t words[2];
603         words[1] = process_sp->ReadUnsignedIntegerFromMemory(
604             data_location, 8, 0, error);
605         if (error.Fail())
606           return false;
607         words[0] = process_sp->ReadUnsignedIntegerFromMemory(
608             data_location + 8, 8, 0, error);
609         if (error.Fail())
610           return false;
611         llvm::APInt i128_value(128, words);
612         NSNumber_FormatInt128(valobj, stream, i128_value, options.GetLanguage());
613         success = true;
614         break;
615       }
616       }
617       return success;
618     }
619   }
620 
621   return false;
622 }
623 
NSDecimalNumberSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)624 bool lldb_private::formatters::NSDecimalNumberSummaryProvider(
625     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
626   ProcessSP process_sp = valobj.GetProcessSP();
627   if (!process_sp)
628     return false;
629 
630   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
631   uint32_t ptr_size = process_sp->GetAddressByteSize();
632 
633   Status error;
634   int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory(
635       valobj_addr + ptr_size, 1, 0, error);
636   if (error.Fail())
637     return false;
638 
639   uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory(
640       valobj_addr + ptr_size + 1, 1, 0, error);
641   if (error.Fail())
642     return false;
643 
644   // Fifth bit marks negativity.
645   const bool is_negative = (length_and_negative >> 4) & 1;
646 
647   // Zero length and negative means NaN.
648   uint8_t length = length_and_negative & 0xf;
649   const bool is_nan = is_negative && (length == 0);
650 
651   if (is_nan) {
652     stream.Printf("NaN");
653     return true;
654   }
655 
656   if (length == 0) {
657     stream.Printf("0");
658     return true;
659   }
660 
661   uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory(
662       valobj_addr + ptr_size + 4, 8, 0, error);
663   if (error.Fail())
664     return false;
665 
666   if (is_negative)
667     stream.Printf("-");
668 
669   stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent);
670   return true;
671 }
672 
NSURLSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)673 bool lldb_private::formatters::NSURLSummaryProvider(
674     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
675   ProcessSP process_sp = valobj.GetProcessSP();
676   if (!process_sp)
677     return false;
678 
679   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
680 
681   if (!runtime)
682     return false;
683 
684   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
685       runtime->GetClassDescriptor(valobj));
686 
687   if (!descriptor || !descriptor->IsValid())
688     return false;
689 
690   uint32_t ptr_size = process_sp->GetAddressByteSize();
691 
692   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
693 
694   if (!valobj_addr)
695     return false;
696 
697   llvm::StringRef class_name = descriptor->GetClassName().GetStringRef();
698 
699   if (!class_name.equals("NSURL"))
700     return false;
701 
702   uint64_t offset_text = ptr_size + ptr_size +
703                          8; // ISA + pointer + 8 bytes of data (even on 32bit)
704   uint64_t offset_base = offset_text + ptr_size;
705   CompilerType type(valobj.GetCompilerType());
706   ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));
707   ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));
708   if (!text || text->GetValueAsUnsigned(0) == 0)
709     return false;
710 
711   StreamString base_summary;
712   if (base && base->GetValueAsUnsigned(0)) {
713     if (!NSURLSummaryProvider(*base, base_summary, options))
714       base_summary.Clear();
715   }
716   if (base_summary.Empty())
717     return NSStringSummaryProvider(*text, stream, options);
718 
719   StreamString summary;
720   if (!NSStringSummaryProvider(*text, summary, options) || summary.Empty())
721     return false;
722 
723   const char quote_char = '"';
724   std::string prefix, suffix;
725   if (Language *language = Language::FindPlugin(options.GetLanguage())) {
726     if (!language->GetFormatterPrefixSuffix(*text, ConstString("NSString"),
727                                             prefix, suffix)) {
728       prefix.clear();
729       suffix.clear();
730     }
731   }
732   // @"A" -> @"A
733   llvm::StringRef summary_str = summary.GetString();
734   bool back_consumed = summary_str.consume_back(quote_char + suffix);
735   assert(back_consumed);
736   UNUSED_IF_ASSERT_DISABLED(back_consumed);
737   // @"B" -> B"
738   llvm::StringRef base_summary_str = base_summary.GetString();
739   bool front_consumed = base_summary_str.consume_front(prefix + quote_char);
740   assert(front_consumed);
741   UNUSED_IF_ASSERT_DISABLED(front_consumed);
742   // @"A -- B"
743   if (!summary_str.empty() && !base_summary_str.empty()) {
744     stream.Printf("%s -- %s", summary_str.str().c_str(),
745                   base_summary_str.str().c_str());
746     return true;
747   }
748 
749   return false;
750 }
751 
752 /// Bias value for tagged pointer exponents.
753 /// Recommended values:
754 /// 0x3e3: encodes all dates between distantPast and distantFuture
755 ///   except for the range within about 1e-28 second of the reference date.
756 /// 0x3ef: encodes all dates for a few million years beyond distantPast and
757 ///   distantFuture, except within about 1e-25 second of the reference date.
758 const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef;
759 
760 struct DoubleBits {
761   uint64_t fraction : 52; // unsigned
762   uint64_t exponent : 11; // signed
763   uint64_t sign : 1;
764 };
765 
766 struct TaggedDoubleBits {
767   uint64_t fraction : 52; // unsigned
768   uint64_t exponent : 7;  // signed
769   uint64_t sign : 1;
770   uint64_t unused : 4; // placeholder for pointer tag bits
771 };
772 
decodeExponent(uint64_t exp)773 static uint64_t decodeExponent(uint64_t exp) {
774   // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits
775   // before performing arithmetic.
776   return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS;
777 }
778 
decodeTaggedTimeInterval(uint64_t encodedTimeInterval)779 static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) {
780   if (encodedTimeInterval == 0)
781     return 0.0;
782   if (encodedTimeInterval == std::numeric_limits<uint64_t>::max())
783     return (uint64_t)-0.0;
784 
785   TaggedDoubleBits encodedBits =
786       llvm::bit_cast<TaggedDoubleBits>(encodedTimeInterval);
787   assert(encodedBits.unused == 0);
788 
789   // Sign and fraction are represented exactly.
790   // Exponent is encoded.
791   DoubleBits decodedBits;
792   decodedBits.sign = encodedBits.sign;
793   decodedBits.fraction = encodedBits.fraction;
794   decodedBits.exponent = decodeExponent(encodedBits.exponent);
795 
796   return llvm::bit_cast<double>(decodedBits);
797 }
798 
NSDateSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)799 bool lldb_private::formatters::NSDateSummaryProvider(
800     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
801   ProcessSP process_sp = valobj.GetProcessSP();
802   if (!process_sp)
803     return false;
804 
805   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
806 
807   if (!runtime)
808     return false;
809 
810   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
811       runtime->GetClassDescriptor(valobj));
812 
813   if (!descriptor || !descriptor->IsValid())
814     return false;
815 
816   uint32_t ptr_size = process_sp->GetAddressByteSize();
817 
818   lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
819 
820   if (!valobj_addr)
821     return false;
822 
823   uint64_t date_value_bits = 0;
824   double date_value = 0.0;
825 
826   ConstString class_name = descriptor->GetClassName();
827 
828   static const ConstString g_NSDate("NSDate");
829   static const ConstString g___NSDate("__NSDate");
830   static const ConstString g___NSTaggedDate("__NSTaggedDate");
831   static const ConstString g_NSCalendarDate("NSCalendarDate");
832   static const ConstString g_NSConstantDate("NSConstantDate");
833 
834   if (class_name.IsEmpty())
835     return false;
836 
837   uint64_t info_bits = 0, value_bits = 0;
838   if ((class_name == g_NSDate) || (class_name == g___NSDate) ||
839       (class_name == g___NSTaggedDate) || (class_name == g_NSConstantDate)) {
840     if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) {
841       date_value_bits = ((value_bits << 8) | (info_bits << 4));
842       memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
843     } else {
844       llvm::Triple triple(
845           process_sp->GetTarget().GetArchitecture().GetTriple());
846       uint32_t delta =
847           (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size;
848       Status error;
849       date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(
850           valobj_addr + delta, 8, 0, error);
851       memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
852       if (error.Fail())
853         return false;
854     }
855   } else if (class_name == g_NSCalendarDate) {
856     Status error;
857     date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(
858         valobj_addr + 2 * ptr_size, 8, 0, error);
859     memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));
860     if (error.Fail())
861       return false;
862   } else
863     return false;
864 
865   // FIXME: It seems old dates are not formatted according to NSDate's calendar
866   // so we hardcode distantPast's value so that it looks like LLDB is doing
867   // the right thing.
868 
869   // The relative time in seconds from Cocoa Epoch to [NSDate distantPast].
870   const double RelSecondsFromCocoaEpochToNSDateDistantPast = -63114076800;
871   if (date_value == RelSecondsFromCocoaEpochToNSDateDistantPast) {
872     stream.Printf("0001-01-01 00:00:00 UTC");
873     return true;
874   }
875 
876   // Accomodate for the __NSTaggedDate format introduced in Foundation 1600.
877   if (class_name == g___NSTaggedDate) {
878     auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
879         ObjCLanguageRuntime::Get(*process_sp));
880     if (runtime && runtime->GetFoundationVersion() >= 1600)
881       date_value = decodeTaggedTimeInterval(value_bits << 4);
882   }
883 
884   // this snippet of code assumes that time_t == seconds since Jan-1-1970 this
885   // is generally true and POSIXly happy, but might break if a library vendor
886   // decides to get creative
887   time_t epoch = GetOSXEpoch();
888   epoch = epoch + static_cast<time_t>(std::floor(date_value));
889   tm *tm_date = gmtime(&epoch);
890   if (!tm_date)
891     return false;
892   std::string buffer(1024, 0);
893   if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)
894     return false;
895   stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,
896                 tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,
897                 tm_date->tm_min, tm_date->tm_sec, buffer.c_str());
898   return true;
899 }
900 
ObjCClassSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)901 bool lldb_private::formatters::ObjCClassSummaryProvider(
902     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
903   ProcessSP process_sp = valobj.GetProcessSP();
904   if (!process_sp)
905     return false;
906 
907   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
908 
909   if (!runtime)
910     return false;
911 
912   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
913       runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0)));
914 
915   if (!descriptor || !descriptor->IsValid())
916     return false;
917 
918   ConstString class_name = descriptor->GetClassName();
919 
920   if (class_name.IsEmpty())
921     return false;
922 
923   if (ConstString cs = Mangled(class_name).GetDemangledName())
924     class_name = cs;
925 
926   stream.Printf("%s", class_name.AsCString("<unknown class>"));
927   return true;
928 }
929 
930 class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd {
931 public:
ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp)932   ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp)
933       : SyntheticChildrenFrontEnd(*valobj_sp) {}
934 
935   ~ObjCClassSyntheticChildrenFrontEnd() override = default;
936 
CalculateNumChildren()937   size_t CalculateNumChildren() override { return 0; }
938 
GetChildAtIndex(size_t idx)939   lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
940     return lldb::ValueObjectSP();
941   }
942 
Update()943   bool Update() override { return false; }
944 
MightHaveChildren()945   bool MightHaveChildren() override { return false; }
946 
GetIndexOfChildWithName(ConstString name)947   size_t GetIndexOfChildWithName(ConstString name) override {
948     return UINT32_MAX;
949   }
950 };
951 
952 SyntheticChildrenFrontEnd *
ObjCClassSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)953 lldb_private::formatters::ObjCClassSyntheticFrontEndCreator(
954     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
955   return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp);
956 }
957 
958 template <bool needs_at>
NSDataSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)959 bool lldb_private::formatters::NSDataSummaryProvider(
960     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
961   ProcessSP process_sp = valobj.GetProcessSP();
962   if (!process_sp)
963     return false;
964 
965   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
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 
ObjCBOOLSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)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 
ObjCBooleanSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)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 = llvm::dyn_cast_or_null<AppleObjCRuntime>(
1065           ObjCLanguageRuntime::Get(*process_sp))) {
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>
ObjCSELSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)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
GetOSXEpoch()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