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