| //===-- ValueObjectSyntheticFilter.cpp ------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Core/ValueObjectSyntheticFilter.h" |
| |
| #include "lldb/Core/Value.h" |
| #include "lldb/Core/ValueObject.h" |
| #include "lldb/DataFormatters/TypeSynthetic.h" |
| #include "lldb/Target/ExecutionContext.h" |
| #include "lldb/Utility/Log.h" |
| #include "lldb/Utility/Logging.h" |
| #include "lldb/Utility/Status.h" |
| |
| #include "llvm/ADT/STLExtras.h" |
| |
| namespace lldb_private { |
| class Declaration; |
| } |
| |
| using namespace lldb_private; |
| |
| class DummySyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
| public: |
| DummySyntheticFrontEnd(ValueObject &backend) |
| : SyntheticChildrenFrontEnd(backend) {} |
| |
| size_t CalculateNumChildren() override { return m_backend.GetNumChildren(); } |
| |
| lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { |
| return m_backend.GetChildAtIndex(idx, true); |
| } |
| |
| size_t GetIndexOfChildWithName(ConstString name) override { |
| return m_backend.GetIndexOfChildWithName(name); |
| } |
| |
| bool MightHaveChildren() override { return true; } |
| |
| bool Update() override { return false; } |
| }; |
| |
| ValueObjectSynthetic::ValueObjectSynthetic(ValueObject &parent, |
| lldb::SyntheticChildrenSP filter) |
| : ValueObject(parent), m_synth_sp(std::move(filter)), m_children_byindex(), |
| m_name_toindex(), m_synthetic_children_cache(), |
| m_synthetic_children_count(UINT32_MAX), |
| m_parent_type_name(parent.GetTypeName()), |
| m_might_have_children(eLazyBoolCalculate), |
| m_provides_value(eLazyBoolCalculate) { |
| SetName(parent.GetName()); |
| // Copying the data of an incomplete type won't work as it has no byte size. |
| if (m_parent->GetCompilerType().IsCompleteType()) |
| CopyValueData(m_parent); |
| CreateSynthFilter(); |
| } |
| |
| ValueObjectSynthetic::~ValueObjectSynthetic() = default; |
| |
| CompilerType ValueObjectSynthetic::GetCompilerTypeImpl() { |
| return m_parent->GetCompilerType(); |
| } |
| |
| ConstString ValueObjectSynthetic::GetTypeName() { |
| return m_parent->GetTypeName(); |
| } |
| |
| ConstString ValueObjectSynthetic::GetQualifiedTypeName() { |
| return m_parent->GetQualifiedTypeName(); |
| } |
| |
| ConstString ValueObjectSynthetic::GetDisplayTypeName() { |
| if (ConstString synth_name = m_synth_filter_up->GetSyntheticTypeName()) |
| return synth_name; |
| |
| return m_parent->GetDisplayTypeName(); |
| } |
| |
| size_t ValueObjectSynthetic::CalculateNumChildren(uint32_t max) { |
| Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS); |
| |
| UpdateValueIfNeeded(); |
| if (m_synthetic_children_count < UINT32_MAX) |
| return m_synthetic_children_count <= max ? m_synthetic_children_count : max; |
| |
| if (max < UINT32_MAX) { |
| size_t num_children = m_synth_filter_up->CalculateNumChildren(max); |
| LLDB_LOGF(log, |
| "[ValueObjectSynthetic::CalculateNumChildren] for VO of name " |
| "%s and type %s, the filter returned %zu child values", |
| GetName().AsCString(), GetTypeName().AsCString(), num_children); |
| return num_children; |
| } else { |
| size_t num_children = (m_synthetic_children_count = |
| m_synth_filter_up->CalculateNumChildren(max)); |
| LLDB_LOGF(log, |
| "[ValueObjectSynthetic::CalculateNumChildren] for VO of name " |
| "%s and type %s, the filter returned %zu child values", |
| GetName().AsCString(), GetTypeName().AsCString(), num_children); |
| return num_children; |
| } |
| } |
| |
| lldb::ValueObjectSP |
| ValueObjectSynthetic::GetDynamicValue(lldb::DynamicValueType valueType) { |
| if (!m_parent) |
| return lldb::ValueObjectSP(); |
| if (IsDynamic() && GetDynamicValueType() == valueType) |
| return GetSP(); |
| return m_parent->GetDynamicValue(valueType); |
| } |
| |
| bool ValueObjectSynthetic::MightHaveChildren() { |
| if (m_might_have_children == eLazyBoolCalculate) |
| m_might_have_children = |
| (m_synth_filter_up->MightHaveChildren() ? eLazyBoolYes : eLazyBoolNo); |
| return (m_might_have_children != eLazyBoolNo); |
| } |
| |
| llvm::Optional<uint64_t> ValueObjectSynthetic::GetByteSize() { |
| return m_parent->GetByteSize(); |
| } |
| |
| lldb::ValueType ValueObjectSynthetic::GetValueType() const { |
| return m_parent->GetValueType(); |
| } |
| |
| void ValueObjectSynthetic::CreateSynthFilter() { |
| ValueObject *valobj_for_frontend = m_parent; |
| if (m_synth_sp->WantsDereference()) |
| { |
| CompilerType type = m_parent->GetCompilerType(); |
| if (type.IsValid() && type.IsPointerOrReferenceType()) |
| { |
| Status error; |
| lldb::ValueObjectSP deref_sp = m_parent->Dereference(error); |
| if (error.Success()) |
| valobj_for_frontend = deref_sp.get(); |
| } |
| } |
| m_synth_filter_up = (m_synth_sp->GetFrontEnd(*valobj_for_frontend)); |
| if (!m_synth_filter_up) |
| m_synth_filter_up = std::make_unique<DummySyntheticFrontEnd>(*m_parent); |
| } |
| |
| bool ValueObjectSynthetic::UpdateValue() { |
| Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS); |
| |
| SetValueIsValid(false); |
| m_error.Clear(); |
| |
| if (!m_parent->UpdateValueIfNeeded(false)) { |
| // our parent could not update.. as we are meaningless without a parent, |
| // just stop |
| if (m_parent->GetError().Fail()) |
| m_error = m_parent->GetError(); |
| return false; |
| } |
| |
| // regenerate the synthetic filter if our typename changes |
| // <rdar://problem/12424824> |
| ConstString new_parent_type_name = m_parent->GetTypeName(); |
| if (new_parent_type_name != m_parent_type_name) { |
| LLDB_LOGF(log, |
| "[ValueObjectSynthetic::UpdateValue] name=%s, type changed " |
| "from %s to %s, recomputing synthetic filter", |
| GetName().AsCString(), m_parent_type_name.AsCString(), |
| new_parent_type_name.AsCString()); |
| m_parent_type_name = new_parent_type_name; |
| CreateSynthFilter(); |
| } |
| |
| // let our backend do its update |
| if (!m_synth_filter_up->Update()) { |
| LLDB_LOGF(log, |
| "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
| "filter said caches are stale - clearing", |
| GetName().AsCString()); |
| // filter said that cached values are stale |
| { |
| std::lock_guard<std::mutex> guard(m_child_mutex); |
| m_children_byindex.clear(); |
| m_name_toindex.clear(); |
| } |
| // usually, an object's value can change but this does not alter its |
| // children count for a synthetic VO that might indeed happen, so we need |
| // to tell the upper echelons that they need to come back to us asking for |
| // children |
| m_flags.m_children_count_valid = false; |
| { |
| std::lock_guard<std::mutex> guard(m_child_mutex); |
| m_synthetic_children_cache.clear(); |
| } |
| m_synthetic_children_count = UINT32_MAX; |
| m_might_have_children = eLazyBoolCalculate; |
| } else { |
| LLDB_LOGF(log, |
| "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
| "filter said caches are still valid", |
| GetName().AsCString()); |
| } |
| |
| m_provides_value = eLazyBoolCalculate; |
| |
| lldb::ValueObjectSP synth_val(m_synth_filter_up->GetSyntheticValue()); |
| |
| if (synth_val && synth_val->CanProvideValue()) { |
| LLDB_LOGF(log, |
| "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
| "filter said it can provide a value", |
| GetName().AsCString()); |
| |
| m_provides_value = eLazyBoolYes; |
| CopyValueData(synth_val.get()); |
| } else { |
| LLDB_LOGF(log, |
| "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
| "filter said it will not provide a value", |
| GetName().AsCString()); |
| |
| m_provides_value = eLazyBoolNo; |
| // Copying the data of an incomplete type won't work as it has no byte size. |
| if (m_parent->GetCompilerType().IsCompleteType()) |
| CopyValueData(m_parent); |
| } |
| |
| SetValueIsValid(true); |
| return true; |
| } |
| |
| lldb::ValueObjectSP ValueObjectSynthetic::GetChildAtIndex(size_t idx, |
| bool can_create) { |
| Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS); |
| |
| LLDB_LOGF(log, |
| "[ValueObjectSynthetic::GetChildAtIndex] name=%s, retrieving " |
| "child at index %zu", |
| GetName().AsCString(), idx); |
| |
| UpdateValueIfNeeded(); |
| |
| ValueObject *valobj; |
| bool child_is_cached; |
| { |
| std::lock_guard<std::mutex> guard(m_child_mutex); |
| auto cached_child_it = m_children_byindex.find(idx); |
| child_is_cached = cached_child_it != m_children_byindex.end(); |
| if (child_is_cached) |
| valobj = cached_child_it->second; |
| } |
| |
| if (!child_is_cached) { |
| if (can_create && m_synth_filter_up != nullptr) { |
| LLDB_LOGF(log, |
| "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at " |
| "index %zu not cached and will be created", |
| GetName().AsCString(), idx); |
| |
| lldb::ValueObjectSP synth_guy = m_synth_filter_up->GetChildAtIndex(idx); |
| |
| LLDB_LOGF( |
| log, |
| "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at index " |
| "%zu created as %p (is " |
| "synthetic: %s)", |
| GetName().AsCString(), idx, static_cast<void *>(synth_guy.get()), |
| synth_guy.get() |
| ? (synth_guy->IsSyntheticChildrenGenerated() ? "yes" : "no") |
| : "no"); |
| |
| if (!synth_guy) |
| return synth_guy; |
| |
| { |
| std::lock_guard<std::mutex> guard(m_child_mutex); |
| if (synth_guy->IsSyntheticChildrenGenerated()) |
| m_synthetic_children_cache.push_back(synth_guy); |
| m_children_byindex[idx] = synth_guy.get(); |
| } |
| synth_guy->SetPreferredDisplayLanguageIfNeeded( |
| GetPreferredDisplayLanguage()); |
| return synth_guy; |
| } else { |
| LLDB_LOGF(log, |
| "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at " |
| "index %zu not cached and cannot " |
| "be created (can_create = %s, synth_filter = %p)", |
| GetName().AsCString(), idx, can_create ? "yes" : "no", |
| static_cast<void *>(m_synth_filter_up.get())); |
| |
| return lldb::ValueObjectSP(); |
| } |
| } else { |
| LLDB_LOGF(log, |
| "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at " |
| "index %zu cached as %p", |
| GetName().AsCString(), idx, static_cast<void *>(valobj)); |
| |
| return valobj->GetSP(); |
| } |
| } |
| |
| lldb::ValueObjectSP |
| ValueObjectSynthetic::GetChildMemberWithName(ConstString name, |
| bool can_create) { |
| UpdateValueIfNeeded(); |
| |
| uint32_t index = GetIndexOfChildWithName(name); |
| |
| if (index == UINT32_MAX) |
| return lldb::ValueObjectSP(); |
| |
| return GetChildAtIndex(index, can_create); |
| } |
| |
| size_t ValueObjectSynthetic::GetIndexOfChildWithName(ConstString name) { |
| UpdateValueIfNeeded(); |
| |
| uint32_t found_index = UINT32_MAX; |
| bool did_find; |
| { |
| std::lock_guard<std::mutex> guard(m_child_mutex); |
| auto name_to_index = m_name_toindex.find(name.GetCString()); |
| did_find = name_to_index != m_name_toindex.end(); |
| if (did_find) |
| found_index = name_to_index->second; |
| } |
| |
| if (!did_find && m_synth_filter_up != nullptr) { |
| uint32_t index = m_synth_filter_up->GetIndexOfChildWithName(name); |
| if (index == UINT32_MAX) |
| return index; |
| std::lock_guard<std::mutex> guard(m_child_mutex); |
| m_name_toindex[name.GetCString()] = index; |
| return index; |
| } else if (!did_find && m_synth_filter_up == nullptr) |
| return UINT32_MAX; |
| else /*if (iter != m_name_toindex.end())*/ |
| return found_index; |
| } |
| |
| bool ValueObjectSynthetic::IsInScope() { return m_parent->IsInScope(); } |
| |
| lldb::ValueObjectSP ValueObjectSynthetic::GetNonSyntheticValue() { |
| return m_parent->GetSP(); |
| } |
| |
| void ValueObjectSynthetic::CopyValueData(ValueObject *source) { |
| m_value = (source->UpdateValueIfNeeded(), source->GetValue()); |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get()); |
| } |
| |
| bool ValueObjectSynthetic::CanProvideValue() { |
| if (!UpdateValueIfNeeded()) |
| return false; |
| if (m_provides_value == eLazyBoolYes) |
| return true; |
| return m_parent->CanProvideValue(); |
| } |
| |
| bool ValueObjectSynthetic::SetValueFromCString(const char *value_str, |
| Status &error) { |
| return m_parent->SetValueFromCString(value_str, error); |
| } |
| |
| void ValueObjectSynthetic::SetFormat(lldb::Format format) { |
| if (m_parent) { |
| m_parent->ClearUserVisibleData(eClearUserVisibleDataItemsAll); |
| m_parent->SetFormat(format); |
| } |
| this->ValueObject::SetFormat(format); |
| this->ClearUserVisibleData(eClearUserVisibleDataItemsAll); |
| } |
| |
| void ValueObjectSynthetic::SetPreferredDisplayLanguage( |
| lldb::LanguageType lang) { |
| this->ValueObject::SetPreferredDisplayLanguage(lang); |
| if (m_parent) |
| m_parent->SetPreferredDisplayLanguage(lang); |
| } |
| |
| lldb::LanguageType ValueObjectSynthetic::GetPreferredDisplayLanguage() { |
| if (m_preferred_display_language == lldb::eLanguageTypeUnknown) { |
| if (m_parent) |
| return m_parent->GetPreferredDisplayLanguage(); |
| return lldb::eLanguageTypeUnknown; |
| } else |
| return m_preferred_display_language; |
| } |
| |
| bool ValueObjectSynthetic::IsSyntheticChildrenGenerated() { |
| if (m_parent) |
| return m_parent->IsSyntheticChildrenGenerated(); |
| return false; |
| } |
| |
| void ValueObjectSynthetic::SetSyntheticChildrenGenerated(bool b) { |
| if (m_parent) |
| m_parent->SetSyntheticChildrenGenerated(b); |
| this->ValueObject::SetSyntheticChildrenGenerated(b); |
| } |
| |
| bool ValueObjectSynthetic::GetDeclaration(Declaration &decl) { |
| if (m_parent) |
| return m_parent->GetDeclaration(decl); |
| |
| return ValueObject::GetDeclaration(decl); |
| } |
| |
| uint64_t ValueObjectSynthetic::GetLanguageFlags() { |
| if (m_parent) |
| return m_parent->GetLanguageFlags(); |
| return this->ValueObject::GetLanguageFlags(); |
| } |
| |
| void ValueObjectSynthetic::SetLanguageFlags(uint64_t flags) { |
| if (m_parent) |
| m_parent->SetLanguageFlags(flags); |
| else |
| this->ValueObject::SetLanguageFlags(flags); |
| } |