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