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