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