xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- NSIndexPath.cpp ---------------------------------------------------===//
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 "Plugins/TypeSystem/Clang/TypeSystemClang.h"
12 #include "lldb/DataFormatters/FormattersHelpers.h"
13 #include "lldb/DataFormatters/TypeSynthetic.h"
14 #include "lldb/Target/Process.h"
15 #include "lldb/Target/Target.h"
16 #include "lldb/ValueObject/ValueObject.h"
17 #include "lldb/ValueObject/ValueObjectConstResult.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 
PACKED_INDEX_SHIFT_64(size_t i)24 static constexpr size_t PACKED_INDEX_SHIFT_64(size_t i) {
25   return (60 - (13 * (4 - i)));
26 }
27 
PACKED_INDEX_SHIFT_32(size_t i)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:
NSIndexPathSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)34   NSIndexPathSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
35       : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_descriptor_sp(nullptr),
36         m_impl(), m_uint_star_type() {
37     m_ptr_size =
38         m_backend.GetTargetSP()->GetArchitecture().GetAddressByteSize();
39   }
40 
41   ~NSIndexPathSyntheticFrontEnd() override = default;
42 
CalculateNumChildren()43   llvm::Expected<uint32_t> CalculateNumChildren() override {
44     return m_impl.GetNumIndexes();
45   }
46 
GetChildAtIndex(uint32_t idx)47   lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
48     return m_impl.GetIndexAtIndex(idx, m_uint_star_type);
49   }
50 
Update()51   lldb::ChildCacheState Update() override {
52     m_impl.Clear();
53 
54     auto type_system = m_backend.GetCompilerType().GetTypeSystem();
55     if (!type_system)
56       return lldb::ChildCacheState::eRefetch;
57 
58     auto ast = ScratchTypeSystemClang::GetForTarget(
59         *m_backend.GetExecutionContextRef().GetTargetSP());
60     if (!ast)
61       return lldb::ChildCacheState::eRefetch;
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 lldb::ChildCacheState::eRefetch;
71 
72     ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
73 
74     if (!runtime)
75       return lldb::ChildCacheState::eRefetch;
76 
77     ObjCLanguageRuntime::ClassDescriptorSP descriptor(
78         runtime->GetClassDescriptor(m_backend));
79 
80     if (!descriptor.get() || !descriptor->IsValid())
81       return lldb::ChildCacheState::eRefetch;
82 
83     uint64_t info_bits(0), value_bits(0), payload(0);
84 
85     if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits, &payload)) {
86       m_impl.m_inlined.SetIndexes(payload, *process_sp);
87       m_impl.m_mode = Mode::Inlined;
88     } else {
89       ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _indexes_id;
90       ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _length_id;
91 
92       bool has_indexes(false), has_length(false);
93 
94       for (size_t x = 0; x < descriptor->GetNumIVars(); x++) {
95         const auto &ivar = descriptor->GetIVarAtIndex(x);
96         if (ivar.m_name == g__indexes) {
97           _indexes_id = ivar;
98           has_indexes = true;
99         } else if (ivar.m_name == g__length) {
100           _length_id = ivar;
101           has_length = true;
102         }
103 
104         if (has_length && has_indexes)
105           break;
106       }
107 
108       if (has_length && has_indexes) {
109         m_impl.m_outsourced.m_indexes =
110             m_backend
111                 .GetSyntheticChildAtOffset(_indexes_id.m_offset,
112                                            m_uint_star_type.GetPointerType(),
113                                            true)
114                 .get();
115         ValueObjectSP length_sp(m_backend.GetSyntheticChildAtOffset(
116             _length_id.m_offset, m_uint_star_type, true));
117         if (length_sp) {
118           m_impl.m_outsourced.m_count = length_sp->GetValueAsUnsigned(0);
119           if (m_impl.m_outsourced.m_indexes)
120             m_impl.m_mode = Mode::Outsourced;
121         }
122       }
123     }
124     return lldb::ChildCacheState::eRefetch;
125   }
126 
MightHaveChildren()127   bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; }
128 
GetIndexOfChildWithName(ConstString name)129   llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
130     auto optional_idx = ExtractIndexFromString(name.AsCString());
131     if (!optional_idx) {
132       return llvm::createStringError("Type has no child named '%s'",
133                                      name.AsCString());
134     }
135     uint32_t idx = *optional_idx;
136     if (idx >= CalculateNumChildrenIgnoringErrors())
137       return llvm::createStringError("Type has no child named '%s'",
138                                      name.AsCString());
139     return idx;
140   }
141 
GetSyntheticValue()142   lldb::ValueObjectSP GetSyntheticValue() override { return nullptr; }
143 
144 protected:
145   ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp;
146 
147   enum class Mode { Inlined, Outsourced, Invalid };
148 
149   struct Impl {
GetNumIndexesNSIndexPathSyntheticFrontEnd::Impl150     size_t GetNumIndexes() {
151       switch (m_mode) {
152       case Mode::Inlined:
153         return m_inlined.GetNumIndexes();
154       case Mode::Outsourced:
155         return m_outsourced.m_count;
156       default:
157         return 0;
158       }
159     }
160 
GetIndexAtIndexNSIndexPathSyntheticFrontEnd::Impl161     lldb::ValueObjectSP GetIndexAtIndex(size_t idx,
162                                         const CompilerType &desired_type) {
163       if (idx >= GetNumIndexes())
164         return nullptr;
165       switch (m_mode) {
166       default:
167         return nullptr;
168       case Mode::Inlined:
169         return m_inlined.GetIndexAtIndex(idx, desired_type);
170       case Mode::Outsourced:
171         return m_outsourced.GetIndexAtIndex(idx);
172       }
173     }
174 
175     struct InlinedIndexes {
176     public:
SetIndexesNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes177       void SetIndexes(uint64_t value, Process &p) {
178         m_indexes = value;
179         _lengthForInlinePayload(p.GetAddressByteSize());
180         m_process = &p;
181       }
182 
GetNumIndexesNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes183       size_t GetNumIndexes() { return m_count; }
184 
GetIndexAtIndexNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes185       lldb::ValueObjectSP GetIndexAtIndex(size_t idx,
186                                           const CompilerType &desired_type) {
187         if (!m_process)
188           return nullptr;
189 
190         std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx));
191         if (!value.second)
192           return nullptr;
193 
194         Value v;
195         if (m_ptr_size == 8) {
196           Scalar scalar((unsigned long long)value.first);
197           v = Value(scalar);
198         } else {
199           Scalar scalar((unsigned int)value.first);
200           v = Value(scalar);
201         }
202 
203         v.SetCompilerType(desired_type);
204 
205         StreamString idx_name;
206         idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
207 
208         return ValueObjectConstResult::Create(
209             m_process, v, ConstString(idx_name.GetString()));
210       }
211 
ClearNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes212       void Clear() {
213         m_indexes = 0;
214         m_count = 0;
215         m_ptr_size = 0;
216         m_process = nullptr;
217       }
218 
InlinedIndexesNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes219       InlinedIndexes() {}
220 
221     private:
222       uint64_t m_indexes = 0;
223       size_t m_count = 0;
224       uint32_t m_ptr_size = 0;
225       Process *m_process = nullptr;
226 
227       // cfr. Foundation for the details of this code
_lengthForInlinePayloadNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes228       size_t _lengthForInlinePayload(uint32_t ptr_size) {
229         m_ptr_size = ptr_size;
230         if (m_ptr_size == 8)
231           m_count = ((m_indexes >> 3) & 0x7);
232         else
233           m_count = ((m_indexes >> 3) & 0x3);
234         return m_count;
235       }
236 
_indexAtPositionForInlinePayloadNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes237       std::pair<uint64_t, bool> _indexAtPositionForInlinePayload(size_t pos) {
238         static const uint64_t PACKED_INDEX_MASK = ((1 << 13) - 1);
239         if (m_ptr_size == 8) {
240           switch (pos) {
241           case 3:
242           case 2:
243           case 1:
244           case 0:
245             return {(m_indexes >> PACKED_INDEX_SHIFT_64(pos)) &
246                         PACKED_INDEX_MASK,
247                     true};
248           default:
249             return {0, false};
250           }
251         } else {
252           switch (pos) {
253           case 0:
254           case 1:
255             return {(m_indexes >> PACKED_INDEX_SHIFT_32(pos)) &
256                         PACKED_INDEX_MASK,
257                     true};
258           default:
259             return {0, false};
260           }
261         }
262         return {0, false};
263       }
264     };
265 
266     struct OutsourcedIndexes {
GetIndexAtIndexNSIndexPathSyntheticFrontEnd::Impl::OutsourcedIndexes267       lldb::ValueObjectSP GetIndexAtIndex(size_t idx) {
268         if (m_indexes) {
269           ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(idx, true));
270           return index_sp;
271         }
272         return nullptr;
273       }
274 
ClearNSIndexPathSyntheticFrontEnd::Impl::OutsourcedIndexes275       void Clear() {
276         m_indexes = nullptr;
277         m_count = 0;
278       }
279 
OutsourcedIndexesNSIndexPathSyntheticFrontEnd::Impl::OutsourcedIndexes280       OutsourcedIndexes() {}
281 
282       ValueObject *m_indexes = nullptr;
283       size_t m_count = 0;
284     };
285 
286     union {
287       struct InlinedIndexes m_inlined;
288       struct OutsourcedIndexes m_outsourced;
289     };
290 
ClearNSIndexPathSyntheticFrontEnd::Impl291     void Clear() {
292       switch (m_mode) {
293       case Mode::Inlined:
294         m_inlined.Clear();
295         break;
296       case Mode::Outsourced:
297         m_outsourced.Clear();
298         break;
299       case Mode::Invalid:
300         break;
301       }
302       m_mode = Mode::Invalid;
303     }
304 
ImplNSIndexPathSyntheticFrontEnd::Impl305     Impl() {}
306 
307     Mode m_mode = Mode::Invalid;
308   } m_impl;
309 
310   uint32_t m_ptr_size = 0;
311   CompilerType m_uint_star_type;
312 };
313 
314 namespace lldb_private {
315 namespace formatters {
316 
317 SyntheticChildrenFrontEnd *
NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)318 NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *,
319                                     lldb::ValueObjectSP valobj_sp) {
320   if (valobj_sp)
321     return new NSIndexPathSyntheticFrontEnd(valobj_sp);
322   return nullptr;
323 }
324 
325 } // namespace formatters
326 } // namespace lldb_private
327