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"
20using namespace lldb;
21using namespace lldb_private;
22using namespace lldb_private::formatters;
23
24static constexpr size_t PACKED_INDEX_SHIFT_64(size_t i) {
25 return (60 - (13 * (4 - i)));
26}
27
28static constexpr size_t PACKED_INDEX_SHIFT_32(size_t i) {
29 return (32 - (13 * (2 - i)));
30}
31
32class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
33public:
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
43 llvm::Expected<uint32_t> CalculateNumChildren() override {
44 return m_impl.GetNumIndexes();
45 }
46
47 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
48 return m_impl.GetIndexAtIndex(idx, desired_type: m_uint_star_type);
49 }
50
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 target&: *m_backend.GetExecutionContextRef().GetTargetSP());
60 if (!ast)
61 return lldb::ChildCacheState::eRefetch;
62
63 m_uint_star_type = ast->GetPointerSizedIntType(is_signed: 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&: *process_sp);
73
74 if (!runtime)
75 return lldb::ChildCacheState::eRefetch;
76
77 ObjCLanguageRuntime::ClassDescriptorSP descriptor(
78 runtime->GetClassDescriptor(in_value&: 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: &info_bits, value_bits: &value_bits, payload: &payload)) {
86 m_impl.m_inlined.SetIndexes(value: payload, p&: *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(idx: 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(offset: _indexes_id.m_offset,
112 type: m_uint_star_type.GetPointerType(),
113 can_create: true)
114 .get();
115 ValueObjectSP length_sp(m_backend.GetSyntheticChildAtOffset(
116 offset: _length_id.m_offset, type: m_uint_star_type, can_create: true));
117 if (length_sp) {
118 m_impl.m_outsourced.m_count = length_sp->GetValueAsUnsigned(fail_value: 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
127 bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; }
128
129 size_t GetIndexOfChildWithName(ConstString name) override {
130 const char *item_name = name.GetCString();
131 uint32_t idx = ExtractIndexFromString(item_name);
132 if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors())
133 return UINT32_MAX;
134 return idx;
135 }
136
137 lldb::ValueObjectSP GetSyntheticValue() override { return nullptr; }
138
139protected:
140 ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp;
141
142 enum class Mode { Inlined, Outsourced, Invalid };
143
144 struct Impl {
145 size_t GetNumIndexes() {
146 switch (m_mode) {
147 case Mode::Inlined:
148 return m_inlined.GetNumIndexes();
149 case Mode::Outsourced:
150 return m_outsourced.m_count;
151 default:
152 return 0;
153 }
154 }
155
156 lldb::ValueObjectSP GetIndexAtIndex(size_t idx,
157 const CompilerType &desired_type) {
158 if (idx >= GetNumIndexes())
159 return nullptr;
160 switch (m_mode) {
161 default:
162 return nullptr;
163 case Mode::Inlined:
164 return m_inlined.GetIndexAtIndex(idx, desired_type);
165 case Mode::Outsourced:
166 return m_outsourced.GetIndexAtIndex(idx);
167 }
168 }
169
170 struct InlinedIndexes {
171 public:
172 void SetIndexes(uint64_t value, Process &p) {
173 m_indexes = value;
174 _lengthForInlinePayload(ptr_size: p.GetAddressByteSize());
175 m_process = &p;
176 }
177
178 size_t GetNumIndexes() { return m_count; }
179
180 lldb::ValueObjectSP GetIndexAtIndex(size_t idx,
181 const CompilerType &desired_type) {
182 if (!m_process)
183 return nullptr;
184
185 std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(pos: idx));
186 if (!value.second)
187 return nullptr;
188
189 Value v;
190 if (m_ptr_size == 8) {
191 Scalar scalar((unsigned long long)value.first);
192 v = Value(scalar);
193 } else {
194 Scalar scalar((unsigned int)value.first);
195 v = Value(scalar);
196 }
197
198 v.SetCompilerType(desired_type);
199
200 StreamString idx_name;
201 idx_name.Printf(format: "[%" PRIu64 "]", (uint64_t)idx);
202
203 return ValueObjectConstResult::Create(
204 exe_scope: m_process, value&: v, name: ConstString(idx_name.GetString()));
205 }
206
207 void Clear() {
208 m_indexes = 0;
209 m_count = 0;
210 m_ptr_size = 0;
211 m_process = nullptr;
212 }
213
214 InlinedIndexes() {}
215
216 private:
217 uint64_t m_indexes = 0;
218 size_t m_count = 0;
219 uint32_t m_ptr_size = 0;
220 Process *m_process = nullptr;
221
222 // cfr. Foundation for the details of this code
223 size_t _lengthForInlinePayload(uint32_t ptr_size) {
224 m_ptr_size = ptr_size;
225 if (m_ptr_size == 8)
226 m_count = ((m_indexes >> 3) & 0x7);
227 else
228 m_count = ((m_indexes >> 3) & 0x3);
229 return m_count;
230 }
231
232 std::pair<uint64_t, bool> _indexAtPositionForInlinePayload(size_t pos) {
233 static const uint64_t PACKED_INDEX_MASK = ((1 << 13) - 1);
234 if (m_ptr_size == 8) {
235 switch (pos) {
236 case 3:
237 case 2:
238 case 1:
239 case 0:
240 return {(m_indexes >> PACKED_INDEX_SHIFT_64(i: pos)) &
241 PACKED_INDEX_MASK,
242 true};
243 default:
244 return {0, false};
245 }
246 } else {
247 switch (pos) {
248 case 0:
249 case 1:
250 return {(m_indexes >> PACKED_INDEX_SHIFT_32(i: pos)) &
251 PACKED_INDEX_MASK,
252 true};
253 default:
254 return {0, false};
255 }
256 }
257 return {0, false};
258 }
259 };
260
261 struct OutsourcedIndexes {
262 lldb::ValueObjectSP GetIndexAtIndex(size_t idx) {
263 if (m_indexes) {
264 ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(index: idx, can_create: true));
265 return index_sp;
266 }
267 return nullptr;
268 }
269
270 void Clear() {
271 m_indexes = nullptr;
272 m_count = 0;
273 }
274
275 OutsourcedIndexes() {}
276
277 ValueObject *m_indexes = nullptr;
278 size_t m_count = 0;
279 };
280
281 union {
282 struct InlinedIndexes m_inlined;
283 struct OutsourcedIndexes m_outsourced;
284 };
285
286 void Clear() {
287 switch (m_mode) {
288 case Mode::Inlined:
289 m_inlined.Clear();
290 break;
291 case Mode::Outsourced:
292 m_outsourced.Clear();
293 break;
294 case Mode::Invalid:
295 break;
296 }
297 m_mode = Mode::Invalid;
298 }
299
300 Impl() {}
301
302 Mode m_mode = Mode::Invalid;
303 } m_impl;
304
305 uint32_t m_ptr_size = 0;
306 CompilerType m_uint_star_type;
307};
308
309namespace lldb_private {
310namespace formatters {
311
312SyntheticChildrenFrontEnd *
313NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *,
314 lldb::ValueObjectSP valobj_sp) {
315 if (valobj_sp)
316 return new NSIndexPathSyntheticFrontEnd(valobj_sp);
317 return nullptr;
318}
319
320} // namespace formatters
321} // namespace lldb_private
322

source code of lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp