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