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
23namespace lldb_private {
24class Declaration;
25}
26
27using namespace lldb_private;
28
29class DummySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
30public:
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
53ValueObjectSynthetic::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
68ValueObjectSynthetic::~ValueObjectSynthetic() = default;
69
70CompilerType ValueObjectSynthetic::GetCompilerTypeImpl() {
71 return m_parent->GetCompilerType();
72}
73
74ConstString ValueObjectSynthetic::GetTypeName() {
75 return m_parent->GetTypeName();
76}
77
78ConstString ValueObjectSynthetic::GetQualifiedTypeName() {
79 return m_parent->GetQualifiedTypeName();
80}
81
82ConstString ValueObjectSynthetic::GetDisplayTypeName() {
83 if (ConstString synth_name = m_synth_filter_up->GetSyntheticTypeName())
84 return synth_name;
85
86 return m_parent->GetDisplayTypeName();
87}
88
89llvm::Expected<uint32_t>
90ValueObjectSynthetic::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
120lldb::ValueObjectSP
121ValueObjectSynthetic::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
129bool 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
136std::optional<uint64_t> ValueObjectSynthetic::GetByteSize() {
137 return m_parent->GetByteSize();
138}
139
140lldb::ValueType ValueObjectSynthetic::GetValueType() const {
141 return m_parent->GetValueType();
142}
143
144void 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
162bool 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
247lldb::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
319lldb::ValueObjectSP
320ValueObjectSynthetic::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
332size_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
360bool ValueObjectSynthetic::IsInScope() { return m_parent->IsInScope(); }
361
362lldb::ValueObjectSP ValueObjectSynthetic::GetNonSyntheticValue() {
363 return m_parent->GetSP();
364}
365
366void 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
372bool ValueObjectSynthetic::CanProvideValue() {
373 if (!UpdateValueIfNeeded())
374 return false;
375 if (m_provides_value == eLazyBoolYes)
376 return true;
377 return m_parent->CanProvideValue();
378}
379
380bool ValueObjectSynthetic::SetValueFromCString(const char *value_str,
381 Status &error) {
382 return m_parent->SetValueFromCString(value_str, error);
383}
384
385void 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
394void ValueObjectSynthetic::SetPreferredDisplayLanguage(
395 lldb::LanguageType lang) {
396 this->ValueObject::SetPreferredDisplayLanguage(lang);
397 if (m_parent)
398 m_parent->SetPreferredDisplayLanguage(lang);
399}
400
401lldb::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
410bool ValueObjectSynthetic::IsSyntheticChildrenGenerated() {
411 if (m_parent)
412 return m_parent->IsSyntheticChildrenGenerated();
413 return false;
414}
415
416void ValueObjectSynthetic::SetSyntheticChildrenGenerated(bool b) {
417 if (m_parent)
418 m_parent->SetSyntheticChildrenGenerated(b);
419 this->ValueObject::SetSyntheticChildrenGenerated(b);
420}
421
422bool ValueObjectSynthetic::GetDeclaration(Declaration &decl) {
423 if (m_parent)
424 return m_parent->GetDeclaration(decl);
425
426 return ValueObject::GetDeclaration(decl);
427}
428
429uint64_t ValueObjectSynthetic::GetLanguageFlags() {
430 if (m_parent)
431 return m_parent->GetLanguageFlags();
432 return this->ValueObject::GetLanguageFlags();
433}
434
435void ValueObjectSynthetic::SetLanguageFlags(uint64_t flags) {
436 if (m_parent)
437 m_parent->SetLanguageFlags(flags);
438 else
439 this->ValueObject::SetLanguageFlags(flags);
440}
441

source code of lldb/source/Core/ValueObjectSyntheticFilter.cpp