1 | //===-- ValueObjectSyntheticFilter.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 "lldb/Core/ValueObjectSyntheticFilter.h" |
10 | |
11 | #include "lldb/Core/Value.h" |
12 | #include "lldb/Core/ValueObject.h" |
13 | #include "lldb/DataFormatters/TypeSynthetic.h" |
14 | #include "lldb/Target/ExecutionContext.h" |
15 | #include "lldb/Utility/LLDBLog.h" |
16 | #include "lldb/Utility/Log.h" |
17 | #include "lldb/Utility/Status.h" |
18 | |
19 | #include "llvm/ADT/STLExtras.h" |
20 | #include <optional> |
21 | |
22 | namespace lldb_private { |
23 | class Declaration; |
24 | } |
25 | |
26 | using namespace lldb_private; |
27 | |
28 | class DummySyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
29 | public: |
30 | DummySyntheticFrontEnd(ValueObject &backend) |
31 | : SyntheticChildrenFrontEnd(backend) {} |
32 | |
33 | size_t CalculateNumChildren() override { return m_backend.GetNumChildren(); } |
34 | |
35 | lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { |
36 | return m_backend.GetChildAtIndex(idx, true); |
37 | } |
38 | |
39 | size_t GetIndexOfChildWithName(ConstString name) override { |
40 | return m_backend.GetIndexOfChildWithName(name); |
41 | } |
42 | |
43 | bool MightHaveChildren() override { return true; } |
44 | |
45 | bool Update() override { return false; } |
46 | }; |
47 | |
48 | ValueObjectSynthetic::ValueObjectSynthetic(ValueObject &parent, |
49 | lldb::SyntheticChildrenSP filter) |
50 | : ValueObject(parent), m_synth_sp(std::move(filter)), m_children_byindex(), |
51 | m_name_toindex(), m_synthetic_children_cache(), |
52 | m_synthetic_children_count(UINT32_MAX), |
53 | m_parent_type_name(parent.GetTypeName()), |
54 | m_might_have_children(eLazyBoolCalculate), |
55 | m_provides_value(eLazyBoolCalculate) { |
56 | SetName(parent.GetName()); |
57 | // Copying the data of an incomplete type won't work as it has no byte size. |
58 | if (m_parent->GetCompilerType().IsCompleteType()) |
59 | CopyValueData(m_parent); |
60 | CreateSynthFilter(); |
61 | } |
62 | |
63 | ValueObjectSynthetic::~ValueObjectSynthetic() = default; |
64 | |
65 | CompilerType ValueObjectSynthetic::GetCompilerTypeImpl() { |
66 | return m_parent->GetCompilerType(); |
67 | } |
68 | |
69 | ConstString ValueObjectSynthetic::GetTypeName() { |
70 | return m_parent->GetTypeName(); |
71 | } |
72 | |
73 | ConstString ValueObjectSynthetic::GetQualifiedTypeName() { |
74 | return m_parent->GetQualifiedTypeName(); |
75 | } |
76 | |
77 | ConstString ValueObjectSynthetic::GetDisplayTypeName() { |
78 | if (ConstString synth_name = m_synth_filter_up->GetSyntheticTypeName()) |
79 | return synth_name; |
80 | |
81 | return m_parent->GetDisplayTypeName(); |
82 | } |
83 | |
84 | size_t ValueObjectSynthetic::CalculateNumChildren(uint32_t max) { |
85 | Log *log = GetLog(LLDBLog::DataFormatters); |
86 | |
87 | UpdateValueIfNeeded(); |
88 | if (m_synthetic_children_count < UINT32_MAX) |
89 | return m_synthetic_children_count <= max ? m_synthetic_children_count : max; |
90 | |
91 | if (max < UINT32_MAX) { |
92 | size_t num_children = m_synth_filter_up->CalculateNumChildren(max); |
93 | LLDB_LOGF(log, |
94 | "[ValueObjectSynthetic::CalculateNumChildren] for VO of name " |
95 | "%s and type %s, the filter returned %zu child values" , |
96 | GetName().AsCString(), GetTypeName().AsCString(), num_children); |
97 | return num_children; |
98 | } else { |
99 | size_t num_children = (m_synthetic_children_count = |
100 | m_synth_filter_up->CalculateNumChildren(max)); |
101 | LLDB_LOGF(log, |
102 | "[ValueObjectSynthetic::CalculateNumChildren] for VO of name " |
103 | "%s and type %s, the filter returned %zu child values" , |
104 | GetName().AsCString(), GetTypeName().AsCString(), num_children); |
105 | return num_children; |
106 | } |
107 | } |
108 | |
109 | lldb::ValueObjectSP |
110 | ValueObjectSynthetic::GetDynamicValue(lldb::DynamicValueType valueType) { |
111 | if (!m_parent) |
112 | return lldb::ValueObjectSP(); |
113 | if (IsDynamic() && GetDynamicValueType() == valueType) |
114 | return GetSP(); |
115 | return m_parent->GetDynamicValue(valueType); |
116 | } |
117 | |
118 | bool ValueObjectSynthetic::MightHaveChildren() { |
119 | if (m_might_have_children == eLazyBoolCalculate) |
120 | m_might_have_children = |
121 | (m_synth_filter_up->MightHaveChildren() ? eLazyBoolYes : eLazyBoolNo); |
122 | return (m_might_have_children != eLazyBoolNo); |
123 | } |
124 | |
125 | std::optional<uint64_t> ValueObjectSynthetic::GetByteSize() { |
126 | return m_parent->GetByteSize(); |
127 | } |
128 | |
129 | lldb::ValueType ValueObjectSynthetic::GetValueType() const { |
130 | return m_parent->GetValueType(); |
131 | } |
132 | |
133 | void ValueObjectSynthetic::CreateSynthFilter() { |
134 | ValueObject *valobj_for_frontend = m_parent; |
135 | if (m_synth_sp->WantsDereference()) |
136 | { |
137 | CompilerType type = m_parent->GetCompilerType(); |
138 | if (type.IsValid() && type.IsPointerOrReferenceType()) |
139 | { |
140 | Status error; |
141 | lldb::ValueObjectSP deref_sp = m_parent->Dereference(error); |
142 | if (error.Success()) |
143 | valobj_for_frontend = deref_sp.get(); |
144 | } |
145 | } |
146 | m_synth_filter_up = (m_synth_sp->GetFrontEnd(*valobj_for_frontend)); |
147 | if (!m_synth_filter_up) |
148 | m_synth_filter_up = std::make_unique<DummySyntheticFrontEnd>(*m_parent); |
149 | } |
150 | |
151 | bool ValueObjectSynthetic::UpdateValue() { |
152 | Log *log = GetLog(LLDBLog::DataFormatters); |
153 | |
154 | SetValueIsValid(false); |
155 | m_error.Clear(); |
156 | |
157 | if (!m_parent->UpdateValueIfNeeded(false)) { |
158 | // our parent could not update.. as we are meaningless without a parent, |
159 | // just stop |
160 | if (m_parent->GetError().Fail()) |
161 | m_error = m_parent->GetError(); |
162 | return false; |
163 | } |
164 | |
165 | // regenerate the synthetic filter if our typename changes |
166 | // <rdar://problem/12424824> |
167 | ConstString new_parent_type_name = m_parent->GetTypeName(); |
168 | if (new_parent_type_name != m_parent_type_name) { |
169 | LLDB_LOGF(log, |
170 | "[ValueObjectSynthetic::UpdateValue] name=%s, type changed " |
171 | "from %s to %s, recomputing synthetic filter" , |
172 | GetName().AsCString(), m_parent_type_name.AsCString(), |
173 | new_parent_type_name.AsCString()); |
174 | m_parent_type_name = new_parent_type_name; |
175 | CreateSynthFilter(); |
176 | } |
177 | |
178 | // let our backend do its update |
179 | if (!m_synth_filter_up->Update()) { |
180 | LLDB_LOGF(log, |
181 | "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
182 | "filter said caches are stale - clearing" , |
183 | GetName().AsCString()); |
184 | // filter said that cached values are stale |
185 | { |
186 | std::lock_guard<std::mutex> guard(m_child_mutex); |
187 | m_children_byindex.clear(); |
188 | m_name_toindex.clear(); |
189 | } |
190 | // usually, an object's value can change but this does not alter its |
191 | // children count for a synthetic VO that might indeed happen, so we need |
192 | // to tell the upper echelons that they need to come back to us asking for |
193 | // children |
194 | m_flags.m_children_count_valid = false; |
195 | { |
196 | std::lock_guard<std::mutex> guard(m_child_mutex); |
197 | m_synthetic_children_cache.clear(); |
198 | } |
199 | m_synthetic_children_count = UINT32_MAX; |
200 | m_might_have_children = eLazyBoolCalculate; |
201 | } else { |
202 | LLDB_LOGF(log, |
203 | "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
204 | "filter said caches are still valid" , |
205 | GetName().AsCString()); |
206 | } |
207 | |
208 | m_provides_value = eLazyBoolCalculate; |
209 | |
210 | lldb::ValueObjectSP synth_val(m_synth_filter_up->GetSyntheticValue()); |
211 | |
212 | if (synth_val && synth_val->CanProvideValue()) { |
213 | LLDB_LOGF(log, |
214 | "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
215 | "filter said it can provide a value" , |
216 | GetName().AsCString()); |
217 | |
218 | m_provides_value = eLazyBoolYes; |
219 | CopyValueData(synth_val.get()); |
220 | } else { |
221 | LLDB_LOGF(log, |
222 | "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
223 | "filter said it will not provide a value" , |
224 | GetName().AsCString()); |
225 | |
226 | m_provides_value = eLazyBoolNo; |
227 | // Copying the data of an incomplete type won't work as it has no byte size. |
228 | if (m_parent->GetCompilerType().IsCompleteType()) |
229 | CopyValueData(m_parent); |
230 | } |
231 | |
232 | SetValueIsValid(true); |
233 | return true; |
234 | } |
235 | |
236 | lldb::ValueObjectSP ValueObjectSynthetic::GetChildAtIndex(size_t idx, |
237 | bool can_create) { |
238 | Log *log = GetLog(LLDBLog::DataFormatters); |
239 | |
240 | LLDB_LOGF(log, |
241 | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, retrieving " |
242 | "child at index %zu" , |
243 | GetName().AsCString(), idx); |
244 | |
245 | UpdateValueIfNeeded(); |
246 | |
247 | ValueObject *valobj; |
248 | bool child_is_cached; |
249 | { |
250 | std::lock_guard<std::mutex> guard(m_child_mutex); |
251 | auto cached_child_it = m_children_byindex.find(idx); |
252 | child_is_cached = cached_child_it != m_children_byindex.end(); |
253 | if (child_is_cached) |
254 | valobj = cached_child_it->second; |
255 | } |
256 | |
257 | if (!child_is_cached) { |
258 | if (can_create && m_synth_filter_up != nullptr) { |
259 | LLDB_LOGF(log, |
260 | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at " |
261 | "index %zu not cached and will be created" , |
262 | GetName().AsCString(), idx); |
263 | |
264 | lldb::ValueObjectSP synth_guy = m_synth_filter_up->GetChildAtIndex(idx); |
265 | |
266 | LLDB_LOGF( |
267 | log, |
268 | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at index " |
269 | "%zu created as %p (is " |
270 | "synthetic: %s)" , |
271 | GetName().AsCString(), idx, static_cast<void *>(synth_guy.get()), |
272 | synth_guy.get() |
273 | ? (synth_guy->IsSyntheticChildrenGenerated() ? "yes" : "no" ) |
274 | : "no" ); |
275 | |
276 | if (!synth_guy) |
277 | return synth_guy; |
278 | |
279 | { |
280 | std::lock_guard<std::mutex> guard(m_child_mutex); |
281 | if (synth_guy->IsSyntheticChildrenGenerated()) |
282 | m_synthetic_children_cache.push_back(synth_guy); |
283 | m_children_byindex[idx] = synth_guy.get(); |
284 | } |
285 | synth_guy->SetPreferredDisplayLanguageIfNeeded( |
286 | GetPreferredDisplayLanguage()); |
287 | return synth_guy; |
288 | } else { |
289 | LLDB_LOGF(log, |
290 | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at " |
291 | "index %zu not cached and cannot " |
292 | "be created (can_create = %s, synth_filter = %p)" , |
293 | GetName().AsCString(), idx, can_create ? "yes" : "no" , |
294 | static_cast<void *>(m_synth_filter_up.get())); |
295 | |
296 | return lldb::ValueObjectSP(); |
297 | } |
298 | } else { |
299 | LLDB_LOGF(log, |
300 | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at " |
301 | "index %zu cached as %p" , |
302 | GetName().AsCString(), idx, static_cast<void *>(valobj)); |
303 | |
304 | return valobj->GetSP(); |
305 | } |
306 | } |
307 | |
308 | lldb::ValueObjectSP |
309 | ValueObjectSynthetic::GetChildMemberWithName(ConstString name, |
310 | bool can_create) { |
311 | UpdateValueIfNeeded(); |
312 | |
313 | uint32_t index = GetIndexOfChildWithName(name); |
314 | |
315 | if (index == UINT32_MAX) |
316 | return lldb::ValueObjectSP(); |
317 | |
318 | return GetChildAtIndex(index, can_create); |
319 | } |
320 | |
321 | size_t ValueObjectSynthetic::GetIndexOfChildWithName(ConstString name) { |
322 | UpdateValueIfNeeded(); |
323 | |
324 | uint32_t found_index = UINT32_MAX; |
325 | bool did_find; |
326 | { |
327 | std::lock_guard<std::mutex> guard(m_child_mutex); |
328 | auto name_to_index = m_name_toindex.find(name.GetCString()); |
329 | did_find = name_to_index != m_name_toindex.end(); |
330 | if (did_find) |
331 | found_index = name_to_index->second; |
332 | } |
333 | |
334 | if (!did_find && m_synth_filter_up != nullptr) { |
335 | uint32_t index = m_synth_filter_up->GetIndexOfChildWithName(name); |
336 | if (index == UINT32_MAX) |
337 | return index; |
338 | std::lock_guard<std::mutex> guard(m_child_mutex); |
339 | m_name_toindex[name.GetCString()] = index; |
340 | return index; |
341 | } else if (!did_find && m_synth_filter_up == nullptr) |
342 | return UINT32_MAX; |
343 | else /*if (iter != m_name_toindex.end())*/ |
344 | return found_index; |
345 | } |
346 | |
347 | bool ValueObjectSynthetic::IsInScope() { return m_parent->IsInScope(); } |
348 | |
349 | lldb::ValueObjectSP ValueObjectSynthetic::GetNonSyntheticValue() { |
350 | return m_parent->GetSP(); |
351 | } |
352 | |
353 | void ValueObjectSynthetic::CopyValueData(ValueObject *source) { |
354 | m_value = (source->UpdateValueIfNeeded(), source->GetValue()); |
355 | ExecutionContext exe_ctx(GetExecutionContextRef()); |
356 | m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get()); |
357 | } |
358 | |
359 | bool ValueObjectSynthetic::CanProvideValue() { |
360 | if (!UpdateValueIfNeeded()) |
361 | return false; |
362 | if (m_provides_value == eLazyBoolYes) |
363 | return true; |
364 | return m_parent->CanProvideValue(); |
365 | } |
366 | |
367 | bool ValueObjectSynthetic::SetValueFromCString(const char *value_str, |
368 | Status &error) { |
369 | return m_parent->SetValueFromCString(value_str, error); |
370 | } |
371 | |
372 | void ValueObjectSynthetic::SetFormat(lldb::Format format) { |
373 | if (m_parent) { |
374 | m_parent->ClearUserVisibleData(eClearUserVisibleDataItemsAll); |
375 | m_parent->SetFormat(format); |
376 | } |
377 | this->ValueObject::SetFormat(format); |
378 | this->ClearUserVisibleData(eClearUserVisibleDataItemsAll); |
379 | } |
380 | |
381 | void ValueObjectSynthetic::SetPreferredDisplayLanguage( |
382 | lldb::LanguageType lang) { |
383 | this->ValueObject::SetPreferredDisplayLanguage(lang); |
384 | if (m_parent) |
385 | m_parent->SetPreferredDisplayLanguage(lang); |
386 | } |
387 | |
388 | lldb::LanguageType ValueObjectSynthetic::GetPreferredDisplayLanguage() { |
389 | if (m_preferred_display_language == lldb::eLanguageTypeUnknown) { |
390 | if (m_parent) |
391 | return m_parent->GetPreferredDisplayLanguage(); |
392 | return lldb::eLanguageTypeUnknown; |
393 | } else |
394 | return m_preferred_display_language; |
395 | } |
396 | |
397 | bool ValueObjectSynthetic::IsSyntheticChildrenGenerated() { |
398 | if (m_parent) |
399 | return m_parent->IsSyntheticChildrenGenerated(); |
400 | return false; |
401 | } |
402 | |
403 | void ValueObjectSynthetic::SetSyntheticChildrenGenerated(bool b) { |
404 | if (m_parent) |
405 | m_parent->SetSyntheticChildrenGenerated(b); |
406 | this->ValueObject::SetSyntheticChildrenGenerated(b); |
407 | } |
408 | |
409 | bool ValueObjectSynthetic::GetDeclaration(Declaration &decl) { |
410 | if (m_parent) |
411 | return m_parent->GetDeclaration(decl); |
412 | |
413 | return ValueObject::GetDeclaration(decl); |
414 | } |
415 | |
416 | uint64_t ValueObjectSynthetic::GetLanguageFlags() { |
417 | if (m_parent) |
418 | return m_parent->GetLanguageFlags(); |
419 | return this->ValueObject::GetLanguageFlags(); |
420 | } |
421 | |
422 | void ValueObjectSynthetic::SetLanguageFlags(uint64_t flags) { |
423 | if (m_parent) |
424 | m_parent->SetLanguageFlags(flags); |
425 | else |
426 | this->ValueObject::SetLanguageFlags(flags); |
427 | } |
428 | |