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 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
51ValueObjectSynthetic::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
66ValueObjectSynthetic::~ValueObjectSynthetic() = default;
67
68CompilerType ValueObjectSynthetic::GetCompilerTypeImpl() {
69 return m_parent->GetCompilerType();
70}
71
72ConstString ValueObjectSynthetic::GetTypeName() {
73 return m_parent->GetTypeName();
74}
75
76ConstString ValueObjectSynthetic::GetQualifiedTypeName() {
77 return m_parent->GetQualifiedTypeName();
78}
79
80ConstString ValueObjectSynthetic::GetDisplayTypeName() {
81 if (ConstString synth_name = m_synth_filter_up->GetSyntheticTypeName())
82 return synth_name;
83
84 return m_parent->GetDisplayTypeName();
85}
86
87size_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
112lldb::ValueObjectSP
113ValueObjectSynthetic::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
121bool 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
128std::optional<uint64_t> ValueObjectSynthetic::GetByteSize() {
129 return m_parent->GetByteSize();
130}
131
132lldb::ValueType ValueObjectSynthetic::GetValueType() const {
133 return m_parent->GetValueType();
134}
135
136void 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
154bool 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
239lldb::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
311lldb::ValueObjectSP
312ValueObjectSynthetic::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
324size_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
352bool ValueObjectSynthetic::IsInScope() { return m_parent->IsInScope(); }
353
354lldb::ValueObjectSP ValueObjectSynthetic::GetNonSyntheticValue() {
355 return m_parent->GetSP();
356}
357
358void 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
364bool ValueObjectSynthetic::CanProvideValue() {
365 if (!UpdateValueIfNeeded())
366 return false;
367 if (m_provides_value == eLazyBoolYes)
368 return true;
369 return m_parent->CanProvideValue();
370}
371
372bool ValueObjectSynthetic::SetValueFromCString(const char *value_str,
373 Status &error) {
374 return m_parent->SetValueFromCString(value_str, error);
375}
376
377void 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
386void ValueObjectSynthetic::SetPreferredDisplayLanguage(
387 lldb::LanguageType lang) {
388 this->ValueObject::SetPreferredDisplayLanguage(lang);
389 if (m_parent)
390 m_parent->SetPreferredDisplayLanguage(lang);
391}
392
393lldb::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
402bool ValueObjectSynthetic::IsSyntheticChildrenGenerated() {
403 if (m_parent)
404 return m_parent->IsSyntheticChildrenGenerated();
405 return false;
406}
407
408void ValueObjectSynthetic::SetSyntheticChildrenGenerated(bool b) {
409 if (m_parent)
410 m_parent->SetSyntheticChildrenGenerated(b);
411 this->ValueObject::SetSyntheticChildrenGenerated(b);
412}
413
414bool ValueObjectSynthetic::GetDeclaration(Declaration &decl) {
415 if (m_parent)
416 return m_parent->GetDeclaration(decl);
417
418 return ValueObject::GetDeclaration(decl);
419}
420
421uint64_t ValueObjectSynthetic::GetLanguageFlags() {
422 if (m_parent)
423 return m_parent->GetLanguageFlags();
424 return this->ValueObject::GetLanguageFlags();
425}
426
427void ValueObjectSynthetic::SetLanguageFlags(uint64_t flags) {
428 if (m_parent)
429 m_parent->SetLanguageFlags(flags);
430 else
431 this->ValueObject::SetLanguageFlags(flags);
432}
433

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