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
36using namespace lldb;
37using namespace lldb_private;
38using namespace lldb_private::formatters;
39
40bool 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
91bool 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
140bool 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
189bool 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
235bool 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
306static 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
322static 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
338static 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
354static 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
370static 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
392static 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
408static 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
424bool 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
717bool 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
766bool 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.
851const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef;
852
853struct DoubleBits {
854  uint64_t fraction : 52; // unsigned
855  uint64_t exponent : 11; // signed
856  uint64_t sign : 1;
857};
858
859struct 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
866static 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
872static 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
892bool 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
994bool 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
1023class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd {
1024public:
1025  ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp)
1026      : SyntheticChildrenFrontEnd(*valobj_sp) {}
1027
1028  ~ObjCClassSyntheticChildrenFrontEnd() override = default;
1029
1030  size_t CalculateNumChildren() override { return 0; }
1031
1032  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
1033    return lldb::ValueObjectSP();
1034  }
1035
1036  bool Update() override { return false; }
1037
1038  bool MightHaveChildren() override { return false; }
1039
1040  size_t GetIndexOfChildWithName(ConstString name) override {
1041    return UINT32_MAX;
1042  }
1043};
1044
1045SyntheticChildrenFrontEnd *
1046lldb_private::formatters::ObjCClassSyntheticFrontEndCreator(
1047    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
1048  return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp);
1049}
1050
1051template <bool needs_at>
1052bool 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
1115bool 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
1146bool 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
1175template <bool is_sel_ptr>
1176bool 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
1214time_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
1235template bool lldb_private::formatters::NSDataSummaryProvider<true>(
1236    ValueObject &, Stream &, const TypeSummaryOptions &);
1237
1238template bool lldb_private::formatters::NSDataSummaryProvider<false>(
1239    ValueObject &, Stream &, const TypeSummaryOptions &);
1240
1241template bool lldb_private::formatters::ObjCSELSummaryProvider<true>(
1242    ValueObject &, Stream &, const TypeSummaryOptions &);
1243
1244template bool lldb_private::formatters::ObjCSELSummaryProvider<false>(
1245    ValueObject &, Stream &, const TypeSummaryOptions &);
1246