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