xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
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/Process.h"
17 #include "lldb/Target/Target.h"
18 
19 #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
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 = ObjCLanguageRuntime::Get(*process_sp);
72 
73     if (!runtime)
74       return false;
75 
76     ObjCLanguageRuntime::ClassDescriptorSP descriptor(
77         runtime->GetClassDescriptor(m_backend));
78 
79     if (!descriptor.get() || !descriptor->IsValid())
80       return false;
81 
82     uint64_t info_bits(0), value_bits(0), payload(0);
83 
84     if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits, &payload)) {
85       m_impl.m_inlined.SetIndexes(payload, *process_sp);
86       m_impl.m_mode = Mode::Inlined;
87     } else {
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; x < descriptor->GetNumIVars(); x++) {
94         const auto &ivar = descriptor->GetIVarAtIndex(x);
95         if (ivar.m_name == g__indexes) {
96           _indexes_id = ivar;
97           has_indexes = true;
98         } else if (ivar.m_name == g__length) {
99           _length_id = ivar;
100           has_length = true;
101         }
102 
103         if (has_length && has_indexes)
104           break;
105       }
106 
107       if (has_length && has_indexes) {
108         m_impl.m_outsourced.m_indexes =
109             m_backend
110                 .GetSyntheticChildAtOffset(_indexes_id.m_offset,
111                                            m_uint_star_type.GetPointerType(),
112                                            true)
113                 .get();
114         ValueObjectSP length_sp(m_backend.GetSyntheticChildAtOffset(
115             _length_id.m_offset, m_uint_star_type, true));
116         if (length_sp) {
117           m_impl.m_outsourced.m_count = length_sp->GetValueAsUnsigned(0);
118           if (m_impl.m_outsourced.m_indexes)
119             m_impl.m_mode = Mode::Outsourced;
120         }
121       }
122     }
123     return false;
124   }
125 
126   bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; }
127 
128   size_t GetIndexOfChildWithName(ConstString name) override {
129     const char *item_name = name.GetCString();
130     uint32_t idx = ExtractIndexFromString(item_name);
131     if (idx < UINT32_MAX && idx >= CalculateNumChildren())
132       return UINT32_MAX;
133     return idx;
134   }
135 
136   lldb::ValueObjectSP GetSyntheticValue() override { return nullptr; }
137 
138 protected:
139   ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp;
140 
141   enum class Mode { Inlined, Outsourced, Invalid };
142 
143   struct Impl {
144     size_t GetNumIndexes() {
145       switch (m_mode) {
146       case Mode::Inlined:
147         return m_inlined.GetNumIndexes();
148       case Mode::Outsourced:
149         return m_outsourced.m_count;
150       default:
151         return 0;
152       }
153     }
154 
155     lldb::ValueObjectSP GetIndexAtIndex(size_t idx,
156                                         const CompilerType &desired_type) {
157       if (idx >= GetNumIndexes())
158         return nullptr;
159       switch (m_mode) {
160       default:
161         return nullptr;
162       case Mode::Inlined:
163         return m_inlined.GetIndexAtIndex(idx, desired_type);
164       case Mode::Outsourced:
165         return m_outsourced.GetIndexAtIndex(idx);
166       }
167     }
168 
169     struct InlinedIndexes {
170     public:
171       void SetIndexes(uint64_t value, Process &p) {
172         m_indexes = value;
173         _lengthForInlinePayload(p.GetAddressByteSize());
174         m_process = &p;
175       }
176 
177       size_t GetNumIndexes() { return m_count; }
178 
179       lldb::ValueObjectSP GetIndexAtIndex(size_t idx,
180                                           const CompilerType &desired_type) {
181         if (!m_process)
182           return nullptr;
183 
184         std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx));
185         if (!value.second)
186           return nullptr;
187 
188         Value v;
189         if (m_ptr_size == 8) {
190           Scalar scalar((unsigned long long)value.first);
191           v = Value(scalar);
192         } else {
193           Scalar scalar((unsigned int)value.first);
194           v = Value(scalar);
195         }
196 
197         v.SetCompilerType(desired_type);
198 
199         StreamString idx_name;
200         idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
201 
202         return ValueObjectConstResult::Create(
203             m_process, v, ConstString(idx_name.GetString()));
204       }
205 
206       void Clear() {
207         m_indexes = 0;
208         m_count = 0;
209         m_ptr_size = 0;
210         m_process = nullptr;
211       }
212 
213       InlinedIndexes()
214           : m_indexes(0), m_count(0), m_ptr_size(0), m_process(nullptr) {}
215 
216     private:
217       uint64_t m_indexes;
218       size_t m_count;
219       uint32_t m_ptr_size;
220       Process *m_process;
221 
222       // cfr. Foundation for the details of this code
223       size_t _lengthForInlinePayload(uint32_t ptr_size) {
224         m_ptr_size = ptr_size;
225         if (m_ptr_size == 8)
226           m_count = ((m_indexes >> 3) & 0x7);
227         else
228           m_count = ((m_indexes >> 3) & 0x3);
229         return m_count;
230       }
231 
232       std::pair<uint64_t, bool> _indexAtPositionForInlinePayload(size_t pos) {
233         static const uint64_t PACKED_INDEX_MASK = ((1 << 13) - 1);
234         if (m_ptr_size == 8) {
235           switch (pos) {
236           case 3:
237           case 2:
238           case 1:
239           case 0:
240             return {(m_indexes >> PACKED_INDEX_SHIFT_64(pos)) &
241                         PACKED_INDEX_MASK,
242                     true};
243           default:
244             return {0, false};
245           }
246         } else {
247           switch (pos) {
248           case 0:
249           case 1:
250             return {(m_indexes >> PACKED_INDEX_SHIFT_32(pos)) &
251                         PACKED_INDEX_MASK,
252                     true};
253           default:
254             return {0, false};
255           }
256         }
257         return {0, false};
258       }
259     };
260 
261     struct OutsourcedIndexes {
262       lldb::ValueObjectSP GetIndexAtIndex(size_t idx) {
263         if (m_indexes) {
264           ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(idx, true));
265           return index_sp;
266         }
267         return nullptr;
268       }
269 
270       void Clear() {
271         m_indexes = nullptr;
272         m_count = 0;
273       }
274 
275       OutsourcedIndexes() : m_indexes(nullptr), m_count(0) {}
276 
277       ValueObject *m_indexes;
278       size_t m_count;
279     };
280 
281     union {
282       struct InlinedIndexes m_inlined;
283       struct OutsourcedIndexes m_outsourced;
284     };
285 
286     void Clear() {
287       m_mode = Mode::Invalid;
288       m_inlined.Clear();
289       m_outsourced.Clear();
290     }
291 
292     Impl() : m_mode(Mode::Invalid) {}
293 
294     Mode m_mode;
295   } m_impl;
296 
297   uint32_t m_ptr_size;
298   CompilerType m_uint_star_type;
299 };
300 
301 namespace lldb_private {
302 namespace formatters {
303 
304 SyntheticChildrenFrontEnd *
305 NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *,
306                                     lldb::ValueObjectSP valobj_sp) {
307   if (valobj_sp)
308     return new NSIndexPathSyntheticFrontEnd(valobj_sp);
309   return nullptr;
310 }
311 
312 } // namespace formatters
313 } // namespace lldb_private
314