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