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