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