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  bool Update() override { return false; }
47};
48
49ValueObjectSynthetic::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
64ValueObjectSynthetic::~ValueObjectSynthetic() = default;
65
66CompilerType ValueObjectSynthetic::GetCompilerTypeImpl() {
67  return m_parent->GetCompilerType();
68}
69
70ConstString ValueObjectSynthetic::GetTypeName() {
71  return m_parent->GetTypeName();
72}
73
74ConstString ValueObjectSynthetic::GetQualifiedTypeName() {
75  return m_parent->GetQualifiedTypeName();
76}
77
78ConstString ValueObjectSynthetic::GetDisplayTypeName() {
79  if (ConstString synth_name = m_synth_filter_up->GetSyntheticTypeName())
80    return synth_name;
81
82  return m_parent->GetDisplayTypeName();
83}
84
85size_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
110lldb::ValueObjectSP
111ValueObjectSynthetic::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
119bool 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
126std::optional<uint64_t> ValueObjectSynthetic::GetByteSize() {
127  return m_parent->GetByteSize();
128}
129
130lldb::ValueType ValueObjectSynthetic::GetValueType() const {
131  return m_parent->GetValueType();
132}
133
134void 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
152bool 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
237lldb::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
309lldb::ValueObjectSP
310ValueObjectSynthetic::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
322size_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
350bool ValueObjectSynthetic::IsInScope() { return m_parent->IsInScope(); }
351
352lldb::ValueObjectSP ValueObjectSynthetic::GetNonSyntheticValue() {
353  return m_parent->GetSP();
354}
355
356void 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
362bool ValueObjectSynthetic::CanProvideValue() {
363  if (!UpdateValueIfNeeded())
364    return false;
365  if (m_provides_value == eLazyBoolYes)
366    return true;
367  return m_parent->CanProvideValue();
368}
369
370bool ValueObjectSynthetic::SetValueFromCString(const char *value_str,
371                                               Status &error) {
372  return m_parent->SetValueFromCString(value_str, error);
373}
374
375void 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
384void ValueObjectSynthetic::SetPreferredDisplayLanguage(
385    lldb::LanguageType lang) {
386  this->ValueObject::SetPreferredDisplayLanguage(lang);
387  if (m_parent)
388    m_parent->SetPreferredDisplayLanguage(lang);
389}
390
391lldb::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
400bool ValueObjectSynthetic::IsSyntheticChildrenGenerated() {
401  if (m_parent)
402    return m_parent->IsSyntheticChildrenGenerated();
403  return false;
404}
405
406void ValueObjectSynthetic::SetSyntheticChildrenGenerated(bool b) {
407  if (m_parent)
408    m_parent->SetSyntheticChildrenGenerated(b);
409  this->ValueObject::SetSyntheticChildrenGenerated(b);
410}
411
412bool ValueObjectSynthetic::GetDeclaration(Declaration &decl) {
413  if (m_parent)
414    return m_parent->GetDeclaration(decl);
415
416  return ValueObject::GetDeclaration(decl);
417}
418
419uint64_t ValueObjectSynthetic::GetLanguageFlags() {
420  if (m_parent)
421    return m_parent->GetLanguageFlags();
422  return this->ValueObject::GetLanguageFlags();
423}
424
425void ValueObjectSynthetic::SetLanguageFlags(uint64_t flags) {
426  if (m_parent)
427    m_parent->SetLanguageFlags(flags);
428  else
429    this->ValueObject::SetLanguageFlags(flags);
430}
431