NSSet.cpp revision 321369
1//===-- NSSet.cpp -----------------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10// C Includes
11// C++ Includes
12// Other libraries and framework includes
13// Project includes
14#include "NSSet.h"
15
16#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
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/ObjCLanguageRuntime.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
33std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
34NSSet_Additionals::GetAdditionalSummaries() {
35  static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
36  return g_map;
37}
38
39std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> &
40NSSet_Additionals::GetAdditionalSynthetics() {
41  static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback>
42      g_map;
43  return g_map;
44}
45
46namespace lldb_private {
47namespace formatters {
48class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
49public:
50  NSSetISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
51
52  ~NSSetISyntheticFrontEnd() override;
53
54  size_t CalculateNumChildren() override;
55
56  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
57
58  bool Update() override;
59
60  bool MightHaveChildren() override;
61
62  size_t GetIndexOfChildWithName(const ConstString &name) override;
63
64private:
65  struct DataDescriptor_32 {
66    uint32_t _used : 26;
67    uint32_t _szidx : 6;
68  };
69
70  struct DataDescriptor_64 {
71    uint64_t _used : 58;
72    uint32_t _szidx : 6;
73  };
74
75  struct SetItemDescriptor {
76    lldb::addr_t item_ptr;
77    lldb::ValueObjectSP valobj_sp;
78  };
79
80  ExecutionContextRef m_exe_ctx_ref;
81  uint8_t m_ptr_size;
82  DataDescriptor_32 *m_data_32;
83  DataDescriptor_64 *m_data_64;
84  lldb::addr_t m_data_ptr;
85  std::vector<SetItemDescriptor> m_children;
86};
87
88template <typename D32, typename D64>
89class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
90public:
91  GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
92
93  ~GenericNSSetMSyntheticFrontEnd() override;
94
95  size_t CalculateNumChildren() override;
96
97  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
98
99  bool Update() override;
100
101  bool MightHaveChildren() override;
102
103  size_t GetIndexOfChildWithName(const ConstString &name) override;
104
105private:
106
107  struct SetItemDescriptor {
108    lldb::addr_t item_ptr;
109    lldb::ValueObjectSP valobj_sp;
110  };
111
112  ExecutionContextRef m_exe_ctx_ref;
113  uint8_t m_ptr_size;
114  D32 *m_data_32;
115  D64 *m_data_64;
116  std::vector<SetItemDescriptor> m_children;
117};
118
119namespace Foundation1300 {
120  struct DataDescriptor_32 {
121    uint32_t _used : 26;
122    uint32_t _size;
123    uint32_t _mutations;
124    uint32_t _objs_addr;
125  };
126
127  struct DataDescriptor_64 {
128    uint64_t _used : 58;
129    uint64_t _size;
130    uint64_t _mutations;
131    uint64_t _objs_addr;
132  };
133
134  using NSSetMSyntheticFrontEnd =
135      GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
136}
137
138namespace Foundation1400 {
139  struct DataDescriptor_32 {
140    uint32_t _used : 26;
141    uint32_t _size;
142    uint32_t _objs_addr;
143    uint32_t _mutations;
144  };
145
146  struct DataDescriptor_64 {
147    uint64_t _used : 58;
148    uint64_t _size;
149    uint64_t _objs_addr;
150    uint64_t _mutations;
151  };
152
153  using NSSetMSyntheticFrontEnd =
154      GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>;
155}
156
157class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
158public:
159  NSSetCodeRunningSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
160
161  ~NSSetCodeRunningSyntheticFrontEnd() override;
162
163  size_t CalculateNumChildren() override;
164
165  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
166
167  bool Update() override;
168
169  bool MightHaveChildren() override;
170
171  size_t GetIndexOfChildWithName(const ConstString &name) override;
172};
173} // namespace formatters
174} // namespace lldb_private
175
176template <bool cf_style>
177bool lldb_private::formatters::NSSetSummaryProvider(
178    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
179  static ConstString g_TypeHint("NSSet");
180
181  ProcessSP process_sp = valobj.GetProcessSP();
182  if (!process_sp)
183    return false;
184
185  ObjCLanguageRuntime *runtime =
186      (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
187          lldb::eLanguageTypeObjC);
188
189  if (!runtime)
190    return false;
191
192  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
193      runtime->GetClassDescriptor(valobj));
194
195  if (!descriptor || !descriptor->IsValid())
196    return false;
197
198  uint32_t ptr_size = process_sp->GetAddressByteSize();
199  bool is_64bit = (ptr_size == 8);
200
201  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
202
203  if (!valobj_addr)
204    return false;
205
206  uint64_t value = 0;
207
208  ConstString class_name_cs = descriptor->GetClassName();
209  const char *class_name = class_name_cs.GetCString();
210
211  if (!class_name || !*class_name)
212    return false;
213
214  if (!strcmp(class_name, "__NSSetI")) {
215    Status error;
216    value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
217                                                      ptr_size, 0, error);
218    if (error.Fail())
219      return false;
220    value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
221  } else if (!strcmp(class_name, "__NSSetM")) {
222    Status error;
223    value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
224                                                      ptr_size, 0, error);
225    if (error.Fail())
226      return false;
227    value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
228  }
229  /*else if (!strcmp(class_name,"__NSCFSet"))
230   {
231   Status error;
232   value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + (is_64bit ?
233   20 : 12), 4, 0, error);
234   if (error.Fail())
235   return false;
236   if (is_64bit)
237   value &= ~0x1fff000000000000UL;
238   }
239   else if (!strcmp(class_name,"NSCountedSet"))
240   {
241   Status error;
242   value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size,
243   ptr_size, 0, error);
244   if (error.Fail())
245   return false;
246   value = process_sp->ReadUnsignedIntegerFromMemory(value + (is_64bit ? 20 :
247   12), 4, 0, error);
248   if (error.Fail())
249   return false;
250   if (is_64bit)
251   value &= ~0x1fff000000000000UL;
252   }*/
253  else {
254    auto &map(NSSet_Additionals::GetAdditionalSummaries());
255    auto iter = map.find(class_name_cs), end = map.end();
256    if (iter != end)
257      return iter->second(valobj, stream, options);
258    else
259      return false;
260  }
261
262  std::string prefix, suffix;
263  if (Language *language = Language::FindPlugin(options.GetLanguage())) {
264    if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
265                                            suffix)) {
266      prefix.clear();
267      suffix.clear();
268    }
269  }
270
271  stream.Printf("%s%" PRIu64 " %s%s%s", prefix.c_str(), value, "element",
272                value == 1 ? "" : "s", suffix.c_str());
273  return true;
274}
275
276SyntheticChildrenFrontEnd *
277lldb_private::formatters::NSSetSyntheticFrontEndCreator(
278    CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) {
279  lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
280  if (!process_sp)
281    return nullptr;
282  ObjCLanguageRuntime *runtime =
283      (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime(
284          lldb::eLanguageTypeObjC);
285  if (!runtime)
286    return nullptr;
287
288  CompilerType valobj_type(valobj_sp->GetCompilerType());
289  Flags flags(valobj_type.GetTypeInfo());
290
291  if (flags.IsClear(eTypeIsPointer)) {
292    Status error;
293    valobj_sp = valobj_sp->AddressOf(error);
294    if (error.Fail() || !valobj_sp)
295      return nullptr;
296  }
297
298  ObjCLanguageRuntime::ClassDescriptorSP descriptor(
299      runtime->GetClassDescriptor(*valobj_sp));
300
301  if (!descriptor || !descriptor->IsValid())
302    return nullptr;
303
304  ConstString class_name_cs = descriptor->GetClassName();
305  const char *class_name = class_name_cs.GetCString();
306
307  if (!class_name || !*class_name)
308    return nullptr;
309
310  if (!strcmp(class_name, "__NSSetI")) {
311    return (new NSSetISyntheticFrontEnd(valobj_sp));
312  } else if (!strcmp(class_name, "__NSSetM")) {
313    AppleObjCRuntime *apple_runtime =
314        llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
315    if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1400)
316      return (new Foundation1400::NSSetMSyntheticFrontEnd(valobj_sp));
317    else
318      return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp));
319  } else {
320    auto &map(NSSet_Additionals::GetAdditionalSynthetics());
321    auto iter = map.find(class_name_cs), end = map.end();
322    if (iter != end)
323      return iter->second(synth, valobj_sp);
324    return nullptr;
325  }
326}
327
328lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd(
329    lldb::ValueObjectSP valobj_sp)
330    : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
331      m_data_32(nullptr), m_data_64(nullptr) {
332  if (valobj_sp)
333    Update();
334}
335
336lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() {
337  delete m_data_32;
338  m_data_32 = nullptr;
339  delete m_data_64;
340  m_data_64 = nullptr;
341}
342
343size_t
344lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName(
345    const ConstString &name) {
346  const char *item_name = name.GetCString();
347  uint32_t idx = ExtractIndexFromString(item_name);
348  if (idx < UINT32_MAX && idx >= CalculateNumChildren())
349    return UINT32_MAX;
350  return idx;
351}
352
353size_t
354lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() {
355  if (!m_data_32 && !m_data_64)
356    return 0;
357  return (m_data_32 ? m_data_32->_used : m_data_64->_used);
358}
359
360bool lldb_private::formatters::NSSetISyntheticFrontEnd::Update() {
361  m_children.clear();
362  delete m_data_32;
363  m_data_32 = nullptr;
364  delete m_data_64;
365  m_data_64 = nullptr;
366  m_ptr_size = 0;
367  ValueObjectSP valobj_sp = m_backend.GetSP();
368  if (!valobj_sp)
369    return false;
370  if (!valobj_sp)
371    return false;
372  m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
373  Status error;
374  if (valobj_sp->IsPointerType()) {
375    valobj_sp = valobj_sp->Dereference(error);
376    if (error.Fail() || !valobj_sp)
377      return false;
378  }
379  error.Clear();
380  lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
381  if (!process_sp)
382    return false;
383  m_ptr_size = process_sp->GetAddressByteSize();
384  uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
385  if (m_ptr_size == 4) {
386    m_data_32 = new DataDescriptor_32();
387    process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32),
388                           error);
389  } else {
390    m_data_64 = new DataDescriptor_64();
391    process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64),
392                           error);
393  }
394  if (error.Fail())
395    return false;
396  m_data_ptr = data_location + m_ptr_size;
397  return false;
398}
399
400bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() {
401  return true;
402}
403
404lldb::ValueObjectSP
405lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex(size_t idx) {
406  uint32_t num_children = CalculateNumChildren();
407
408  if (idx >= num_children)
409    return lldb::ValueObjectSP();
410
411  ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
412  if (!process_sp)
413    return lldb::ValueObjectSP();
414
415  if (m_children.empty()) {
416    // do the scan phase
417    lldb::addr_t obj_at_idx = 0;
418
419    uint32_t tries = 0;
420    uint32_t test_idx = 0;
421
422    while (tries < num_children) {
423      obj_at_idx = m_data_ptr + (test_idx * m_ptr_size);
424      if (!process_sp)
425        return lldb::ValueObjectSP();
426      Status error;
427      obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
428      if (error.Fail())
429        return lldb::ValueObjectSP();
430
431      test_idx++;
432
433      if (!obj_at_idx)
434        continue;
435      tries++;
436
437      SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
438
439      m_children.push_back(descriptor);
440    }
441  }
442
443  if (idx >= m_children.size()) // should never happen
444    return lldb::ValueObjectSP();
445
446  SetItemDescriptor &set_item = m_children[idx];
447  if (!set_item.valobj_sp) {
448    auto ptr_size = process_sp->GetAddressByteSize();
449    DataBufferHeap buffer(ptr_size, 0);
450    switch (ptr_size) {
451    case 0: // architecture has no clue?? - fail
452      return lldb::ValueObjectSP();
453    case 4:
454      *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
455      break;
456    case 8:
457      *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
458      break;
459    default:
460      assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
461    }
462    StreamString idx_name;
463    idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
464
465    DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
466                       process_sp->GetByteOrder(),
467                       process_sp->GetAddressByteSize());
468
469    set_item.valobj_sp = CreateValueObjectFromData(
470        idx_name.GetString(), data, m_exe_ctx_ref,
471        m_backend.GetCompilerType().GetBasicTypeFromAST(
472            lldb::eBasicTypeObjCID));
473  }
474  return set_item.valobj_sp;
475}
476
477template <typename D32, typename D64>
478lldb_private::formatters::
479  GenericNSSetMSyntheticFrontEnd<D32, D64>::GenericNSSetMSyntheticFrontEnd(
480    lldb::ValueObjectSP valobj_sp)
481    : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_ptr_size(8),
482      m_data_32(nullptr), m_data_64(nullptr) {
483  if (valobj_sp)
484    Update();
485}
486
487template <typename D32, typename D64>
488lldb_private::formatters::
489  GenericNSSetMSyntheticFrontEnd<D32, D64>::~GenericNSSetMSyntheticFrontEnd() {
490  delete m_data_32;
491  m_data_32 = nullptr;
492  delete m_data_64;
493  m_data_64 = nullptr;
494}
495
496template <typename D32, typename D64>
497size_t
498lldb_private::formatters::
499  GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName(
500    const ConstString &name) {
501  const char *item_name = name.GetCString();
502  uint32_t idx = ExtractIndexFromString(item_name);
503  if (idx < UINT32_MAX && idx >= CalculateNumChildren())
504    return UINT32_MAX;
505  return idx;
506}
507
508template <typename D32, typename D64>
509size_t
510lldb_private::formatters::
511  GenericNSSetMSyntheticFrontEnd<D32, D64>::CalculateNumChildren() {
512  if (!m_data_32 && !m_data_64)
513    return 0;
514  return (m_data_32 ? m_data_32->_used : m_data_64->_used);
515}
516
517template <typename D32, typename D64>
518bool
519lldb_private::formatters::
520  GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() {
521  m_children.clear();
522  ValueObjectSP valobj_sp = m_backend.GetSP();
523  m_ptr_size = 0;
524  delete m_data_32;
525  m_data_32 = nullptr;
526  delete m_data_64;
527  m_data_64 = nullptr;
528  if (!valobj_sp)
529    return false;
530  if (!valobj_sp)
531    return false;
532  m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
533  Status error;
534  if (valobj_sp->IsPointerType()) {
535    valobj_sp = valobj_sp->Dereference(error);
536    if (error.Fail() || !valobj_sp)
537      return false;
538  }
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  uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size;
545  if (m_ptr_size == 4) {
546    m_data_32 = new D32();
547    process_sp->ReadMemory(data_location, m_data_32, sizeof(D32),
548                           error);
549  } else {
550    m_data_64 = new D64();
551    process_sp->ReadMemory(data_location, m_data_64, sizeof(D64),
552                           error);
553  }
554  if (error.Fail())
555    return false;
556  return false;
557}
558
559template <typename D32, typename D64>
560bool
561lldb_private::formatters::
562  GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() {
563  return true;
564}
565
566template <typename D32, typename D64>
567lldb::ValueObjectSP
568lldb_private::formatters::
569  GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(size_t idx) {
570  lldb::addr_t m_objs_addr =
571      (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr);
572
573  uint32_t num_children = CalculateNumChildren();
574
575  if (idx >= num_children)
576    return lldb::ValueObjectSP();
577
578  ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
579  if (!process_sp)
580    return lldb::ValueObjectSP();
581
582  if (m_children.empty()) {
583    // do the scan phase
584    lldb::addr_t obj_at_idx = 0;
585
586    uint32_t tries = 0;
587    uint32_t test_idx = 0;
588
589    while (tries < num_children) {
590      obj_at_idx = m_objs_addr + (test_idx * m_ptr_size);
591      if (!process_sp)
592        return lldb::ValueObjectSP();
593      Status error;
594      obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error);
595      if (error.Fail())
596        return lldb::ValueObjectSP();
597
598      test_idx++;
599
600      if (!obj_at_idx)
601        continue;
602      tries++;
603
604      SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()};
605
606      m_children.push_back(descriptor);
607    }
608  }
609
610  if (idx >= m_children.size()) // should never happen
611    return lldb::ValueObjectSP();
612
613  SetItemDescriptor &set_item = m_children[idx];
614  if (!set_item.valobj_sp) {
615    auto ptr_size = process_sp->GetAddressByteSize();
616    DataBufferHeap buffer(ptr_size, 0);
617    switch (ptr_size) {
618    case 0: // architecture has no clue?? - fail
619      return lldb::ValueObjectSP();
620    case 4:
621      *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr;
622      break;
623    case 8:
624      *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr;
625      break;
626    default:
627      assert(false && "pointer size is not 4 nor 8 - get out of here ASAP");
628    }
629    StreamString idx_name;
630    idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
631
632    DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(),
633                       process_sp->GetByteOrder(),
634                       process_sp->GetAddressByteSize());
635
636    set_item.valobj_sp = CreateValueObjectFromData(
637        idx_name.GetString(), data, m_exe_ctx_ref,
638        m_backend.GetCompilerType().GetBasicTypeFromAST(
639            lldb::eBasicTypeObjCID));
640  }
641  return set_item.valobj_sp;
642}
643
644template bool lldb_private::formatters::NSSetSummaryProvider<true>(
645    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
646
647template bool lldb_private::formatters::NSSetSummaryProvider<false>(
648    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
649