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/ValueObject/ValueObjectSyntheticFilter.h" |
10 | |
11 | #include "lldb/Core/Value.h" |
12 | #include "lldb/DataFormatters/TypeSynthetic.h" |
13 | #include "lldb/Target/ExecutionContext.h" |
14 | #include "lldb/Utility/ConstString.h" |
15 | #include "lldb/Utility/LLDBLog.h" |
16 | #include "lldb/Utility/Log.h" |
17 | #include "lldb/Utility/Status.h" |
18 | #include "lldb/ValueObject/ValueObject.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 | llvm::Expected<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 | llvm::Expected<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 | CompilerType type = m_parent->GetCompilerType(); |
148 | if (type.IsValid() && type.IsPointerOrReferenceType()) { |
149 | Status error; |
150 | lldb::ValueObjectSP deref_sp = m_parent->Dereference(error); |
151 | if (error.Success()) |
152 | valobj_for_frontend = deref_sp.get(); |
153 | } |
154 | } |
155 | m_synth_filter_up = (m_synth_sp->GetFrontEnd(backend&: *valobj_for_frontend)); |
156 | if (!m_synth_filter_up) |
157 | m_synth_filter_up = std::make_unique<DummySyntheticFrontEnd>(args&: *m_parent); |
158 | } |
159 | |
160 | bool ValueObjectSynthetic::UpdateValue() { |
161 | Log *log = GetLog(mask: LLDBLog::DataFormatters); |
162 | |
163 | SetValueIsValid(false); |
164 | m_error.Clear(); |
165 | |
166 | if (!m_parent->UpdateValueIfNeeded(update_format: false)) { |
167 | // our parent could not update.. as we are meaningless without a parent, |
168 | // just stop |
169 | if (m_parent->GetError().Fail()) |
170 | m_error = m_parent->GetError().Clone(); |
171 | return false; |
172 | } |
173 | |
174 | // Regenerate the synthetic filter if our typename changes. When the (dynamic) |
175 | // type of an object changes, so does their synthetic filter of choice. |
176 | ConstString new_parent_type_name = m_parent->GetTypeName(); |
177 | if (new_parent_type_name != m_parent_type_name) { |
178 | LLDB_LOGF(log, |
179 | "[ValueObjectSynthetic::UpdateValue] name=%s, type changed " |
180 | "from %s to %s, recomputing synthetic filter" , |
181 | GetName().AsCString(), m_parent_type_name.AsCString(), |
182 | new_parent_type_name.AsCString()); |
183 | m_parent_type_name = new_parent_type_name; |
184 | CreateSynthFilter(); |
185 | } |
186 | |
187 | // let our backend do its update |
188 | if (m_synth_filter_up->Update() == lldb::ChildCacheState::eRefetch) { |
189 | LLDB_LOGF(log, |
190 | "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
191 | "filter said caches are stale - clearing" , |
192 | GetName().AsCString()); |
193 | // filter said that cached values are stale |
194 | { |
195 | std::lock_guard<std::mutex> guard(m_child_mutex); |
196 | m_children_byindex.clear(); |
197 | m_name_toindex.clear(); |
198 | } |
199 | // usually, an object's value can change but this does not alter its |
200 | // children count for a synthetic VO that might indeed happen, so we need |
201 | // to tell the upper echelons that they need to come back to us asking for |
202 | // children |
203 | m_flags.m_children_count_valid = false; |
204 | { |
205 | std::lock_guard<std::mutex> guard(m_child_mutex); |
206 | m_synthetic_children_cache.clear(); |
207 | } |
208 | m_synthetic_children_count = UINT32_MAX; |
209 | m_might_have_children = eLazyBoolCalculate; |
210 | } else { |
211 | LLDB_LOGF(log, |
212 | "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
213 | "filter said caches are still valid" , |
214 | GetName().AsCString()); |
215 | } |
216 | |
217 | m_provides_value = eLazyBoolCalculate; |
218 | |
219 | lldb::ValueObjectSP synth_val(m_synth_filter_up->GetSyntheticValue()); |
220 | |
221 | if (synth_val && synth_val->CanProvideValue()) { |
222 | LLDB_LOGF(log, |
223 | "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
224 | "filter said it can provide a value" , |
225 | GetName().AsCString()); |
226 | |
227 | m_provides_value = eLazyBoolYes; |
228 | CopyValueData(source: synth_val.get()); |
229 | } else { |
230 | LLDB_LOGF(log, |
231 | "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
232 | "filter said it will not provide a value" , |
233 | GetName().AsCString()); |
234 | |
235 | m_provides_value = eLazyBoolNo; |
236 | // Copying the data of an incomplete type won't work as it has no byte size. |
237 | if (m_parent->GetCompilerType().IsCompleteType()) |
238 | CopyValueData(source: m_parent); |
239 | } |
240 | |
241 | SetValueIsValid(true); |
242 | return true; |
243 | } |
244 | |
245 | lldb::ValueObjectSP ValueObjectSynthetic::GetChildAtIndex(uint32_t idx, |
246 | bool can_create) { |
247 | Log *log = GetLog(mask: LLDBLog::DataFormatters); |
248 | |
249 | LLDB_LOGF(log, |
250 | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, retrieving " |
251 | "child at index %u" , |
252 | GetName().AsCString(), idx); |
253 | |
254 | UpdateValueIfNeeded(); |
255 | |
256 | ValueObject *valobj; |
257 | bool child_is_cached; |
258 | { |
259 | std::lock_guard<std::mutex> guard(m_child_mutex); |
260 | auto cached_child_it = m_children_byindex.find(x: idx); |
261 | child_is_cached = cached_child_it != m_children_byindex.end(); |
262 | if (child_is_cached) |
263 | valobj = cached_child_it->second; |
264 | } |
265 | |
266 | if (!child_is_cached) { |
267 | if (can_create && m_synth_filter_up != nullptr) { |
268 | LLDB_LOGF(log, |
269 | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at " |
270 | "index %u not cached and will be created" , |
271 | GetName().AsCString(), idx); |
272 | |
273 | lldb::ValueObjectSP synth_guy = m_synth_filter_up->GetChildAtIndex(idx); |
274 | |
275 | LLDB_LOGF( |
276 | log, |
277 | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at index " |
278 | "%u created as %p (is " |
279 | "synthetic: %s)" , |
280 | GetName().AsCString(), idx, static_cast<void *>(synth_guy.get()), |
281 | synth_guy.get() |
282 | ? (synth_guy->IsSyntheticChildrenGenerated() ? "yes" : "no" ) |
283 | : "no" ); |
284 | |
285 | if (!synth_guy) |
286 | return synth_guy; |
287 | |
288 | { |
289 | std::lock_guard<std::mutex> guard(m_child_mutex); |
290 | if (synth_guy->IsSyntheticChildrenGenerated()) |
291 | m_synthetic_children_cache.push_back(x: synth_guy); |
292 | m_children_byindex[idx] = synth_guy.get(); |
293 | } |
294 | synth_guy->SetPreferredDisplayLanguageIfNeeded( |
295 | GetPreferredDisplayLanguage()); |
296 | return synth_guy; |
297 | } else { |
298 | LLDB_LOGF(log, |
299 | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at " |
300 | "index %u not cached and cannot " |
301 | "be created (can_create = %s, synth_filter = %p)" , |
302 | GetName().AsCString(), idx, can_create ? "yes" : "no" , |
303 | static_cast<void *>(m_synth_filter_up.get())); |
304 | |
305 | return lldb::ValueObjectSP(); |
306 | } |
307 | } else { |
308 | LLDB_LOGF(log, |
309 | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at " |
310 | "index %u cached as %p" , |
311 | GetName().AsCString(), idx, static_cast<void *>(valobj)); |
312 | |
313 | return valobj->GetSP(); |
314 | } |
315 | } |
316 | |
317 | lldb::ValueObjectSP |
318 | ValueObjectSynthetic::GetChildMemberWithName(llvm::StringRef name, |
319 | bool can_create) { |
320 | UpdateValueIfNeeded(); |
321 | |
322 | auto index_or_err = GetIndexOfChildWithName(name); |
323 | |
324 | if (!index_or_err) { |
325 | llvm::consumeError(Err: index_or_err.takeError()); |
326 | return lldb::ValueObjectSP(); |
327 | } |
328 | |
329 | return GetChildAtIndex(idx: *index_or_err, can_create); |
330 | } |
331 | |
332 | llvm::Expected<size_t> |
333 | ValueObjectSynthetic::GetIndexOfChildWithName(llvm::StringRef name_ref) { |
334 | UpdateValueIfNeeded(); |
335 | |
336 | ConstString name(name_ref); |
337 | |
338 | std::optional<uint32_t> found_index = std::nullopt; |
339 | { |
340 | std::lock_guard<std::mutex> guard(m_child_mutex); |
341 | auto name_to_index = m_name_toindex.find(x: name.GetCString()); |
342 | if (name_to_index != m_name_toindex.end()) |
343 | found_index = name_to_index->second; |
344 | } |
345 | |
346 | if (!found_index && m_synth_filter_up != nullptr) { |
347 | auto index_or_err = m_synth_filter_up->GetIndexOfChildWithName(name); |
348 | if (!index_or_err) |
349 | return index_or_err.takeError(); |
350 | std::lock_guard<std::mutex> guard(m_child_mutex); |
351 | m_name_toindex[name.GetCString()] = *index_or_err; |
352 | return *index_or_err; |
353 | } else if (!found_index && m_synth_filter_up == nullptr) { |
354 | return llvm::createStringError(Fmt: "Type has no child named '%s'" , |
355 | Vals: name.AsCString()); |
356 | } else if (found_index) |
357 | return *found_index; |
358 | |
359 | return llvm::createStringError(Fmt: "Type has no child named '%s'" , |
360 | Vals: name.AsCString()); |
361 | } |
362 | |
363 | bool ValueObjectSynthetic::IsInScope() { return m_parent->IsInScope(); } |
364 | |
365 | lldb::ValueObjectSP ValueObjectSynthetic::GetNonSyntheticValue() { |
366 | return m_parent->GetSP(); |
367 | } |
368 | |
369 | void ValueObjectSynthetic::CopyValueData(ValueObject *source) { |
370 | if (!source->UpdateValueIfNeeded()) |
371 | return; |
372 | m_value = source->GetValue(); |
373 | ExecutionContext exe_ctx(GetExecutionContextRef()); |
374 | m_error = m_value.GetValueAsData(exe_ctx: &exe_ctx, data&: m_data, module: GetModule().get()); |
375 | } |
376 | |
377 | bool ValueObjectSynthetic::CanProvideValue() { |
378 | if (!UpdateValueIfNeeded()) |
379 | return false; |
380 | if (m_provides_value == eLazyBoolYes) |
381 | return true; |
382 | return m_parent->CanProvideValue(); |
383 | } |
384 | |
385 | bool ValueObjectSynthetic::SetValueFromCString(const char *value_str, |
386 | Status &error) { |
387 | return m_parent->SetValueFromCString(value_str, error); |
388 | } |
389 | |
390 | void ValueObjectSynthetic::SetFormat(lldb::Format format) { |
391 | if (m_parent) { |
392 | m_parent->ClearUserVisibleData(items: eClearUserVisibleDataItemsAll); |
393 | m_parent->SetFormat(format); |
394 | } |
395 | this->ValueObject::SetFormat(format); |
396 | this->ClearUserVisibleData(items: eClearUserVisibleDataItemsAll); |
397 | } |
398 | |
399 | void ValueObjectSynthetic::SetPreferredDisplayLanguage( |
400 | lldb::LanguageType lang) { |
401 | this->ValueObject::SetPreferredDisplayLanguage(lang); |
402 | if (m_parent) |
403 | m_parent->SetPreferredDisplayLanguage(lang); |
404 | } |
405 | |
406 | lldb::LanguageType ValueObjectSynthetic::GetPreferredDisplayLanguage() { |
407 | if (m_preferred_display_language == lldb::eLanguageTypeUnknown) { |
408 | if (m_parent) |
409 | return m_parent->GetPreferredDisplayLanguage(); |
410 | return lldb::eLanguageTypeUnknown; |
411 | } else |
412 | return m_preferred_display_language; |
413 | } |
414 | |
415 | bool ValueObjectSynthetic::IsSyntheticChildrenGenerated() { |
416 | if (m_parent) |
417 | return m_parent->IsSyntheticChildrenGenerated(); |
418 | return false; |
419 | } |
420 | |
421 | void ValueObjectSynthetic::SetSyntheticChildrenGenerated(bool b) { |
422 | if (m_parent) |
423 | m_parent->SetSyntheticChildrenGenerated(b); |
424 | this->ValueObject::SetSyntheticChildrenGenerated(b); |
425 | } |
426 | |
427 | bool ValueObjectSynthetic::GetDeclaration(Declaration &decl) { |
428 | if (m_parent) |
429 | return m_parent->GetDeclaration(decl); |
430 | |
431 | return ValueObject::GetDeclaration(decl); |
432 | } |
433 | |
434 | uint64_t ValueObjectSynthetic::GetLanguageFlags() { |
435 | if (m_parent) |
436 | return m_parent->GetLanguageFlags(); |
437 | return this->ValueObject::GetLanguageFlags(); |
438 | } |
439 | |
440 | void ValueObjectSynthetic::SetLanguageFlags(uint64_t flags) { |
441 | if (m_parent) |
442 | m_parent->SetLanguageFlags(flags); |
443 | else |
444 | this->ValueObject::SetLanguageFlags(flags); |
445 | } |
446 | |