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