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