1 //===-- NSIndexPath.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 "Cocoa.h"
15 
16 #include "lldb/Core/ValueObject.h"
17 #include "lldb/Core/ValueObjectConstResult.h"
18 #include "lldb/DataFormatters/FormattersHelpers.h"
19 #include "lldb/DataFormatters/TypeSynthetic.h"
20 #include "lldb/Symbol/ClangASTContext.h"
21 #include "lldb/Target/ObjCLanguageRuntime.h"
22 #include "lldb/Target/Process.h"
23 #include "lldb/Target/Target.h"
24 
25 using namespace lldb;
26 using namespace lldb_private;
27 using namespace lldb_private::formatters;
28 
29 class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd
30 {
31 public:
32     NSIndexPathSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
33     SyntheticChildrenFrontEnd (*valobj_sp.get()),
34     m_ptr_size(0),
35     m_uint_star_type()
36     {
37         m_ptr_size = m_backend.GetTargetSP()->GetArchitecture().GetAddressByteSize();
38     }
39 
40     ~NSIndexPathSyntheticFrontEnd() override = default;
41 
42     size_t
43     CalculateNumChildren() override
44     {
45         return m_impl.GetNumIndexes();
46     }
47 
48     lldb::ValueObjectSP
49     GetChildAtIndex(size_t idx) override
50     {
51         return m_impl.GetIndexAtIndex(idx, m_uint_star_type);
52     }
53 
54     bool
55     Update() override
56     {
57         m_impl.Clear();
58 
59         TypeSystem* type_system = m_backend.GetCompilerType().GetTypeSystem();
60         if (!type_system)
61             return false;
62 
63         ClangASTContext *ast = m_backend.GetExecutionContextRef().GetTargetSP()->GetScratchClangASTContext();
64         if (!ast)
65             return false;
66 
67         m_uint_star_type = ast->GetPointerSizedIntType(false);
68 
69         static ConstString g__indexes("_indexes");
70         static ConstString g__length("_length");
71 
72         ProcessSP process_sp = m_backend.GetProcessSP();
73         if (!process_sp)
74             return false;
75 
76         ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
77 
78         if (!runtime)
79             return false;
80 
81         ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(m_backend));
82 
83         if (!descriptor.get() || !descriptor->IsValid())
84             return false;
85 
86         uint64_t info_bits(0),value_bits(0),payload(0);
87 
88         if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits, &payload))
89         {
90             m_impl.m_inlined.SetIndexes(payload, *process_sp);
91             m_impl.m_mode = Mode::Inlined;
92         }
93         else
94         {
95             ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _indexes_id;
96             ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _length_id;
97 
98             bool has_indexes(false),has_length(false);
99 
100             for (size_t x = 0;
101                  x < descriptor->GetNumIVars();
102                  x++)
103             {
104                 const auto& ivar = descriptor->GetIVarAtIndex(x);
105                 if (ivar.m_name == g__indexes)
106                 {
107                     _indexes_id = ivar;
108                     has_indexes = true;
109                 }
110                 else if (ivar.m_name == g__length)
111                 {
112                     _length_id = ivar;
113                     has_length = true;
114                 }
115 
116                 if (has_length && has_indexes)
117                     break;
118             }
119 
120             if (has_length && has_indexes)
121             {
122                 m_impl.m_outsourced.m_indexes = m_backend.GetSyntheticChildAtOffset(_indexes_id.m_offset,
123                                                                                     m_uint_star_type.GetPointerType(),
124                                                                                     true).get();
125                 ValueObjectSP length_sp(m_backend.GetSyntheticChildAtOffset(_length_id.m_offset,
126                                                                             m_uint_star_type,
127                                                                             true));
128                 if (length_sp)
129                 {
130                     m_impl.m_outsourced.m_count = length_sp->GetValueAsUnsigned(0);
131                     if (m_impl.m_outsourced.m_indexes)
132                         m_impl.m_mode = Mode::Outsourced;
133                 }
134             }
135         }
136         return false;
137     }
138 
139     bool
140     MightHaveChildren() override
141     {
142         if (m_impl.m_mode == Mode::Invalid)
143             return false;
144         return true;
145     }
146 
147     size_t
148     GetIndexOfChildWithName(const ConstString &name) override
149     {
150         const char* item_name = name.GetCString();
151         uint32_t idx = ExtractIndexFromString(item_name);
152         if (idx < UINT32_MAX && idx >= CalculateNumChildren())
153             return UINT32_MAX;
154         return idx;
155     }
156 
157     lldb::ValueObjectSP
158     GetSyntheticValue() override
159     {
160         return nullptr;
161     }
162 
163 protected:
164     ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp;
165 
166     enum class Mode {
167         Inlined,
168         Outsourced,
169         Invalid
170     };
171 
172     struct Impl {
173         Mode m_mode;
174 
175         size_t
176         GetNumIndexes ()
177         {
178             switch (m_mode)
179             {
180                 case Mode::Inlined:
181                     return m_inlined.GetNumIndexes();
182                 case Mode::Outsourced:
183                     return m_outsourced.m_count;
184                 default:
185                     return 0;
186             }
187         }
188 
189         lldb::ValueObjectSP
190         GetIndexAtIndex (size_t idx, const CompilerType& desired_type)
191         {
192             if (idx >= GetNumIndexes())
193                 return nullptr;
194             switch (m_mode)
195             {
196                 default: return nullptr;
197                 case Mode::Inlined:
198                     return m_inlined.GetIndexAtIndex (idx, desired_type);
199                 case Mode::Outsourced:
200                     return m_outsourced.GetIndexAtIndex (idx);
201             }
202         }
203 
204         struct InlinedIndexes {
205         public:
206           void SetIndexes(uint64_t value, Process& p)
207           {
208               m_indexes = value;
209               _lengthForInlinePayload(p.GetAddressByteSize());
210               m_process = &p;
211           }
212 
213           size_t
214           GetNumIndexes ()
215           {
216               return m_count;
217           }
218 
219           lldb::ValueObjectSP
220           GetIndexAtIndex (size_t idx, const CompilerType& desired_type)
221           {
222               std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx));
223               if (!value.second)
224                   return nullptr;
225 
226               Value v;
227               if (m_ptr_size == 8)
228               {
229                   Scalar scalar( (unsigned long long)value.first );
230                   v = Value(scalar);
231               }
232               else
233               {
234                   Scalar scalar( (unsigned int)value.first );
235                   v = Value(scalar);
236               }
237 
238               v.SetCompilerType(desired_type);
239 
240               StreamString idx_name;
241               idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
242 
243               return ValueObjectConstResult::Create(m_process, v, ConstString(idx_name.GetData()));
244           }
245 
246             void
247             Clear ()
248             {
249                 m_indexes = 0;
250                 m_count = 0;
251                 m_ptr_size = 0;
252                 m_process = nullptr;
253             }
254 
255         private:
256           uint64_t m_indexes;
257           size_t m_count;
258           uint32_t m_ptr_size;
259           Process *m_process;
260 
261           // cfr. Foundation for the details of this code
262           size_t _lengthForInlinePayload(uint32_t ptr_size) {
263               m_ptr_size = ptr_size;
264               if (m_ptr_size == 8)
265               m_count = ((m_indexes >> 3) & 0x7);
266               else
267               m_count = ((m_indexes >> 3) & 0x3);
268               return m_count;
269           }
270 
271           std::pair<uint64_t, bool>
272           _indexAtPositionForInlinePayload(size_t pos)
273           {
274               if (m_ptr_size == 8)
275               {
276                 switch (pos) {
277                     case 5: return {((m_indexes >> 51) & 0x1ff),true};
278                     case 4: return {((m_indexes >> 42) & 0x1ff),true};
279                     case 3: return {((m_indexes >> 33) & 0x1ff),true};
280                     case 2: return {((m_indexes >> 24) & 0x1ff),true};
281                     case 1: return {((m_indexes >> 15) & 0x1ff),true};
282                     case 0: return {((m_indexes >>  6) & 0x1ff),true};
283                 }
284               }
285               else
286                   {
287                   switch (pos) {
288                       case 2: return {((m_indexes >> 23) & 0x1ff),true};
289                       case 1: return {((m_indexes >> 14) & 0x1ff),true};
290                       case 0: return {((m_indexes >>  5) & 0x1ff),true};
291                   }
292               }
293               return {0,false};
294           }
295 
296         };
297         struct OutsourcedIndexes {
298             ValueObject *m_indexes;
299             size_t m_count;
300 
301             lldb::ValueObjectSP
302             GetIndexAtIndex (size_t idx)
303             {
304                 if (m_indexes)
305                 {
306                     ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(idx, true));
307                     return index_sp;
308                 }
309                 return nullptr;
310             }
311 
312             void
313             Clear ()
314             {
315                 m_indexes = nullptr;
316                 m_count = 0;
317             }
318         };
319 
320         union {
321             struct InlinedIndexes m_inlined;
322             struct OutsourcedIndexes m_outsourced;
323         };
324 
325         void
326         Clear ()
327         {
328             m_mode = Mode::Invalid;
329             m_inlined.Clear();
330             m_outsourced.Clear();
331         }
332     } m_impl;
333 
334     uint32_t m_ptr_size;
335     CompilerType m_uint_star_type;
336 };
337 
338 namespace lldb_private {
339     namespace formatters {
340 
341         SyntheticChildrenFrontEnd* NSIndexPathSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
342         {
343             if (valobj_sp)
344                 return new NSIndexPathSyntheticFrontEnd(valobj_sp);
345             return nullptr;
346         }
347 
348     } // namespace formatters
349 } // namespace lldb_private
350