/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
24 | 40 | static constexpr size_t PACKED_INDEX_SHIFT_64(size_t i) { |
25 | 40 | return (60 - (13 * (4 - i))); |
26 | 40 | } |
27 | | |
28 | 0 | static constexpr size_t PACKED_INDEX_SHIFT_32(size_t i) { |
29 | 0 | return (32 - (13 * (2 - i))); |
30 | 0 | } |
31 | | |
32 | | class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
33 | | public: |
34 | | NSIndexPathSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
35 | 40 | : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_descriptor_sp(nullptr), |
36 | 40 | m_impl(), m_uint_star_type() { |
37 | 40 | m_ptr_size = |
38 | 40 | m_backend.GetTargetSP()->GetArchitecture().GetAddressByteSize(); |
39 | 40 | } |
40 | | |
41 | 40 | ~NSIndexPathSyntheticFrontEnd() override = default; |
42 | | |
43 | 20 | size_t CalculateNumChildren() override { return m_impl.GetNumIndexes(); } |
44 | | |
45 | 60 | lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { |
46 | 60 | return m_impl.GetIndexAtIndex(idx, m_uint_star_type); |
47 | 60 | } |
48 | | |
49 | 20 | bool Update() override { |
50 | 20 | m_impl.Clear(); |
51 | | |
52 | 20 | auto type_system = m_backend.GetCompilerType().GetTypeSystem(); |
53 | 20 | if (!type_system) |
54 | 0 | return false; |
55 | | |
56 | 20 | auto ast = ScratchTypeSystemClang::GetForTarget( |
57 | 20 | *m_backend.GetExecutionContextRef().GetTargetSP()); |
58 | 20 | if (!ast) |
59 | 0 | return false; |
60 | | |
61 | 20 | m_uint_star_type = ast->GetPointerSizedIntType(false); |
62 | | |
63 | 20 | static ConstString g__indexes("_indexes"); |
64 | 20 | static ConstString g__length("_length"); |
65 | | |
66 | 20 | ProcessSP process_sp = m_backend.GetProcessSP(); |
67 | 20 | if (!process_sp) |
68 | 0 | return false; |
69 | | |
70 | 20 | ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); |
71 | | |
72 | 20 | if (!runtime) |
73 | 0 | return false; |
74 | | |
75 | 20 | ObjCLanguageRuntime::ClassDescriptorSP descriptor( |
76 | 20 | runtime->GetClassDescriptor(m_backend)); |
77 | | |
78 | 20 | if (!descriptor.get() || !descriptor->IsValid()) |
79 | 0 | return false; |
80 | | |
81 | 20 | uint64_t info_bits(0), value_bits(0), payload(0); |
82 | | |
83 | 20 | if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits, &payload)) { |
84 | 16 | m_impl.m_inlined.SetIndexes(payload, *process_sp); |
85 | 16 | m_impl.m_mode = Mode::Inlined; |
86 | 16 | } else { |
87 | 4 | ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _indexes_id; |
88 | 4 | ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _length_id; |
89 | | |
90 | 4 | bool has_indexes(false), has_length(false); |
91 | | |
92 | 8 | for (size_t x = 0; x < descriptor->GetNumIVars(); x++4 ) { |
93 | 8 | const auto &ivar = descriptor->GetIVarAtIndex(x); |
94 | 8 | if (ivar.m_name == g__indexes) { |
95 | 4 | _indexes_id = ivar; |
96 | 4 | has_indexes = true; |
97 | 4 | } else if (ivar.m_name == g__length) { |
98 | 4 | _length_id = ivar; |
99 | 4 | has_length = true; |
100 | 4 | } |
101 | | |
102 | 8 | if (has_length && has_indexes4 ) |
103 | 4 | break; |
104 | 8 | } |
105 | | |
106 | 4 | if (has_length && has_indexes) { |
107 | 4 | m_impl.m_outsourced.m_indexes = |
108 | 4 | m_backend |
109 | 4 | .GetSyntheticChildAtOffset(_indexes_id.m_offset, |
110 | 4 | m_uint_star_type.GetPointerType(), |
111 | 4 | true) |
112 | 4 | .get(); |
113 | 4 | ValueObjectSP length_sp(m_backend.GetSyntheticChildAtOffset( |
114 | 4 | _length_id.m_offset, m_uint_star_type, true)); |
115 | 4 | if (length_sp) { |
116 | 4 | m_impl.m_outsourced.m_count = length_sp->GetValueAsUnsigned(0); |
117 | 4 | if (m_impl.m_outsourced.m_indexes) |
118 | 4 | m_impl.m_mode = Mode::Outsourced; |
119 | 4 | } |
120 | 4 | } |
121 | 4 | } |
122 | 20 | return false; |
123 | 20 | } |
124 | | |
125 | 0 | bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; } |
126 | | |
127 | 0 | size_t GetIndexOfChildWithName(ConstString name) override { |
128 | 0 | const char *item_name = name.GetCString(); |
129 | 0 | uint32_t idx = ExtractIndexFromString(item_name); |
130 | 0 | if (idx < UINT32_MAX && idx >= CalculateNumChildren()) |
131 | 0 | return UINT32_MAX; |
132 | 0 | return idx; |
133 | 0 | } |
134 | | |
135 | 20 | 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 { |
143 | 80 | size_t GetNumIndexes() { |
144 | 80 | switch (m_mode) { |
145 | 56 | case Mode::Inlined: |
146 | 56 | return m_inlined.GetNumIndexes(); |
147 | 24 | case Mode::Outsourced: |
148 | 24 | return m_outsourced.m_count; |
149 | 0 | default: |
150 | 0 | return 0; |
151 | 80 | } |
152 | 80 | } |
153 | | |
154 | | lldb::ValueObjectSP GetIndexAtIndex(size_t idx, |
155 | 60 | const CompilerType &desired_type) { |
156 | 60 | if (idx >= GetNumIndexes()) |
157 | 0 | return nullptr; |
158 | 60 | switch (m_mode) { |
159 | 0 | default: |
160 | 0 | return nullptr; |
161 | 40 | case Mode::Inlined: |
162 | 40 | return m_inlined.GetIndexAtIndex(idx, desired_type); |
163 | 20 | case Mode::Outsourced: |
164 | 20 | return m_outsourced.GetIndexAtIndex(idx); |
165 | 60 | } |
166 | 60 | } |
167 | | |
168 | | struct InlinedIndexes { |
169 | | public: |
170 | 16 | void SetIndexes(uint64_t value, Process &p) { |
171 | 16 | m_indexes = value; |
172 | 16 | _lengthForInlinePayload(p.GetAddressByteSize()); |
173 | 16 | m_process = &p; |
174 | 16 | } |
175 | | |
176 | 56 | size_t GetNumIndexes() { return m_count; } |
177 | | |
178 | | lldb::ValueObjectSP GetIndexAtIndex(size_t idx, |
179 | 40 | const CompilerType &desired_type) { |
180 | 40 | if (!m_process) |
181 | 0 | return nullptr; |
182 | | |
183 | 40 | std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx)); |
184 | 40 | if (!value.second) |
185 | 0 | return nullptr; |
186 | | |
187 | 40 | Value v; |
188 | 40 | if (m_ptr_size == 8) { |
189 | 40 | Scalar scalar((unsigned long long)value.first); |
190 | 40 | v = Value(scalar); |
191 | 40 | } else { |
192 | 0 | Scalar scalar((unsigned int)value.first); |
193 | 0 | v = Value(scalar); |
194 | 0 | } |
195 | | |
196 | 40 | v.SetCompilerType(desired_type); |
197 | | |
198 | 40 | StreamString idx_name; |
199 | 40 | idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); |
200 | | |
201 | 40 | return ValueObjectConstResult::Create( |
202 | 40 | m_process, v, ConstString(idx_name.GetString())); |
203 | 40 | } |
204 | | |
205 | 0 | void Clear() { |
206 | 0 | m_indexes = 0; |
207 | 0 | m_count = 0; |
208 | 0 | m_ptr_size = 0; |
209 | 0 | m_process = nullptr; |
210 | 0 | } |
211 | | |
212 | 0 | InlinedIndexes() {} |
213 | | |
214 | | private: |
215 | | uint64_t m_indexes = 0; |
216 | | size_t m_count = 0; |
217 | | uint32_t m_ptr_size = 0; |
218 | | Process *m_process = nullptr; |
219 | | |
220 | | // cfr. Foundation for the details of this code |
221 | 16 | size_t _lengthForInlinePayload(uint32_t ptr_size) { |
222 | 16 | m_ptr_size = ptr_size; |
223 | 16 | if (m_ptr_size == 8) |
224 | 16 | m_count = ((m_indexes >> 3) & 0x7); |
225 | 0 | else |
226 | 0 | m_count = ((m_indexes >> 3) & 0x3); |
227 | 16 | return m_count; |
228 | 16 | } |
229 | | |
230 | 40 | std::pair<uint64_t, bool> _indexAtPositionForInlinePayload(size_t pos) { |
231 | 40 | static const uint64_t PACKED_INDEX_MASK = ((1 << 13) - 1); |
232 | 40 | if (m_ptr_size == 8) { |
233 | 40 | switch (pos) { |
234 | 4 | case 3: |
235 | 12 | case 2: |
236 | 24 | case 1: |
237 | 40 | case 0: |
238 | 40 | return {(m_indexes >> PACKED_INDEX_SHIFT_64(pos)) & |
239 | 40 | PACKED_INDEX_MASK, |
240 | 40 | true}; |
241 | 0 | default: |
242 | 0 | return {0, false}; |
243 | 40 | } |
244 | 40 | } else { |
245 | 0 | switch (pos) { |
246 | 0 | case 0: |
247 | 0 | case 1: |
248 | 0 | return {(m_indexes >> PACKED_INDEX_SHIFT_32(pos)) & |
249 | 0 | PACKED_INDEX_MASK, |
250 | 0 | true}; |
251 | 0 | default: |
252 | 0 | return {0, false}; |
253 | 0 | } |
254 | 0 | } |
255 | 0 | return {0, false}; |
256 | 40 | } |
257 | | }; |
258 | | |
259 | | struct OutsourcedIndexes { |
260 | 20 | lldb::ValueObjectSP GetIndexAtIndex(size_t idx) { |
261 | 20 | if (m_indexes) { |
262 | 20 | ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(idx, true)); |
263 | 20 | return index_sp; |
264 | 20 | } |
265 | 0 | return nullptr; |
266 | 20 | } |
267 | | |
268 | 0 | void Clear() { |
269 | 0 | m_indexes = nullptr; |
270 | 0 | m_count = 0; |
271 | 0 | } |
272 | | |
273 | 0 | OutsourcedIndexes() {} |
274 | | |
275 | | ValueObject *m_indexes = nullptr; |
276 | | size_t m_count = 0; |
277 | | }; |
278 | | |
279 | | union { |
280 | | struct InlinedIndexes m_inlined; |
281 | | struct OutsourcedIndexes m_outsourced; |
282 | | }; |
283 | | |
284 | 20 | void Clear() { |
285 | 20 | switch (m_mode) { |
286 | 0 | case Mode::Inlined: |
287 | 0 | m_inlined.Clear(); |
288 | 0 | break; |
289 | 0 | case Mode::Outsourced: |
290 | 0 | m_outsourced.Clear(); |
291 | 0 | break; |
292 | 20 | case Mode::Invalid: |
293 | 20 | break; |
294 | 20 | } |
295 | 20 | m_mode = Mode::Invalid; |
296 | 20 | } |
297 | | |
298 | 40 | Impl() {} |
299 | | |
300 | | Mode m_mode = Mode::Invalid; |
301 | | } m_impl; |
302 | | |
303 | | uint32_t m_ptr_size = 0; |
304 | | CompilerType m_uint_star_type; |
305 | | }; |
306 | | |
307 | | namespace lldb_private { |
308 | | namespace formatters { |
309 | | |
310 | | SyntheticChildrenFrontEnd * |
311 | | NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *, |
312 | 40 | lldb::ValueObjectSP valobj_sp) { |
313 | 40 | if (valobj_sp) |
314 | 40 | return new NSIndexPathSyntheticFrontEnd(valobj_sp); |
315 | 0 | return nullptr; |
316 | 40 | } |
317 | | |
318 | | } // namespace formatters |
319 | | } // namespace lldb_private |