| //===-- 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/ABI.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/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/ValueObjectSynthetic.h" | |
| #include "lldb/ValueObject/ValueObjectVTable.h" | |
| #include "lldb/lldb-enumerations.h" | |
| #include "llvm/Support/Compiler.h" | |
| #include <algorithm> | |
| #include <atomic> | |
| #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 std::atomic<user_id_t> g_value_obj_uid{0}; | |
| // FIXME: this will return true for vector types whose elements | |
| // are floats. Audit all usages of this function and call | |
| // IsFloatingPointType() instead if vectors of floats aren't intended | |
| // to be supported. | |
| static bool HasFloatingRepresentation(CompilerType ct) { | |
| return ct.GetTypeInfo() & eTypeIsFloat; | |
| } | |
| // 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::GetCompilerType() { | |
| 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::CheckValueObjectOwnership(ValueObject *child) { | |
| Target *target_ptr = GetTargetSP().get(); | |
| if (!target_ptr) | |
| return {}; | |
| if (target_ptr->GetCheckValueObjectOwnership()) { | |
| // Child value objects should always be owned by their parent's manager. | |
| if (child && (child->GetManager() != GetManager())) { | |
| Status error = Status::FromErrorStringWithFormatv( | |
| "ValueObject: '{0}' not owned by its parent: '{1}'", child->GetName(), | |
| GetName()); | |
| return ValueObjectConstResult::Create(target_ptr, std::move(error), | |
| this->GetManager()); | |
| } | |
| } | |
| return {}; | |
| } | |
| 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; | |
| } | |
| llvm::Expected<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 = GetPointerValue().address; | |
| 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 0; | |
| } else /* (items > 1) */ | |
| { | |
| Status error; | |
| lldb_private::DataBufferHeap *heap_buf_ptr = nullptr; | |
| lldb::DataBufferSP data_sp(heap_buf_ptr = | |
| new lldb_private::DataBufferHeap()); | |
| auto [addr, addr_type] = | |
| is_pointer_type ? GetPointerValue() : GetAddressOf(true); | |
| 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()); | |
| if (Target *target = exe_ctx.GetTargetPtr()) { | |
| heap_buf_ptr->SetByteSize(bytes); | |
| Address target_addr; | |
| target_addr.SetLoadAddress(addr + offset, target); | |
| size_t bytes_read = | |
| target->ReadMemory(target_addr, heap_buf_ptr->GetBytes(), bytes, | |
| error, /*force_live_memory=*/true); | |
| if (!error.Success()) { | |
| // The live read failed. Fall back to the object file's read-only | |
| // sections, but keep the live-memory error to report if the fallback | |
| // fails too. | |
| Status file_error; | |
| size_t file_bytes_read = | |
| target->ReadMemory(target_addr, heap_buf_ptr->GetBytes(), bytes, | |
| file_error, /*force_live_memory=*/false); | |
| if (file_error.Success() || file_bytes_read > 0) { | |
| bytes_read = file_bytes_read; | |
| error = std::move(file_error); | |
| } | |
| } | |
| if (error.Success() || bytes_read > 0) { | |
| data.SetData(data_sp); | |
| return bytes_read; | |
| } | |
| } | |
| } break; | |
| case eAddressTypeHost: { | |
| auto max_bytes = llvm::expectedToOptional(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(); | |
| if (llvm::Error err = CanSetValue()) { | |
| error = Status::FromError(std::move(err)); | |
| return false; | |
| } | |
| // 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; | |
| } | |
| const Encoding encoding = GetCompilerType().GetEncoding(); | |
| 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()) { | |
| AddrAndType cstr_address; | |
| 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); | |
| } else { | |
| // We have a pointer | |
| cstr_address = GetPointerValue(); | |
| } | |
| if (cstr_address.address == 0 || | |
| cstr_address.address == LLDB_INVALID_ADDRESS) { | |
| if (cstr_address.type == eAddressTypeHost && is_array) { | |
| // The array is not required to be null-terminated, so ask for the | |
| // bytes rather than for a C string. Its data can also be shorter than | |
| // the array type, as for a DW_AT_const_value string, so clamp the | |
| // length to what is really there. | |
| DataExtractor &data = GetDataExtractor(); | |
| const uint8_t *cstr = data.PeekData(0, 1); | |
| if (cstr == nullptr) { | |
| s << "<invalid address>"; | |
| error = Status::FromErrorString("invalid address"); | |
| CopyStringDataToBufferSP(s, buffer_sp); | |
| return {0, was_capped}; | |
| } | |
| cstr_len = std::min<uint64_t>(cstr_len, data.GetByteSize()); | |
| s << llvm::StringRef(reinterpret_cast<const char *>(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.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.PutChar(*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; | |
| // The chunk holds the middle of a string as often as its end, so scan | |
| // for a terminator instead of requiring one. | |
| const uint8_t *bytes = data.PeekData(0, bytes_read); | |
| if (bytes == nullptr) | |
| break; | |
| size_t len = strnlen(reinterpret_cast<const char *>(bytes), bytes_read); | |
| 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.PutChar(*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::createStringError("type cannot be converted to APSInt"); | |
| if (CanProvideValue()) { | |
| Scalar scalar; | |
| if (ResolveValue(scalar)) | |
| return scalar.GetAPSInt(); | |
| } | |
| return llvm::createStringError("error occurred; unable to convert to APSInt"); | |
| } | |
| llvm::Expected<llvm::APFloat> ValueObject::GetValueAsAPFloat() { | |
| if (!HasFloatingRepresentation(GetCompilerType())) | |
| return llvm::createStringError("type cannot be converted to APFloat"); | |
| if (CanProvideValue()) { | |
| Scalar scalar; | |
| if (ResolveValue(scalar)) | |
| return scalar.GetAPFloat(); | |
| } | |
| return llvm::createStringError( | |
| "error occurred; unable to convert to APFloat"); | |
| } | |
| 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(); | |
| else | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Types), value_or_err.takeError(), | |
| "GetValueAsAPSInt failed: {0}"); | |
| } | |
| if (HasFloatingRepresentation(val_type)) { | |
| auto value_or_err = GetValueAsAPFloat(); | |
| if (value_or_err) | |
| return value_or_err->isNonZero(); | |
| else | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Types), value_or_err.takeError(), | |
| "GetValueAsAPFloat failed: {0}"); | |
| } | |
| if (val_type.IsArrayType()) | |
| return GetAddressOf().address != 0; | |
| if (val_type.IsNullPtrType()) | |
| return false; | |
| return llvm::createStringError("type cannot be converted to bool"); | |
| } | |
| llvm::Error ValueObject::SetValueFromInteger(const llvm::APInt &value, | |
| bool can_update_var) { | |
| // Verify the current object is an integer object | |
| CompilerType val_type = GetCompilerType(); | |
| if (!val_type.IsInteger() && !val_type.IsUnscopedEnumerationType() && | |
| !HasFloatingRepresentation(val_type) && !val_type.IsPointerType() && | |
| !val_type.IsScalarType()) | |
| return llvm::createStringError( | |
| "Not allowed to change the value of a non-scalar object"); | |
| // Verify, if current object is associated with a program variable, that | |
| // we are allowing updating program variables in this case. | |
| if (GetVariable() && !can_update_var) | |
| return llvm::createStringError( | |
| "Not allowed to update program variables in this case"); | |
| // Make sure we're not trying to assign to a constant. | |
| if (GetIsConstant()) | |
| return llvm::createStringError( | |
| "Not allowed to change the value of a constant"); | |
| // Verify the proposed new value is the right size. | |
| lldb::TargetSP target = GetTargetSP(); | |
| uint64_t byte_size = 0; | |
| // Exclude size check when assigning an integer 1 or 0 to a boolean. | |
| if (!val_type.IsBoolean() || (!value.isOne() && !value.isZero())) { | |
| byte_size = llvm::expectedToOptional(GetByteSize()).value_or(0); | |
| // Check that the value is representable in the destination type. | |
| unsigned dest_bits = byte_size * CHAR_BIT; | |
| unsigned needed_bits = val_type.IsSigned() ? value.getSignificantBits() | |
| : value.getActiveBits(); | |
| if (needed_bits > dest_bits) | |
| return llvm::createStringError("Illegal argument: new value is too big"); | |
| } | |
| // The DataExtractor below reads exactly byte_size bytes from the APInt's raw | |
| // storage. If the incoming value has fewer bits than the destination type, | |
| // reading byte_size bytes could run past the APInt's backing store and pull | |
| // in garbage (an out-of-bounds read). Extend the value so its storage always | |
| // covers the full read, preserving the sign so that negative values keep | |
| // their value in the wider destination. | |
| llvm::APInt sized_value = value; | |
| if (sized_value.getBitWidth() < byte_size * CHAR_BIT) | |
| sized_value = sized_value.sext(byte_size * CHAR_BIT); | |
| Status error; | |
| lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>( | |
| reinterpret_cast<const void *>(sized_value.getRawData()), byte_size, | |
| target->GetArchitecture().GetByteOrder(), | |
| static_cast<uint8_t>(target->GetArchitecture().GetAddressByteSize())); | |
| SetData(*data_sp, error); | |
| return error.takeError(); | |
| } | |
| llvm::Error ValueObject::SetValueFromInteger(lldb::ValueObjectSP new_val_sp, | |
| bool can_update_var) { | |
| // Verify the current object is an integer object | |
| CompilerType val_type = GetCompilerType(); | |
| if (!val_type.IsInteger() && !val_type.IsUnscopedEnumerationType() && | |
| !HasFloatingRepresentation(val_type) && !val_type.IsPointerType() && | |
| !val_type.IsScalarType()) | |
| return llvm::createStringError("Not allowed to update a non-scalar object"); | |
| // Verify, if current object is associated with a program variable, that | |
| // we are allowing updating program variables in this case. | |
| if (GetVariable() && !can_update_var) | |
| return llvm::createStringError( | |
| "Not allowed to update program variables in this case"); | |
| // 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.IsUnscopedEnumerationType() && | |
| !HasFloatingRepresentation(new_val_type) && !new_val_type.IsPointerType()) | |
| return llvm::createStringError( | |
| "Illegal argument: new value is not a scalar object"); | |
| if (new_val_type.IsInteger() || new_val_type.IsUnscopedEnumerationType()) { | |
| auto value_or_err = new_val_sp->GetValueAsAPSInt(); | |
| if (value_or_err) | |
| return SetValueFromInteger(*value_or_err, can_update_var); | |
| } else if (HasFloatingRepresentation(new_val_type)) { | |
| auto value_or_err = new_val_sp->GetValueAsAPFloat(); | |
| if (value_or_err) | |
| return SetValueFromInteger(value_or_err->bitcastToAPInt(), | |
| can_update_var); | |
| } 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(); | |
| return SetValueFromInteger(llvm::APInt(num_bits, int_val), | |
| can_update_var); | |
| } else | |
| return llvm::createStringError("Error converting new_val_sp to integer"); | |
| } | |
| llvm_unreachable("Unrecognized type for RHS of assignment"); | |
| } | |
| // 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); | |
| if (custom_format == eFormatVectorOfChar) { | |
| options.SetZeroTermination( | |
| formatters::StringPrinter::ZeroTermination::Ignore); | |
| } else { | |
| options.SetZeroTermination( | |
| formatters::StringPrinter::ZeroTermination::ZeroTerminate); | |
| } | |
| 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 == eFormatFloat128) || | |
| (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: { | |
| if (auto err = GetNumChildren()) { | |
| strm.Printf("%" PRIu32, *err); | |
| str = strm.GetString(); | |
| } else { | |
| strm << "error: " << toString(err.takeError()); | |
| 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.Format("{0} @ {1}", GetTypeName(), 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; | |
| } | |
| ValueObject::AddrAndType | |
| ValueObject::GetAddressOf(bool scalar_is_load_address) { | |
| // Can't take address of a bitfield | |
| if (IsBitfield()) | |
| return {}; | |
| if (!UpdateValueIfNeeded(false)) | |
| return {}; | |
| switch (m_value.GetValueType()) { | |
| case Value::ValueType::Invalid: | |
| return {}; | |
| case Value::ValueType::Scalar: | |
| if (scalar_is_load_address) { | |
| return {m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS), | |
| eAddressTypeLoad}; | |
| } | |
| return {}; | |
| case Value::ValueType::LoadAddress: | |
| case Value::ValueType::FileAddress: | |
| return {m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS), | |
| m_value.GetValueAddressType()}; | |
| case Value::ValueType::HostAddress: | |
| return {LLDB_INVALID_ADDRESS, m_value.GetValueAddressType()}; | |
| } | |
| llvm_unreachable("Unhandled value type!"); | |
| } | |
| std::optional<addr_t> ValueObject::GetStrippedPointerValue(addr_t address) { | |
| if (GetCompilerType().HasPointerAuthQualifier()) { | |
| ExecutionContext exe_ctx(GetExecutionContextRef()); | |
| if (Process *process = exe_ctx.GetProcessPtr()) | |
| if (ABISP abi_sp = process->GetABI()) | |
| return abi_sp->FixCodeAddress(address); | |
| } | |
| return std::nullopt; | |
| } | |
| ValueObject::AddrAndType ValueObject::GetPointerValue() { | |
| if (!UpdateValueIfNeeded(false)) | |
| return {}; | |
| switch (m_value.GetValueType()) { | |
| case Value::ValueType::Invalid: | |
| return {}; | |
| case Value::ValueType::Scalar: | |
| return {m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS), | |
| GetAddressTypeOfChildren()}; | |
| case Value::ValueType::HostAddress: | |
| case Value::ValueType::LoadAddress: | |
| case Value::ValueType::FileAddress: { | |
| lldb::offset_t data_offset = 0; | |
| return {m_data.GetAddress(&data_offset), GetAddressTypeOfChildren()}; | |
| } | |
| } | |
| llvm_unreachable("Unhandled value type!"); | |
| } | |
| 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(); | |
| if (llvm::Error err = CanSetValue()) { | |
| error = Status::FromError(std::move(err)); | |
| return false; | |
| } | |
| // 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; | |
| } | |
| const Encoding encoding = GetCompilerType().GetEncoding(); | |
| 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) { | |
| // Register values use exact-size host buffers. Do not create array children | |
| // that would read beyond the bytes supplied by the register context. | |
| uint64_t array_size = 0; | |
| if (GetValueType() == eValueTypeRegister && | |
| GetCompilerType().IsArrayType(nullptr, &array_size) && | |
| index >= array_size) | |
| return {}; | |
| 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(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) { | |
| if (from > to) | |
| return {}; | |
| // Register children must stay within the exact bytes supplied by the | |
| // register context. | |
| if (GetValueType() == eValueTypeRegister) { | |
| std::optional<uint64_t> byte_size = llvm::expectedToOptional(GetByteSize()); | |
| if (!byte_size || to / 8 >= *byte_size) | |
| return {}; | |
| } | |
| 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(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; | |
| synthetic_child_sp->SetSyntheticChildrenGenerated(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 llvm::StringRef | |
| SkipLeadingExpressionPathSeparators(llvm::StringRef expression) { | |
| if (expression.starts_with('.')) | |
| return expression.drop_front(); | |
| if (expression.starts_with("->")) | |
| return expression.drop_front(2); | |
| return expression; | |
| } | |
| ValueObjectSP | |
| ValueObject::GetSyntheticExpressionPathChild(llvm::StringRef 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 prev_synth_sp(GetSyntheticChildren()); | |
| if (!UpdateFormatsIfNeeded() && m_synthetic_value) | |
| return; | |
| lldb::SyntheticChildrenSP curr_synth_sp(GetSyntheticChildren()); | |
| if (curr_synth_sp.get() == nullptr) | |
| return; | |
| if (curr_synth_sp == prev_synth_sp && m_synthetic_value) | |
| return; | |
| m_synthetic_value = new ValueObjectSynthetic(*this, curr_synth_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; | |
| } | |
| 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); | |
| const CompilerType parentType = parent->GetCompilerType(); | |
| if (parentType.IsPointerType() && | |
| parentType.GetPointeeType().IsArrayType(nullptr, nullptr, nullptr)) { | |
| // When the parent is a pointer to an array, then we have to: | |
| // - follow the expression path of the parent with "[0]" | |
| // (that will indicate dereferencing the pointer to the array) | |
| // - and then follow that with this ValueObject's name | |
| // (which will be something like "[i]" to indicate | |
| // the i-th element of the array) | |
| s.PutCString("[0]"); | |
| s.PutCString(GetName().GetCString()); | |
| return; | |
| } | |
| } | |
| // 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 | eTypeIsVector))) { | |
| s.PutChar('.'); | |
| } | |
| } | |
| } | |
| } | |
| const char *name = GetName().GetCString(); | |
| if (name) | |
| s.PutCString(name); | |
| } | |
| } | |
| if (is_deref_of_parent && | |
| epformat == eGetExpressionPathFormatDereferencePointers) { | |
| s.PutChar(')'); | |
| } | |
| } | |
| // Return the alternate value (synthetic if the input object is non-synthetic | |
| // and otherwise) this is permitted by the expression path options. | |
| static ValueObjectSP GetAlternateValue( | |
| ValueObject &valobj, | |
| ValueObject::GetValueForExpressionPathOptions::SyntheticChildrenTraversal | |
| synth_traversal) { | |
| using SynthTraversal = | |
| ValueObject::GetValueForExpressionPathOptions::SyntheticChildrenTraversal; | |
| if (valobj.IsSynthetic()) { | |
| if (synth_traversal == SynthTraversal::FromSynthetic || | |
| synth_traversal == SynthTraversal::Both) | |
| return valobj.GetNonSyntheticValue(); | |
| } else { | |
| if (synth_traversal == SynthTraversal::ToSynthetic || | |
| synth_traversal == SynthTraversal::Both) | |
| return valobj.GetSyntheticValue(); | |
| } | |
| return nullptr; | |
| } | |
| // Dereference the provided object or the alternate value, if permitted by the | |
| // expression path options. | |
| static ValueObjectSP DereferenceValueOrAlternate( | |
| ValueObject &valobj, | |
| ValueObject::GetValueForExpressionPathOptions::SyntheticChildrenTraversal | |
| synth_traversal, | |
| Status &error) { | |
| error.Clear(); | |
| ValueObjectSP result = valobj.Dereference(error); | |
| if (!result || error.Fail()) { | |
| if (ValueObjectSP alt_obj = GetAlternateValue(valobj, synth_traversal)) { | |
| error.Clear(); | |
| result = alt_obj->Dereference(error); | |
| } | |
| } | |
| return result; | |
| } | |
| 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 = DereferenceValueOrAlternate( | |
| *ret_val, options.m_synthetic_children_traversal, 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) { | |
| if (ValueObjectSP altroot = GetAlternateValue( | |
| *root, options.m_synthetic_children_traversal)) | |
| child_valobj_sp = altroot->GetChildMemberWithName(child_name); | |
| } | |
| if (child_valobj_sp) { | |
| *reason_to_stop = | |
| ValueObject::eExpressionPathScanEndReasonEndOfString; | |
| *final_result = ValueObject::eExpressionPathEndResultTypePlain; | |
| return child_valobj_sp; | |
| } | |
| *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; | |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; | |
| return nullptr; | |
| } | |
| 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) { | |
| if (ValueObjectSP altroot = GetAlternateValue( | |
| *root, options.m_synthetic_children_traversal)) | |
| child_valobj_sp = altroot->GetChildMemberWithName(child_name); | |
| } | |
| if (child_valobj_sp) { | |
| root = child_valobj_sp; | |
| remainder = next_separator; | |
| *final_result = ValueObject::eExpressionPathEndResultTypePlain; | |
| continue; | |
| } | |
| *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; | |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; | |
| return nullptr; | |
| } | |
| 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.size() == 1) { | |
| *reason_to_stop = | |
| ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; | |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; | |
| return nullptr; | |
| } | |
| 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; | |
| } | |
| if (root->GetValueType() == eValueTypeRegister && | |
| !root_compiler_type_info.Test(eTypeIsPointer) && | |
| index > UINT32_MAX) { | |
| *reason_to_stop = | |
| ValueObject::eExpressionPathScanEndReasonNoSuchChild; | |
| *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::expectedToOptional( | |
| 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 = DereferenceValueOrAlternate( | |
| *root, options.m_synthetic_children_traversal, 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->GetValueType() == eValueTypeRegister && | |
| high_index > UINT32_MAX) { | |
| *reason_to_stop = | |
| ValueObject::eExpressionPathScanEndReasonNoSuchChild; | |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; | |
| return nullptr; | |
| } | |
| 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 = DereferenceValueOrAlternate( | |
| *root, options.m_synthetic_children_traversal, 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().address); | |
| } | |
| 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(); | |
| std::string deref_name_str; | |
| uint32_t deref_byte_size = 0; | |
| int32_t deref_byte_offset = 0; | |
| CompilerType compiler_type = GetCompilerType(); | |
| uint64_t language_flags = 0; | |
| ExecutionContext exe_ctx(GetExecutionContextRef()); | |
| CompilerType deref_compiler_type; | |
| auto deref_compiler_type_or_err = compiler_type.GetDereferencedType( | |
| &exe_ctx, deref_name_str, deref_byte_size, deref_byte_offset, this, | |
| language_flags); | |
| std::string deref_error; | |
| if (deref_compiler_type_or_err) { | |
| deref_compiler_type = *deref_compiler_type_or_err; | |
| } else { | |
| deref_error = llvm::toString(deref_compiler_type_or_err.takeError()); | |
| LLDB_LOG(GetLog(LLDBLog::Types), "could not find child: {0}", deref_error); | |
| } | |
| if (deref_compiler_type && deref_byte_size) { | |
| ConstString deref_name; | |
| if (!deref_name_str.empty()) | |
| deref_name.SetCString(deref_name_str.c_str()); | |
| m_deref_valobj = | |
| new ValueObjectChild(*this, deref_compiler_type, deref_name, | |
| deref_byte_size, deref_byte_offset, 0, 0, false, | |
| true, eAddressTypeInvalid, language_flags); | |
| } | |
| // In case of incomplete deref 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()) { | |
| deref_compiler_type = compiler_type.GetPointeeType(); | |
| if (deref_compiler_type) { | |
| ConstString deref_name; | |
| if (!deref_name_str.empty()) | |
| deref_name.SetCString(deref_name_str.c_str()); | |
| m_deref_valobj = new ValueObjectChild( | |
| *this, deref_compiler_type, deref_name, deref_byte_size, | |
| deref_byte_offset, 0, 0, false, true, eAddressTypeInvalid, | |
| language_flags); | |
| } | |
| } | |
| } | |
| if (!m_deref_valobj && IsSynthetic()) | |
| m_deref_valobj = GetChildMemberWithName("$$dereference$$").get(); | |
| if (m_deref_valobj) { | |
| error.Clear(); | |
| return m_deref_valobj->GetSP(); | |
| } else { | |
| StreamString strm; | |
| GetExpressionPath(strm); | |
| if (deref_error.empty()) | |
| error = Status::FromErrorStringWithFormat( | |
| "dereference failed: (%s) %s", | |
| GetTypeName().AsCString("<invalid type>"), strm.GetData()); | |
| else | |
| error = Status::FromErrorStringWithFormat( | |
| "dereference failed: %s: (%s) %s", deref_error.c_str(), | |
| GetTypeName().AsCString("<invalid type>"), strm.GetData()); | |
| return ValueObjectSP(); | |
| } | |
| } | |
| ValueObjectSP ValueObject::AddressOf(Status &error) { | |
| auto [addr, address_type] = GetAddressOf(/*scalar_is_load_address=*/false); | |
| 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: { | |
| if (m_addr_of_valobj_sp && | |
| m_addr_of_valobj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS) == addr) | |
| return m_addr_of_valobj_sp; | |
| m_addr_of_valobj_sp.reset(); | |
| CompilerType compiler_type = GetCompilerType(); | |
| if (compiler_type) { | |
| std::string name(1, '&'); | |
| name.append(m_name.AsCString("")); | |
| ExecutionContext exe_ctx(GetExecutionContextRef()); | |
| lldb::DataBufferSP buffer( | |
| new lldb_private::DataBufferHeap(&addr, sizeof(lldb::addr_t))); | |
| m_addr_of_valobj_sp = ValueObjectConstResult::Create( | |
| exe_ctx.GetBestExecutionContextScope(), | |
| compiler_type.GetPointerType(), ConstString(name), buffer, | |
| endian::InlHostByteOrder(), exe_ctx.GetAddressByteSize(), | |
| LLDB_INVALID_ADDRESS, this->GetManager()); | |
| } | |
| } 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(llvm::StringRef new_name) { | |
| return ValueObjectCast::Create(*this, new_name, GetCompilerType()); | |
| } | |
| ValueObjectSP ValueObject::CastPointerType(const char *name, | |
| CompilerType &compiler_type) { | |
| ValueObjectSP valobj_sp; | |
| addr_t ptr_value = GetPointerValue().address; | |
| 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; | |
| addr_t ptr_value = GetPointerValue().address; | |
| 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() { | |
| if (auto target_sp = GetTargetSP()) { | |
| const bool scalar_is_load_address = true; | |
| auto [addr_value, addr_type] = GetAddressOf(scalar_is_load_address); | |
| 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; | |
| } | |
| return LLDB_INVALID_ADDRESS; | |
| } | |
| 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::createStringError( | |
| "Invalid target type: should be a pointer or a reference"); | |
| 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::createStringError( | |
| "Underlying start & target types should be record types"); | |
| if (target_record_type.CompareTypes(start_record_type)) | |
| return llvm::createStringError( | |
| "Underlying start & target types should be different"); | |
| if (base_type_indices.empty()) | |
| return llvm::createStringError("children sequence must be non-empty"); | |
| // 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::createStringError( | |
| "casted value doesn't match the desired type"); | |
| 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::createStringError( | |
| "casted value doesn't match the desired type"); | |
| 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::createStringError( | |
| "Invalid target type: should be a pointer or a reference"); | |
| 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::createStringError( | |
| "Underlying start & target types should be record types"); | |
| if (target_record_type.CompareTypes(start_record_type)) | |
| return llvm::createStringError( | |
| "Underlying start & target types should be different"); | |
| CompilerType virtual_base; | |
| if (target_record_type.IsVirtualBase(start_record_type, &virtual_base)) { | |
| if (!virtual_base.IsValid()) | |
| return llvm::createStringError("virtual base should be valid"); | |
| return llvm::createStringError( | |
| llvm::Twine("cannot cast " + start_type.TypeDescription() + " to " + | |
| type.TypeDescription() + " via virtual base " + | |
| virtual_base.TypeDescription()) | |
| .str()); | |
| } | |
| // 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 = HasFloatingRepresentation(GetCompilerType()); | |
| 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( | |
| exe_ctx, type.GetTypeSystem().GetSharedPointer(), | |
| GetValueAsUnsigned(0) != 0, "result"); | |
| else if (is_scalar && is_float) { | |
| auto float_value_or_err = GetValueAsAPFloat(); | |
| if (float_value_or_err) | |
| return ValueObject::CreateValueObjectFromBool( | |
| exe_ctx, type.GetTypeSystem().GetSharedPointer(), | |
| !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(exe_ctx, 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 cast from float to integer")); | |
| return ValueObject::CreateValueObjectFromAPInt(exe_ctx, integer, 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())); | |
| } | |
| } | |
| } | |
| if (HasFloatingRepresentation(type)) { | |
| 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.GetBasicTypeEnumeration()); | |
| return ValueObject::CreateValueObjectFromAPFloat(exe_ctx, 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.GetBasicTypeEnumeration()); | |
| return ValueObject::CreateValueObjectFromAPFloat(exe_ctx, 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.GetBasicTypeEnumeration()); | |
| return ValueObject::CreateValueObjectFromAPFloat(exe_ctx, 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 = HasFloatingRepresentation(GetCompilerType()); | |
| 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.IsEnumerationIntegerTypeSigned()); | |
| 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::FromErrorString("invalid cast from float to integer")); | |
| return ValueObject::CreateValueObjectFromAPInt(exe_ctx, integer, type, | |
| "result"); | |
| } else | |
| return ValueObjectConstResult::Create( | |
| exe_ctx.GetBestExecutionContextScope(), | |
| Status::FromErrorStringWithFormatv( | |
| "cannot get value as APFloat: {0}", | |
| llvm::toString(value_or_err.takeError()))); | |
| } 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(exe_ctx, 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, ValueObject *parent) { | |
| return CreateValueObjectFromExpression(name, expression, exe_ctx, | |
| EvaluateExpressionOptions(), parent); | |
| } | |
| lldb::ValueObjectSP ValueObject::CreateValueObjectFromExpression( | |
| llvm::StringRef name, llvm::StringRef expression, | |
| const ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options, | |
| ValueObject *parent) { | |
| // FIXME: I haven't handled parent in this case yet. That is a WHOLE lot of | |
| // plumbing. | |
| 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(name); | |
| return retval_sp; | |
| } | |
| lldb::ValueObjectSP ValueObject::CreateValueObjectFromAddress( | |
| llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, | |
| CompilerType type, bool do_deref, ValueObject *parent) { | |
| 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(), /*address=*/LLDB_INVALID_ADDRESS, | |
| parent ? parent->GetManager() : nullptr)); | |
| 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(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, ValueObject *parent) { | |
| lldb::ValueObjectSP new_value_sp; | |
| new_value_sp = ValueObjectConstResult::Create( | |
| exe_ctx.GetBestExecutionContextScope(), type, ConstString(name), data, | |
| LLDB_INVALID_ADDRESS, parent ? parent->GetManager() : nullptr); | |
| new_value_sp->SetAddressTypeOfChildren(eAddressTypeLoad); | |
| if (new_value_sp && !name.empty()) | |
| new_value_sp->SetName(name); | |
| return new_value_sp; | |
| } | |
| lldb::ValueObjectSP ValueObject::CreateValueObjectFromAPInt( | |
| const ExecutionContext &exe_ctx, const llvm::APInt &v, CompilerType type, | |
| llvm::StringRef name, ValueObject *parent) { | |
| uint64_t byte_size = | |
| llvm::expectedToOptional( | |
| type.GetByteSize(exe_ctx.GetBestExecutionContextScope())) | |
| .value_or(0); | |
| 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, | |
| parent); | |
| } | |
| lldb::ValueObjectSP ValueObject::CreateValueObjectFromAPFloat( | |
| const ExecutionContext &exe_ctx, const llvm::APFloat &v, CompilerType type, | |
| llvm::StringRef name, ValueObject *parent) { | |
| return CreateValueObjectFromAPInt(exe_ctx, v.bitcastToAPInt(), type, name, | |
| parent); | |
| } | |
| lldb::ValueObjectSP ValueObject::CreateValueObjectFromScalar( | |
| const ExecutionContext &exe_ctx, Scalar &s, CompilerType type, | |
| llvm::StringRef name, ValueObject *parent) { | |
| return ValueObjectConstResult::Create( | |
| exe_ctx.GetBestExecutionContextScope(), type, s, ConstString(name), | |
| /*module_ptr=*/nullptr, parent ? parent->GetManager() : nullptr); | |
| } | |
| lldb::ValueObjectSP ValueObject::CreateValueObjectFromBool( | |
| const ExecutionContext &exe_ctx, TypeSystemSP typesystem_sp, bool value, | |
| llvm::StringRef name, ValueObject *parent) { | |
| CompilerType type = typesystem_sp->GetBasicTypeFromAST(lldb::eBasicTypeBool); | |
| ExecutionContextScope *exe_scope = exe_ctx.GetBestExecutionContextScope(); | |
| uint64_t byte_size = | |
| llvm::expectedToOptional(type.GetByteSize(exe_scope)).value_or(0); | |
| 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, type, | |
| parent); | |
| } | |
| lldb::ValueObjectSP ValueObject::CreateValueObjectFromNullptr( | |
| const ExecutionContext &exe_ctx, CompilerType type, llvm::StringRef name, | |
| ValueObject *parent) { | |
| if (!type.IsNullPtrType()) { | |
| lldb::ValueObjectSP ret_val; | |
| return ret_val; | |
| } | |
| uintptr_t zero = 0; | |
| uint64_t byte_size = 0; | |
| if (auto temp = llvm::expectedToOptional( | |
| type.GetByteSize(exe_ctx.GetBestExecutionContextScope()))) | |
| 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, | |
| parent); | |
| } | |
| 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); | |
| } | |
| ValueImpl::ValueImpl(lldb::ValueObjectSP in_valobj_sp, | |
| lldb::DynamicValueType use_dynamic, bool use_synthetic, | |
| const char *name) | |
| : m_use_dynamic(use_dynamic), m_use_synthetic(use_synthetic), m_name(name) { | |
| if (in_valobj_sp) { | |
| if ((m_valobj_sp = in_valobj_sp->GetQualifiedRepresentationIfAvailable( | |
| lldb::eNoDynamicValues, false))) { | |
| if (!m_name.IsEmpty()) | |
| m_valobj_sp->SetName(m_name); | |
| } | |
| } | |
| } | |
| ValueImpl &ValueImpl::operator=(const ValueImpl &rhs) { | |
| if (this != &rhs) { | |
| m_valobj_sp = rhs.m_valobj_sp; | |
| m_use_dynamic = rhs.m_use_dynamic; | |
| m_use_synthetic = rhs.m_use_synthetic; | |
| m_name = rhs.m_name; | |
| } | |
| return *this; | |
| } | |
| bool ValueImpl::IsValid() { | |
| if (m_valobj_sp.get() == nullptr) | |
| return false; | |
| // FIXME: This check is necessary but not sufficient. We for sure don't | |
| // want to touch SBValues whose owning | |
| // targets have gone away. This check is a little weak in that it | |
| // enforces that restriction when you call IsValid, but since IsValid | |
| // doesn't lock the target, you have no guarantee that the SBValue won't | |
| // go invalid after you call this... Also, an SBValue could depend on | |
| // data from one of the modules in the target, and those could go away | |
| // independently of the target, for instance if a module is unloaded. | |
| // But right now, neither SBValues nor ValueObjects know which modules | |
| // they depend on. So I have no good way to make that check without | |
| // tracking that in all the ValueObject subclasses. | |
| TargetSP target_sp = m_valobj_sp->GetTargetSP(); | |
| return target_sp && target_sp->IsValid(); | |
| } | |
| lldb::ValueObjectSP ValueImpl::GetSP(Process::StopLocker &stop_locker, | |
| TargetAPIMutex &api_mutex, | |
| std::unique_lock<TargetAPIMutex> &lock, | |
| Status &error) { | |
| if (!m_valobj_sp) { | |
| error = Status::FromErrorString("invalid value object"); | |
| return m_valobj_sp; | |
| } | |
| lldb::ValueObjectSP value_sp = m_valobj_sp; | |
| Target *target = value_sp->GetTargetSP().get(); | |
| // If this ValueObject holds an error, then it is valuable for that. | |
| if (value_sp->GetError().Fail()) | |
| return value_sp; | |
| if (!target) | |
| return ValueObjectSP(); | |
| api_mutex = target->GetAPIMutex(); | |
| lock = std::unique_lock<TargetAPIMutex>(api_mutex); | |
| ProcessSP process_sp(value_sp->GetProcessSP()); | |
| if (process_sp && !stop_locker.TryLock(&process_sp->GetRunLock())) { | |
| // We don't allow people to play around with ValueObject if the process | |
| // is running. If you want to look at values, pause the process, then | |
| // look. | |
| error = Status::FromErrorString("process must be stopped."); | |
| return ValueObjectSP(); | |
| } | |
| if (m_use_dynamic != eNoDynamicValues) { | |
| ValueObjectSP dynamic_sp = value_sp->GetDynamicValue(m_use_dynamic); | |
| if (dynamic_sp) | |
| value_sp = dynamic_sp; | |
| } | |
| if (m_use_synthetic) { | |
| ValueObjectSP synthetic_sp = value_sp->GetSyntheticValue(); | |
| if (synthetic_sp) | |
| value_sp = synthetic_sp; | |
| } | |
| if (!value_sp) | |
| error = Status::FromErrorString("invalid value object"); | |
| if (!m_name.IsEmpty()) | |
| value_sp->SetName(m_name); | |
| return value_sp; | |
| } |