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