//===-- ValueObjectSyntheticFilter.cpp --------------------------*- C++ -*-===// // // 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/SharingPtr.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(filter), m_children_byindex(), m_name_toindex(), m_synthetic_children_count(UINT32_MAX), m_synthetic_children_cache(), m_parent_type_name(parent.GetTypeName()), m_might_have_children(eLazyBoolCalculate), m_provides_value(eLazyBoolCalculate) { SetName(parent.GetName()); 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); } 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(*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 // 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 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_children_count_valid = false; 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; 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; if (!m_children_byindex.GetValueForKey(idx, valobj)) { 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(synth_guy.get()), synth_guy.get() ? (synth_guy->IsSyntheticChildrenGenerated() ? "yes" : "no") : "no"); if (!synth_guy) return synth_guy; if (synth_guy->IsSyntheticChildrenGenerated()) m_synthetic_children_cache.AppendObject(synth_guy); m_children_byindex.SetValueForKey(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(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(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 = m_name_toindex.GetValueForKey(name.GetCString(), found_index); if (!did_find && m_synth_filter_up != nullptr) { uint32_t index = m_synth_filter_up->GetIndexOfChildWithName(name); if (index == UINT32_MAX) return index; m_name_toindex.SetValueForKey(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); }