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