This patch pushes the error handling boundary for the GetBitSize() methods from Runtime into the Type and CompilerType APIs. This makes it easier to diagnose problems thanks to more meaningful error messages being available. GetBitSize() is often the first thing LLDB asks about a type, so this method is particularly important for a better user experience. rdar://145667239
3836 lines
138 KiB
C++
3836 lines
138 KiB
C++
//===-- ValueObject.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/ValueObject.h"
|
|
|
|
#include "lldb/Core/Address.h"
|
|
#include "lldb/Core/Declaration.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/DataFormatters/DataVisualization.h"
|
|
#include "lldb/DataFormatters/DumpValueObjectOptions.h"
|
|
#include "lldb/DataFormatters/FormatManager.h"
|
|
#include "lldb/DataFormatters/StringPrinter.h"
|
|
#include "lldb/DataFormatters/TypeFormat.h"
|
|
#include "lldb/DataFormatters/TypeSummary.h"
|
|
#include "lldb/DataFormatters/ValueObjectPrinter.h"
|
|
#include "lldb/Expression/ExpressionVariable.h"
|
|
#include "lldb/Host/Config.h"
|
|
#include "lldb/Symbol/CompileUnit.h"
|
|
#include "lldb/Symbol/CompilerType.h"
|
|
#include "lldb/Symbol/SymbolContext.h"
|
|
#include "lldb/Symbol/Type.h"
|
|
#include "lldb/Symbol/Variable.h"
|
|
#include "lldb/Target/ExecutionContext.h"
|
|
#include "lldb/Target/Language.h"
|
|
#include "lldb/Target/LanguageRuntime.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/StackFrame.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Target/Thread.h"
|
|
#include "lldb/Target/ThreadList.h"
|
|
#include "lldb/Utility/DataBuffer.h"
|
|
#include "lldb/Utility/DataBufferHeap.h"
|
|
#include "lldb/Utility/Flags.h"
|
|
#include "lldb/Utility/LLDBLog.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/Scalar.h"
|
|
#include "lldb/Utility/Stream.h"
|
|
#include "lldb/Utility/StreamString.h"
|
|
#include "lldb/ValueObject/ValueObject.h"
|
|
#include "lldb/ValueObject/ValueObjectCast.h"
|
|
#include "lldb/ValueObject/ValueObjectChild.h"
|
|
#include "lldb/ValueObject/ValueObjectConstResult.h"
|
|
#include "lldb/ValueObject/ValueObjectDynamicValue.h"
|
|
#include "lldb/ValueObject/ValueObjectMemory.h"
|
|
#include "lldb/ValueObject/ValueObjectSyntheticFilter.h"
|
|
#include "lldb/ValueObject/ValueObjectVTable.h"
|
|
#include "lldb/lldb-private-types.h"
|
|
|
|
#include "llvm/Support/Compiler.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <tuple>
|
|
|
|
#include <cassert>
|
|
#include <cinttypes>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
namespace lldb_private {
|
|
class ExecutionContextScope;
|
|
}
|
|
namespace lldb_private {
|
|
class SymbolContextScope;
|
|
}
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
static user_id_t g_value_obj_uid = 0;
|
|
|
|
// ValueObject constructor
|
|
ValueObject::ValueObject(ValueObject &parent)
|
|
: m_parent(&parent), m_update_point(parent.GetUpdatePoint()),
|
|
m_manager(parent.GetManager()), m_id(++g_value_obj_uid) {
|
|
m_flags.m_is_synthetic_children_generated =
|
|
parent.m_flags.m_is_synthetic_children_generated;
|
|
m_data.SetByteOrder(parent.GetDataExtractor().GetByteOrder());
|
|
m_data.SetAddressByteSize(parent.GetDataExtractor().GetAddressByteSize());
|
|
m_manager->ManageObject(this);
|
|
}
|
|
|
|
// ValueObject constructor
|
|
ValueObject::ValueObject(ExecutionContextScope *exe_scope,
|
|
ValueObjectManager &manager,
|
|
AddressType child_ptr_or_ref_addr_type)
|
|
: m_update_point(exe_scope), m_manager(&manager),
|
|
m_address_type_of_ptr_or_ref_children(child_ptr_or_ref_addr_type),
|
|
m_id(++g_value_obj_uid) {
|
|
if (exe_scope) {
|
|
TargetSP target_sp(exe_scope->CalculateTarget());
|
|
if (target_sp) {
|
|
const ArchSpec &arch = target_sp->GetArchitecture();
|
|
m_data.SetByteOrder(arch.GetByteOrder());
|
|
m_data.SetAddressByteSize(arch.GetAddressByteSize());
|
|
}
|
|
}
|
|
m_manager->ManageObject(this);
|
|
}
|
|
|
|
// Destructor
|
|
ValueObject::~ValueObject() = default;
|
|
|
|
bool ValueObject::UpdateValueIfNeeded(bool update_format) {
|
|
|
|
bool did_change_formats = false;
|
|
|
|
if (update_format)
|
|
did_change_formats = UpdateFormatsIfNeeded();
|
|
|
|
// If this is a constant value, then our success is predicated on whether we
|
|
// have an error or not
|
|
if (GetIsConstant()) {
|
|
// if you are constant, things might still have changed behind your back
|
|
// (e.g. you are a frozen object and things have changed deeper than you
|
|
// cared to freeze-dry yourself) in this case, your value has not changed,
|
|
// but "computed" entries might have, so you might now have a different
|
|
// summary, or a different object description. clear these so we will
|
|
// recompute them
|
|
if (update_format && !did_change_formats)
|
|
ClearUserVisibleData(eClearUserVisibleDataItemsSummary |
|
|
eClearUserVisibleDataItemsDescription);
|
|
return m_error.Success();
|
|
}
|
|
|
|
bool first_update = IsChecksumEmpty();
|
|
|
|
if (NeedsUpdating()) {
|
|
m_update_point.SetUpdated();
|
|
|
|
// Save the old value using swap to avoid a string copy which also will
|
|
// clear our m_value_str
|
|
if (m_value_str.empty()) {
|
|
m_flags.m_old_value_valid = false;
|
|
} else {
|
|
m_flags.m_old_value_valid = true;
|
|
m_old_value_str.swap(m_value_str);
|
|
ClearUserVisibleData(eClearUserVisibleDataItemsValue);
|
|
}
|
|
|
|
ClearUserVisibleData();
|
|
|
|
if (IsInScope()) {
|
|
const bool value_was_valid = GetValueIsValid();
|
|
SetValueDidChange(false);
|
|
|
|
m_error.Clear();
|
|
|
|
// Call the pure virtual function to update the value
|
|
|
|
bool need_compare_checksums = false;
|
|
llvm::SmallVector<uint8_t, 16> old_checksum;
|
|
|
|
if (!first_update && CanProvideValue()) {
|
|
need_compare_checksums = true;
|
|
old_checksum.resize(m_value_checksum.size());
|
|
std::copy(m_value_checksum.begin(), m_value_checksum.end(),
|
|
old_checksum.begin());
|
|
}
|
|
|
|
bool success = UpdateValue();
|
|
|
|
SetValueIsValid(success);
|
|
|
|
if (success) {
|
|
UpdateChildrenAddressType();
|
|
const uint64_t max_checksum_size = 128;
|
|
m_data.Checksum(m_value_checksum, max_checksum_size);
|
|
} else {
|
|
need_compare_checksums = false;
|
|
m_value_checksum.clear();
|
|
}
|
|
|
|
assert(!need_compare_checksums ||
|
|
(!old_checksum.empty() && !m_value_checksum.empty()));
|
|
|
|
if (first_update)
|
|
SetValueDidChange(false);
|
|
else if (!m_flags.m_value_did_change && !success) {
|
|
// The value wasn't gotten successfully, so we mark this as changed if
|
|
// the value used to be valid and now isn't
|
|
SetValueDidChange(value_was_valid);
|
|
} else if (need_compare_checksums) {
|
|
SetValueDidChange(memcmp(&old_checksum[0], &m_value_checksum[0],
|
|
m_value_checksum.size()));
|
|
}
|
|
|
|
} else {
|
|
m_error = Status::FromErrorString("out of scope");
|
|
}
|
|
}
|
|
return m_error.Success();
|
|
}
|
|
|
|
bool ValueObject::UpdateFormatsIfNeeded() {
|
|
Log *log = GetLog(LLDBLog::DataFormatters);
|
|
LLDB_LOGF(log,
|
|
"[%s %p] checking for FormatManager revisions. ValueObject "
|
|
"rev: %d - Global rev: %d",
|
|
GetName().GetCString(), static_cast<void *>(this),
|
|
m_last_format_mgr_revision,
|
|
DataVisualization::GetCurrentRevision());
|
|
|
|
bool any_change = false;
|
|
|
|
if ((m_last_format_mgr_revision != DataVisualization::GetCurrentRevision())) {
|
|
m_last_format_mgr_revision = DataVisualization::GetCurrentRevision();
|
|
any_change = true;
|
|
|
|
SetValueFormat(DataVisualization::GetFormat(*this, GetDynamicValueType()));
|
|
SetSummaryFormat(
|
|
DataVisualization::GetSummaryFormat(*this, GetDynamicValueType()));
|
|
SetSyntheticChildren(
|
|
DataVisualization::GetSyntheticChildren(*this, GetDynamicValueType()));
|
|
}
|
|
|
|
return any_change;
|
|
}
|
|
|
|
void ValueObject::SetNeedsUpdate() {
|
|
m_update_point.SetNeedsUpdate();
|
|
// We have to clear the value string here so ConstResult children will notice
|
|
// if their values are changed by hand (i.e. with SetValueAsCString).
|
|
ClearUserVisibleData(eClearUserVisibleDataItemsValue);
|
|
}
|
|
|
|
void ValueObject::ClearDynamicTypeInformation() {
|
|
m_flags.m_children_count_valid = false;
|
|
m_flags.m_did_calculate_complete_objc_class_type = false;
|
|
m_last_format_mgr_revision = 0;
|
|
m_override_type = CompilerType();
|
|
SetValueFormat(lldb::TypeFormatImplSP());
|
|
SetSummaryFormat(lldb::TypeSummaryImplSP());
|
|
SetSyntheticChildren(lldb::SyntheticChildrenSP());
|
|
}
|
|
|
|
CompilerType ValueObject::MaybeCalculateCompleteType() {
|
|
CompilerType compiler_type(GetCompilerTypeImpl());
|
|
|
|
if (m_flags.m_did_calculate_complete_objc_class_type) {
|
|
if (m_override_type.IsValid())
|
|
return m_override_type;
|
|
else
|
|
return compiler_type;
|
|
}
|
|
|
|
m_flags.m_did_calculate_complete_objc_class_type = true;
|
|
|
|
ProcessSP process_sp(
|
|
GetUpdatePoint().GetExecutionContextRef().GetProcessSP());
|
|
|
|
if (!process_sp)
|
|
return compiler_type;
|
|
|
|
if (auto *runtime =
|
|
process_sp->GetLanguageRuntime(GetObjectRuntimeLanguage())) {
|
|
if (std::optional<CompilerType> complete_type =
|
|
runtime->GetRuntimeType(compiler_type)) {
|
|
m_override_type = *complete_type;
|
|
if (m_override_type.IsValid())
|
|
return m_override_type;
|
|
}
|
|
}
|
|
return compiler_type;
|
|
}
|
|
|
|
DataExtractor &ValueObject::GetDataExtractor() {
|
|
UpdateValueIfNeeded(false);
|
|
return m_data;
|
|
}
|
|
|
|
const Status &ValueObject::GetError() {
|
|
UpdateValueIfNeeded(false);
|
|
return m_error;
|
|
}
|
|
|
|
const char *ValueObject::GetLocationAsCStringImpl(const Value &value,
|
|
const DataExtractor &data) {
|
|
if (UpdateValueIfNeeded(false)) {
|
|
if (m_location_str.empty()) {
|
|
StreamString sstr;
|
|
|
|
Value::ValueType value_type = value.GetValueType();
|
|
|
|
switch (value_type) {
|
|
case Value::ValueType::Invalid:
|
|
m_location_str = "invalid";
|
|
break;
|
|
case Value::ValueType::Scalar:
|
|
if (value.GetContextType() == Value::ContextType::RegisterInfo) {
|
|
RegisterInfo *reg_info = value.GetRegisterInfo();
|
|
if (reg_info) {
|
|
if (reg_info->name)
|
|
m_location_str = reg_info->name;
|
|
else if (reg_info->alt_name)
|
|
m_location_str = reg_info->alt_name;
|
|
if (m_location_str.empty())
|
|
m_location_str = (reg_info->encoding == lldb::eEncodingVector)
|
|
? "vector"
|
|
: "scalar";
|
|
}
|
|
}
|
|
if (m_location_str.empty())
|
|
m_location_str = "scalar";
|
|
break;
|
|
|
|
case Value::ValueType::LoadAddress:
|
|
case Value::ValueType::FileAddress:
|
|
case Value::ValueType::HostAddress: {
|
|
uint32_t addr_nibble_size = data.GetAddressByteSize() * 2;
|
|
sstr.Printf("0x%*.*llx", addr_nibble_size, addr_nibble_size,
|
|
value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS));
|
|
m_location_str = std::string(sstr.GetString());
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
return m_location_str.c_str();
|
|
}
|
|
|
|
bool ValueObject::ResolveValue(Scalar &scalar) {
|
|
if (UpdateValueIfNeeded(
|
|
false)) // make sure that you are up to date before returning anything
|
|
{
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
Value tmp_value(m_value);
|
|
scalar = tmp_value.ResolveValue(&exe_ctx, GetModule().get());
|
|
if (scalar.IsValid()) {
|
|
const uint32_t bitfield_bit_size = GetBitfieldBitSize();
|
|
if (bitfield_bit_size)
|
|
return scalar.ExtractBitfield(bitfield_bit_size,
|
|
GetBitfieldBitOffset());
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ValueObject::IsLogicalTrue(Status &error) {
|
|
if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) {
|
|
LazyBool is_logical_true = language->IsLogicalTrue(*this, error);
|
|
switch (is_logical_true) {
|
|
case eLazyBoolYes:
|
|
case eLazyBoolNo:
|
|
return (is_logical_true == true);
|
|
case eLazyBoolCalculate:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Scalar scalar_value;
|
|
|
|
if (!ResolveValue(scalar_value)) {
|
|
error = Status::FromErrorString("failed to get a scalar result");
|
|
return false;
|
|
}
|
|
|
|
bool ret;
|
|
ret = scalar_value.ULongLong(1) != 0;
|
|
error.Clear();
|
|
return ret;
|
|
}
|
|
|
|
ValueObjectSP ValueObject::GetChildAtIndex(uint32_t idx, bool can_create) {
|
|
ValueObjectSP child_sp;
|
|
// We may need to update our value if we are dynamic
|
|
if (IsPossibleDynamicType())
|
|
UpdateValueIfNeeded(false);
|
|
if (idx < GetNumChildrenIgnoringErrors()) {
|
|
// Check if we have already made the child value object?
|
|
if (can_create && !m_children.HasChildAtIndex(idx)) {
|
|
// No we haven't created the child at this index, so lets have our
|
|
// subclass do it and cache the result for quick future access.
|
|
m_children.SetChildAtIndex(idx, CreateChildAtIndex(idx));
|
|
}
|
|
|
|
ValueObject *child = m_children.GetChildAtIndex(idx);
|
|
if (child != nullptr)
|
|
return child->GetSP();
|
|
}
|
|
return child_sp;
|
|
}
|
|
|
|
lldb::ValueObjectSP
|
|
ValueObject::GetChildAtNamePath(llvm::ArrayRef<llvm::StringRef> names) {
|
|
if (names.size() == 0)
|
|
return GetSP();
|
|
ValueObjectSP root(GetSP());
|
|
for (llvm::StringRef name : names) {
|
|
root = root->GetChildMemberWithName(name);
|
|
if (!root) {
|
|
return root;
|
|
}
|
|
}
|
|
return root;
|
|
}
|
|
|
|
size_t ValueObject::GetIndexOfChildWithName(llvm::StringRef name) {
|
|
bool omit_empty_base_classes = true;
|
|
return GetCompilerType().GetIndexOfChildWithName(name,
|
|
omit_empty_base_classes);
|
|
}
|
|
|
|
ValueObjectSP ValueObject::GetChildMemberWithName(llvm::StringRef name,
|
|
bool can_create) {
|
|
// We may need to update our value if we are dynamic.
|
|
if (IsPossibleDynamicType())
|
|
UpdateValueIfNeeded(false);
|
|
|
|
// When getting a child by name, it could be buried inside some base classes
|
|
// (which really aren't part of the expression path), so we need a vector of
|
|
// indexes that can get us down to the correct child.
|
|
std::vector<uint32_t> child_indexes;
|
|
bool omit_empty_base_classes = true;
|
|
|
|
if (!GetCompilerType().IsValid())
|
|
return ValueObjectSP();
|
|
|
|
const size_t num_child_indexes =
|
|
GetCompilerType().GetIndexOfChildMemberWithName(
|
|
name, omit_empty_base_classes, child_indexes);
|
|
if (num_child_indexes == 0)
|
|
return nullptr;
|
|
|
|
ValueObjectSP child_sp = GetSP();
|
|
for (uint32_t idx : child_indexes)
|
|
if (child_sp)
|
|
child_sp = child_sp->GetChildAtIndex(idx, can_create);
|
|
return child_sp;
|
|
}
|
|
|
|
llvm::Expected<uint32_t> ValueObject::GetNumChildren(uint32_t max) {
|
|
UpdateValueIfNeeded();
|
|
|
|
if (max < UINT32_MAX) {
|
|
if (m_flags.m_children_count_valid) {
|
|
size_t children_count = m_children.GetChildrenCount();
|
|
return children_count <= max ? children_count : max;
|
|
} else
|
|
return CalculateNumChildren(max);
|
|
}
|
|
|
|
if (!m_flags.m_children_count_valid) {
|
|
auto num_children_or_err = CalculateNumChildren();
|
|
if (num_children_or_err)
|
|
SetNumChildren(*num_children_or_err);
|
|
else
|
|
return num_children_or_err;
|
|
}
|
|
return m_children.GetChildrenCount();
|
|
}
|
|
|
|
uint32_t ValueObject::GetNumChildrenIgnoringErrors(uint32_t max) {
|
|
auto value_or_err = GetNumChildren(max);
|
|
if (value_or_err)
|
|
return *value_or_err;
|
|
LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), value_or_err.takeError(),
|
|
"{0}");
|
|
return 0;
|
|
}
|
|
|
|
bool ValueObject::MightHaveChildren() {
|
|
bool has_children = false;
|
|
const uint32_t type_info = GetTypeInfo();
|
|
if (type_info) {
|
|
if (type_info & (eTypeHasChildren | eTypeIsPointer | eTypeIsReference))
|
|
has_children = true;
|
|
} else {
|
|
has_children = GetNumChildrenIgnoringErrors() > 0;
|
|
}
|
|
return has_children;
|
|
}
|
|
|
|
// Should only be called by ValueObject::GetNumChildren()
|
|
void ValueObject::SetNumChildren(uint32_t num_children) {
|
|
m_flags.m_children_count_valid = true;
|
|
m_children.SetChildrenCount(num_children);
|
|
}
|
|
|
|
ValueObject *ValueObject::CreateChildAtIndex(size_t idx) {
|
|
bool omit_empty_base_classes = true;
|
|
bool ignore_array_bounds = false;
|
|
std::string child_name;
|
|
uint32_t child_byte_size = 0;
|
|
int32_t child_byte_offset = 0;
|
|
uint32_t child_bitfield_bit_size = 0;
|
|
uint32_t child_bitfield_bit_offset = 0;
|
|
bool child_is_base_class = false;
|
|
bool child_is_deref_of_parent = false;
|
|
uint64_t language_flags = 0;
|
|
const bool transparent_pointers = true;
|
|
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
|
|
auto child_compiler_type_or_err =
|
|
GetCompilerType().GetChildCompilerTypeAtIndex(
|
|
&exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
|
|
ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
|
|
child_bitfield_bit_size, child_bitfield_bit_offset,
|
|
child_is_base_class, child_is_deref_of_parent, this, language_flags);
|
|
if (!child_compiler_type_or_err || !child_compiler_type_or_err->IsValid()) {
|
|
LLDB_LOG_ERROR(GetLog(LLDBLog::Types),
|
|
child_compiler_type_or_err.takeError(),
|
|
"could not find child: {0}");
|
|
return nullptr;
|
|
}
|
|
|
|
return new ValueObjectChild(
|
|
*this, *child_compiler_type_or_err, ConstString(child_name),
|
|
child_byte_size, child_byte_offset, child_bitfield_bit_size,
|
|
child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent,
|
|
eAddressTypeInvalid, language_flags);
|
|
}
|
|
|
|
ValueObject *ValueObject::CreateSyntheticArrayMember(size_t idx) {
|
|
bool omit_empty_base_classes = true;
|
|
bool ignore_array_bounds = true;
|
|
std::string child_name;
|
|
uint32_t child_byte_size = 0;
|
|
int32_t child_byte_offset = 0;
|
|
uint32_t child_bitfield_bit_size = 0;
|
|
uint32_t child_bitfield_bit_offset = 0;
|
|
bool child_is_base_class = false;
|
|
bool child_is_deref_of_parent = false;
|
|
uint64_t language_flags = 0;
|
|
const bool transparent_pointers = false;
|
|
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
|
|
auto child_compiler_type_or_err =
|
|
GetCompilerType().GetChildCompilerTypeAtIndex(
|
|
&exe_ctx, 0, transparent_pointers, omit_empty_base_classes,
|
|
ignore_array_bounds, child_name, child_byte_size, child_byte_offset,
|
|
child_bitfield_bit_size, child_bitfield_bit_offset,
|
|
child_is_base_class, child_is_deref_of_parent, this, language_flags);
|
|
if (!child_compiler_type_or_err) {
|
|
LLDB_LOG_ERROR(GetLog(LLDBLog::Types),
|
|
child_compiler_type_or_err.takeError(),
|
|
"could not find child: {0}");
|
|
return nullptr;
|
|
}
|
|
|
|
if (child_compiler_type_or_err->IsValid()) {
|
|
child_byte_offset += child_byte_size * idx;
|
|
|
|
return new ValueObjectChild(
|
|
*this, *child_compiler_type_or_err, ConstString(child_name),
|
|
child_byte_size, child_byte_offset, child_bitfield_bit_size,
|
|
child_bitfield_bit_offset, child_is_base_class,
|
|
child_is_deref_of_parent, eAddressTypeInvalid, language_flags);
|
|
}
|
|
|
|
// In case of an incomplete type, try to use the ValueObject's
|
|
// synthetic value to create the child ValueObject.
|
|
if (ValueObjectSP synth_valobj_sp = GetSyntheticValue())
|
|
return synth_valobj_sp->GetChildAtIndex(idx, /*can_create=*/true).get();
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool ValueObject::GetSummaryAsCString(TypeSummaryImpl *summary_ptr,
|
|
std::string &destination,
|
|
lldb::LanguageType lang) {
|
|
return GetSummaryAsCString(summary_ptr, destination,
|
|
TypeSummaryOptions().SetLanguage(lang));
|
|
}
|
|
|
|
bool ValueObject::GetSummaryAsCString(TypeSummaryImpl *summary_ptr,
|
|
std::string &destination,
|
|
const TypeSummaryOptions &options) {
|
|
destination.clear();
|
|
|
|
// If we have a forcefully completed type, don't try and show a summary from
|
|
// a valid summary string or function because the type is not complete and
|
|
// no member variables or member functions will be available.
|
|
if (GetCompilerType().IsForcefullyCompleted()) {
|
|
destination = "<incomplete type>";
|
|
return true;
|
|
}
|
|
|
|
// ideally we would like to bail out if passing NULL, but if we do so we end
|
|
// up not providing the summary for function pointers anymore
|
|
if (/*summary_ptr == NULL ||*/ m_flags.m_is_getting_summary)
|
|
return false;
|
|
|
|
m_flags.m_is_getting_summary = true;
|
|
|
|
TypeSummaryOptions actual_options(options);
|
|
|
|
if (actual_options.GetLanguage() == lldb::eLanguageTypeUnknown)
|
|
actual_options.SetLanguage(GetPreferredDisplayLanguage());
|
|
|
|
// this is a hot path in code and we prefer to avoid setting this string all
|
|
// too often also clearing out other information that we might care to see in
|
|
// a crash log. might be useful in very specific situations though.
|
|
/*Host::SetCrashDescriptionWithFormat("Trying to fetch a summary for %s %s.
|
|
Summary provider's description is %s",
|
|
GetTypeName().GetCString(),
|
|
GetName().GetCString(),
|
|
summary_ptr->GetDescription().c_str());*/
|
|
|
|
if (UpdateValueIfNeeded(false) && summary_ptr) {
|
|
if (HasSyntheticValue())
|
|
m_synthetic_value->UpdateValueIfNeeded(); // the summary might depend on
|
|
// the synthetic children being
|
|
// up-to-date (e.g. ${svar%#})
|
|
|
|
if (TargetSP target_sp = GetExecutionContextRef().GetTargetSP()) {
|
|
SummaryStatisticsSP stats_sp =
|
|
target_sp->GetSummaryStatisticsCache()
|
|
.GetSummaryStatisticsForProvider(*summary_ptr);
|
|
|
|
// Construct RAII types to time and collect data on summary creation.
|
|
SummaryStatistics::SummaryInvocation invocation(stats_sp);
|
|
summary_ptr->FormatObject(this, destination, actual_options);
|
|
} else
|
|
summary_ptr->FormatObject(this, destination, actual_options);
|
|
}
|
|
m_flags.m_is_getting_summary = false;
|
|
return !destination.empty();
|
|
}
|
|
|
|
const char *ValueObject::GetSummaryAsCString(lldb::LanguageType lang) {
|
|
if (UpdateValueIfNeeded(true) && m_summary_str.empty()) {
|
|
TypeSummaryOptions summary_options;
|
|
summary_options.SetLanguage(lang);
|
|
GetSummaryAsCString(GetSummaryFormat().get(), m_summary_str,
|
|
summary_options);
|
|
}
|
|
if (m_summary_str.empty())
|
|
return nullptr;
|
|
return m_summary_str.c_str();
|
|
}
|
|
|
|
bool ValueObject::GetSummaryAsCString(std::string &destination,
|
|
const TypeSummaryOptions &options) {
|
|
return GetSummaryAsCString(GetSummaryFormat().get(), destination, options);
|
|
}
|
|
|
|
bool ValueObject::IsCStringContainer(bool check_pointer) {
|
|
CompilerType pointee_or_element_compiler_type;
|
|
const Flags type_flags(GetTypeInfo(&pointee_or_element_compiler_type));
|
|
bool is_char_arr_ptr(type_flags.AnySet(eTypeIsArray | eTypeIsPointer) &&
|
|
pointee_or_element_compiler_type.IsCharType());
|
|
if (!is_char_arr_ptr)
|
|
return false;
|
|
if (!check_pointer)
|
|
return true;
|
|
if (type_flags.Test(eTypeIsArray))
|
|
return true;
|
|
addr_t cstr_address = LLDB_INVALID_ADDRESS;
|
|
AddressType cstr_address_type = eAddressTypeInvalid;
|
|
cstr_address = GetPointerValue(&cstr_address_type);
|
|
return (cstr_address != LLDB_INVALID_ADDRESS);
|
|
}
|
|
|
|
size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx,
|
|
uint32_t item_count) {
|
|
CompilerType pointee_or_element_compiler_type;
|
|
const uint32_t type_info = GetTypeInfo(&pointee_or_element_compiler_type);
|
|
const bool is_pointer_type = type_info & eTypeIsPointer;
|
|
const bool is_array_type = type_info & eTypeIsArray;
|
|
if (!(is_pointer_type || is_array_type))
|
|
return 0;
|
|
|
|
if (item_count == 0)
|
|
return 0;
|
|
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
|
|
std::optional<uint64_t> item_type_size =
|
|
llvm::expectedToOptional(pointee_or_element_compiler_type.GetByteSize(
|
|
exe_ctx.GetBestExecutionContextScope()));
|
|
if (!item_type_size)
|
|
return 0;
|
|
const uint64_t bytes = item_count * *item_type_size;
|
|
const uint64_t offset = item_idx * *item_type_size;
|
|
|
|
if (item_idx == 0 && item_count == 1) // simply a deref
|
|
{
|
|
if (is_pointer_type) {
|
|
Status error;
|
|
ValueObjectSP pointee_sp = Dereference(error);
|
|
if (error.Fail() || pointee_sp.get() == nullptr)
|
|
return 0;
|
|
return pointee_sp->GetData(data, error);
|
|
} else {
|
|
ValueObjectSP child_sp = GetChildAtIndex(0);
|
|
if (child_sp.get() == nullptr)
|
|
return 0;
|
|
Status error;
|
|
return child_sp->GetData(data, error);
|
|
}
|
|
return true;
|
|
} else /* (items > 1) */
|
|
{
|
|
Status error;
|
|
lldb_private::DataBufferHeap *heap_buf_ptr = nullptr;
|
|
lldb::DataBufferSP data_sp(heap_buf_ptr =
|
|
new lldb_private::DataBufferHeap());
|
|
|
|
AddressType addr_type;
|
|
lldb::addr_t addr = is_pointer_type ? GetPointerValue(&addr_type)
|
|
: GetAddressOf(true, &addr_type);
|
|
|
|
switch (addr_type) {
|
|
case eAddressTypeFile: {
|
|
ModuleSP module_sp(GetModule());
|
|
if (module_sp) {
|
|
addr = addr + offset;
|
|
Address so_addr;
|
|
module_sp->ResolveFileAddress(addr, so_addr);
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
Target *target = exe_ctx.GetTargetPtr();
|
|
if (target) {
|
|
heap_buf_ptr->SetByteSize(bytes);
|
|
size_t bytes_read = target->ReadMemory(
|
|
so_addr, heap_buf_ptr->GetBytes(), bytes, error, true);
|
|
if (error.Success()) {
|
|
data.SetData(data_sp);
|
|
return bytes_read;
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
case eAddressTypeLoad: {
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
if (process) {
|
|
heap_buf_ptr->SetByteSize(bytes);
|
|
size_t bytes_read = process->ReadMemory(
|
|
addr + offset, heap_buf_ptr->GetBytes(), bytes, error);
|
|
if (error.Success() || bytes_read > 0) {
|
|
data.SetData(data_sp);
|
|
return bytes_read;
|
|
}
|
|
}
|
|
} break;
|
|
case eAddressTypeHost: {
|
|
auto max_bytes =
|
|
GetCompilerType().GetByteSize(exe_ctx.GetBestExecutionContextScope());
|
|
if (max_bytes && *max_bytes > offset) {
|
|
size_t bytes_read = std::min<uint64_t>(*max_bytes - offset, bytes);
|
|
addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
if (addr == 0 || addr == LLDB_INVALID_ADDRESS)
|
|
break;
|
|
heap_buf_ptr->CopyData((uint8_t *)(addr + offset), bytes_read);
|
|
data.SetData(data_sp);
|
|
return bytes_read;
|
|
}
|
|
} break;
|
|
case eAddressTypeInvalid:
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint64_t ValueObject::GetData(DataExtractor &data, Status &error) {
|
|
UpdateValueIfNeeded(false);
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
error = m_value.GetValueAsData(&exe_ctx, data, GetModule().get());
|
|
if (error.Fail()) {
|
|
if (m_data.GetByteSize()) {
|
|
data = m_data;
|
|
error.Clear();
|
|
return data.GetByteSize();
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
data.SetAddressByteSize(m_data.GetAddressByteSize());
|
|
data.SetByteOrder(m_data.GetByteOrder());
|
|
return data.GetByteSize();
|
|
}
|
|
|
|
bool ValueObject::SetData(DataExtractor &data, Status &error) {
|
|
error.Clear();
|
|
// Make sure our value is up to date first so that our location and location
|
|
// type is valid.
|
|
if (!UpdateValueIfNeeded(false)) {
|
|
error = Status::FromErrorString("unable to read value");
|
|
return false;
|
|
}
|
|
|
|
uint64_t count = 0;
|
|
const Encoding encoding = GetCompilerType().GetEncoding(count);
|
|
|
|
const size_t byte_size = llvm::expectedToOptional(GetByteSize()).value_or(0);
|
|
|
|
Value::ValueType value_type = m_value.GetValueType();
|
|
|
|
switch (value_type) {
|
|
case Value::ValueType::Invalid:
|
|
error = Status::FromErrorString("invalid location");
|
|
return false;
|
|
case Value::ValueType::Scalar: {
|
|
Status set_error =
|
|
m_value.GetScalar().SetValueFromData(data, encoding, byte_size);
|
|
|
|
if (!set_error.Success()) {
|
|
error = Status::FromErrorStringWithFormat(
|
|
"unable to set scalar value: %s", set_error.AsCString());
|
|
return false;
|
|
}
|
|
} break;
|
|
case Value::ValueType::LoadAddress: {
|
|
// If it is a load address, then the scalar value is the storage location
|
|
// of the data, and we have to shove this value down to that load location.
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
if (process) {
|
|
addr_t target_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
size_t bytes_written = process->WriteMemory(
|
|
target_addr, data.GetDataStart(), byte_size, error);
|
|
if (!error.Success())
|
|
return false;
|
|
if (bytes_written != byte_size) {
|
|
error = Status::FromErrorString("unable to write value to memory");
|
|
return false;
|
|
}
|
|
}
|
|
} break;
|
|
case Value::ValueType::HostAddress: {
|
|
// If it is a host address, then we stuff the scalar as a DataBuffer into
|
|
// the Value's data.
|
|
DataBufferSP buffer_sp(new DataBufferHeap(byte_size, 0));
|
|
m_data.SetData(buffer_sp, 0);
|
|
data.CopyByteOrderedData(0, byte_size,
|
|
const_cast<uint8_t *>(m_data.GetDataStart()),
|
|
byte_size, m_data.GetByteOrder());
|
|
m_value.GetScalar() = (uintptr_t)m_data.GetDataStart();
|
|
} break;
|
|
case Value::ValueType::FileAddress:
|
|
break;
|
|
}
|
|
|
|
// If we have reached this point, then we have successfully changed the
|
|
// value.
|
|
SetNeedsUpdate();
|
|
return true;
|
|
}
|
|
|
|
llvm::ArrayRef<uint8_t> ValueObject::GetLocalBuffer() const {
|
|
if (m_value.GetValueType() != Value::ValueType::HostAddress)
|
|
return {};
|
|
auto start = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
if (start == LLDB_INVALID_ADDRESS)
|
|
return {};
|
|
// Does our pointer point to this value object's m_data buffer?
|
|
if ((uint64_t)m_data.GetDataStart() == start)
|
|
return m_data.GetData();
|
|
// Does our pointer point to the value's buffer?
|
|
if ((uint64_t)m_value.GetBuffer().GetBytes() == start)
|
|
return m_value.GetBuffer().GetData();
|
|
// Our pointer points to something else. We can't know what the size is.
|
|
return {};
|
|
}
|
|
|
|
static bool CopyStringDataToBufferSP(const StreamString &source,
|
|
lldb::WritableDataBufferSP &destination) {
|
|
llvm::StringRef src = source.GetString();
|
|
src = src.rtrim('\0');
|
|
destination = std::make_shared<DataBufferHeap>(src.size(), 0);
|
|
memcpy(destination->GetBytes(), src.data(), src.size());
|
|
return true;
|
|
}
|
|
|
|
std::pair<size_t, bool>
|
|
ValueObject::ReadPointedString(lldb::WritableDataBufferSP &buffer_sp,
|
|
Status &error, bool honor_array) {
|
|
bool was_capped = false;
|
|
StreamString s;
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
Target *target = exe_ctx.GetTargetPtr();
|
|
|
|
if (!target) {
|
|
s << "<no target to read from>";
|
|
error = Status::FromErrorString("no target to read from");
|
|
CopyStringDataToBufferSP(s, buffer_sp);
|
|
return {0, was_capped};
|
|
}
|
|
|
|
const auto max_length = target->GetMaximumSizeOfStringSummary();
|
|
|
|
size_t bytes_read = 0;
|
|
size_t total_bytes_read = 0;
|
|
|
|
CompilerType compiler_type = GetCompilerType();
|
|
CompilerType elem_or_pointee_compiler_type;
|
|
const Flags type_flags(GetTypeInfo(&elem_or_pointee_compiler_type));
|
|
if (type_flags.AnySet(eTypeIsArray | eTypeIsPointer) &&
|
|
elem_or_pointee_compiler_type.IsCharType()) {
|
|
addr_t cstr_address = LLDB_INVALID_ADDRESS;
|
|
AddressType cstr_address_type = eAddressTypeInvalid;
|
|
|
|
size_t cstr_len = 0;
|
|
bool capped_data = false;
|
|
const bool is_array = type_flags.Test(eTypeIsArray);
|
|
if (is_array) {
|
|
// We have an array
|
|
uint64_t array_size = 0;
|
|
if (compiler_type.IsArrayType(nullptr, &array_size)) {
|
|
cstr_len = array_size;
|
|
if (cstr_len > max_length) {
|
|
capped_data = true;
|
|
cstr_len = max_length;
|
|
}
|
|
}
|
|
cstr_address = GetAddressOf(true, &cstr_address_type);
|
|
} else {
|
|
// We have a pointer
|
|
cstr_address = GetPointerValue(&cstr_address_type);
|
|
}
|
|
|
|
if (cstr_address == 0 || cstr_address == LLDB_INVALID_ADDRESS) {
|
|
if (cstr_address_type == eAddressTypeHost && is_array) {
|
|
const char *cstr = GetDataExtractor().PeekCStr(0);
|
|
if (cstr == nullptr) {
|
|
s << "<invalid address>";
|
|
error = Status::FromErrorString("invalid address");
|
|
CopyStringDataToBufferSP(s, buffer_sp);
|
|
return {0, was_capped};
|
|
}
|
|
s << llvm::StringRef(cstr, cstr_len);
|
|
CopyStringDataToBufferSP(s, buffer_sp);
|
|
return {cstr_len, was_capped};
|
|
} else {
|
|
s << "<invalid address>";
|
|
error = Status::FromErrorString("invalid address");
|
|
CopyStringDataToBufferSP(s, buffer_sp);
|
|
return {0, was_capped};
|
|
}
|
|
}
|
|
|
|
Address cstr_so_addr(cstr_address);
|
|
DataExtractor data;
|
|
if (cstr_len > 0 && honor_array) {
|
|
// I am using GetPointeeData() here to abstract the fact that some
|
|
// ValueObjects are actually frozen pointers in the host but the pointed-
|
|
// to data lives in the debuggee, and GetPointeeData() automatically
|
|
// takes care of this
|
|
GetPointeeData(data, 0, cstr_len);
|
|
|
|
if ((bytes_read = data.GetByteSize()) > 0) {
|
|
total_bytes_read = bytes_read;
|
|
for (size_t offset = 0; offset < bytes_read; offset++)
|
|
s.Printf("%c", *data.PeekData(offset, 1));
|
|
if (capped_data)
|
|
was_capped = true;
|
|
}
|
|
} else {
|
|
cstr_len = max_length;
|
|
const size_t k_max_buf_size = 64;
|
|
|
|
size_t offset = 0;
|
|
|
|
int cstr_len_displayed = -1;
|
|
bool capped_cstr = false;
|
|
// I am using GetPointeeData() here to abstract the fact that some
|
|
// ValueObjects are actually frozen pointers in the host but the pointed-
|
|
// to data lives in the debuggee, and GetPointeeData() automatically
|
|
// takes care of this
|
|
while ((bytes_read = GetPointeeData(data, offset, k_max_buf_size)) > 0) {
|
|
total_bytes_read += bytes_read;
|
|
const char *cstr = data.PeekCStr(0);
|
|
size_t len = strnlen(cstr, k_max_buf_size);
|
|
if (cstr_len_displayed < 0)
|
|
cstr_len_displayed = len;
|
|
|
|
if (len == 0)
|
|
break;
|
|
cstr_len_displayed += len;
|
|
if (len > bytes_read)
|
|
len = bytes_read;
|
|
if (len > cstr_len)
|
|
len = cstr_len;
|
|
|
|
for (size_t offset = 0; offset < bytes_read; offset++)
|
|
s.Printf("%c", *data.PeekData(offset, 1));
|
|
|
|
if (len < k_max_buf_size)
|
|
break;
|
|
|
|
if (len >= cstr_len) {
|
|
capped_cstr = true;
|
|
break;
|
|
}
|
|
|
|
cstr_len -= len;
|
|
offset += len;
|
|
}
|
|
|
|
if (cstr_len_displayed >= 0) {
|
|
if (capped_cstr)
|
|
was_capped = true;
|
|
}
|
|
}
|
|
} else {
|
|
error = Status::FromErrorString("not a string object");
|
|
s << "<not a string object>";
|
|
}
|
|
CopyStringDataToBufferSP(s, buffer_sp);
|
|
return {total_bytes_read, was_capped};
|
|
}
|
|
|
|
llvm::Expected<std::string> ValueObject::GetObjectDescription() {
|
|
if (!UpdateValueIfNeeded(true))
|
|
return llvm::createStringError("could not update value");
|
|
|
|
// Return cached value.
|
|
if (!m_object_desc_str.empty())
|
|
return m_object_desc_str;
|
|
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
if (!process)
|
|
return llvm::createStringError("no process");
|
|
|
|
// Returns the object description produced by one language runtime.
|
|
auto get_object_description =
|
|
[&](LanguageType language) -> llvm::Expected<std::string> {
|
|
if (LanguageRuntime *runtime = process->GetLanguageRuntime(language)) {
|
|
StreamString s;
|
|
if (llvm::Error error = runtime->GetObjectDescription(s, *this))
|
|
return error;
|
|
m_object_desc_str = s.GetString();
|
|
return m_object_desc_str;
|
|
}
|
|
return llvm::createStringError("no native language runtime");
|
|
};
|
|
|
|
// Try the native language runtime first.
|
|
LanguageType native_language = GetObjectRuntimeLanguage();
|
|
llvm::Expected<std::string> desc = get_object_description(native_language);
|
|
if (desc)
|
|
return desc;
|
|
|
|
// Try the Objective-C language runtime. This fallback is necessary
|
|
// for Objective-C++ and mixed Objective-C / C++ programs.
|
|
if (Language::LanguageIsCFamily(native_language)) {
|
|
// We're going to try again, so let's drop the first error.
|
|
llvm::consumeError(desc.takeError());
|
|
return get_object_description(eLanguageTypeObjC);
|
|
}
|
|
return desc;
|
|
}
|
|
|
|
bool ValueObject::GetValueAsCString(const lldb_private::TypeFormatImpl &format,
|
|
std::string &destination) {
|
|
if (UpdateValueIfNeeded(false))
|
|
return format.FormatObject(this, destination);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool ValueObject::GetValueAsCString(lldb::Format format,
|
|
std::string &destination) {
|
|
return GetValueAsCString(TypeFormatImpl_Format(format), destination);
|
|
}
|
|
|
|
const char *ValueObject::GetValueAsCString() {
|
|
if (UpdateValueIfNeeded(true)) {
|
|
lldb::TypeFormatImplSP format_sp;
|
|
lldb::Format my_format = GetFormat();
|
|
if (my_format == lldb::eFormatDefault) {
|
|
if (m_type_format_sp)
|
|
format_sp = m_type_format_sp;
|
|
else {
|
|
if (m_flags.m_is_bitfield_for_scalar)
|
|
my_format = eFormatUnsigned;
|
|
else {
|
|
if (m_value.GetContextType() == Value::ContextType::RegisterInfo) {
|
|
const RegisterInfo *reg_info = m_value.GetRegisterInfo();
|
|
if (reg_info)
|
|
my_format = reg_info->format;
|
|
} else {
|
|
my_format = GetValue().GetCompilerType().GetFormat();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (my_format != m_last_format || m_value_str.empty()) {
|
|
m_last_format = my_format;
|
|
if (!format_sp)
|
|
format_sp = std::make_shared<TypeFormatImpl_Format>(my_format);
|
|
if (GetValueAsCString(*format_sp.get(), m_value_str)) {
|
|
if (!m_flags.m_value_did_change && m_flags.m_old_value_valid) {
|
|
// The value was gotten successfully, so we consider the value as
|
|
// changed if the value string differs
|
|
SetValueDidChange(m_old_value_str != m_value_str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (m_value_str.empty())
|
|
return nullptr;
|
|
return m_value_str.c_str();
|
|
}
|
|
|
|
// if > 8bytes, 0 is returned. this method should mostly be used to read
|
|
// address values out of pointers
|
|
uint64_t ValueObject::GetValueAsUnsigned(uint64_t fail_value, bool *success) {
|
|
// If our byte size is zero this is an aggregate type that has children
|
|
if (CanProvideValue()) {
|
|
Scalar scalar;
|
|
if (ResolveValue(scalar)) {
|
|
if (success)
|
|
*success = true;
|
|
scalar.MakeUnsigned();
|
|
return scalar.ULongLong(fail_value);
|
|
}
|
|
// fallthrough, otherwise...
|
|
}
|
|
|
|
if (success)
|
|
*success = false;
|
|
return fail_value;
|
|
}
|
|
|
|
int64_t ValueObject::GetValueAsSigned(int64_t fail_value, bool *success) {
|
|
// If our byte size is zero this is an aggregate type that has children
|
|
if (CanProvideValue()) {
|
|
Scalar scalar;
|
|
if (ResolveValue(scalar)) {
|
|
if (success)
|
|
*success = true;
|
|
scalar.MakeSigned();
|
|
return scalar.SLongLong(fail_value);
|
|
}
|
|
// fallthrough, otherwise...
|
|
}
|
|
|
|
if (success)
|
|
*success = false;
|
|
return fail_value;
|
|
}
|
|
|
|
llvm::Expected<llvm::APSInt> ValueObject::GetValueAsAPSInt() {
|
|
// Make sure the type can be converted to an APSInt.
|
|
if (!GetCompilerType().IsInteger() &&
|
|
!GetCompilerType().IsScopedEnumerationType() &&
|
|
!GetCompilerType().IsEnumerationType() &&
|
|
!GetCompilerType().IsPointerType() &&
|
|
!GetCompilerType().IsNullPtrType() &&
|
|
!GetCompilerType().IsReferenceType() && !GetCompilerType().IsBoolean())
|
|
return llvm::make_error<llvm::StringError>(
|
|
"type cannot be converted to APSInt", llvm::inconvertibleErrorCode());
|
|
|
|
if (CanProvideValue()) {
|
|
Scalar scalar;
|
|
if (ResolveValue(scalar))
|
|
return scalar.GetAPSInt();
|
|
}
|
|
|
|
return llvm::make_error<llvm::StringError>(
|
|
"error occurred; unable to convert to APSInt",
|
|
llvm::inconvertibleErrorCode());
|
|
}
|
|
|
|
llvm::Expected<llvm::APFloat> ValueObject::GetValueAsAPFloat() {
|
|
if (!GetCompilerType().IsFloat())
|
|
return llvm::make_error<llvm::StringError>(
|
|
"type cannot be converted to APFloat", llvm::inconvertibleErrorCode());
|
|
|
|
if (CanProvideValue()) {
|
|
Scalar scalar;
|
|
if (ResolveValue(scalar))
|
|
return scalar.GetAPFloat();
|
|
}
|
|
|
|
return llvm::make_error<llvm::StringError>(
|
|
"error occurred; unable to convert to APFloat",
|
|
llvm::inconvertibleErrorCode());
|
|
}
|
|
|
|
llvm::Expected<bool> ValueObject::GetValueAsBool() {
|
|
CompilerType val_type = GetCompilerType();
|
|
if (val_type.IsInteger() || val_type.IsUnscopedEnumerationType() ||
|
|
val_type.IsPointerType()) {
|
|
auto value_or_err = GetValueAsAPSInt();
|
|
if (value_or_err)
|
|
return value_or_err->getBoolValue();
|
|
}
|
|
if (val_type.IsFloat()) {
|
|
auto value_or_err = GetValueAsAPFloat();
|
|
if (value_or_err)
|
|
return value_or_err->isNonZero();
|
|
}
|
|
if (val_type.IsArrayType())
|
|
return GetAddressOf() != 0;
|
|
|
|
return llvm::make_error<llvm::StringError>("type cannot be converted to bool",
|
|
llvm::inconvertibleErrorCode());
|
|
}
|
|
|
|
void ValueObject::SetValueFromInteger(const llvm::APInt &value, Status &error) {
|
|
// Verify the current object is an integer object
|
|
CompilerType val_type = GetCompilerType();
|
|
if (!val_type.IsInteger() && !val_type.IsUnscopedEnumerationType() &&
|
|
!val_type.IsFloat() && !val_type.IsPointerType() &&
|
|
!val_type.IsScalarType()) {
|
|
error =
|
|
Status::FromErrorString("current value object is not an integer objet");
|
|
return;
|
|
}
|
|
|
|
// Verify the current object is not actually associated with any program
|
|
// variable.
|
|
if (GetVariable()) {
|
|
error = Status::FromErrorString(
|
|
"current value object is not a temporary object");
|
|
return;
|
|
}
|
|
|
|
// Verify the proposed new value is the right size.
|
|
lldb::TargetSP target = GetTargetSP();
|
|
uint64_t byte_size = 0;
|
|
if (auto temp =
|
|
llvm::expectedToOptional(GetCompilerType().GetByteSize(target.get())))
|
|
byte_size = temp.value();
|
|
if (value.getBitWidth() != byte_size * CHAR_BIT) {
|
|
error = Status::FromErrorString(
|
|
"illegal argument: new value should be of the same size");
|
|
return;
|
|
}
|
|
|
|
lldb::DataExtractorSP data_sp;
|
|
data_sp->SetData(value.getRawData(), byte_size,
|
|
target->GetArchitecture().GetByteOrder());
|
|
data_sp->SetAddressByteSize(
|
|
static_cast<uint8_t>(target->GetArchitecture().GetAddressByteSize()));
|
|
SetData(*data_sp, error);
|
|
}
|
|
|
|
void ValueObject::SetValueFromInteger(lldb::ValueObjectSP new_val_sp,
|
|
Status &error) {
|
|
// Verify the current object is an integer object
|
|
CompilerType val_type = GetCompilerType();
|
|
if (!val_type.IsInteger() && !val_type.IsUnscopedEnumerationType() &&
|
|
!val_type.IsFloat() && !val_type.IsPointerType() &&
|
|
!val_type.IsScalarType()) {
|
|
error =
|
|
Status::FromErrorString("current value object is not an integer objet");
|
|
return;
|
|
}
|
|
|
|
// Verify the current object is not actually associated with any program
|
|
// variable.
|
|
if (GetVariable()) {
|
|
error = Status::FromErrorString(
|
|
"current value object is not a temporary object");
|
|
return;
|
|
}
|
|
|
|
// Verify the proposed new value is the right type.
|
|
CompilerType new_val_type = new_val_sp->GetCompilerType();
|
|
if (!new_val_type.IsInteger() && !new_val_type.IsFloat() &&
|
|
!new_val_type.IsPointerType()) {
|
|
error = Status::FromErrorString(
|
|
"illegal argument: new value should be of the same size");
|
|
return;
|
|
}
|
|
|
|
if (new_val_type.IsInteger()) {
|
|
auto value_or_err = new_val_sp->GetValueAsAPSInt();
|
|
if (value_or_err)
|
|
SetValueFromInteger(*value_or_err, error);
|
|
else
|
|
error = Status::FromErrorString("error getting APSInt from new_val_sp");
|
|
} else if (new_val_type.IsFloat()) {
|
|
auto value_or_err = new_val_sp->GetValueAsAPFloat();
|
|
if (value_or_err)
|
|
SetValueFromInteger(value_or_err->bitcastToAPInt(), error);
|
|
else
|
|
error = Status::FromErrorString("error getting APFloat from new_val_sp");
|
|
} else if (new_val_type.IsPointerType()) {
|
|
bool success = true;
|
|
uint64_t int_val = new_val_sp->GetValueAsUnsigned(0, &success);
|
|
if (success) {
|
|
lldb::TargetSP target = GetTargetSP();
|
|
uint64_t num_bits = 0;
|
|
if (auto temp = llvm::expectedToOptional(
|
|
new_val_sp->GetCompilerType().GetBitSize(target.get())))
|
|
num_bits = temp.value();
|
|
SetValueFromInteger(llvm::APInt(num_bits, int_val), error);
|
|
} else
|
|
error = Status::FromErrorString("error converting new_val_sp to integer");
|
|
}
|
|
}
|
|
|
|
// if any more "special cases" are added to
|
|
// ValueObject::DumpPrintableRepresentation() please keep this call up to date
|
|
// by returning true for your new special cases. We will eventually move to
|
|
// checking this call result before trying to display special cases
|
|
bool ValueObject::HasSpecialPrintableRepresentation(
|
|
ValueObjectRepresentationStyle val_obj_display, Format custom_format) {
|
|
Flags flags(GetTypeInfo());
|
|
if (flags.AnySet(eTypeIsArray | eTypeIsPointer) &&
|
|
val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) {
|
|
if (IsCStringContainer(true) &&
|
|
(custom_format == eFormatCString || custom_format == eFormatCharArray ||
|
|
custom_format == eFormatChar || custom_format == eFormatVectorOfChar))
|
|
return true;
|
|
|
|
if (flags.Test(eTypeIsArray)) {
|
|
if ((custom_format == eFormatBytes) ||
|
|
(custom_format == eFormatBytesWithASCII))
|
|
return true;
|
|
|
|
if ((custom_format == eFormatVectorOfChar) ||
|
|
(custom_format == eFormatVectorOfFloat32) ||
|
|
(custom_format == eFormatVectorOfFloat64) ||
|
|
(custom_format == eFormatVectorOfSInt16) ||
|
|
(custom_format == eFormatVectorOfSInt32) ||
|
|
(custom_format == eFormatVectorOfSInt64) ||
|
|
(custom_format == eFormatVectorOfSInt8) ||
|
|
(custom_format == eFormatVectorOfUInt128) ||
|
|
(custom_format == eFormatVectorOfUInt16) ||
|
|
(custom_format == eFormatVectorOfUInt32) ||
|
|
(custom_format == eFormatVectorOfUInt64) ||
|
|
(custom_format == eFormatVectorOfUInt8))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ValueObject::DumpPrintableRepresentation(
|
|
Stream &s, ValueObjectRepresentationStyle val_obj_display,
|
|
Format custom_format, PrintableRepresentationSpecialCases special,
|
|
bool do_dump_error) {
|
|
|
|
// If the ValueObject has an error, we might end up dumping the type, which
|
|
// is useful, but if we don't even have a type, then don't examine the object
|
|
// further as that's not meaningful, only the error is.
|
|
if (m_error.Fail() && !GetCompilerType().IsValid()) {
|
|
if (do_dump_error)
|
|
s.Printf("<%s>", m_error.AsCString());
|
|
return false;
|
|
}
|
|
|
|
Flags flags(GetTypeInfo());
|
|
|
|
bool allow_special =
|
|
(special == ValueObject::PrintableRepresentationSpecialCases::eAllow);
|
|
const bool only_special = false;
|
|
|
|
if (allow_special) {
|
|
if (flags.AnySet(eTypeIsArray | eTypeIsPointer) &&
|
|
val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) {
|
|
// when being asked to get a printable display an array or pointer type
|
|
// directly, try to "do the right thing"
|
|
|
|
if (IsCStringContainer(true) &&
|
|
(custom_format == eFormatCString ||
|
|
custom_format == eFormatCharArray || custom_format == eFormatChar ||
|
|
custom_format ==
|
|
eFormatVectorOfChar)) // print char[] & char* directly
|
|
{
|
|
Status error;
|
|
lldb::WritableDataBufferSP buffer_sp;
|
|
std::pair<size_t, bool> read_string =
|
|
ReadPointedString(buffer_sp, error,
|
|
(custom_format == eFormatVectorOfChar) ||
|
|
(custom_format == eFormatCharArray));
|
|
lldb_private::formatters::StringPrinter::
|
|
ReadBufferAndDumpToStreamOptions options(*this);
|
|
options.SetData(DataExtractor(
|
|
buffer_sp, lldb::eByteOrderInvalid,
|
|
8)); // none of this matters for a string - pass some defaults
|
|
options.SetStream(&s);
|
|
options.SetPrefixToken(nullptr);
|
|
options.SetQuote('"');
|
|
options.SetSourceSize(buffer_sp->GetByteSize());
|
|
options.SetIsTruncated(read_string.second);
|
|
options.SetBinaryZeroIsTerminator(custom_format != eFormatVectorOfChar);
|
|
formatters::StringPrinter::ReadBufferAndDumpToStream<
|
|
lldb_private::formatters::StringPrinter::StringElementType::ASCII>(
|
|
options);
|
|
return !error.Fail();
|
|
}
|
|
|
|
if (custom_format == eFormatEnum)
|
|
return false;
|
|
|
|
// this only works for arrays, because I have no way to know when the
|
|
// pointed memory ends, and no special \0 end of data marker
|
|
if (flags.Test(eTypeIsArray)) {
|
|
if ((custom_format == eFormatBytes) ||
|
|
(custom_format == eFormatBytesWithASCII)) {
|
|
const size_t count = GetNumChildrenIgnoringErrors();
|
|
|
|
s << '[';
|
|
for (size_t low = 0; low < count; low++) {
|
|
|
|
if (low)
|
|
s << ',';
|
|
|
|
ValueObjectSP child = GetChildAtIndex(low);
|
|
if (!child.get()) {
|
|
s << "<invalid child>";
|
|
continue;
|
|
}
|
|
child->DumpPrintableRepresentation(
|
|
s, ValueObject::eValueObjectRepresentationStyleValue,
|
|
custom_format);
|
|
}
|
|
|
|
s << ']';
|
|
|
|
return true;
|
|
}
|
|
|
|
if ((custom_format == eFormatVectorOfChar) ||
|
|
(custom_format == eFormatVectorOfFloat32) ||
|
|
(custom_format == eFormatVectorOfFloat64) ||
|
|
(custom_format == eFormatVectorOfSInt16) ||
|
|
(custom_format == eFormatVectorOfSInt32) ||
|
|
(custom_format == eFormatVectorOfSInt64) ||
|
|
(custom_format == eFormatVectorOfSInt8) ||
|
|
(custom_format == eFormatVectorOfUInt128) ||
|
|
(custom_format == eFormatVectorOfUInt16) ||
|
|
(custom_format == eFormatVectorOfUInt32) ||
|
|
(custom_format == eFormatVectorOfUInt64) ||
|
|
(custom_format == eFormatVectorOfUInt8)) // arrays of bytes, bytes
|
|
// with ASCII or any vector
|
|
// format should be printed
|
|
// directly
|
|
{
|
|
const size_t count = GetNumChildrenIgnoringErrors();
|
|
|
|
Format format = FormatManager::GetSingleItemFormat(custom_format);
|
|
|
|
s << '[';
|
|
for (size_t low = 0; low < count; low++) {
|
|
|
|
if (low)
|
|
s << ',';
|
|
|
|
ValueObjectSP child = GetChildAtIndex(low);
|
|
if (!child.get()) {
|
|
s << "<invalid child>";
|
|
continue;
|
|
}
|
|
child->DumpPrintableRepresentation(
|
|
s, ValueObject::eValueObjectRepresentationStyleValue, format);
|
|
}
|
|
|
|
s << ']';
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if ((custom_format == eFormatBoolean) ||
|
|
(custom_format == eFormatBinary) || (custom_format == eFormatChar) ||
|
|
(custom_format == eFormatCharPrintable) ||
|
|
(custom_format == eFormatComplexFloat) ||
|
|
(custom_format == eFormatDecimal) || (custom_format == eFormatHex) ||
|
|
(custom_format == eFormatHexUppercase) ||
|
|
(custom_format == eFormatFloat) || (custom_format == eFormatOctal) ||
|
|
(custom_format == eFormatOSType) ||
|
|
(custom_format == eFormatUnicode16) ||
|
|
(custom_format == eFormatUnicode32) ||
|
|
(custom_format == eFormatUnsigned) ||
|
|
(custom_format == eFormatPointer) ||
|
|
(custom_format == eFormatComplexInteger) ||
|
|
(custom_format == eFormatComplex) ||
|
|
(custom_format == eFormatDefault)) // use the [] operator
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (only_special)
|
|
return false;
|
|
|
|
bool var_success = false;
|
|
|
|
{
|
|
llvm::StringRef str;
|
|
|
|
// this is a local stream that we are using to ensure that the data pointed
|
|
// to by cstr survives long enough for us to copy it to its destination -
|
|
// it is necessary to have this temporary storage area for cases where our
|
|
// desired output is not backed by some other longer-term storage
|
|
StreamString strm;
|
|
|
|
if (custom_format != eFormatInvalid)
|
|
SetFormat(custom_format);
|
|
|
|
switch (val_obj_display) {
|
|
case eValueObjectRepresentationStyleValue:
|
|
str = GetValueAsCString();
|
|
break;
|
|
|
|
case eValueObjectRepresentationStyleSummary:
|
|
str = GetSummaryAsCString();
|
|
break;
|
|
|
|
case eValueObjectRepresentationStyleLanguageSpecific: {
|
|
llvm::Expected<std::string> desc = GetObjectDescription();
|
|
if (!desc) {
|
|
strm << "error: " << toString(desc.takeError());
|
|
str = strm.GetString();
|
|
} else {
|
|
strm << *desc;
|
|
str = strm.GetString();
|
|
}
|
|
} break;
|
|
|
|
case eValueObjectRepresentationStyleLocation:
|
|
str = GetLocationAsCString();
|
|
break;
|
|
|
|
case eValueObjectRepresentationStyleChildrenCount:
|
|
strm.Printf("%" PRIu64 "", (uint64_t)GetNumChildrenIgnoringErrors());
|
|
str = strm.GetString();
|
|
break;
|
|
|
|
case eValueObjectRepresentationStyleType:
|
|
str = GetTypeName().GetStringRef();
|
|
break;
|
|
|
|
case eValueObjectRepresentationStyleName:
|
|
str = GetName().GetStringRef();
|
|
break;
|
|
|
|
case eValueObjectRepresentationStyleExpressionPath:
|
|
GetExpressionPath(strm);
|
|
str = strm.GetString();
|
|
break;
|
|
}
|
|
|
|
// If the requested display style produced no output, try falling back to
|
|
// alternative presentations.
|
|
if (str.empty()) {
|
|
if (val_obj_display == eValueObjectRepresentationStyleValue)
|
|
str = GetSummaryAsCString();
|
|
else if (val_obj_display == eValueObjectRepresentationStyleSummary) {
|
|
if (!CanProvideValue()) {
|
|
strm.Printf("%s @ %s", GetTypeName().AsCString(),
|
|
GetLocationAsCString());
|
|
str = strm.GetString();
|
|
} else
|
|
str = GetValueAsCString();
|
|
}
|
|
}
|
|
|
|
if (!str.empty())
|
|
s << str;
|
|
else {
|
|
// We checked for errors at the start, but do it again here in case
|
|
// realizing the value for dumping produced an error.
|
|
if (m_error.Fail()) {
|
|
if (do_dump_error)
|
|
s.Printf("<%s>", m_error.AsCString());
|
|
else
|
|
return false;
|
|
} else if (val_obj_display == eValueObjectRepresentationStyleSummary)
|
|
s.PutCString("<no summary available>");
|
|
else if (val_obj_display == eValueObjectRepresentationStyleValue)
|
|
s.PutCString("<no value available>");
|
|
else if (val_obj_display ==
|
|
eValueObjectRepresentationStyleLanguageSpecific)
|
|
s.PutCString("<not a valid Objective-C object>"); // edit this if we
|
|
// have other runtimes
|
|
// that support a
|
|
// description
|
|
else
|
|
s.PutCString("<no printable representation>");
|
|
}
|
|
|
|
// we should only return false here if we could not do *anything* even if
|
|
// we have an error message as output, that's a success from our callers'
|
|
// perspective, so return true
|
|
var_success = true;
|
|
|
|
if (custom_format != eFormatInvalid)
|
|
SetFormat(eFormatDefault);
|
|
}
|
|
|
|
return var_success;
|
|
}
|
|
|
|
addr_t ValueObject::GetAddressOf(bool scalar_is_load_address,
|
|
AddressType *address_type) {
|
|
// Can't take address of a bitfield
|
|
if (IsBitfield())
|
|
return LLDB_INVALID_ADDRESS;
|
|
|
|
if (!UpdateValueIfNeeded(false))
|
|
return LLDB_INVALID_ADDRESS;
|
|
|
|
switch (m_value.GetValueType()) {
|
|
case Value::ValueType::Invalid:
|
|
return LLDB_INVALID_ADDRESS;
|
|
case Value::ValueType::Scalar:
|
|
if (scalar_is_load_address) {
|
|
if (address_type)
|
|
*address_type = eAddressTypeLoad;
|
|
return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
}
|
|
break;
|
|
|
|
case Value::ValueType::LoadAddress:
|
|
case Value::ValueType::FileAddress: {
|
|
if (address_type)
|
|
*address_type = m_value.GetValueAddressType();
|
|
return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
} break;
|
|
case Value::ValueType::HostAddress: {
|
|
if (address_type)
|
|
*address_type = m_value.GetValueAddressType();
|
|
return LLDB_INVALID_ADDRESS;
|
|
} break;
|
|
}
|
|
if (address_type)
|
|
*address_type = eAddressTypeInvalid;
|
|
return LLDB_INVALID_ADDRESS;
|
|
}
|
|
|
|
addr_t ValueObject::GetPointerValue(AddressType *address_type) {
|
|
addr_t address = LLDB_INVALID_ADDRESS;
|
|
if (address_type)
|
|
*address_type = eAddressTypeInvalid;
|
|
|
|
if (!UpdateValueIfNeeded(false))
|
|
return address;
|
|
|
|
switch (m_value.GetValueType()) {
|
|
case Value::ValueType::Invalid:
|
|
return LLDB_INVALID_ADDRESS;
|
|
case Value::ValueType::Scalar:
|
|
address = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
break;
|
|
|
|
case Value::ValueType::HostAddress:
|
|
case Value::ValueType::LoadAddress:
|
|
case Value::ValueType::FileAddress: {
|
|
lldb::offset_t data_offset = 0;
|
|
address = m_data.GetAddress(&data_offset);
|
|
} break;
|
|
}
|
|
|
|
if (address_type)
|
|
*address_type = GetAddressTypeOfChildren();
|
|
|
|
return address;
|
|
}
|
|
|
|
static const char *ConvertBoolean(lldb::LanguageType language_type,
|
|
const char *value_str) {
|
|
if (Language *language = Language::FindPlugin(language_type))
|
|
if (auto boolean = language->GetBooleanFromString(value_str))
|
|
return *boolean ? "1" : "0";
|
|
|
|
return llvm::StringSwitch<const char *>(value_str)
|
|
.Case("true", "1")
|
|
.Case("false", "0")
|
|
.Default(value_str);
|
|
}
|
|
|
|
bool ValueObject::SetValueFromCString(const char *value_str, Status &error) {
|
|
error.Clear();
|
|
// Make sure our value is up to date first so that our location and location
|
|
// type is valid.
|
|
if (!UpdateValueIfNeeded(false)) {
|
|
error = Status::FromErrorString("unable to read value");
|
|
return false;
|
|
}
|
|
|
|
uint64_t count = 0;
|
|
const Encoding encoding = GetCompilerType().GetEncoding(count);
|
|
|
|
const size_t byte_size = llvm::expectedToOptional(GetByteSize()).value_or(0);
|
|
|
|
Value::ValueType value_type = m_value.GetValueType();
|
|
|
|
if (value_type == Value::ValueType::Scalar) {
|
|
// If the value is already a scalar, then let the scalar change itself:
|
|
m_value.GetScalar().SetValueFromCString(value_str, encoding, byte_size);
|
|
} else if (byte_size <= 16) {
|
|
if (GetCompilerType().IsBoolean())
|
|
value_str = ConvertBoolean(GetObjectRuntimeLanguage(), value_str);
|
|
|
|
// If the value fits in a scalar, then make a new scalar and again let the
|
|
// scalar code do the conversion, then figure out where to put the new
|
|
// value.
|
|
Scalar new_scalar;
|
|
error = new_scalar.SetValueFromCString(value_str, encoding, byte_size);
|
|
if (error.Success()) {
|
|
switch (value_type) {
|
|
case Value::ValueType::LoadAddress: {
|
|
// If it is a load address, then the scalar value is the storage
|
|
// location of the data, and we have to shove this value down to that
|
|
// load location.
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
if (process) {
|
|
addr_t target_addr =
|
|
m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
size_t bytes_written = process->WriteScalarToMemory(
|
|
target_addr, new_scalar, byte_size, error);
|
|
if (!error.Success())
|
|
return false;
|
|
if (bytes_written != byte_size) {
|
|
error = Status::FromErrorString("unable to write value to memory");
|
|
return false;
|
|
}
|
|
}
|
|
} break;
|
|
case Value::ValueType::HostAddress: {
|
|
// If it is a host address, then we stuff the scalar as a DataBuffer
|
|
// into the Value's data.
|
|
DataExtractor new_data;
|
|
new_data.SetByteOrder(m_data.GetByteOrder());
|
|
|
|
DataBufferSP buffer_sp(new DataBufferHeap(byte_size, 0));
|
|
m_data.SetData(buffer_sp, 0);
|
|
bool success = new_scalar.GetData(new_data);
|
|
if (success) {
|
|
new_data.CopyByteOrderedData(
|
|
0, byte_size, const_cast<uint8_t *>(m_data.GetDataStart()),
|
|
byte_size, m_data.GetByteOrder());
|
|
}
|
|
m_value.GetScalar() = (uintptr_t)m_data.GetDataStart();
|
|
|
|
} break;
|
|
case Value::ValueType::Invalid:
|
|
error = Status::FromErrorString("invalid location");
|
|
return false;
|
|
case Value::ValueType::FileAddress:
|
|
case Value::ValueType::Scalar:
|
|
break;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
// We don't support setting things bigger than a scalar at present.
|
|
error = Status::FromErrorString("unable to write aggregate data type");
|
|
return false;
|
|
}
|
|
|
|
// If we have reached this point, then we have successfully changed the
|
|
// value.
|
|
SetNeedsUpdate();
|
|
return true;
|
|
}
|
|
|
|
bool ValueObject::GetDeclaration(Declaration &decl) {
|
|
decl.Clear();
|
|
return false;
|
|
}
|
|
|
|
void ValueObject::AddSyntheticChild(ConstString key, ValueObject *valobj) {
|
|
m_synthetic_children[key] = valobj;
|
|
}
|
|
|
|
ValueObjectSP ValueObject::GetSyntheticChild(ConstString key) const {
|
|
ValueObjectSP synthetic_child_sp;
|
|
std::map<ConstString, ValueObject *>::const_iterator pos =
|
|
m_synthetic_children.find(key);
|
|
if (pos != m_synthetic_children.end())
|
|
synthetic_child_sp = pos->second->GetSP();
|
|
return synthetic_child_sp;
|
|
}
|
|
|
|
bool ValueObject::IsPossibleDynamicType() {
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
if (process)
|
|
return process->IsPossibleDynamicValue(*this);
|
|
else
|
|
return GetCompilerType().IsPossibleDynamicType(nullptr, true, true);
|
|
}
|
|
|
|
bool ValueObject::IsRuntimeSupportValue() {
|
|
Process *process(GetProcessSP().get());
|
|
if (!process)
|
|
return false;
|
|
|
|
// We trust that the compiler did the right thing and marked runtime support
|
|
// values as artificial.
|
|
if (!GetVariable() || !GetVariable()->IsArtificial())
|
|
return false;
|
|
|
|
if (auto *runtime = process->GetLanguageRuntime(GetVariable()->GetLanguage()))
|
|
if (runtime->IsAllowedRuntimeValue(GetName()))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ValueObject::IsNilReference() {
|
|
if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) {
|
|
return language->IsNilReference(*this);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ValueObject::IsUninitializedReference() {
|
|
if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) {
|
|
return language->IsUninitializedReference(*this);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// This allows you to create an array member using and index that doesn't not
|
|
// fall in the normal bounds of the array. Many times structure can be defined
|
|
// as: struct Collection {
|
|
// uint32_t item_count;
|
|
// Item item_array[0];
|
|
// };
|
|
// The size of the "item_array" is 1, but many times in practice there are more
|
|
// items in "item_array".
|
|
|
|
ValueObjectSP ValueObject::GetSyntheticArrayMember(size_t index,
|
|
bool can_create) {
|
|
ValueObjectSP synthetic_child_sp;
|
|
if (IsPointerType() || IsArrayType()) {
|
|
std::string index_str = llvm::formatv("[{0}]", index);
|
|
ConstString index_const_str(index_str);
|
|
// Check if we have already created a synthetic array member in this valid
|
|
// object. If we have we will re-use it.
|
|
synthetic_child_sp = GetSyntheticChild(index_const_str);
|
|
if (!synthetic_child_sp) {
|
|
ValueObject *synthetic_child;
|
|
// We haven't made a synthetic array member for INDEX yet, so lets make
|
|
// one and cache it for any future reference.
|
|
synthetic_child = CreateSyntheticArrayMember(index);
|
|
|
|
// Cache the value if we got one back...
|
|
if (synthetic_child) {
|
|
AddSyntheticChild(index_const_str, synthetic_child);
|
|
synthetic_child_sp = synthetic_child->GetSP();
|
|
synthetic_child_sp->SetName(ConstString(index_str));
|
|
synthetic_child_sp->m_flags.m_is_array_item_for_pointer = true;
|
|
}
|
|
}
|
|
}
|
|
return synthetic_child_sp;
|
|
}
|
|
|
|
ValueObjectSP ValueObject::GetSyntheticBitFieldChild(uint32_t from, uint32_t to,
|
|
bool can_create) {
|
|
ValueObjectSP synthetic_child_sp;
|
|
if (IsScalarType()) {
|
|
std::string index_str = llvm::formatv("[{0}-{1}]", from, to);
|
|
ConstString index_const_str(index_str);
|
|
// Check if we have already created a synthetic array member in this valid
|
|
// object. If we have we will re-use it.
|
|
synthetic_child_sp = GetSyntheticChild(index_const_str);
|
|
if (!synthetic_child_sp) {
|
|
uint32_t bit_field_size = to - from + 1;
|
|
uint32_t bit_field_offset = from;
|
|
if (GetDataExtractor().GetByteOrder() == eByteOrderBig)
|
|
bit_field_offset =
|
|
llvm::expectedToOptional(GetByteSize()).value_or(0) * 8 -
|
|
bit_field_size - bit_field_offset;
|
|
// We haven't made a synthetic array member for INDEX yet, so lets make
|
|
// one and cache it for any future reference.
|
|
ValueObjectChild *synthetic_child = new ValueObjectChild(
|
|
*this, GetCompilerType(), index_const_str,
|
|
llvm::expectedToOptional(GetByteSize()).value_or(0), 0,
|
|
bit_field_size, bit_field_offset, false, false, eAddressTypeInvalid,
|
|
0);
|
|
|
|
// Cache the value if we got one back...
|
|
if (synthetic_child) {
|
|
AddSyntheticChild(index_const_str, synthetic_child);
|
|
synthetic_child_sp = synthetic_child->GetSP();
|
|
synthetic_child_sp->SetName(ConstString(index_str));
|
|
synthetic_child_sp->m_flags.m_is_bitfield_for_scalar = true;
|
|
}
|
|
}
|
|
}
|
|
return synthetic_child_sp;
|
|
}
|
|
|
|
ValueObjectSP ValueObject::GetSyntheticChildAtOffset(
|
|
uint32_t offset, const CompilerType &type, bool can_create,
|
|
ConstString name_const_str) {
|
|
|
|
ValueObjectSP synthetic_child_sp;
|
|
|
|
if (name_const_str.IsEmpty()) {
|
|
name_const_str.SetString("@" + std::to_string(offset));
|
|
}
|
|
|
|
// Check if we have already created a synthetic array member in this valid
|
|
// object. If we have we will re-use it.
|
|
synthetic_child_sp = GetSyntheticChild(name_const_str);
|
|
|
|
if (synthetic_child_sp.get())
|
|
return synthetic_child_sp;
|
|
|
|
if (!can_create)
|
|
return {};
|
|
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
std::optional<uint64_t> size = llvm::expectedToOptional(
|
|
type.GetByteSize(exe_ctx.GetBestExecutionContextScope()));
|
|
if (!size)
|
|
return {};
|
|
ValueObjectChild *synthetic_child =
|
|
new ValueObjectChild(*this, type, name_const_str, *size, offset, 0, 0,
|
|
false, false, eAddressTypeInvalid, 0);
|
|
if (synthetic_child) {
|
|
AddSyntheticChild(name_const_str, synthetic_child);
|
|
synthetic_child_sp = synthetic_child->GetSP();
|
|
synthetic_child_sp->SetName(name_const_str);
|
|
synthetic_child_sp->m_flags.m_is_child_at_offset = true;
|
|
}
|
|
return synthetic_child_sp;
|
|
}
|
|
|
|
ValueObjectSP ValueObject::GetSyntheticBase(uint32_t offset,
|
|
const CompilerType &type,
|
|
bool can_create,
|
|
ConstString name_const_str) {
|
|
ValueObjectSP synthetic_child_sp;
|
|
|
|
if (name_const_str.IsEmpty()) {
|
|
char name_str[128];
|
|
snprintf(name_str, sizeof(name_str), "base%s@%i",
|
|
type.GetTypeName().AsCString("<unknown>"), offset);
|
|
name_const_str.SetCString(name_str);
|
|
}
|
|
|
|
// Check if we have already created a synthetic array member in this valid
|
|
// object. If we have we will re-use it.
|
|
synthetic_child_sp = GetSyntheticChild(name_const_str);
|
|
|
|
if (synthetic_child_sp.get())
|
|
return synthetic_child_sp;
|
|
|
|
if (!can_create)
|
|
return {};
|
|
|
|
const bool is_base_class = true;
|
|
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
std::optional<uint64_t> size = llvm::expectedToOptional(
|
|
type.GetByteSize(exe_ctx.GetBestExecutionContextScope()));
|
|
if (!size)
|
|
return {};
|
|
ValueObjectChild *synthetic_child =
|
|
new ValueObjectChild(*this, type, name_const_str, *size, offset, 0, 0,
|
|
is_base_class, false, eAddressTypeInvalid, 0);
|
|
if (synthetic_child) {
|
|
AddSyntheticChild(name_const_str, synthetic_child);
|
|
synthetic_child_sp = synthetic_child->GetSP();
|
|
synthetic_child_sp->SetName(name_const_str);
|
|
}
|
|
return synthetic_child_sp;
|
|
}
|
|
|
|
// your expression path needs to have a leading . or -> (unless it somehow
|
|
// "looks like" an array, in which case it has a leading [ symbol). while the [
|
|
// is meaningful and should be shown to the user, . and -> are just parser
|
|
// design, but by no means added information for the user.. strip them off
|
|
static const char *SkipLeadingExpressionPathSeparators(const char *expression) {
|
|
if (!expression || !expression[0])
|
|
return expression;
|
|
if (expression[0] == '.')
|
|
return expression + 1;
|
|
if (expression[0] == '-' && expression[1] == '>')
|
|
return expression + 2;
|
|
return expression;
|
|
}
|
|
|
|
ValueObjectSP
|
|
ValueObject::GetSyntheticExpressionPathChild(const char *expression,
|
|
bool can_create) {
|
|
ValueObjectSP synthetic_child_sp;
|
|
ConstString name_const_string(expression);
|
|
// Check if we have already created a synthetic array member in this valid
|
|
// object. If we have we will re-use it.
|
|
synthetic_child_sp = GetSyntheticChild(name_const_string);
|
|
if (!synthetic_child_sp) {
|
|
// We haven't made a synthetic array member for expression yet, so lets
|
|
// make one and cache it for any future reference.
|
|
synthetic_child_sp = GetValueForExpressionPath(
|
|
expression, nullptr, nullptr,
|
|
GetValueForExpressionPathOptions().SetSyntheticChildrenTraversal(
|
|
GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
|
|
None));
|
|
|
|
// Cache the value if we got one back...
|
|
if (synthetic_child_sp.get()) {
|
|
// FIXME: this causes a "real" child to end up with its name changed to
|
|
// the contents of expression
|
|
AddSyntheticChild(name_const_string, synthetic_child_sp.get());
|
|
synthetic_child_sp->SetName(
|
|
ConstString(SkipLeadingExpressionPathSeparators(expression)));
|
|
}
|
|
}
|
|
return synthetic_child_sp;
|
|
}
|
|
|
|
void ValueObject::CalculateSyntheticValue() {
|
|
TargetSP target_sp(GetTargetSP());
|
|
if (target_sp && !target_sp->GetEnableSyntheticValue()) {
|
|
m_synthetic_value = nullptr;
|
|
return;
|
|
}
|
|
|
|
lldb::SyntheticChildrenSP current_synth_sp(m_synthetic_children_sp);
|
|
|
|
if (!UpdateFormatsIfNeeded() && m_synthetic_value)
|
|
return;
|
|
|
|
if (m_synthetic_children_sp.get() == nullptr)
|
|
return;
|
|
|
|
if (current_synth_sp == m_synthetic_children_sp && m_synthetic_value)
|
|
return;
|
|
|
|
m_synthetic_value = new ValueObjectSynthetic(*this, m_synthetic_children_sp);
|
|
}
|
|
|
|
void ValueObject::CalculateDynamicValue(DynamicValueType use_dynamic) {
|
|
if (use_dynamic == eNoDynamicValues)
|
|
return;
|
|
|
|
if (!m_dynamic_value && !IsDynamic()) {
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
if (process && process->IsPossibleDynamicValue(*this)) {
|
|
ClearDynamicTypeInformation();
|
|
m_dynamic_value = new ValueObjectDynamicValue(*this, use_dynamic);
|
|
}
|
|
}
|
|
}
|
|
|
|
ValueObjectSP ValueObject::GetDynamicValue(DynamicValueType use_dynamic) {
|
|
if (use_dynamic == eNoDynamicValues)
|
|
return ValueObjectSP();
|
|
|
|
if (!IsDynamic() && m_dynamic_value == nullptr) {
|
|
CalculateDynamicValue(use_dynamic);
|
|
}
|
|
if (m_dynamic_value && m_dynamic_value->GetError().Success())
|
|
return m_dynamic_value->GetSP();
|
|
else
|
|
return ValueObjectSP();
|
|
}
|
|
|
|
ValueObjectSP ValueObject::GetSyntheticValue() {
|
|
CalculateSyntheticValue();
|
|
|
|
if (m_synthetic_value)
|
|
return m_synthetic_value->GetSP();
|
|
else
|
|
return ValueObjectSP();
|
|
}
|
|
|
|
bool ValueObject::HasSyntheticValue() {
|
|
UpdateFormatsIfNeeded();
|
|
|
|
if (m_synthetic_children_sp.get() == nullptr)
|
|
return false;
|
|
|
|
CalculateSyntheticValue();
|
|
|
|
return m_synthetic_value != nullptr;
|
|
}
|
|
|
|
ValueObject *ValueObject::GetNonBaseClassParent() {
|
|
if (GetParent()) {
|
|
if (GetParent()->IsBaseClass())
|
|
return GetParent()->GetNonBaseClassParent();
|
|
else
|
|
return GetParent();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool ValueObject::IsBaseClass(uint32_t &depth) {
|
|
if (!IsBaseClass()) {
|
|
depth = 0;
|
|
return false;
|
|
}
|
|
if (GetParent()) {
|
|
GetParent()->IsBaseClass(depth);
|
|
depth = depth + 1;
|
|
return true;
|
|
}
|
|
// TODO: a base of no parent? weird..
|
|
depth = 1;
|
|
return true;
|
|
}
|
|
|
|
void ValueObject::GetExpressionPath(Stream &s,
|
|
GetExpressionPathFormat epformat) {
|
|
// synthetic children do not actually "exist" as part of the hierarchy, and
|
|
// sometimes they are consed up in ways that don't make sense from an
|
|
// underlying language/API standpoint. So, use a special code path here to
|
|
// return something that can hopefully be used in expression
|
|
if (m_flags.m_is_synthetic_children_generated) {
|
|
UpdateValueIfNeeded();
|
|
|
|
if (m_value.GetValueType() == Value::ValueType::LoadAddress) {
|
|
if (IsPointerOrReferenceType()) {
|
|
s.Printf("((%s)0x%" PRIx64 ")", GetTypeName().AsCString("void"),
|
|
GetValueAsUnsigned(0));
|
|
return;
|
|
} else {
|
|
uint64_t load_addr =
|
|
m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
if (load_addr != LLDB_INVALID_ADDRESS) {
|
|
s.Printf("(*( (%s *)0x%" PRIx64 "))", GetTypeName().AsCString("void"),
|
|
load_addr);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CanProvideValue()) {
|
|
s.Printf("((%s)%s)", GetTypeName().AsCString("void"),
|
|
GetValueAsCString());
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
const bool is_deref_of_parent = IsDereferenceOfParent();
|
|
|
|
if (is_deref_of_parent &&
|
|
epformat == eGetExpressionPathFormatDereferencePointers) {
|
|
// this is the original format of GetExpressionPath() producing code like
|
|
// *(a_ptr).memberName, which is entirely fine, until you put this into
|
|
// StackFrame::GetValueForVariableExpressionPath() which prefers to see
|
|
// a_ptr->memberName. the eHonorPointers mode is meant to produce strings
|
|
// in this latter format
|
|
s.PutCString("*(");
|
|
}
|
|
|
|
ValueObject *parent = GetParent();
|
|
|
|
if (parent)
|
|
parent->GetExpressionPath(s, epformat);
|
|
|
|
// if we are a deref_of_parent just because we are synthetic array members
|
|
// made up to allow ptr[%d] syntax to work in variable printing, then add our
|
|
// name ([%d]) to the expression path
|
|
if (m_flags.m_is_array_item_for_pointer &&
|
|
epformat == eGetExpressionPathFormatHonorPointers)
|
|
s.PutCString(m_name.GetStringRef());
|
|
|
|
if (!IsBaseClass()) {
|
|
if (!is_deref_of_parent) {
|
|
ValueObject *non_base_class_parent = GetNonBaseClassParent();
|
|
if (non_base_class_parent &&
|
|
!non_base_class_parent->GetName().IsEmpty()) {
|
|
CompilerType non_base_class_parent_compiler_type =
|
|
non_base_class_parent->GetCompilerType();
|
|
if (non_base_class_parent_compiler_type) {
|
|
if (parent && parent->IsDereferenceOfParent() &&
|
|
epformat == eGetExpressionPathFormatHonorPointers) {
|
|
s.PutCString("->");
|
|
} else {
|
|
const uint32_t non_base_class_parent_type_info =
|
|
non_base_class_parent_compiler_type.GetTypeInfo();
|
|
|
|
if (non_base_class_parent_type_info & eTypeIsPointer) {
|
|
s.PutCString("->");
|
|
} else if ((non_base_class_parent_type_info & eTypeHasChildren) &&
|
|
!(non_base_class_parent_type_info & eTypeIsArray)) {
|
|
s.PutChar('.');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const char *name = GetName().GetCString();
|
|
if (name)
|
|
s.PutCString(name);
|
|
}
|
|
}
|
|
|
|
if (is_deref_of_parent &&
|
|
epformat == eGetExpressionPathFormatDereferencePointers) {
|
|
s.PutChar(')');
|
|
}
|
|
}
|
|
|
|
ValueObjectSP ValueObject::GetValueForExpressionPath(
|
|
llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop,
|
|
ExpressionPathEndResultType *final_value_type,
|
|
const GetValueForExpressionPathOptions &options,
|
|
ExpressionPathAftermath *final_task_on_target) {
|
|
|
|
ExpressionPathScanEndReason dummy_reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonUnknown;
|
|
ExpressionPathEndResultType dummy_final_value_type =
|
|
ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
ExpressionPathAftermath dummy_final_task_on_target =
|
|
ValueObject::eExpressionPathAftermathNothing;
|
|
|
|
ValueObjectSP ret_val = GetValueForExpressionPath_Impl(
|
|
expression, reason_to_stop ? reason_to_stop : &dummy_reason_to_stop,
|
|
final_value_type ? final_value_type : &dummy_final_value_type, options,
|
|
final_task_on_target ? final_task_on_target
|
|
: &dummy_final_task_on_target);
|
|
|
|
if (!final_task_on_target ||
|
|
*final_task_on_target == ValueObject::eExpressionPathAftermathNothing)
|
|
return ret_val;
|
|
|
|
if (ret_val.get() &&
|
|
((final_value_type ? *final_value_type : dummy_final_value_type) ==
|
|
eExpressionPathEndResultTypePlain)) // I can only deref and takeaddress
|
|
// of plain objects
|
|
{
|
|
if ((final_task_on_target ? *final_task_on_target
|
|
: dummy_final_task_on_target) ==
|
|
ValueObject::eExpressionPathAftermathDereference) {
|
|
Status error;
|
|
ValueObjectSP final_value = ret_val->Dereference(error);
|
|
if (error.Fail() || !final_value.get()) {
|
|
if (reason_to_stop)
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
|
|
if (final_value_type)
|
|
*final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return ValueObjectSP();
|
|
} else {
|
|
if (final_task_on_target)
|
|
*final_task_on_target = ValueObject::eExpressionPathAftermathNothing;
|
|
return final_value;
|
|
}
|
|
}
|
|
if (*final_task_on_target ==
|
|
ValueObject::eExpressionPathAftermathTakeAddress) {
|
|
Status error;
|
|
ValueObjectSP final_value = ret_val->AddressOf(error);
|
|
if (error.Fail() || !final_value.get()) {
|
|
if (reason_to_stop)
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonTakingAddressFailed;
|
|
if (final_value_type)
|
|
*final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return ValueObjectSP();
|
|
} else {
|
|
if (final_task_on_target)
|
|
*final_task_on_target = ValueObject::eExpressionPathAftermathNothing;
|
|
return final_value;
|
|
}
|
|
}
|
|
}
|
|
return ret_val; // final_task_on_target will still have its original value, so
|
|
// you know I did not do it
|
|
}
|
|
|
|
ValueObjectSP ValueObject::GetValueForExpressionPath_Impl(
|
|
llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop,
|
|
ExpressionPathEndResultType *final_result,
|
|
const GetValueForExpressionPathOptions &options,
|
|
ExpressionPathAftermath *what_next) {
|
|
ValueObjectSP root = GetSP();
|
|
|
|
if (!root)
|
|
return nullptr;
|
|
|
|
llvm::StringRef remainder = expression;
|
|
|
|
while (true) {
|
|
llvm::StringRef temp_expression = remainder;
|
|
|
|
CompilerType root_compiler_type = root->GetCompilerType();
|
|
CompilerType pointee_compiler_type;
|
|
Flags pointee_compiler_type_info;
|
|
|
|
Flags root_compiler_type_info(
|
|
root_compiler_type.GetTypeInfo(&pointee_compiler_type));
|
|
if (pointee_compiler_type)
|
|
pointee_compiler_type_info.Reset(pointee_compiler_type.GetTypeInfo());
|
|
|
|
if (temp_expression.empty()) {
|
|
*reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString;
|
|
return root;
|
|
}
|
|
|
|
switch (temp_expression.front()) {
|
|
case '-': {
|
|
temp_expression = temp_expression.drop_front();
|
|
if (options.m_check_dot_vs_arrow_syntax &&
|
|
root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to
|
|
// use -> on a
|
|
// non-pointer and I
|
|
// must catch the error
|
|
{
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonArrowInsteadOfDot;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return ValueObjectSP();
|
|
}
|
|
if (root_compiler_type_info.Test(eTypeIsObjC) && // if yo are trying to
|
|
// extract an ObjC IVar
|
|
// when this is forbidden
|
|
root_compiler_type_info.Test(eTypeIsPointer) &&
|
|
options.m_no_fragile_ivar) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonFragileIVarNotAllowed;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return ValueObjectSP();
|
|
}
|
|
if (!temp_expression.starts_with(">")) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return ValueObjectSP();
|
|
}
|
|
}
|
|
[[fallthrough]];
|
|
case '.': // or fallthrough from ->
|
|
{
|
|
if (options.m_check_dot_vs_arrow_syntax &&
|
|
temp_expression.front() == '.' &&
|
|
root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to
|
|
// use . on a pointer
|
|
// and I must catch the
|
|
// error
|
|
{
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonDotInsteadOfArrow;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
}
|
|
temp_expression = temp_expression.drop_front(); // skip . or >
|
|
|
|
size_t next_sep_pos = temp_expression.find_first_of("-.[", 1);
|
|
if (next_sep_pos == llvm::StringRef::npos) // if no other separator just
|
|
// expand this last layer
|
|
{
|
|
llvm::StringRef child_name = temp_expression;
|
|
ValueObjectSP child_valobj_sp =
|
|
root->GetChildMemberWithName(child_name);
|
|
|
|
if (child_valobj_sp.get()) // we know we are done, so just return
|
|
{
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonEndOfString;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypePlain;
|
|
return child_valobj_sp;
|
|
} else {
|
|
switch (options.m_synthetic_children_traversal) {
|
|
case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
|
|
None:
|
|
break;
|
|
case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
|
|
FromSynthetic:
|
|
if (root->IsSynthetic()) {
|
|
child_valobj_sp = root->GetNonSyntheticValue();
|
|
if (child_valobj_sp.get())
|
|
child_valobj_sp =
|
|
child_valobj_sp->GetChildMemberWithName(child_name);
|
|
}
|
|
break;
|
|
case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
|
|
ToSynthetic:
|
|
if (!root->IsSynthetic()) {
|
|
child_valobj_sp = root->GetSyntheticValue();
|
|
if (child_valobj_sp.get())
|
|
child_valobj_sp =
|
|
child_valobj_sp->GetChildMemberWithName(child_name);
|
|
}
|
|
break;
|
|
case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
|
|
Both:
|
|
if (root->IsSynthetic()) {
|
|
child_valobj_sp = root->GetNonSyntheticValue();
|
|
if (child_valobj_sp.get())
|
|
child_valobj_sp =
|
|
child_valobj_sp->GetChildMemberWithName(child_name);
|
|
} else {
|
|
child_valobj_sp = root->GetSyntheticValue();
|
|
if (child_valobj_sp.get())
|
|
child_valobj_sp =
|
|
child_valobj_sp->GetChildMemberWithName(child_name);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if we are here and options.m_no_synthetic_children is true,
|
|
// child_valobj_sp is going to be a NULL SP, so we hit the "else"
|
|
// branch, and return an error
|
|
if (child_valobj_sp.get()) // if it worked, just return
|
|
{
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonEndOfString;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypePlain;
|
|
return child_valobj_sp;
|
|
} else {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonNoSuchChild;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
}
|
|
} else // other layers do expand
|
|
{
|
|
llvm::StringRef next_separator = temp_expression.substr(next_sep_pos);
|
|
llvm::StringRef child_name = temp_expression.slice(0, next_sep_pos);
|
|
|
|
ValueObjectSP child_valobj_sp =
|
|
root->GetChildMemberWithName(child_name);
|
|
if (child_valobj_sp.get()) // store the new root and move on
|
|
{
|
|
root = child_valobj_sp;
|
|
remainder = next_separator;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypePlain;
|
|
continue;
|
|
} else {
|
|
switch (options.m_synthetic_children_traversal) {
|
|
case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
|
|
None:
|
|
break;
|
|
case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
|
|
FromSynthetic:
|
|
if (root->IsSynthetic()) {
|
|
child_valobj_sp = root->GetNonSyntheticValue();
|
|
if (child_valobj_sp.get())
|
|
child_valobj_sp =
|
|
child_valobj_sp->GetChildMemberWithName(child_name);
|
|
}
|
|
break;
|
|
case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
|
|
ToSynthetic:
|
|
if (!root->IsSynthetic()) {
|
|
child_valobj_sp = root->GetSyntheticValue();
|
|
if (child_valobj_sp.get())
|
|
child_valobj_sp =
|
|
child_valobj_sp->GetChildMemberWithName(child_name);
|
|
}
|
|
break;
|
|
case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
|
|
Both:
|
|
if (root->IsSynthetic()) {
|
|
child_valobj_sp = root->GetNonSyntheticValue();
|
|
if (child_valobj_sp.get())
|
|
child_valobj_sp =
|
|
child_valobj_sp->GetChildMemberWithName(child_name);
|
|
} else {
|
|
child_valobj_sp = root->GetSyntheticValue();
|
|
if (child_valobj_sp.get())
|
|
child_valobj_sp =
|
|
child_valobj_sp->GetChildMemberWithName(child_name);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if we are here and options.m_no_synthetic_children is true,
|
|
// child_valobj_sp is going to be a NULL SP, so we hit the "else"
|
|
// branch, and return an error
|
|
if (child_valobj_sp.get()) // if it worked, move on
|
|
{
|
|
root = child_valobj_sp;
|
|
remainder = next_separator;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypePlain;
|
|
continue;
|
|
} else {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonNoSuchChild;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case '[': {
|
|
if (!root_compiler_type_info.Test(eTypeIsArray) &&
|
|
!root_compiler_type_info.Test(eTypeIsPointer) &&
|
|
!root_compiler_type_info.Test(
|
|
eTypeIsVector)) // if this is not a T[] nor a T*
|
|
{
|
|
if (!root_compiler_type_info.Test(
|
|
eTypeIsScalar)) // if this is not even a scalar...
|
|
{
|
|
if (options.m_synthetic_children_traversal ==
|
|
GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
|
|
None) // ...only chance left is synthetic
|
|
{
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return ValueObjectSP();
|
|
}
|
|
} else if (!options.m_allow_bitfields_syntax) // if this is a scalar,
|
|
// check that we can
|
|
// expand bitfields
|
|
{
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return ValueObjectSP();
|
|
}
|
|
}
|
|
if (temp_expression[1] ==
|
|
']') // if this is an unbounded range it only works for arrays
|
|
{
|
|
if (!root_compiler_type_info.Test(eTypeIsArray)) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
} else // even if something follows, we cannot expand unbounded ranges,
|
|
// just let the caller do it
|
|
{
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet;
|
|
*final_result =
|
|
ValueObject::eExpressionPathEndResultTypeUnboundedRange;
|
|
return root;
|
|
}
|
|
}
|
|
|
|
size_t close_bracket_position = temp_expression.find(']', 1);
|
|
if (close_bracket_position ==
|
|
llvm::StringRef::npos) // if there is no ], this is a syntax error
|
|
{
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
}
|
|
|
|
llvm::StringRef bracket_expr =
|
|
temp_expression.slice(1, close_bracket_position);
|
|
|
|
// If this was an empty expression it would have been caught by the if
|
|
// above.
|
|
assert(!bracket_expr.empty());
|
|
|
|
if (!bracket_expr.contains('-')) {
|
|
// if no separator, this is of the form [N]. Note that this cannot be
|
|
// an unbounded range of the form [], because that case was handled
|
|
// above with an unconditional return.
|
|
unsigned long index = 0;
|
|
if (bracket_expr.getAsInteger(0, index)) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
}
|
|
|
|
// from here on we do have a valid index
|
|
if (root_compiler_type_info.Test(eTypeIsArray)) {
|
|
ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index);
|
|
if (!child_valobj_sp)
|
|
child_valobj_sp = root->GetSyntheticArrayMember(index, true);
|
|
if (!child_valobj_sp)
|
|
if (root->HasSyntheticValue() &&
|
|
llvm::expectedToStdOptional(
|
|
root->GetSyntheticValue()->GetNumChildren())
|
|
.value_or(0) > index)
|
|
child_valobj_sp =
|
|
root->GetSyntheticValue()->GetChildAtIndex(index);
|
|
if (child_valobj_sp) {
|
|
root = child_valobj_sp;
|
|
remainder =
|
|
temp_expression.substr(close_bracket_position + 1); // skip ]
|
|
*final_result = ValueObject::eExpressionPathEndResultTypePlain;
|
|
continue;
|
|
} else {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonNoSuchChild;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
}
|
|
} else if (root_compiler_type_info.Test(eTypeIsPointer)) {
|
|
if (*what_next ==
|
|
ValueObject::
|
|
eExpressionPathAftermathDereference && // if this is a
|
|
// ptr-to-scalar, I
|
|
// am accessing it
|
|
// by index and I
|
|
// would have
|
|
// deref'ed anyway,
|
|
// then do it now
|
|
// and use this as
|
|
// a bitfield
|
|
pointee_compiler_type_info.Test(eTypeIsScalar)) {
|
|
Status error;
|
|
root = root->Dereference(error);
|
|
if (error.Fail() || !root) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
} else {
|
|
*what_next = eExpressionPathAftermathNothing;
|
|
continue;
|
|
}
|
|
} else {
|
|
if (root->GetCompilerType().GetMinimumLanguage() ==
|
|
eLanguageTypeObjC &&
|
|
pointee_compiler_type_info.AllClear(eTypeIsPointer) &&
|
|
root->HasSyntheticValue() &&
|
|
(options.m_synthetic_children_traversal ==
|
|
GetValueForExpressionPathOptions::
|
|
SyntheticChildrenTraversal::ToSynthetic ||
|
|
options.m_synthetic_children_traversal ==
|
|
GetValueForExpressionPathOptions::
|
|
SyntheticChildrenTraversal::Both)) {
|
|
root = root->GetSyntheticValue()->GetChildAtIndex(index);
|
|
} else
|
|
root = root->GetSyntheticArrayMember(index, true);
|
|
if (!root) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonNoSuchChild;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
} else {
|
|
remainder =
|
|
temp_expression.substr(close_bracket_position + 1); // skip ]
|
|
*final_result = ValueObject::eExpressionPathEndResultTypePlain;
|
|
continue;
|
|
}
|
|
}
|
|
} else if (root_compiler_type_info.Test(eTypeIsScalar)) {
|
|
root = root->GetSyntheticBitFieldChild(index, index, true);
|
|
if (!root) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonNoSuchChild;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
} else // we do not know how to expand members of bitfields, so we
|
|
// just return and let the caller do any further processing
|
|
{
|
|
*reason_to_stop = ValueObject::
|
|
eExpressionPathScanEndReasonBitfieldRangeOperatorMet;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeBitfield;
|
|
return root;
|
|
}
|
|
} else if (root_compiler_type_info.Test(eTypeIsVector)) {
|
|
root = root->GetChildAtIndex(index);
|
|
if (!root) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonNoSuchChild;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return ValueObjectSP();
|
|
} else {
|
|
remainder =
|
|
temp_expression.substr(close_bracket_position + 1); // skip ]
|
|
*final_result = ValueObject::eExpressionPathEndResultTypePlain;
|
|
continue;
|
|
}
|
|
} else if (options.m_synthetic_children_traversal ==
|
|
GetValueForExpressionPathOptions::
|
|
SyntheticChildrenTraversal::ToSynthetic ||
|
|
options.m_synthetic_children_traversal ==
|
|
GetValueForExpressionPathOptions::
|
|
SyntheticChildrenTraversal::Both) {
|
|
if (root->HasSyntheticValue())
|
|
root = root->GetSyntheticValue();
|
|
else if (!root->IsSynthetic()) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
}
|
|
// if we are here, then root itself is a synthetic VO.. should be
|
|
// good to go
|
|
|
|
if (!root) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
}
|
|
root = root->GetChildAtIndex(index);
|
|
if (!root) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonNoSuchChild;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
} else {
|
|
remainder =
|
|
temp_expression.substr(close_bracket_position + 1); // skip ]
|
|
*final_result = ValueObject::eExpressionPathEndResultTypePlain;
|
|
continue;
|
|
}
|
|
} else {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonNoSuchChild;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
}
|
|
} else {
|
|
// we have a low and a high index
|
|
llvm::StringRef sleft, sright;
|
|
unsigned long low_index, high_index;
|
|
std::tie(sleft, sright) = bracket_expr.split('-');
|
|
if (sleft.getAsInteger(0, low_index) ||
|
|
sright.getAsInteger(0, high_index)) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
}
|
|
|
|
if (low_index > high_index) // swap indices if required
|
|
std::swap(low_index, high_index);
|
|
|
|
if (root_compiler_type_info.Test(
|
|
eTypeIsScalar)) // expansion only works for scalars
|
|
{
|
|
root = root->GetSyntheticBitFieldChild(low_index, high_index, true);
|
|
if (!root) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonNoSuchChild;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
} else {
|
|
*reason_to_stop = ValueObject::
|
|
eExpressionPathScanEndReasonBitfieldRangeOperatorMet;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeBitfield;
|
|
return root;
|
|
}
|
|
} else if (root_compiler_type_info.Test(
|
|
eTypeIsPointer) && // if this is a ptr-to-scalar, I am
|
|
// accessing it by index and I would
|
|
// have deref'ed anyway, then do it
|
|
// now and use this as a bitfield
|
|
*what_next ==
|
|
ValueObject::eExpressionPathAftermathDereference &&
|
|
pointee_compiler_type_info.Test(eTypeIsScalar)) {
|
|
Status error;
|
|
root = root->Dereference(error);
|
|
if (error.Fail() || !root) {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
} else {
|
|
*what_next = ValueObject::eExpressionPathAftermathNothing;
|
|
continue;
|
|
}
|
|
} else {
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeBoundedRange;
|
|
return root;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default: // some non-separator is in the way
|
|
{
|
|
*reason_to_stop =
|
|
ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
|
|
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
llvm::Error ValueObject::Dump(Stream &s) {
|
|
return Dump(s, DumpValueObjectOptions(*this));
|
|
}
|
|
|
|
llvm::Error ValueObject::Dump(Stream &s,
|
|
const DumpValueObjectOptions &options) {
|
|
ValueObjectPrinter printer(*this, &s, options);
|
|
return printer.PrintValueObject();
|
|
}
|
|
|
|
ValueObjectSP ValueObject::CreateConstantValue(ConstString name) {
|
|
ValueObjectSP valobj_sp;
|
|
|
|
if (UpdateValueIfNeeded(false) && m_error.Success()) {
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
|
|
DataExtractor data;
|
|
data.SetByteOrder(m_data.GetByteOrder());
|
|
data.SetAddressByteSize(m_data.GetAddressByteSize());
|
|
|
|
if (IsBitfield()) {
|
|
Value v(Scalar(GetValueAsUnsigned(UINT64_MAX)));
|
|
m_error = v.GetValueAsData(&exe_ctx, data, GetModule().get());
|
|
} else
|
|
m_error = m_value.GetValueAsData(&exe_ctx, data, GetModule().get());
|
|
|
|
valobj_sp = ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(), GetCompilerType(), name, data,
|
|
GetAddressOf());
|
|
}
|
|
|
|
if (!valobj_sp) {
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
valobj_sp = ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(), m_error.Clone());
|
|
}
|
|
return valobj_sp;
|
|
}
|
|
|
|
ValueObjectSP ValueObject::GetQualifiedRepresentationIfAvailable(
|
|
lldb::DynamicValueType dynValue, bool synthValue) {
|
|
ValueObjectSP result_sp;
|
|
switch (dynValue) {
|
|
case lldb::eDynamicCanRunTarget:
|
|
case lldb::eDynamicDontRunTarget: {
|
|
if (!IsDynamic())
|
|
result_sp = GetDynamicValue(dynValue);
|
|
} break;
|
|
case lldb::eNoDynamicValues: {
|
|
if (IsDynamic())
|
|
result_sp = GetStaticValue();
|
|
} break;
|
|
}
|
|
if (!result_sp)
|
|
result_sp = GetSP();
|
|
assert(result_sp);
|
|
|
|
bool is_synthetic = result_sp->IsSynthetic();
|
|
if (synthValue && !is_synthetic) {
|
|
if (auto synth_sp = result_sp->GetSyntheticValue())
|
|
return synth_sp;
|
|
}
|
|
if (!synthValue && is_synthetic) {
|
|
if (auto non_synth_sp = result_sp->GetNonSyntheticValue())
|
|
return non_synth_sp;
|
|
}
|
|
|
|
return result_sp;
|
|
}
|
|
|
|
ValueObjectSP ValueObject::Dereference(Status &error) {
|
|
if (m_deref_valobj)
|
|
return m_deref_valobj->GetSP();
|
|
|
|
const bool is_pointer_or_reference_type = IsPointerOrReferenceType();
|
|
if (is_pointer_or_reference_type) {
|
|
bool omit_empty_base_classes = true;
|
|
bool ignore_array_bounds = false;
|
|
|
|
std::string child_name_str;
|
|
uint32_t child_byte_size = 0;
|
|
int32_t child_byte_offset = 0;
|
|
uint32_t child_bitfield_bit_size = 0;
|
|
uint32_t child_bitfield_bit_offset = 0;
|
|
bool child_is_base_class = false;
|
|
bool child_is_deref_of_parent = false;
|
|
const bool transparent_pointers = false;
|
|
CompilerType compiler_type = GetCompilerType();
|
|
uint64_t language_flags = 0;
|
|
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
|
|
CompilerType child_compiler_type;
|
|
auto child_compiler_type_or_err = compiler_type.GetChildCompilerTypeAtIndex(
|
|
&exe_ctx, 0, transparent_pointers, omit_empty_base_classes,
|
|
ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset,
|
|
child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
|
|
child_is_deref_of_parent, this, language_flags);
|
|
if (!child_compiler_type_or_err)
|
|
LLDB_LOG_ERROR(GetLog(LLDBLog::Types),
|
|
child_compiler_type_or_err.takeError(),
|
|
"could not find child: {0}");
|
|
else
|
|
child_compiler_type = *child_compiler_type_or_err;
|
|
|
|
if (child_compiler_type && child_byte_size) {
|
|
ConstString child_name;
|
|
if (!child_name_str.empty())
|
|
child_name.SetCString(child_name_str.c_str());
|
|
|
|
m_deref_valobj = new ValueObjectChild(
|
|
*this, child_compiler_type, child_name, child_byte_size,
|
|
child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset,
|
|
child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid,
|
|
language_flags);
|
|
}
|
|
|
|
// In case of incomplete child compiler type, use the pointee type and try
|
|
// to recreate a new ValueObjectChild using it.
|
|
if (!m_deref_valobj) {
|
|
// FIXME(#59012): C++ stdlib formatters break with incomplete types (e.g.
|
|
// `std::vector<int> &`). Remove ObjC restriction once that's resolved.
|
|
if (Language::LanguageIsObjC(GetPreferredDisplayLanguage()) &&
|
|
HasSyntheticValue()) {
|
|
child_compiler_type = compiler_type.GetPointeeType();
|
|
|
|
if (child_compiler_type) {
|
|
ConstString child_name;
|
|
if (!child_name_str.empty())
|
|
child_name.SetCString(child_name_str.c_str());
|
|
|
|
m_deref_valobj = new ValueObjectChild(
|
|
*this, child_compiler_type, child_name, child_byte_size,
|
|
child_byte_offset, child_bitfield_bit_size,
|
|
child_bitfield_bit_offset, child_is_base_class,
|
|
child_is_deref_of_parent, eAddressTypeInvalid, language_flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (HasSyntheticValue()) {
|
|
m_deref_valobj =
|
|
GetSyntheticValue()->GetChildMemberWithName("$$dereference$$").get();
|
|
} else if (IsSynthetic()) {
|
|
m_deref_valobj = GetChildMemberWithName("$$dereference$$").get();
|
|
}
|
|
|
|
if (m_deref_valobj) {
|
|
error.Clear();
|
|
return m_deref_valobj->GetSP();
|
|
} else {
|
|
StreamString strm;
|
|
GetExpressionPath(strm);
|
|
|
|
if (is_pointer_or_reference_type)
|
|
error = Status::FromErrorStringWithFormat(
|
|
"dereference failed: (%s) %s",
|
|
GetTypeName().AsCString("<invalid type>"), strm.GetData());
|
|
else
|
|
error = Status::FromErrorStringWithFormat(
|
|
"not a pointer or reference type: (%s) %s",
|
|
GetTypeName().AsCString("<invalid type>"), strm.GetData());
|
|
return ValueObjectSP();
|
|
}
|
|
}
|
|
|
|
ValueObjectSP ValueObject::AddressOf(Status &error) {
|
|
if (m_addr_of_valobj_sp)
|
|
return m_addr_of_valobj_sp;
|
|
|
|
AddressType address_type = eAddressTypeInvalid;
|
|
const bool scalar_is_load_address = false;
|
|
addr_t addr = GetAddressOf(scalar_is_load_address, &address_type);
|
|
error.Clear();
|
|
if (addr != LLDB_INVALID_ADDRESS && address_type != eAddressTypeHost) {
|
|
switch (address_type) {
|
|
case eAddressTypeInvalid: {
|
|
StreamString expr_path_strm;
|
|
GetExpressionPath(expr_path_strm);
|
|
error = Status::FromErrorStringWithFormat("'%s' is not in memory",
|
|
expr_path_strm.GetData());
|
|
} break;
|
|
|
|
case eAddressTypeFile:
|
|
case eAddressTypeLoad: {
|
|
CompilerType compiler_type = GetCompilerType();
|
|
if (compiler_type) {
|
|
std::string name(1, '&');
|
|
name.append(m_name.AsCString(""));
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
m_addr_of_valobj_sp = ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
compiler_type.GetPointerType(), ConstString(name.c_str()), addr,
|
|
eAddressTypeInvalid, m_data.GetAddressByteSize());
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
StreamString expr_path_strm;
|
|
GetExpressionPath(expr_path_strm);
|
|
error = Status::FromErrorStringWithFormat(
|
|
"'%s' doesn't have a valid address", expr_path_strm.GetData());
|
|
}
|
|
|
|
return m_addr_of_valobj_sp;
|
|
}
|
|
|
|
ValueObjectSP ValueObject::DoCast(const CompilerType &compiler_type) {
|
|
return ValueObjectCast::Create(*this, GetName(), compiler_type);
|
|
}
|
|
|
|
ValueObjectSP ValueObject::Cast(const CompilerType &compiler_type) {
|
|
// Only allow casts if the original type is equal or larger than the cast
|
|
// type, unless we know this is a load address. Getting the size wrong for
|
|
// a host side storage could leak lldb memory, so we absolutely want to
|
|
// prevent that. We may not always get the right value, for instance if we
|
|
// have an expression result value that's copied into a storage location in
|
|
// the target may not have copied enough memory. I'm not trying to fix that
|
|
// here, I'm just making Cast from a smaller to a larger possible in all the
|
|
// cases where that doesn't risk making a Value out of random lldb memory.
|
|
// You have to check the ValueObject's Value for the address types, since
|
|
// ValueObjects that use live addresses will tell you they fetch data from the
|
|
// live address, but once they are made, they actually don't.
|
|
// FIXME: Can we make ValueObject's with a live address fetch "more data" from
|
|
// the live address if it is still valid?
|
|
|
|
Status error;
|
|
CompilerType my_type = GetCompilerType();
|
|
|
|
ExecutionContextScope *exe_scope =
|
|
ExecutionContext(GetExecutionContextRef()).GetBestExecutionContextScope();
|
|
if (llvm::expectedToOptional(compiler_type.GetByteSize(exe_scope))
|
|
.value_or(0) <=
|
|
llvm::expectedToOptional(GetCompilerType().GetByteSize(exe_scope))
|
|
.value_or(0) ||
|
|
m_value.GetValueType() == Value::ValueType::LoadAddress)
|
|
return DoCast(compiler_type);
|
|
|
|
error = Status::FromErrorString(
|
|
"Can only cast to a type that is equal to or smaller "
|
|
"than the orignal type.");
|
|
|
|
return ValueObjectConstResult::Create(
|
|
ExecutionContext(GetExecutionContextRef()).GetBestExecutionContextScope(),
|
|
std::move(error));
|
|
}
|
|
|
|
lldb::ValueObjectSP ValueObject::Clone(ConstString new_name) {
|
|
return ValueObjectCast::Create(*this, new_name, GetCompilerType());
|
|
}
|
|
|
|
ValueObjectSP ValueObject::CastPointerType(const char *name,
|
|
CompilerType &compiler_type) {
|
|
ValueObjectSP valobj_sp;
|
|
AddressType address_type;
|
|
addr_t ptr_value = GetPointerValue(&address_type);
|
|
|
|
if (ptr_value != LLDB_INVALID_ADDRESS) {
|
|
Address ptr_addr(ptr_value);
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
valobj_sp = ValueObjectMemory::Create(
|
|
exe_ctx.GetBestExecutionContextScope(), name, ptr_addr, compiler_type);
|
|
}
|
|
return valobj_sp;
|
|
}
|
|
|
|
ValueObjectSP ValueObject::CastPointerType(const char *name, TypeSP &type_sp) {
|
|
ValueObjectSP valobj_sp;
|
|
AddressType address_type;
|
|
addr_t ptr_value = GetPointerValue(&address_type);
|
|
|
|
if (ptr_value != LLDB_INVALID_ADDRESS) {
|
|
Address ptr_addr(ptr_value);
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
valobj_sp = ValueObjectMemory::Create(
|
|
exe_ctx.GetBestExecutionContextScope(), name, ptr_addr, type_sp);
|
|
}
|
|
return valobj_sp;
|
|
}
|
|
|
|
lldb::addr_t ValueObject::GetLoadAddress() {
|
|
lldb::addr_t addr_value = LLDB_INVALID_ADDRESS;
|
|
if (auto target_sp = GetTargetSP()) {
|
|
const bool scalar_is_load_address = true;
|
|
AddressType addr_type;
|
|
addr_value = GetAddressOf(scalar_is_load_address, &addr_type);
|
|
if (addr_type == eAddressTypeFile) {
|
|
lldb::ModuleSP module_sp(GetModule());
|
|
if (!module_sp)
|
|
addr_value = LLDB_INVALID_ADDRESS;
|
|
else {
|
|
Address tmp_addr;
|
|
module_sp->ResolveFileAddress(addr_value, tmp_addr);
|
|
addr_value = tmp_addr.GetLoadAddress(target_sp.get());
|
|
}
|
|
} else if (addr_type == eAddressTypeHost ||
|
|
addr_type == eAddressTypeInvalid)
|
|
addr_value = LLDB_INVALID_ADDRESS;
|
|
}
|
|
return addr_value;
|
|
}
|
|
|
|
llvm::Expected<lldb::ValueObjectSP> ValueObject::CastDerivedToBaseType(
|
|
CompilerType type, const llvm::ArrayRef<uint32_t> &base_type_indices) {
|
|
// Make sure the starting type and the target type are both valid for this
|
|
// type of cast; otherwise return the shared pointer to the original
|
|
// (unchanged) ValueObject.
|
|
if (!type.IsPointerType() && !type.IsReferenceType())
|
|
return llvm::make_error<llvm::StringError>(
|
|
"Invalid target type: should be a pointer or a reference",
|
|
llvm::inconvertibleErrorCode());
|
|
|
|
CompilerType start_type = GetCompilerType();
|
|
if (start_type.IsReferenceType())
|
|
start_type = start_type.GetNonReferenceType();
|
|
|
|
auto target_record_type =
|
|
type.IsPointerType() ? type.GetPointeeType() : type.GetNonReferenceType();
|
|
auto start_record_type =
|
|
start_type.IsPointerType() ? start_type.GetPointeeType() : start_type;
|
|
|
|
if (!target_record_type.IsRecordType() || !start_record_type.IsRecordType())
|
|
return llvm::make_error<llvm::StringError>(
|
|
"Underlying start & target types should be record types",
|
|
llvm::inconvertibleErrorCode());
|
|
|
|
if (target_record_type.CompareTypes(start_record_type))
|
|
return llvm::make_error<llvm::StringError>(
|
|
"Underlying start & target types should be different",
|
|
llvm::inconvertibleErrorCode());
|
|
|
|
if (base_type_indices.empty())
|
|
return llvm::make_error<llvm::StringError>(
|
|
"Children sequence must be non-empty", llvm::inconvertibleErrorCode());
|
|
|
|
// Both the starting & target types are valid for the cast, and the list of
|
|
// base class indices is non-empty, so we can proceed with the cast.
|
|
|
|
lldb::TargetSP target = GetTargetSP();
|
|
// The `value` can be a pointer, but GetChildAtIndex works for pointers too.
|
|
lldb::ValueObjectSP inner_value = GetSP();
|
|
|
|
for (const uint32_t i : base_type_indices)
|
|
// Create synthetic value if needed.
|
|
inner_value =
|
|
inner_value->GetChildAtIndex(i, /*can_create_synthetic*/ true);
|
|
|
|
// At this point type of `inner_value` should be the dereferenced target
|
|
// type.
|
|
CompilerType inner_value_type = inner_value->GetCompilerType();
|
|
if (type.IsPointerType()) {
|
|
if (!inner_value_type.CompareTypes(type.GetPointeeType()))
|
|
return llvm::make_error<llvm::StringError>(
|
|
"casted value doesn't match the desired type",
|
|
llvm::inconvertibleErrorCode());
|
|
|
|
uintptr_t addr = inner_value->GetLoadAddress();
|
|
llvm::StringRef name = "";
|
|
ExecutionContext exe_ctx(target.get(), false);
|
|
return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx, type,
|
|
/* do deref */ false);
|
|
}
|
|
|
|
// At this point the target type should be a reference.
|
|
if (!inner_value_type.CompareTypes(type.GetNonReferenceType()))
|
|
return llvm::make_error<llvm::StringError>(
|
|
"casted value doesn't match the desired type",
|
|
llvm::inconvertibleErrorCode());
|
|
|
|
return lldb::ValueObjectSP(inner_value->Cast(type.GetNonReferenceType()));
|
|
}
|
|
|
|
llvm::Expected<lldb::ValueObjectSP>
|
|
ValueObject::CastBaseToDerivedType(CompilerType type, uint64_t offset) {
|
|
// Make sure the starting type and the target type are both valid for this
|
|
// type of cast; otherwise return the shared pointer to the original
|
|
// (unchanged) ValueObject.
|
|
if (!type.IsPointerType() && !type.IsReferenceType())
|
|
return llvm::make_error<llvm::StringError>(
|
|
"Invalid target type: should be a pointer or a reference",
|
|
llvm::inconvertibleErrorCode());
|
|
|
|
CompilerType start_type = GetCompilerType();
|
|
if (start_type.IsReferenceType())
|
|
start_type = start_type.GetNonReferenceType();
|
|
|
|
auto target_record_type =
|
|
type.IsPointerType() ? type.GetPointeeType() : type.GetNonReferenceType();
|
|
auto start_record_type =
|
|
start_type.IsPointerType() ? start_type.GetPointeeType() : start_type;
|
|
|
|
if (!target_record_type.IsRecordType() || !start_record_type.IsRecordType())
|
|
return llvm::make_error<llvm::StringError>(
|
|
"Underlying start & target types should be record types",
|
|
llvm::inconvertibleErrorCode());
|
|
|
|
if (target_record_type.CompareTypes(start_record_type))
|
|
return llvm::make_error<llvm::StringError>(
|
|
"Underlying start & target types should be different",
|
|
llvm::inconvertibleErrorCode());
|
|
|
|
CompilerType virtual_base;
|
|
if (target_record_type.IsVirtualBase(start_record_type, &virtual_base)) {
|
|
if (!virtual_base.IsValid())
|
|
return llvm::make_error<llvm::StringError>(
|
|
"virtual base should be valid", llvm::inconvertibleErrorCode());
|
|
return llvm::make_error<llvm::StringError>(
|
|
llvm::Twine("cannot cast " + start_type.TypeDescription() + " to " +
|
|
type.TypeDescription() + " via virtual base " +
|
|
virtual_base.TypeDescription()),
|
|
llvm::inconvertibleErrorCode());
|
|
}
|
|
|
|
// Both the starting & target types are valid for the cast, so we can
|
|
// proceed with the cast.
|
|
|
|
lldb::TargetSP target = GetTargetSP();
|
|
auto pointer_type =
|
|
type.IsPointerType() ? type : type.GetNonReferenceType().GetPointerType();
|
|
|
|
uintptr_t addr =
|
|
type.IsPointerType() ? GetValueAsUnsigned(0) : GetLoadAddress();
|
|
|
|
llvm::StringRef name = "";
|
|
ExecutionContext exe_ctx(target.get(), false);
|
|
lldb::ValueObjectSP value = ValueObject::CreateValueObjectFromAddress(
|
|
name, addr - offset, exe_ctx, pointer_type, /* do_deref */ false);
|
|
|
|
if (type.IsPointerType())
|
|
return value;
|
|
|
|
// At this point the target type is a reference. Since `value` is a pointer,
|
|
// it has to be dereferenced.
|
|
Status error;
|
|
return value->Dereference(error);
|
|
}
|
|
|
|
lldb::ValueObjectSP ValueObject::CastToBasicType(CompilerType type) {
|
|
bool is_scalar = GetCompilerType().IsScalarType();
|
|
bool is_enum = GetCompilerType().IsEnumerationType();
|
|
bool is_pointer =
|
|
GetCompilerType().IsPointerType() || GetCompilerType().IsNullPtrType();
|
|
bool is_float = GetCompilerType().IsFloat();
|
|
bool is_integer = GetCompilerType().IsInteger();
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
|
|
if (!type.IsScalarType())
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorString("target type must be a scalar"));
|
|
|
|
if (!is_scalar && !is_enum && !is_pointer)
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorString("argument must be a scalar, enum, or pointer"));
|
|
|
|
lldb::TargetSP target = GetTargetSP();
|
|
uint64_t type_byte_size = 0;
|
|
uint64_t val_byte_size = 0;
|
|
if (auto temp = llvm::expectedToOptional(type.GetByteSize(target.get())))
|
|
type_byte_size = temp.value();
|
|
if (auto temp =
|
|
llvm::expectedToOptional(GetCompilerType().GetByteSize(target.get())))
|
|
val_byte_size = temp.value();
|
|
|
|
if (is_pointer) {
|
|
if (!type.IsInteger() && !type.IsBoolean())
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorString("target type must be an integer or boolean"));
|
|
if (!type.IsBoolean() && type_byte_size < val_byte_size)
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorString(
|
|
"target type cannot be smaller than the pointer type"));
|
|
}
|
|
|
|
if (type.IsBoolean()) {
|
|
if (!is_scalar || is_integer)
|
|
return ValueObject::CreateValueObjectFromBool(
|
|
target, GetValueAsUnsigned(0) != 0, "result");
|
|
else if (is_scalar && is_float) {
|
|
auto float_value_or_err = GetValueAsAPFloat();
|
|
if (float_value_or_err)
|
|
return ValueObject::CreateValueObjectFromBool(
|
|
target, !float_value_or_err->isZero(), "result");
|
|
else
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorStringWithFormat(
|
|
"cannot get value as APFloat: %s",
|
|
llvm::toString(float_value_or_err.takeError()).c_str()));
|
|
}
|
|
}
|
|
|
|
if (type.IsInteger()) {
|
|
if (!is_scalar || is_integer) {
|
|
auto int_value_or_err = GetValueAsAPSInt();
|
|
if (int_value_or_err) {
|
|
// Get the value as APSInt and extend or truncate it to the requested
|
|
// size.
|
|
llvm::APSInt ext =
|
|
int_value_or_err->extOrTrunc(type_byte_size * CHAR_BIT);
|
|
return ValueObject::CreateValueObjectFromAPInt(target, ext, type,
|
|
"result");
|
|
} else
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorStringWithFormat(
|
|
"cannot get value as APSInt: %s",
|
|
llvm::toString(int_value_or_err.takeError()).c_str()));
|
|
} else if (is_scalar && is_float) {
|
|
llvm::APSInt integer(type_byte_size * CHAR_BIT, !type.IsSigned());
|
|
bool is_exact;
|
|
auto float_value_or_err = GetValueAsAPFloat();
|
|
if (float_value_or_err) {
|
|
llvm::APFloatBase::opStatus status =
|
|
float_value_or_err->convertToInteger(
|
|
integer, llvm::APFloat::rmTowardZero, &is_exact);
|
|
|
|
// Casting floating point values that are out of bounds of the target
|
|
// type is undefined behaviour.
|
|
if (status & llvm::APFloatBase::opInvalidOp)
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorStringWithFormat(
|
|
"invalid type cast detected: %s",
|
|
llvm::toString(float_value_or_err.takeError()).c_str()));
|
|
return ValueObject::CreateValueObjectFromAPInt(target, integer, type,
|
|
"result");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type.IsFloat()) {
|
|
if (!is_scalar) {
|
|
auto int_value_or_err = GetValueAsAPSInt();
|
|
if (int_value_or_err) {
|
|
llvm::APSInt ext =
|
|
int_value_or_err->extOrTrunc(type_byte_size * CHAR_BIT);
|
|
Scalar scalar_int(ext);
|
|
llvm::APFloat f = scalar_int.CreateAPFloatFromAPSInt(
|
|
type.GetCanonicalType().GetBasicTypeEnumeration());
|
|
return ValueObject::CreateValueObjectFromAPFloat(target, f, type,
|
|
"result");
|
|
} else {
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorStringWithFormat(
|
|
"cannot get value as APSInt: %s",
|
|
llvm::toString(int_value_or_err.takeError()).c_str()));
|
|
}
|
|
} else {
|
|
if (is_integer) {
|
|
auto int_value_or_err = GetValueAsAPSInt();
|
|
if (int_value_or_err) {
|
|
Scalar scalar_int(*int_value_or_err);
|
|
llvm::APFloat f = scalar_int.CreateAPFloatFromAPSInt(
|
|
type.GetCanonicalType().GetBasicTypeEnumeration());
|
|
return ValueObject::CreateValueObjectFromAPFloat(target, f, type,
|
|
"result");
|
|
} else {
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorStringWithFormat(
|
|
"cannot get value as APSInt: %s",
|
|
llvm::toString(int_value_or_err.takeError()).c_str()));
|
|
}
|
|
}
|
|
if (is_float) {
|
|
auto float_value_or_err = GetValueAsAPFloat();
|
|
if (float_value_or_err) {
|
|
Scalar scalar_float(*float_value_or_err);
|
|
llvm::APFloat f = scalar_float.CreateAPFloatFromAPFloat(
|
|
type.GetCanonicalType().GetBasicTypeEnumeration());
|
|
return ValueObject::CreateValueObjectFromAPFloat(target, f, type,
|
|
"result");
|
|
} else {
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorStringWithFormat(
|
|
"cannot get value as APFloat: %s",
|
|
llvm::toString(float_value_or_err.takeError()).c_str()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorString("Unable to perform requested cast"));
|
|
}
|
|
|
|
lldb::ValueObjectSP ValueObject::CastToEnumType(CompilerType type) {
|
|
bool is_enum = GetCompilerType().IsEnumerationType();
|
|
bool is_integer = GetCompilerType().IsInteger();
|
|
bool is_float = GetCompilerType().IsFloat();
|
|
ExecutionContext exe_ctx(GetExecutionContextRef());
|
|
|
|
if (!is_enum && !is_integer && !is_float)
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorString(
|
|
"argument must be an integer, a float, or an enum"));
|
|
|
|
if (!type.IsEnumerationType())
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorString("target type must be an enum"));
|
|
|
|
lldb::TargetSP target = GetTargetSP();
|
|
uint64_t byte_size = 0;
|
|
if (auto temp = llvm::expectedToOptional(type.GetByteSize(target.get())))
|
|
byte_size = temp.value();
|
|
|
|
if (is_float) {
|
|
llvm::APSInt integer(byte_size * CHAR_BIT, !type.IsSigned());
|
|
bool is_exact;
|
|
auto value_or_err = GetValueAsAPFloat();
|
|
if (value_or_err) {
|
|
llvm::APFloatBase::opStatus status = value_or_err->convertToInteger(
|
|
integer, llvm::APFloat::rmTowardZero, &is_exact);
|
|
|
|
// Casting floating point values that are out of bounds of the target
|
|
// type is undefined behaviour.
|
|
if (status & llvm::APFloatBase::opInvalidOp)
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorStringWithFormat(
|
|
"invalid type cast detected: %s",
|
|
llvm::toString(value_or_err.takeError()).c_str()));
|
|
return ValueObject::CreateValueObjectFromAPInt(target, integer, type,
|
|
"result");
|
|
} else
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorString("cannot get value as APFloat"));
|
|
} else {
|
|
// Get the value as APSInt and extend or truncate it to the requested size.
|
|
auto value_or_err = GetValueAsAPSInt();
|
|
if (value_or_err) {
|
|
llvm::APSInt ext = value_or_err->extOrTrunc(byte_size * CHAR_BIT);
|
|
return ValueObject::CreateValueObjectFromAPInt(target, ext, type,
|
|
"result");
|
|
} else
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorStringWithFormat(
|
|
"cannot get value as APSInt: %s",
|
|
llvm::toString(value_or_err.takeError()).c_str()));
|
|
}
|
|
return ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(),
|
|
Status::FromErrorString("Cannot perform requested cast"));
|
|
}
|
|
|
|
ValueObject::EvaluationPoint::EvaluationPoint() : m_mod_id(), m_exe_ctx_ref() {}
|
|
|
|
ValueObject::EvaluationPoint::EvaluationPoint(ExecutionContextScope *exe_scope,
|
|
bool use_selected)
|
|
: m_mod_id(), m_exe_ctx_ref() {
|
|
ExecutionContext exe_ctx(exe_scope);
|
|
TargetSP target_sp(exe_ctx.GetTargetSP());
|
|
if (target_sp) {
|
|
m_exe_ctx_ref.SetTargetSP(target_sp);
|
|
ProcessSP process_sp(exe_ctx.GetProcessSP());
|
|
if (!process_sp)
|
|
process_sp = target_sp->GetProcessSP();
|
|
|
|
if (process_sp) {
|
|
m_mod_id = process_sp->GetModID();
|
|
m_exe_ctx_ref.SetProcessSP(process_sp);
|
|
|
|
ThreadSP thread_sp(exe_ctx.GetThreadSP());
|
|
|
|
if (!thread_sp) {
|
|
if (use_selected)
|
|
thread_sp = process_sp->GetThreadList().GetSelectedThread();
|
|
}
|
|
|
|
if (thread_sp) {
|
|
m_exe_ctx_ref.SetThreadSP(thread_sp);
|
|
|
|
StackFrameSP frame_sp(exe_ctx.GetFrameSP());
|
|
if (!frame_sp) {
|
|
if (use_selected)
|
|
frame_sp = thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame);
|
|
}
|
|
if (frame_sp)
|
|
m_exe_ctx_ref.SetFrameSP(frame_sp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ValueObject::EvaluationPoint::EvaluationPoint(
|
|
const ValueObject::EvaluationPoint &rhs)
|
|
: m_mod_id(), m_exe_ctx_ref(rhs.m_exe_ctx_ref) {}
|
|
|
|
ValueObject::EvaluationPoint::~EvaluationPoint() = default;
|
|
|
|
// This function checks the EvaluationPoint against the current process state.
|
|
// If the current state matches the evaluation point, or the evaluation point
|
|
// is already invalid, then we return false, meaning "no change". If the
|
|
// current state is different, we update our state, and return true meaning
|
|
// "yes, change". If we did see a change, we also set m_needs_update to true,
|
|
// so future calls to NeedsUpdate will return true. exe_scope will be set to
|
|
// the current execution context scope.
|
|
|
|
bool ValueObject::EvaluationPoint::SyncWithProcessState(
|
|
bool accept_invalid_exe_ctx) {
|
|
// Start with the target, if it is NULL, then we're obviously not going to
|
|
// get any further:
|
|
const bool thread_and_frame_only_if_stopped = true;
|
|
ExecutionContext exe_ctx(
|
|
m_exe_ctx_ref.Lock(thread_and_frame_only_if_stopped));
|
|
|
|
if (exe_ctx.GetTargetPtr() == nullptr)
|
|
return false;
|
|
|
|
// If we don't have a process nothing can change.
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
if (process == nullptr)
|
|
return false;
|
|
|
|
// If our stop id is the current stop ID, nothing has changed:
|
|
ProcessModID current_mod_id = process->GetModID();
|
|
|
|
// If the current stop id is 0, either we haven't run yet, or the process
|
|
// state has been cleared. In either case, we aren't going to be able to sync
|
|
// with the process state.
|
|
if (current_mod_id.GetStopID() == 0)
|
|
return false;
|
|
|
|
bool changed = false;
|
|
const bool was_valid = m_mod_id.IsValid();
|
|
if (was_valid) {
|
|
if (m_mod_id == current_mod_id) {
|
|
// Everything is already up to date in this object, no need to update the
|
|
// execution context scope.
|
|
changed = false;
|
|
} else {
|
|
m_mod_id = current_mod_id;
|
|
m_needs_update = true;
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
// Now re-look up the thread and frame in case the underlying objects have
|
|
// gone away & been recreated. That way we'll be sure to return a valid
|
|
// exe_scope. If we used to have a thread or a frame but can't find it
|
|
// anymore, then mark ourselves as invalid.
|
|
|
|
if (!accept_invalid_exe_ctx) {
|
|
if (m_exe_ctx_ref.HasThreadRef()) {
|
|
ThreadSP thread_sp(m_exe_ctx_ref.GetThreadSP());
|
|
if (thread_sp) {
|
|
if (m_exe_ctx_ref.HasFrameRef()) {
|
|
StackFrameSP frame_sp(m_exe_ctx_ref.GetFrameSP());
|
|
if (!frame_sp) {
|
|
// We used to have a frame, but now it is gone
|
|
SetInvalid();
|
|
changed = was_valid;
|
|
}
|
|
}
|
|
} else {
|
|
// We used to have a thread, but now it is gone
|
|
SetInvalid();
|
|
changed = was_valid;
|
|
}
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
void ValueObject::EvaluationPoint::SetUpdated() {
|
|
ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
|
|
if (process_sp)
|
|
m_mod_id = process_sp->GetModID();
|
|
m_needs_update = false;
|
|
}
|
|
|
|
void ValueObject::ClearUserVisibleData(uint32_t clear_mask) {
|
|
if ((clear_mask & eClearUserVisibleDataItemsValue) ==
|
|
eClearUserVisibleDataItemsValue)
|
|
m_value_str.clear();
|
|
|
|
if ((clear_mask & eClearUserVisibleDataItemsLocation) ==
|
|
eClearUserVisibleDataItemsLocation)
|
|
m_location_str.clear();
|
|
|
|
if ((clear_mask & eClearUserVisibleDataItemsSummary) ==
|
|
eClearUserVisibleDataItemsSummary)
|
|
m_summary_str.clear();
|
|
|
|
if ((clear_mask & eClearUserVisibleDataItemsDescription) ==
|
|
eClearUserVisibleDataItemsDescription)
|
|
m_object_desc_str.clear();
|
|
|
|
if ((clear_mask & eClearUserVisibleDataItemsSyntheticChildren) ==
|
|
eClearUserVisibleDataItemsSyntheticChildren) {
|
|
if (m_synthetic_value)
|
|
m_synthetic_value = nullptr;
|
|
}
|
|
}
|
|
|
|
SymbolContextScope *ValueObject::GetSymbolContextScope() {
|
|
if (m_parent) {
|
|
if (!m_parent->IsPointerOrReferenceType())
|
|
return m_parent->GetSymbolContextScope();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
lldb::ValueObjectSP
|
|
ValueObject::CreateValueObjectFromExpression(llvm::StringRef name,
|
|
llvm::StringRef expression,
|
|
const ExecutionContext &exe_ctx) {
|
|
return CreateValueObjectFromExpression(name, expression, exe_ctx,
|
|
EvaluateExpressionOptions());
|
|
}
|
|
|
|
lldb::ValueObjectSP ValueObject::CreateValueObjectFromExpression(
|
|
llvm::StringRef name, llvm::StringRef expression,
|
|
const ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options) {
|
|
lldb::ValueObjectSP retval_sp;
|
|
lldb::TargetSP target_sp(exe_ctx.GetTargetSP());
|
|
if (!target_sp)
|
|
return retval_sp;
|
|
if (expression.empty())
|
|
return retval_sp;
|
|
target_sp->EvaluateExpression(expression, exe_ctx.GetFrameSP().get(),
|
|
retval_sp, options);
|
|
if (retval_sp && !name.empty())
|
|
retval_sp->SetName(ConstString(name));
|
|
return retval_sp;
|
|
}
|
|
|
|
lldb::ValueObjectSP ValueObject::CreateValueObjectFromAddress(
|
|
llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx,
|
|
CompilerType type, bool do_deref) {
|
|
if (type) {
|
|
CompilerType pointer_type(type.GetPointerType());
|
|
if (!do_deref)
|
|
pointer_type = type;
|
|
if (pointer_type) {
|
|
lldb::DataBufferSP buffer(
|
|
new lldb_private::DataBufferHeap(&address, sizeof(lldb::addr_t)));
|
|
lldb::ValueObjectSP ptr_result_valobj_sp(ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(), pointer_type,
|
|
ConstString(name), buffer, exe_ctx.GetByteOrder(),
|
|
exe_ctx.GetAddressByteSize()));
|
|
if (ptr_result_valobj_sp) {
|
|
if (do_deref)
|
|
ptr_result_valobj_sp->GetValue().SetValueType(
|
|
Value::ValueType::LoadAddress);
|
|
Status err;
|
|
if (do_deref)
|
|
ptr_result_valobj_sp = ptr_result_valobj_sp->Dereference(err);
|
|
if (ptr_result_valobj_sp && !name.empty())
|
|
ptr_result_valobj_sp->SetName(ConstString(name));
|
|
}
|
|
return ptr_result_valobj_sp;
|
|
}
|
|
}
|
|
return lldb::ValueObjectSP();
|
|
}
|
|
|
|
lldb::ValueObjectSP ValueObject::CreateValueObjectFromData(
|
|
llvm::StringRef name, const DataExtractor &data,
|
|
const ExecutionContext &exe_ctx, CompilerType type) {
|
|
lldb::ValueObjectSP new_value_sp;
|
|
new_value_sp = ValueObjectConstResult::Create(
|
|
exe_ctx.GetBestExecutionContextScope(), type, ConstString(name), data,
|
|
LLDB_INVALID_ADDRESS);
|
|
new_value_sp->SetAddressTypeOfChildren(eAddressTypeLoad);
|
|
if (new_value_sp && !name.empty())
|
|
new_value_sp->SetName(ConstString(name));
|
|
return new_value_sp;
|
|
}
|
|
|
|
lldb::ValueObjectSP
|
|
ValueObject::CreateValueObjectFromAPInt(lldb::TargetSP target,
|
|
const llvm::APInt &v, CompilerType type,
|
|
llvm::StringRef name) {
|
|
ExecutionContext exe_ctx(target.get(), false);
|
|
uint64_t byte_size = 0;
|
|
if (auto temp = llvm::expectedToOptional(type.GetByteSize(target.get())))
|
|
byte_size = temp.value();
|
|
lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>(
|
|
reinterpret_cast<const void *>(v.getRawData()), byte_size,
|
|
exe_ctx.GetByteOrder(), exe_ctx.GetAddressByteSize());
|
|
return ValueObject::CreateValueObjectFromData(name, *data_sp, exe_ctx, type);
|
|
}
|
|
|
|
lldb::ValueObjectSP ValueObject::CreateValueObjectFromAPFloat(
|
|
lldb::TargetSP target, const llvm::APFloat &v, CompilerType type,
|
|
llvm::StringRef name) {
|
|
return CreateValueObjectFromAPInt(target, v.bitcastToAPInt(), type, name);
|
|
}
|
|
|
|
lldb::ValueObjectSP
|
|
ValueObject::CreateValueObjectFromBool(lldb::TargetSP target, bool value,
|
|
llvm::StringRef name) {
|
|
CompilerType target_type;
|
|
if (target) {
|
|
for (auto type_system_sp : target->GetScratchTypeSystems())
|
|
if (auto compiler_type =
|
|
type_system_sp->GetBasicTypeFromAST(lldb::eBasicTypeBool)) {
|
|
target_type = compiler_type;
|
|
break;
|
|
}
|
|
}
|
|
ExecutionContext exe_ctx(target.get(), false);
|
|
uint64_t byte_size = 0;
|
|
if (auto temp =
|
|
llvm::expectedToOptional(target_type.GetByteSize(target.get())))
|
|
byte_size = temp.value();
|
|
lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>(
|
|
reinterpret_cast<const void *>(&value), byte_size, exe_ctx.GetByteOrder(),
|
|
exe_ctx.GetAddressByteSize());
|
|
return ValueObject::CreateValueObjectFromData(name, *data_sp, exe_ctx,
|
|
target_type);
|
|
}
|
|
|
|
lldb::ValueObjectSP ValueObject::CreateValueObjectFromNullptr(
|
|
lldb::TargetSP target, CompilerType type, llvm::StringRef name) {
|
|
if (!type.IsNullPtrType()) {
|
|
lldb::ValueObjectSP ret_val;
|
|
return ret_val;
|
|
}
|
|
uintptr_t zero = 0;
|
|
ExecutionContext exe_ctx(target.get(), false);
|
|
uint64_t byte_size = 0;
|
|
if (auto temp = llvm::expectedToOptional(type.GetByteSize(target.get())))
|
|
byte_size = temp.value();
|
|
lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>(
|
|
reinterpret_cast<const void *>(zero), byte_size, exe_ctx.GetByteOrder(),
|
|
exe_ctx.GetAddressByteSize());
|
|
return ValueObject::CreateValueObjectFromData(name, *data_sp, exe_ctx, type);
|
|
}
|
|
|
|
ModuleSP ValueObject::GetModule() {
|
|
ValueObject *root(GetRoot());
|
|
if (root != this)
|
|
return root->GetModule();
|
|
return lldb::ModuleSP();
|
|
}
|
|
|
|
ValueObject *ValueObject::GetRoot() {
|
|
if (m_root)
|
|
return m_root;
|
|
return (m_root = FollowParentChain([](ValueObject *vo) -> bool {
|
|
return (vo->m_parent != nullptr);
|
|
}));
|
|
}
|
|
|
|
ValueObject *
|
|
ValueObject::FollowParentChain(std::function<bool(ValueObject *)> f) {
|
|
ValueObject *vo = this;
|
|
while (vo) {
|
|
if (!f(vo))
|
|
break;
|
|
vo = vo->m_parent;
|
|
}
|
|
return vo;
|
|
}
|
|
|
|
AddressType ValueObject::GetAddressTypeOfChildren() {
|
|
if (m_address_type_of_ptr_or_ref_children == eAddressTypeInvalid) {
|
|
ValueObject *root(GetRoot());
|
|
if (root != this)
|
|
return root->GetAddressTypeOfChildren();
|
|
}
|
|
return m_address_type_of_ptr_or_ref_children;
|
|
}
|
|
|
|
lldb::DynamicValueType ValueObject::GetDynamicValueType() {
|
|
ValueObject *with_dv_info = this;
|
|
while (with_dv_info) {
|
|
if (with_dv_info->HasDynamicValueTypeInfo())
|
|
return with_dv_info->GetDynamicValueTypeImpl();
|
|
with_dv_info = with_dv_info->m_parent;
|
|
}
|
|
return lldb::eNoDynamicValues;
|
|
}
|
|
|
|
lldb::Format ValueObject::GetFormat() const {
|
|
const ValueObject *with_fmt_info = this;
|
|
while (with_fmt_info) {
|
|
if (with_fmt_info->m_format != lldb::eFormatDefault)
|
|
return with_fmt_info->m_format;
|
|
with_fmt_info = with_fmt_info->m_parent;
|
|
}
|
|
return m_format;
|
|
}
|
|
|
|
lldb::LanguageType ValueObject::GetPreferredDisplayLanguage() {
|
|
lldb::LanguageType type = m_preferred_display_language;
|
|
if (m_preferred_display_language == lldb::eLanguageTypeUnknown) {
|
|
if (GetRoot()) {
|
|
if (GetRoot() == this) {
|
|
if (StackFrameSP frame_sp = GetFrameSP()) {
|
|
const SymbolContext &sc(
|
|
frame_sp->GetSymbolContext(eSymbolContextCompUnit));
|
|
if (CompileUnit *cu = sc.comp_unit)
|
|
type = cu->GetLanguage();
|
|
}
|
|
} else {
|
|
type = GetRoot()->GetPreferredDisplayLanguage();
|
|
}
|
|
}
|
|
}
|
|
return (m_preferred_display_language = type); // only compute it once
|
|
}
|
|
|
|
void ValueObject::SetPreferredDisplayLanguageIfNeeded(lldb::LanguageType lt) {
|
|
if (m_preferred_display_language == lldb::eLanguageTypeUnknown)
|
|
SetPreferredDisplayLanguage(lt);
|
|
}
|
|
|
|
bool ValueObject::CanProvideValue() {
|
|
// we need to support invalid types as providers of values because some bare-
|
|
// board debugging scenarios have no notion of types, but still manage to
|
|
// have raw numeric values for things like registers. sigh.
|
|
CompilerType type = GetCompilerType();
|
|
return (!type.IsValid()) || (0 != (type.GetTypeInfo() & eTypeHasValue));
|
|
}
|
|
|
|
ValueObjectSP ValueObject::Persist() {
|
|
if (!UpdateValueIfNeeded())
|
|
return nullptr;
|
|
|
|
TargetSP target_sp(GetTargetSP());
|
|
if (!target_sp)
|
|
return nullptr;
|
|
|
|
PersistentExpressionState *persistent_state =
|
|
target_sp->GetPersistentExpressionStateForLanguage(
|
|
GetPreferredDisplayLanguage());
|
|
|
|
if (!persistent_state)
|
|
return nullptr;
|
|
|
|
ConstString name = persistent_state->GetNextPersistentVariableName();
|
|
|
|
ValueObjectSP const_result_sp =
|
|
ValueObjectConstResult::Create(target_sp.get(), GetValue(), name);
|
|
|
|
ExpressionVariableSP persistent_var_sp =
|
|
persistent_state->CreatePersistentVariable(const_result_sp);
|
|
persistent_var_sp->m_live_sp = persistent_var_sp->m_frozen_sp;
|
|
persistent_var_sp->m_flags |= ExpressionVariable::EVIsProgramReference;
|
|
|
|
return persistent_var_sp->GetValueObject();
|
|
}
|
|
|
|
lldb::ValueObjectSP ValueObject::GetVTable() {
|
|
return ValueObjectVTable::Create(*this);
|
|
}
|