
Change the name of `ValueObjectSyntheticFilter.{h,cpp}` to match the main type they declare and define: `ValueObjectSynthetic`.
446 lines
14 KiB
C++
446 lines
14 KiB
C++
//===-- 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/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 <optional>
|
|
|
|
namespace lldb_private {
|
|
class Declaration;
|
|
}
|
|
|
|
using namespace lldb_private;
|
|
|
|
class DummySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
|
|
public:
|
|
DummySyntheticFrontEnd(ValueObject &backend)
|
|
: SyntheticChildrenFrontEnd(backend) {}
|
|
|
|
llvm::Expected<uint32_t> CalculateNumChildren() override {
|
|
return m_backend.GetNumChildren();
|
|
}
|
|
|
|
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
|
|
return m_backend.GetChildAtIndex(idx);
|
|
}
|
|
|
|
llvm::Expected<size_t> 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<uint32_t>
|
|
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_LOGF(log,
|
|
"[ValueObjectSynthetic::CalculateNumChildren] for VO of name "
|
|
"%s and type %s, the filter returned %u child values",
|
|
GetName().AsCString(), GetTypeName().AsCString(),
|
|
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_LOGF(log,
|
|
"[ValueObjectSynthetic::CalculateNumChildren] for VO of name "
|
|
"%s and type %s, the filter returned %u 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::Expected<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 = 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_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::ChildCacheState::eRefetch) {
|
|
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(uint32_t idx,
|
|
bool can_create) {
|
|
Log *log = GetLog(LLDBLog::DataFormatters);
|
|
|
|
LLDB_LOGF(log,
|
|
"[ValueObjectSynthetic::GetChildAtIndex] name=%s, retrieving "
|
|
"child at index %u",
|
|
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 %u 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 "
|
|
"%u 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 %u 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 %u cached as %p",
|
|
GetName().AsCString(), idx, static_cast<void *>(valobj));
|
|
|
|
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<size_t>
|
|
ValueObjectSynthetic::GetIndexOfChildWithName(llvm::StringRef name_ref) {
|
|
UpdateValueIfNeeded();
|
|
|
|
ConstString name(name_ref);
|
|
|
|
std::optional<uint32_t> found_index = std::nullopt;
|
|
{
|
|
std::lock_guard<std::mutex> 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) {
|
|
auto index_or_err = m_synth_filter_up->GetIndexOfChildWithName(name);
|
|
if (!index_or_err)
|
|
return index_or_err.takeError();
|
|
std::lock_guard<std::mutex> guard(m_child_mutex);
|
|
m_name_toindex[name.GetCString()] = *index_or_err;
|
|
return *index_or_err;
|
|
} else if (!found_index && m_synth_filter_up == nullptr) {
|
|
return llvm::createStringError("Type has no child named '%s'",
|
|
name.AsCString());
|
|
} else if (found_index)
|
|
return *found_index;
|
|
|
|
return llvm::createStringError("Type has no child named '%s'",
|
|
name.AsCString());
|
|
}
|
|
|
|
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);
|
|
}
|