//===-- ValueObjectSynthetic.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/ValueObject/ValueObjectSynthetic.h" #include "lldb/Core/Value.h" #include "lldb/DataFormatters/FormattersHelpers.h" #include "lldb/DataFormatters/TypeSynthetic.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" #include "lldb/ValueObject/ValueObject.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorExtras.h" #include namespace lldb_private { class Declaration; } using namespace lldb_private; class DummySyntheticFrontEnd : public SyntheticChildrenFrontEnd { public: DummySyntheticFrontEnd(ValueObject &backend) : SyntheticChildrenFrontEnd(backend) {} llvm::Expected CalculateNumChildren() override { return m_backend.GetNumChildren(); } lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { return m_backend.GetChildAtIndex(idx); } llvm::Expected GetIndexOfChildWithName(ConstString name) override { return m_backend.GetIndexOfChildWithName(name); } bool MightHaveChildren() override { return m_backend.MightHaveChildren(); } lldb::ChildCacheState Update() override { return lldb::ChildCacheState::eRefetch; } }; 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(); } llvm::Expected ValueObjectSynthetic::CalculateNumChildren(uint32_t max) { Log *log = GetLog(LLDBLog::DataFormatters); UpdateValueIfNeeded(); if (m_synthetic_children_count < UINT32_MAX) return m_synthetic_children_count <= max ? m_synthetic_children_count : max; if (max < UINT32_MAX) { auto num_children = m_synth_filter_up->CalculateNumChildren(max); LLDB_LOG(log, "[ValueObjectSynthetic::CalculateNumChildren] for VO of name " "{0} and type {1}, the filter returned {2} child values", GetName(), GetTypeName(), num_children ? *num_children : 0); return num_children; } else { auto num_children_or_err = m_synth_filter_up->CalculateNumChildren(max); if (!num_children_or_err) { m_synthetic_children_count = 0; return num_children_or_err; } auto num_children = (m_synthetic_children_count = *num_children_or_err); LLDB_LOG(log, "[ValueObjectSynthetic::CalculateNumChildren] for VO of name " "{0} and type {1}, the filter returned {2} child values", GetName(), GetTypeName(), 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::Expected 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 = GetLog(LLDBLog::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().Clone(); return false; } // Regenerate the synthetic filter if our typename changes. When the (dynamic) // type of an object changes, so does their synthetic filter of choice. ConstString new_parent_type_name = m_parent->GetTypeName(); if (new_parent_type_name != m_parent_type_name) { LLDB_LOG(log, "[ValueObjectSynthetic::UpdateValue] name={0}, type changed " "from {1} to {2}, recomputing synthetic filter", GetName(), m_parent_type_name, new_parent_type_name); m_parent_type_name = new_parent_type_name; CreateSynthFilter(); } // let our backend do its update if (m_synth_filter_up->Update() == lldb::ChildCacheState::eRefetch) { LLDB_LOG(log, "[ValueObjectSynthetic::UpdateValue] name={0}, synthetic " "filter said caches are stale - clearing", GetName()); // filter said that cached values are stale { std::lock_guard 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 guard(m_child_mutex); m_synthetic_children_cache.clear(); } m_synthetic_children_count = UINT32_MAX; m_might_have_children = eLazyBoolCalculate; } else { LLDB_LOG(log, "[ValueObjectSynthetic::UpdateValue] name={0}, synthetic " "filter said caches are still valid", GetName()); } m_provides_value = eLazyBoolCalculate; lldb::ValueObjectSP synth_val(m_synth_filter_up->GetSyntheticValue()); if (synth_val && synth_val->CanProvideValue()) { LLDB_LOG(log, "[ValueObjectSynthetic::UpdateValue] name={0}, synthetic " "filter said it can provide a value", GetName()); m_provides_value = eLazyBoolYes; CopyValueData(synth_val.get()); } else { LLDB_LOG(log, "[ValueObjectSynthetic::UpdateValue] name={0}, synthetic " "filter said it will not provide a value", GetName()); 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(uint32_t idx, bool can_create) { Log *log = GetLog(LLDBLog::DataFormatters); LLDB_LOG(log, "[ValueObjectSynthetic::GetChildAtIndex] name={0}, retrieving " "child at index {1}", GetName(), idx); UpdateValueIfNeeded(); ValueObject *valobj; bool child_is_cached; { std::lock_guard 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_LOG(log, "[ValueObjectSynthetic::GetChildAtIndex] name={0}, child at " "index {1} not cached and will be created", GetName(), idx); lldb::ValueObjectSP synth_guy = m_synth_filter_up->GetChildAtIndex(idx); LLDB_LOG( log, "[ValueObjectSynthetic::GetChildAtIndex] name={0}, child at index " "{1} created as {2} (is " "synthetic: {3})", GetName(), idx, static_cast(synth_guy.get()), synth_guy.get() ? (synth_guy->IsSyntheticChildrenGenerated() ? "yes" : "no") : "no"); if (!synth_guy) return synth_guy; { std::lock_guard 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()); if (lldb::ValueObjectSP check_sp = CheckValueObjectOwnership(synth_guy.get())) return check_sp; return synth_guy; } else { LLDB_LOG(log, "[ValueObjectSynthetic::GetChildAtIndex] name={0}, child at " "index {1} not cached and cannot " "be created (can_create = {2}, synth_filter = {3})", GetName(), idx, can_create ? "yes" : "no", static_cast(m_synth_filter_up.get())); return lldb::ValueObjectSP(); } } else { LLDB_LOG(log, "[ValueObjectSynthetic::GetChildAtIndex] name={0}, child at " "index {1} cached as {2}", GetName(), idx, static_cast(valobj)); lldb::ValueObjectSP check_sp = CheckValueObjectOwnership(valobj); if (check_sp) return check_sp; return valobj->GetSP(); } } lldb::ValueObjectSP ValueObjectSynthetic::GetChildMemberWithName(llvm::StringRef name, bool can_create) { UpdateValueIfNeeded(); auto index_or_err = GetIndexOfChildWithName(name); if (!index_or_err) { llvm::consumeError(index_or_err.takeError()); return lldb::ValueObjectSP(); } return GetChildAtIndex(*index_or_err, can_create); } llvm::Expected ValueObjectSynthetic::GetIndexOfChildWithName(llvm::StringRef name_ref) { UpdateValueIfNeeded(); ConstString name(name_ref); std::optional found_index = std::nullopt; { std::lock_guard guard(m_child_mutex); auto name_to_index = m_name_toindex.find(name.GetCString()); if (name_to_index != m_name_toindex.end()) found_index = name_to_index->second; } if (!found_index && m_synth_filter_up != nullptr) { size_t index = SIZE_MAX; if (auto index_or_err = m_synth_filter_up->GetIndexOfChildWithName(name)) { index = *index_or_err; } else if (!m_synth_sp->CustomSubscripting()) { // Provide automatic support for subscript child names ("[N]"). auto maybe_index = formatters::ExtractIndexFromString(name.GetCString()); if (!maybe_index) // The child name was not of the form "[N]", return the original error. return index_or_err.takeError(); // Subscripting succeeded, ignore the original error. llvm::consumeError(index_or_err.takeError()); index = *maybe_index; // Prevent unnecessary work by limiting max to one past the index. uint32_t max = index + 1; auto num_children = GetNumChildrenIgnoringErrors(max); if (index >= num_children) return llvm::createStringErrorV("subscript index out of range: {0}", index); } std::lock_guard guard(m_child_mutex); m_name_toindex[name.GetCString()] = index; return index; } else if (!found_index && m_synth_filter_up == nullptr) { return llvm::createStringErrorV("type has no child named '{0}'", name); } else if (found_index) return *found_index; return llvm::createStringErrorV("type has no child named '{0}'", name); } bool ValueObjectSynthetic::IsInScope() { return m_parent->IsInScope(); } lldb::ValueObjectSP ValueObjectSynthetic::GetNonSyntheticValue() { return m_parent->GetSP(); } void ValueObjectSynthetic::CopyValueData(ValueObject *source) { if (!source->UpdateValueIfNeeded()) return; m_value = 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); } void ValueObjectSynthetic::GetExpressionPath(Stream &stream, GetExpressionPathFormat epformat) { // A synthetic ValueObject may wrap an underlying Register or RegisterSet // ValueObject, which requires a different approach to generating the // expression path. In such cases, delegate to the non-synthetic value object. if (const lldb::ValueType obj_value_type = GetValueType(); IsSynthetic() && (obj_value_type == lldb::eValueTypeRegister || obj_value_type == lldb::eValueTypeRegisterSet)) { if (const lldb::ValueObjectSP raw_value = GetNonSyntheticValue()) return raw_value->GetExpressionPath(stream, epformat); } return ValueObject::GetExpressionPath(stream, epformat); }