1//===-- NSDictionary.cpp ----------------------------------------*- C++ -*-===//
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 <mutex>
10
11#include "clang/AST/DeclCXX.h"
12
13#include "NSDictionary.h"
14
15#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
16
17#include "lldb/Core/ValueObject.h"
18#include "lldb/Core/ValueObjectConstResult.h"
19#include "lldb/DataFormatters/FormattersHelpers.h"
20#include "lldb/Symbol/ClangASTContext.h"
21#include "lldb/Target/Language.h"
22#include "lldb/Target/StackFrame.h"
23#include "lldb/Target/Target.h"
24#include "lldb/Utility/DataBufferHeap.h"
25#include "lldb/Utility/Endian.h"
26#include "lldb/Utility/Status.h"
27#include "lldb/Utility/Stream.h"
28
29using namespace lldb;
30using namespace lldb_private;
31using namespace lldb_private::formatters;
32
33NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Prefix(
34    ConstString p)
35    : m_prefix(p) {}
36
37bool NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Match(
38    ConstString class_name) {
39  return class_name.GetStringRef().startswith(m_prefix.GetStringRef());
40}
41
42NSDictionary_Additionals::AdditionalFormatterMatching::Full::Full(ConstString n)
43    : m_name(n) {}
44
45bool NSDictionary_Additionals::AdditionalFormatterMatching::Full::Match(
46    ConstString class_name) {
47  return (class_name == m_name);
48}
49
50NSDictionary_Additionals::AdditionalFormatters<
51    CXXFunctionSummaryFormat::Callback> &
52NSDictionary_Additionals::GetAdditionalSummaries() {
53  static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> g_map;
54  return g_map;
55}
56
57NSDictionary_Additionals::AdditionalFormatters<
58    CXXSyntheticChildren::CreateFrontEndCallback> &
59NSDictionary_Additionals::GetAdditionalSynthetics() {
60  static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback>
61      g_map;
62  return g_map;
63}
64
65static CompilerType GetLLDBNSPairType(TargetSP target_sp) {
66  CompilerType compiler_type;
67
68  ClangASTContext *target_ast_context = ClangASTContext::GetScratch(*target_sp);
69
70  if (target_ast_context) {
71    ConstString g___lldb_autogen_nspair("__lldb_autogen_nspair");
72
73    compiler_type =
74        target_ast_context->GetTypeForIdentifier<clang::CXXRecordDecl>(
75            g___lldb_autogen_nspair);
76
77    if (!compiler_type) {
78      compiler_type = target_ast_context->CreateRecordType(
79          nullptr, lldb::eAccessPublic, g___lldb_autogen_nspair.GetCString(),
80          clang::TTK_Struct, lldb::eLanguageTypeC);
81
82      if (compiler_type) {
83        ClangASTContext::StartTagDeclarationDefinition(compiler_type);
84        CompilerType id_compiler_type =
85            target_ast_context->GetBasicType(eBasicTypeObjCID);
86        ClangASTContext::AddFieldToRecordType(
87            compiler_type, "key", id_compiler_type, lldb::eAccessPublic, 0);
88        ClangASTContext::AddFieldToRecordType(
89            compiler_type, "value", id_compiler_type, lldb::eAccessPublic, 0);
90        ClangASTContext::CompleteTagDeclarationDefinition(compiler_type);
91      }
92    }
93  }
94  return compiler_type;
95}
96
97namespace lldb_private {
98namespace formatters {
99class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
100public:
101  NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
102
103  ~NSDictionaryISyntheticFrontEnd() override;
104
105  size_t CalculateNumChildren() override;
106
107  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
108
109  bool Update() override;
110
111  bool MightHaveChildren() override;
112
113  size_t GetIndexOfChildWithName(ConstString name) override;
114
115private:
116  struct DataDescriptor_32 {
117    uint32_t _used : 26;
118    uint32_t _szidx : 6;
119  };
120
121  struct DataDescriptor_64 {
122    uint64_t _used : 58;
123    uint32_t _szidx : 6;
124  };
125
126  struct DictionaryItemDescriptor {
127    lldb::addr_t key_ptr;
128    lldb::addr_t val_ptr;
129    lldb::ValueObjectSP valobj_sp;
130  };
131
132  ExecutionContextRef m_exe_ctx_ref;
133  uint8_t m_ptr_size;
134  lldb::ByteOrder m_order;
135  DataDescriptor_32 *m_data_32;
136  DataDescriptor_64 *m_data_64;
137  lldb::addr_t m_data_ptr;
138  CompilerType m_pair_type;
139  std::vector<DictionaryItemDescriptor> m_children;
140};
141
142class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
143public:
144  NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
145
146  ~NSDictionary1SyntheticFrontEnd() override = default;
147
148  size_t CalculateNumChildren() override;
149
150  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
151
152  bool Update() override;
153
154  bool MightHaveChildren() override;
155
156  size_t GetIndexOfChildWithName(ConstString name) override;
157
158private:
159  ValueObjectSP m_pair;
160};
161
162template <typename D32, typename D64>
163class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
164public:
165  GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
166
167  ~GenericNSDictionaryMSyntheticFrontEnd() override;
168
169  size_t CalculateNumChildren() override;
170
171  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
172
173  bool Update() override;
174
175  bool MightHaveChildren() override;
176
177  size_t GetIndexOfChildWithName(ConstString name) override;
178
179private:
180  struct DictionaryItemDescriptor {
181    lldb::addr_t key_ptr;
182    lldb::addr_t val_ptr;
183    lldb::ValueObjectSP valobj_sp;
184  };
185
186  ExecutionContextRef m_exe_ctx_ref;
187  uint8_t m_ptr_size;
188  lldb::ByteOrder m_order;
189  D32 *m_data_32;
190  D64 *m_data_64;
191  CompilerType m_pair_type;
192  std::vector<DictionaryItemDescriptor> m_children;
193};
194
195namespace Foundation1100 {
196  class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
197  public:
198    NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
199
200    ~NSDictionaryMSyntheticFrontEnd() override;
201
202    size_t CalculateNumChildren() override;
203
204    lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
205
206    bool Update() override;
207
208    bool MightHaveChildren() override;
209
210    size_t GetIndexOfChildWithName(ConstString name) override;
211
212  private:
213    struct DataDescriptor_32 {
214      uint32_t _used : 26;
215      uint32_t _kvo : 1;
216      uint32_t _size;
217      uint32_t _mutations;
218      uint32_t _objs_addr;
219      uint32_t _keys_addr;
220    };
221
222    struct DataDescriptor_64 {
223      uint64_t _used : 58;
224      uint32_t _kvo : 1;
225      uint64_t _size;
226      uint64_t _mutations;
227      uint64_t _objs_addr;
228      uint64_t _keys_addr;
229    };
230
231    struct DictionaryItemDescriptor {
232      lldb::addr_t key_ptr;
233      lldb::addr_t val_ptr;
234      lldb::ValueObjectSP valobj_sp;
235    };
236
237    ExecutionContextRef m_exe_ctx_ref;
238    uint8_t m_ptr_size;
239    lldb::ByteOrder m_order;
240    DataDescriptor_32 *m_data_32;
241    DataDescriptor_64 *m_data_64;
242    CompilerType m_pair_type;
243    std::vector<DictionaryItemDescriptor> m_children;
244  };
245}
246
247namespace Foundation1428 {
248  struct DataDescriptor_32 {
249    uint32_t _used : 26;
250    uint32_t _kvo : 1;
251    uint32_t _size;
252    uint32_t _buffer;
253    uint64_t GetSize() { return _size; }
254  };
255
256  struct DataDescriptor_64 {
257    uint64_t _used : 58;
258    uint32_t _kvo : 1;
259    uint64_t _size;
260    uint64_t _buffer;
261    uint64_t GetSize() { return _size; }
262  };
263
264
265
266  using NSDictionaryMSyntheticFrontEnd =
267    GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
268}
269
270namespace Foundation1437 {
271  static const uint64_t NSDictionaryCapacities[] = {
272      0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723,
273      2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607,
274      214519, 346607, 561109, 907759, 1468927, 2376191, 3845119,
275      6221311, 10066421, 16287743, 26354171, 42641881, 68996069,
276      111638519, 180634607, 292272623, 472907251
277  };
278
279  static const size_t NSDictionaryNumSizeBuckets = sizeof(NSDictionaryCapacities) / sizeof(uint64_t);
280
281  struct DataDescriptor_32 {
282    uint32_t _buffer;
283    uint32_t _muts;
284    uint32_t _used : 25;
285    uint32_t _kvo : 1;
286    uint32_t _szidx : 6;
287
288    uint64_t GetSize() {
289      return (_szidx) >= NSDictionaryNumSizeBuckets ?
290          0 : NSDictionaryCapacities[_szidx];
291    }
292  };
293
294  struct DataDescriptor_64 {
295    uint64_t _buffer;
296    uint32_t _muts;
297    uint32_t _used : 25;
298    uint32_t _kvo : 1;
299    uint32_t _szidx : 6;
300
301    uint64_t GetSize() {
302      return (_szidx) >= NSDictionaryNumSizeBuckets ?
303          0 : NSDictionaryCapacities[_szidx];
304    }
305  };
306
307  using NSDictionaryMSyntheticFrontEnd =
308    GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
309
310  template <typename DD>
311  uint64_t
312  __NSDictionaryMSize_Impl(lldb_private::Process &process,
313                           lldb::addr_t valobj_addr, Status &error) {
314    const lldb::addr_t start_of_descriptor =
315        valobj_addr + process.GetAddressByteSize();
316    DD descriptor = DD();
317    process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor),
318                       error);
319    if (error.Fail()) {
320      return 0;
321    }
322    return descriptor._used;
323  }
324
325  uint64_t
326  __NSDictionaryMSize(lldb_private::Process &process, lldb::addr_t valobj_addr,
327               Status &error) {
328    if (process.GetAddressByteSize() == 4) {
329      return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr,
330                                                         error);
331    } else {
332      return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr,
333                                                         error);
334    }
335  }
336
337}
338} // namespace formatters
339} // namespace lldb_private
340
341template <bool name_entries>
342bool lldb_private::formatters::NSDictionarySummaryProvider(
343    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
344  static ConstString g_TypeHint("NSDictionary");
345  ProcessSP process_sp = valobj.GetProcessSP();
346  if (!process_sp)
347    return false;
348
349  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
350
351  if (!runtime)
352    return false;
353
354  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
355      runtime->GetClassDescriptor(valobj));
356
357  if (!descriptor || !descriptor->IsValid())
358    return false;
359
360  uint32_t ptr_size = process_sp->GetAddressByteSize();
361  bool is_64bit = (ptr_size == 8);
362
363  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
364
365  if (!valobj_addr)
366    return false;
367
368  uint64_t value = 0;
369
370  ConstString class_name(descriptor->GetClassName());
371
372  static const ConstString g_DictionaryI("__NSDictionaryI");
373  static const ConstString g_DictionaryM("__NSDictionaryM");
374  static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
375  static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable");
376  static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
377  static const ConstString g_Dictionary0("__NSDictionary0");
378  static const ConstString g_DictionaryCF("__NSCFDictionary");
379
380  if (class_name.IsEmpty())
381    return false;
382
383  if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) {
384    Status error;
385    value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
386                                                      ptr_size, 0, error);
387    if (error.Fail())
388      return false;
389    value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
390  } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
391             class_name == g_DictionaryCF) {
392    AppleObjCRuntime *apple_runtime =
393    llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
394    Status error;
395    if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) {
396      value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr,
397                                                  error);
398    } else {
399      value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
400                                                        ptr_size, 0, error);
401      value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
402    }
403    if (error.Fail())
404      return false;
405  } else if (class_name == g_Dictionary1) {
406    value = 1;
407  } else if (class_name == g_Dictionary0) {
408    value = 0;
409  }
410  else {
411    auto &map(NSDictionary_Additionals::GetAdditionalSummaries());
412    for (auto &candidate : map) {
413      if (candidate.first && candidate.first->Match(class_name))
414        return candidate.second(valobj, stream, options);
415    }
416    return false;
417  }
418
419  std::string prefix, suffix;
420  if (Language *language = Language::FindPlugin(options.GetLanguage())) {
421    if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
422                                            suffix)) {
423      prefix.clear();
424      suffix.clear();
425    }
426  }
427
428  stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "key/value pair",
429                value == 1 ? "" : "s", suffix.c_str());
430  return true;
431}
432
433SyntheticChildrenFrontEnd *
434lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
435    CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
436  lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
437  if (!process_sp)
438    return nullptr;
439  AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(
440      ObjCLanguageRuntime::Get(*process_sp));
441  if (!runtime)
442    return nullptr;
443
444  CompilerType valobj_type(valobj_sp->GetCompilerType());
445  Flags flags(valobj_type.GetTypeInfo());
446
447  if (flags.IsClear(eTypeIsPointer)) {
448    Status error;
449    valobj_sp = valobj_sp->AddressOf(error);
450    if (error.Fail() || !valobj_sp)
451      return nullptr;
452  }
453
454  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
455      runtime->GetClassDescriptor(*valobj_sp));
456
457  if (!descriptor || !descriptor->IsValid())
458    return nullptr;
459
460  ConstString class_name(descriptor->GetClassName());
461
462  static const ConstString g_DictionaryI("__NSDictionaryI");
463  static const ConstString g_DictionaryM("__NSDictionaryM");
464  static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI");
465  static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable");
466  static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy");
467  static const ConstString g_Dictionary0("__NSDictionary0");
468
469  if (class_name.IsEmpty())
470    return nullptr;
471
472  if (class_name == g_DictionaryI) {
473    return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
474  } else if (class_name == g_DictionaryM) {
475    if (runtime->GetFoundationVersion() >= 1437) {
476      return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp));
477    } else if (runtime->GetFoundationVersion() >= 1428) {
478      return (new Foundation1428::NSDictionaryMSyntheticFrontEnd(valobj_sp));
479    } else {
480      return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
481    }
482  } else if (class_name == g_DictionaryMLegacy) {
483      return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
484  } else if (class_name == g_Dictionary1) {
485    return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
486  } else {
487    auto &map(NSDictionary_Additionals::GetAdditionalSynthetics());
488    for (auto &candidate : map) {
489      if (candidate.first && candidate.first->Match((class_name)))
490        return candidate.second(synth, valobj_sp);
491    }
492  }
493
494  return nullptr;
495}
496
497lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
498    NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
499    : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
500      m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
501      m_pair_type() {}
502
503lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
504    ~NSDictionaryISyntheticFrontEnd() {
505  delete m_data_32;
506  m_data_32 = nullptr;
507  delete m_data_64;
508  m_data_64 = nullptr;
509}
510
511size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
512    GetIndexOfChildWithName(ConstString name) {
513  const char *item_name = name.GetCString();
514  uint32_t idx = ExtractIndexFromString(item_name);
515  if (idx < UINT32_MAX && idx >= CalculateNumChildren())
516    return UINT32_MAX;
517  return idx;
518}
519
520size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
521    CalculateNumChildren() {
522  if (!m_data_32 && !m_data_64)
523    return 0;
524  return (m_data_32 ? m_data_32->_used : m_data_64->_used);
525}
526
527bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() {
528  m_children.clear();
529  delete m_data_32;
530  m_data_32 = nullptr;
531  delete m_data_64;
532  m_data_64 = nullptr;
533  m_ptr_size = 0;
534  ValueObjectSP valobj_sp = m_backend.GetSP();
535  if (!valobj_sp)
536    return false;
537  m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
538  Status error;
539  error.Clear();
540  lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
541  if (!process_sp)
542    return false;
543  m_ptr_size = process_sp->GetAddressByteSize();
544  m_order = process_sp->GetByteOrder();
545  uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
546  if (m_ptr_size == 4) {
547    m_data_32 = new DataDescriptor_32();
548    process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
549                           error);
550  } else {
551    m_data_64 = new DataDescriptor_64();
552    process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
553                           error);
554  }
555  if (error.Fail())
556    return false;
557  m_data_ptr = data_location + m_ptr_size;
558  return false;
559}
560
561bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd::
562    MightHaveChildren() {
563  return true;
564}
565
566lldb::ValueObjectSP
567lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex(
568    size_t idx) {
569  uint32_t num_children = CalculateNumChildren();
570
571  if (idx >= num_children)
572    return lldb::ValueObjectSP();
573
574  if (m_children.empty()) {
575    // do the scan phase
576    lldb::addr_t key_at_idx = 0, val_at_idx = 0;
577
578    uint32_t tries = 0;
579    uint32_t test_idx = 0;
580
581    while (tries < num_children) {
582      key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size);
583      val_at_idx = key_at_idx + m_ptr_size;
584      ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
585      if (!process_sp)
586        return lldb::ValueObjectSP();
587      Status error;
588      key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
589      if (error.Fail())
590        return lldb::ValueObjectSP();
591      val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
592      if (error.Fail())
593        return lldb::ValueObjectSP();
594
595      test_idx++;
596
597      if (!key_at_idx || !val_at_idx)
598        continue;
599      tries++;
600
601      DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
602                                             lldb::ValueObjectSP()};
603
604      m_children.push_back(descriptor);
605    }
606  }
607
608  if (idx >= m_children.size()) // should never happen
609    return lldb::ValueObjectSP();
610
611  DictionaryItemDescriptor &dict_item = m_children[idx];
612  if (!dict_item.valobj_sp) {
613    if (!m_pair_type.IsValid()) {
614      TargetSP target_sp(m_backend.GetTargetSP());
615      if (!target_sp)
616        return ValueObjectSP();
617      m_pair_type = GetLLDBNSPairType(target_sp);
618    }
619    if (!m_pair_type.IsValid())
620      return ValueObjectSP();
621
622    DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
623
624    if (m_ptr_size == 8) {
625      uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
626      *data_ptr = dict_item.key_ptr;
627      *(data_ptr + 1) = dict_item.val_ptr;
628    } else {
629      uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
630      *data_ptr = dict_item.key_ptr;
631      *(data_ptr + 1) = dict_item.val_ptr;
632    }
633
634    StreamString idx_name;
635    idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
636    DataExtractor data(buffer_sp, m_order, m_ptr_size);
637    dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
638                                                    m_exe_ctx_ref, m_pair_type);
639  }
640  return dict_item.valobj_sp;
641}
642
643lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
644    NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
645    : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
646
647size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
648    GetIndexOfChildWithName(ConstString name) {
649  static const ConstString g_zero("[0]");
650  return name == g_zero ? 0 : UINT32_MAX;
651}
652
653size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
654    CalculateNumChildren() {
655  return 1;
656}
657
658bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() {
659  m_pair.reset();
660  return false;
661}
662
663bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
664    MightHaveChildren() {
665  return true;
666}
667
668lldb::ValueObjectSP
669lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex(
670    size_t idx) {
671  if (idx != 0)
672    return lldb::ValueObjectSP();
673
674  if (m_pair.get())
675    return m_pair;
676
677  auto process_sp(m_backend.GetProcessSP());
678  if (!process_sp)
679    return nullptr;
680
681  auto ptr_size = process_sp->GetAddressByteSize();
682
683  lldb::addr_t key_ptr =
684      m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size;
685  lldb::addr_t value_ptr = key_ptr + ptr_size;
686
687  Status error;
688
689  lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error);
690  if (error.Fail())
691    return nullptr;
692  lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error);
693  if (error.Fail())
694    return nullptr;
695
696  auto pair_type =
697      GetLLDBNSPairType(process_sp->GetTarget().shared_from_this());
698
699  DataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0));
700
701  if (ptr_size == 8) {
702    uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
703    *data_ptr = key_at_idx;
704    *(data_ptr + 1) = value_at_idx;
705  } else {
706    uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
707    *data_ptr = key_at_idx;
708    *(data_ptr + 1) = value_at_idx;
709  }
710
711  DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size);
712  m_pair = CreateValueObjectFromData(
713      "[0]", data, m_backend.GetExecutionContextRef(), pair_type);
714
715  return m_pair;
716}
717
718template <typename D32, typename D64>
719lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
720    GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
721    : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
722      m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
723      m_pair_type() {}
724
725template <typename D32, typename D64>
726lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
727    ~GenericNSDictionaryMSyntheticFrontEnd() {
728  delete m_data_32;
729  m_data_32 = nullptr;
730  delete m_data_64;
731  m_data_64 = nullptr;
732}
733
734template <typename D32, typename D64>
735size_t
736lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::    GetIndexOfChildWithName(ConstString name) {
737  const char *item_name = name.GetCString();
738  uint32_t idx = ExtractIndexFromString(item_name);
739  if (idx < UINT32_MAX && idx >= CalculateNumChildren())
740    return UINT32_MAX;
741  return idx;
742}
743
744template <typename D32, typename D64>
745size_t
746lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::CalculateNumChildren() {
747  if (!m_data_32 && !m_data_64)
748    return 0;
749  return (m_data_32 ? m_data_32->_used : m_data_64->_used);
750}
751
752template <typename D32, typename D64>
753bool
754lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
755  Update() {
756  m_children.clear();
757  ValueObjectSP valobj_sp = m_backend.GetSP();
758  m_ptr_size = 0;
759  delete m_data_32;
760  m_data_32 = nullptr;
761  delete m_data_64;
762  m_data_64 = nullptr;
763  if (!valobj_sp)
764    return false;
765  m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
766  Status error;
767  error.Clear();
768  lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
769  if (!process_sp)
770    return false;
771  m_ptr_size = process_sp->GetAddressByteSize();
772  m_order = process_sp->GetByteOrder();
773  uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
774  if (m_ptr_size == 4) {
775    m_data_32 = new D32();
776    process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
777                           error);
778  } else {
779    m_data_64 = new D64();
780    process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
781                           error);
782  }
783  if (error.Fail())
784    return false;
785  return false;
786}
787
788template <typename D32, typename D64>
789bool
790lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
791    MightHaveChildren() {
792  return true;
793}
794
795template <typename D32, typename D64>
796lldb::ValueObjectSP
797lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>::
798    GetChildAtIndex(
799    size_t idx) {
800  lldb::addr_t m_keys_ptr;
801  lldb::addr_t m_values_ptr;
802  if (m_data_32) {
803    uint32_t size = m_data_32->GetSize();
804    m_keys_ptr = m_data_32->_buffer;
805    m_values_ptr = m_data_32->_buffer + (m_ptr_size * size);
806  } else {
807    uint32_t size = m_data_64->GetSize();
808    m_keys_ptr = m_data_64->_buffer;
809    m_values_ptr = m_data_64->_buffer + (m_ptr_size * size);
810  }
811
812  uint32_t num_children = CalculateNumChildren();
813
814  if (idx >= num_children)
815    return lldb::ValueObjectSP();
816
817  if (m_children.empty()) {
818    // do the scan phase
819    lldb::addr_t key_at_idx = 0, val_at_idx = 0;
820
821    uint32_t tries = 0;
822    uint32_t test_idx = 0;
823
824    while (tries < num_children) {
825      key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
826      val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
827      ;
828      ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
829      if (!process_sp)
830        return lldb::ValueObjectSP();
831      Status error;
832      key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
833      if (error.Fail())
834        return lldb::ValueObjectSP();
835      val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
836      if (error.Fail())
837        return lldb::ValueObjectSP();
838
839      test_idx++;
840
841      if (!key_at_idx || !val_at_idx)
842        continue;
843      tries++;
844
845      DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
846                                             lldb::ValueObjectSP()};
847
848      m_children.push_back(descriptor);
849    }
850  }
851
852  if (idx >= m_children.size()) // should never happen
853    return lldb::ValueObjectSP();
854
855  DictionaryItemDescriptor &dict_item = m_children[idx];
856  if (!dict_item.valobj_sp) {
857    if (!m_pair_type.IsValid()) {
858      TargetSP target_sp(m_backend.GetTargetSP());
859      if (!target_sp)
860        return ValueObjectSP();
861      m_pair_type = GetLLDBNSPairType(target_sp);
862    }
863    if (!m_pair_type.IsValid())
864      return ValueObjectSP();
865
866    DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
867
868    if (m_ptr_size == 8) {
869      uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
870      *data_ptr = dict_item.key_ptr;
871      *(data_ptr + 1) = dict_item.val_ptr;
872    } else {
873      uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
874      *data_ptr = dict_item.key_ptr;
875      *(data_ptr + 1) = dict_item.val_ptr;
876    }
877
878    StreamString idx_name;
879    idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
880    DataExtractor data(buffer_sp, m_order, m_ptr_size);
881    dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
882                                                    m_exe_ctx_ref, m_pair_type);
883  }
884  return dict_item.valobj_sp;
885}
886
887
888lldb_private::formatters::Foundation1100::
889  NSDictionaryMSyntheticFrontEnd::
890    NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
891    : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
892      m_order(lldb::eByteOrderInvalid), m_data_32(nullptr), m_data_64(nullptr),
893      m_pair_type() {}
894
895lldb_private::formatters::Foundation1100::
896  NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() {
897  delete m_data_32;
898  m_data_32 = nullptr;
899  delete m_data_64;
900  m_data_64 = nullptr;
901}
902
903size_t
904lldb_private::formatters::Foundation1100::
905  NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
906  const char *item_name = name.GetCString();
907  uint32_t idx = ExtractIndexFromString(item_name);
908  if (idx < UINT32_MAX && idx >= CalculateNumChildren())
909    return UINT32_MAX;
910  return idx;
911}
912
913size_t
914lldb_private::formatters::Foundation1100::
915  NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() {
916  if (!m_data_32 && !m_data_64)
917    return 0;
918  return (m_data_32 ? m_data_32->_used : m_data_64->_used);
919}
920
921bool
922lldb_private::formatters::Foundation1100::
923  NSDictionaryMSyntheticFrontEnd::Update() {
924  m_children.clear();
925  ValueObjectSP valobj_sp = m_backend.GetSP();
926  m_ptr_size = 0;
927  delete m_data_32;
928  m_data_32 = nullptr;
929  delete m_data_64;
930  m_data_64 = nullptr;
931  if (!valobj_sp)
932    return false;
933  m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
934  Status error;
935  error.Clear();
936  lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
937  if (!process_sp)
938    return false;
939  m_ptr_size = process_sp->GetAddressByteSize();
940  m_order = process_sp->GetByteOrder();
941  uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size;
942  if (m_ptr_size == 4) {
943    m_data_32 = new DataDescriptor_32();
944    process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
945                           error);
946  } else {
947    m_data_64 = new DataDescriptor_64();
948    process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
949                           error);
950  }
951  if (error.Fail())
952    return false;
953  return false;
954}
955
956bool
957lldb_private::formatters::Foundation1100::
958  NSDictionaryMSyntheticFrontEnd::MightHaveChildren() {
959  return true;
960}
961
962lldb::ValueObjectSP
963lldb_private::formatters::Foundation1100::
964  NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
965  lldb::addr_t m_keys_ptr =
966      (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr);
967  lldb::addr_t m_values_ptr =
968      (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
969
970  uint32_t num_children = CalculateNumChildren();
971
972  if (idx >= num_children)
973    return lldb::ValueObjectSP();
974
975  if (m_children.empty()) {
976    // do the scan phase
977    lldb::addr_t key_at_idx = 0, val_at_idx = 0;
978
979    uint32_t tries = 0;
980    uint32_t test_idx = 0;
981
982    while (tries < num_children) {
983      key_at_idx = m_keys_ptr + (test_idx * m_ptr_size);
984      val_at_idx = m_values_ptr + (test_idx * m_ptr_size);
985      ;
986      ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
987      if (!process_sp)
988        return lldb::ValueObjectSP();
989      Status error;
990      key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error);
991      if (error.Fail())
992        return lldb::ValueObjectSP();
993      val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error);
994      if (error.Fail())
995        return lldb::ValueObjectSP();
996
997      test_idx++;
998
999      if (!key_at_idx || !val_at_idx)
1000        continue;
1001      tries++;
1002
1003      DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
1004                                             lldb::ValueObjectSP()};
1005
1006      m_children.push_back(descriptor);
1007    }
1008  }
1009
1010  if (idx >= m_children.size()) // should never happen
1011    return lldb::ValueObjectSP();
1012
1013  DictionaryItemDescriptor &dict_item = m_children[idx];
1014  if (!dict_item.valobj_sp) {
1015    if (!m_pair_type.IsValid()) {
1016      TargetSP target_sp(m_backend.GetTargetSP());
1017      if (!target_sp)
1018        return ValueObjectSP();
1019      m_pair_type = GetLLDBNSPairType(target_sp);
1020    }
1021    if (!m_pair_type.IsValid())
1022      return ValueObjectSP();
1023
1024    DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));
1025
1026    if (m_ptr_size == 8) {
1027      uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
1028      *data_ptr = dict_item.key_ptr;
1029      *(data_ptr + 1) = dict_item.val_ptr;
1030    } else {
1031      uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
1032      *data_ptr = dict_item.key_ptr;
1033      *(data_ptr + 1) = dict_item.val_ptr;
1034    }
1035
1036    StreamString idx_name;
1037    idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
1038    DataExtractor data(buffer_sp, m_order, m_ptr_size);
1039    dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
1040                                                    m_exe_ctx_ref, m_pair_type);
1041  }
1042  return dict_item.valobj_sp;
1043}
1044
1045template bool lldb_private::formatters::NSDictionarySummaryProvider<true>(
1046    ValueObject &, Stream &, const TypeSummaryOptions &);
1047
1048template bool lldb_private::formatters::NSDictionarySummaryProvider<false>(
1049    ValueObject &, Stream &, const TypeSummaryOptions &);
1050