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 
29 using namespace lldb;
30 using namespace lldb_private;
31 using namespace lldb_private::formatters;
32 
33 std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
34 NSSet_Additionals::GetAdditionalSummaries() {
35   static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
36   return g_map;
37 }
38 
39 std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> &
40 NSSet_Additionals::GetAdditionalSynthetics() {
41   static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback>
42       g_map;
43   return g_map;
44 }
45 
46 namespace lldb_private {
47 namespace formatters {
48 class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd {
49 public:
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 
64 private:
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 
88 template <typename D32, typename D64>
89 class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
90 public:
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 
105 private:
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 
119 namespace 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 
138 namespace 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 
157 class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
158 public:
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 
176 template <bool cf_style>
177 bool 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 
276 SyntheticChildrenFrontEnd *
277 lldb_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 
328 lldb_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 
336 lldb_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 
343 size_t
344 lldb_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 
353 size_t
354 lldb_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 
360 bool 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 
400 bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() {
401   return true;
402 }
403 
404 lldb::ValueObjectSP
405 lldb_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 
477 template <typename D32, typename D64>
478 lldb_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 
487 template <typename D32, typename D64>
488 lldb_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 
496 template <typename D32, typename D64>
497 size_t
498 lldb_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 
508 template <typename D32, typename D64>
509 size_t
510 lldb_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 
517 template <typename D32, typename D64>
518 bool
519 lldb_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 
559 template <typename D32, typename D64>
560 bool
561 lldb_private::formatters::
562   GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() {
563   return true;
564 }
565 
566 template <typename D32, typename D64>
567 lldb::ValueObjectSP
568 lldb_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 
644 template bool lldb_private::formatters::NSSetSummaryProvider<true>(
645     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
646 
647 template bool lldb_private::formatters::NSSetSummaryProvider<false>(
648     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options);
649