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
PACKED_INDEX_SHIFT_64(size_t i)25 static constexpr size_t PACKED_INDEX_SHIFT_64(size_t i) {
26 return (60 - (13 * (4 - i)));
27 }
28
PACKED_INDEX_SHIFT_32(size_t i)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:
NSIndexPathSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)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
CalculateNumChildren()44 size_t CalculateNumChildren() override { return m_impl.GetNumIndexes(); }
45
GetChildAtIndex(size_t idx)46 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
47 return m_impl.GetIndexAtIndex(idx, m_uint_star_type);
48 }
49
Update()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
MightHaveChildren()129 bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; }
130
GetIndexOfChildWithName(const ConstString & name)131 size_t GetIndexOfChildWithName(const ConstString &name) override {
132 const char *item_name = name.GetCString();
133 uint32_t idx = ExtractIndexFromString(item_name);
134 if (idx < UINT32_MAX && idx >= CalculateNumChildren())
135 return UINT32_MAX;
136 return idx;
137 }
138
GetSyntheticValue()139 lldb::ValueObjectSP GetSyntheticValue() override { return nullptr; }
140
141 protected:
142 ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp;
143
144 enum class Mode { Inlined, Outsourced, Invalid };
145
146 struct Impl {
GetNumIndexesNSIndexPathSyntheticFrontEnd::Impl147 size_t GetNumIndexes() {
148 switch (m_mode) {
149 case Mode::Inlined:
150 return m_inlined.GetNumIndexes();
151 case Mode::Outsourced:
152 return m_outsourced.m_count;
153 default:
154 return 0;
155 }
156 }
157
GetIndexAtIndexNSIndexPathSyntheticFrontEnd::Impl158 lldb::ValueObjectSP GetIndexAtIndex(size_t idx,
159 const CompilerType &desired_type) {
160 if (idx >= GetNumIndexes())
161 return nullptr;
162 switch (m_mode) {
163 default:
164 return nullptr;
165 case Mode::Inlined:
166 return m_inlined.GetIndexAtIndex(idx, desired_type);
167 case Mode::Outsourced:
168 return m_outsourced.GetIndexAtIndex(idx);
169 }
170 }
171
172 struct InlinedIndexes {
173 public:
SetIndexesNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes174 void SetIndexes(uint64_t value, Process &p) {
175 m_indexes = value;
176 _lengthForInlinePayload(p.GetAddressByteSize());
177 m_process = &p;
178 }
179
GetNumIndexesNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes180 size_t GetNumIndexes() { return m_count; }
181
GetIndexAtIndexNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes182 lldb::ValueObjectSP GetIndexAtIndex(size_t idx,
183 const CompilerType &desired_type) {
184 if (!m_process)
185 return nullptr;
186
187 std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx));
188 if (!value.second)
189 return nullptr;
190
191 Value v;
192 if (m_ptr_size == 8) {
193 Scalar scalar((unsigned long long)value.first);
194 v = Value(scalar);
195 } else {
196 Scalar scalar((unsigned int)value.first);
197 v = Value(scalar);
198 }
199
200 v.SetCompilerType(desired_type);
201
202 StreamString idx_name;
203 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
204
205 return ValueObjectConstResult::Create(
206 m_process, v, ConstString(idx_name.GetString()));
207 }
208
ClearNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes209 void Clear() {
210 m_indexes = 0;
211 m_count = 0;
212 m_ptr_size = 0;
213 m_process = nullptr;
214 }
215
InlinedIndexesNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes216 InlinedIndexes()
217 : m_indexes(0), m_count(0), m_ptr_size(0), m_process(nullptr) {}
218
219 private:
220 uint64_t m_indexes;
221 size_t m_count;
222 uint32_t m_ptr_size;
223 Process *m_process;
224
225 // cfr. Foundation for the details of this code
_lengthForInlinePayloadNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes226 size_t _lengthForInlinePayload(uint32_t ptr_size) {
227 m_ptr_size = ptr_size;
228 if (m_ptr_size == 8)
229 m_count = ((m_indexes >> 3) & 0x7);
230 else
231 m_count = ((m_indexes >> 3) & 0x3);
232 return m_count;
233 }
234
_indexAtPositionForInlinePayloadNSIndexPathSyntheticFrontEnd::Impl::InlinedIndexes235 std::pair<uint64_t, bool> _indexAtPositionForInlinePayload(size_t pos) {
236 static const uint64_t PACKED_INDEX_MASK = ((1 << 13) - 1);
237 if (m_ptr_size == 8) {
238 switch (pos) {
239 case 3:
240 case 2:
241 case 1:
242 case 0:
243 return {(m_indexes >> PACKED_INDEX_SHIFT_64(pos)) &
244 PACKED_INDEX_MASK,
245 true};
246 default:
247 return {0, false};
248 }
249 } else {
250 switch (pos) {
251 case 0:
252 case 1:
253 return {(m_indexes >> PACKED_INDEX_SHIFT_32(pos)) &
254 PACKED_INDEX_MASK,
255 true};
256 default:
257 return {0, false};
258 }
259 }
260 return {0, false};
261 }
262 };
263
264 struct OutsourcedIndexes {
GetIndexAtIndexNSIndexPathSyntheticFrontEnd::Impl::OutsourcedIndexes265 lldb::ValueObjectSP GetIndexAtIndex(size_t idx) {
266 if (m_indexes) {
267 ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(idx, true));
268 return index_sp;
269 }
270 return nullptr;
271 }
272
ClearNSIndexPathSyntheticFrontEnd::Impl::OutsourcedIndexes273 void Clear() {
274 m_indexes = nullptr;
275 m_count = 0;
276 }
277
OutsourcedIndexesNSIndexPathSyntheticFrontEnd::Impl::OutsourcedIndexes278 OutsourcedIndexes() : m_indexes(nullptr), m_count(0) {}
279
280 ValueObject *m_indexes;
281 size_t m_count;
282 };
283
284 union {
285 struct InlinedIndexes m_inlined;
286 struct OutsourcedIndexes m_outsourced;
287 };
288
ClearNSIndexPathSyntheticFrontEnd::Impl289 void Clear() {
290 m_mode = Mode::Invalid;
291 m_inlined.Clear();
292 m_outsourced.Clear();
293 }
294
ImplNSIndexPathSyntheticFrontEnd::Impl295 Impl() : m_mode(Mode::Invalid) {}
296
297 Mode m_mode;
298 } m_impl;
299
300 uint32_t m_ptr_size;
301 CompilerType m_uint_star_type;
302 };
303
304 namespace lldb_private {
305 namespace formatters {
306
307 SyntheticChildrenFrontEnd *
NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)308 NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *,
309 lldb::ValueObjectSP valobj_sp) {
310 if (valobj_sp)
311 return new NSIndexPathSyntheticFrontEnd(valobj_sp);
312 return nullptr;
313 }
314
315 } // namespace formatters
316 } // namespace lldb_private
317