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