| //===-- 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/Core/ValueObject.h" |
| |
| #include "lldb/Core/Address.h" |
| #include "lldb/Core/Declaration.h" |
| #include "lldb/Core/Module.h" |
| #include "lldb/Core/ValueObjectCast.h" |
| #include "lldb/Core/ValueObjectChild.h" |
| #include "lldb/Core/ValueObjectConstResult.h" |
| #include "lldb/Core/ValueObjectDynamicValue.h" |
| #include "lldb/Core/ValueObjectMemory.h" |
| #include "lldb/Core/ValueObjectSyntheticFilter.h" |
| #include "lldb/DataFormatters/DataVisualization.h" |
| #include "lldb/DataFormatters/DumpValueObjectOptions.h" |
| #include "lldb/DataFormatters/FormatManager.h" |
| #include "lldb/DataFormatters/StringPrinter.h" |
| #include "lldb/DataFormatters/TypeFormat.h" |
| #include "lldb/DataFormatters/TypeSummary.h" |
| #include "lldb/DataFormatters/ValueObjectPrinter.h" |
| #include "lldb/Expression/ExpressionVariable.h" |
| #include "lldb/Host/Config.h" |
| #include "lldb/Symbol/CompileUnit.h" |
| #include "lldb/Symbol/CompilerType.h" |
| #include "lldb/Symbol/SymbolContext.h" |
| #include "lldb/Symbol/Type.h" |
| #include "lldb/Symbol/Variable.h" |
| #include "lldb/Target/ExecutionContext.h" |
| #include "lldb/Target/Language.h" |
| #include "lldb/Target/LanguageRuntime.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/StackFrame.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Target/Thread.h" |
| #include "lldb/Target/ThreadList.h" |
| #include "lldb/Utility/DataBuffer.h" |
| #include "lldb/Utility/DataBufferHeap.h" |
| #include "lldb/Utility/Flags.h" |
| #include "lldb/Utility/Log.h" |
| #include "lldb/Utility/Logging.h" |
| #include "lldb/Utility/Scalar.h" |
| #include "lldb/Utility/Stream.h" |
| #include "lldb/Utility/StreamString.h" |
| #include "lldb/lldb-private-types.h" |
| |
| #include "llvm/Support/Compiler.h" |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <cstdlib> |
| #include <memory> |
| #include <tuple> |
| |
| #include <cassert> |
| #include <cinttypes> |
| #include <cstdio> |
| #include <cstring> |
| |
| #include <lldb/Core/ValueObject.h> |
| |
| namespace lldb_private { |
| class ExecutionContextScope; |
| } |
| namespace lldb_private { |
| class SymbolContextScope; |
| } |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| static user_id_t g_value_obj_uid = 0; |
| |
| // ValueObject constructor |
| ValueObject::ValueObject(ValueObject &parent) |
| : m_parent(&parent), m_update_point(parent.GetUpdatePoint()), |
| m_manager(parent.GetManager()), m_id(++g_value_obj_uid) { |
| m_flags.m_is_synthetic_children_generated = |
| parent.m_flags.m_is_synthetic_children_generated; |
| m_data.SetByteOrder(parent.GetDataExtractor().GetByteOrder()); |
| m_data.SetAddressByteSize(parent.GetDataExtractor().GetAddressByteSize()); |
| m_manager->ManageObject(this); |
| } |
| |
| // ValueObject constructor |
| ValueObject::ValueObject(ExecutionContextScope *exe_scope, |
| ValueObjectManager &manager, |
| AddressType child_ptr_or_ref_addr_type) |
| : m_update_point(exe_scope), m_manager(&manager), |
| m_address_type_of_ptr_or_ref_children(child_ptr_or_ref_addr_type), |
| m_id(++g_value_obj_uid) { |
| if (exe_scope) { |
| TargetSP target_sp(exe_scope->CalculateTarget()); |
| if (target_sp) { |
| const ArchSpec &arch = target_sp->GetArchitecture(); |
| m_data.SetByteOrder(arch.GetByteOrder()); |
| m_data.SetAddressByteSize(arch.GetAddressByteSize()); |
| } |
| } |
| m_manager->ManageObject(this); |
| } |
| |
| // Destructor |
| ValueObject::~ValueObject() = default; |
| |
| bool ValueObject::UpdateValueIfNeeded(bool update_format) { |
| |
| bool did_change_formats = false; |
| |
| if (update_format) |
| did_change_formats = UpdateFormatsIfNeeded(); |
| |
| // If this is a constant value, then our success is predicated on whether we |
| // have an error or not |
| if (GetIsConstant()) { |
| // if you are constant, things might still have changed behind your back |
| // (e.g. you are a frozen object and things have changed deeper than you |
| // cared to freeze-dry yourself) in this case, your value has not changed, |
| // but "computed" entries might have, so you might now have a different |
| // summary, or a different object description. clear these so we will |
| // recompute them |
| if (update_format && !did_change_formats) |
| ClearUserVisibleData(eClearUserVisibleDataItemsSummary | |
| eClearUserVisibleDataItemsDescription); |
| return m_error.Success(); |
| } |
| |
| bool first_update = IsChecksumEmpty(); |
| |
| if (NeedsUpdating()) { |
| m_update_point.SetUpdated(); |
| |
| // Save the old value using swap to avoid a string copy which also will |
| // clear our m_value_str |
| if (m_value_str.empty()) { |
| m_flags.m_old_value_valid = false; |
| } else { |
| m_flags.m_old_value_valid = true; |
| m_old_value_str.swap(m_value_str); |
| ClearUserVisibleData(eClearUserVisibleDataItemsValue); |
| } |
| |
| ClearUserVisibleData(); |
| |
| if (IsInScope()) { |
| const bool value_was_valid = GetValueIsValid(); |
| SetValueDidChange(false); |
| |
| m_error.Clear(); |
| |
| // Call the pure virtual function to update the value |
| |
| bool need_compare_checksums = false; |
| llvm::SmallVector<uint8_t, 16> old_checksum; |
| |
| if (!first_update && CanProvideValue()) { |
| need_compare_checksums = true; |
| old_checksum.resize(m_value_checksum.size()); |
| std::copy(m_value_checksum.begin(), m_value_checksum.end(), |
| old_checksum.begin()); |
| } |
| |
| bool success = UpdateValue(); |
| |
| SetValueIsValid(success); |
| |
| if (success) { |
| UpdateChildrenAddressType(); |
| const uint64_t max_checksum_size = 128; |
| m_data.Checksum(m_value_checksum, max_checksum_size); |
| } else { |
| need_compare_checksums = false; |
| m_value_checksum.clear(); |
| } |
| |
| assert(!need_compare_checksums || |
| (!old_checksum.empty() && !m_value_checksum.empty())); |
| |
| if (first_update) |
| SetValueDidChange(false); |
| else if (!m_flags.m_value_did_change && !success) { |
| // The value wasn't gotten successfully, so we mark this as changed if |
| // the value used to be valid and now isn't |
| SetValueDidChange(value_was_valid); |
| } else if (need_compare_checksums) { |
| SetValueDidChange(memcmp(&old_checksum[0], &m_value_checksum[0], |
| m_value_checksum.size())); |
| } |
| |
| } else { |
| m_error.SetErrorString("out of scope"); |
| } |
| } |
| return m_error.Success(); |
| } |
| |
| bool ValueObject::UpdateFormatsIfNeeded() { |
| Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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, eNoDynamicValues)); |
| SetSummaryFormat( |
| DataVisualization::GetSummaryFormat(*this, GetDynamicValueType())); |
| #if LLDB_ENABLE_PYTHON |
| SetSyntheticChildren( |
| DataVisualization::GetSyntheticChildren(*this, GetDynamicValueType())); |
| #endif |
| } |
| |
| return any_change; |
| } |
| |
| void ValueObject::SetNeedsUpdate() { |
| m_update_point.SetNeedsUpdate(); |
| // We have to clear the value string here so ConstResult children will notice |
| // if their values are changed by hand (i.e. with SetValueAsCString). |
| ClearUserVisibleData(eClearUserVisibleDataItemsValue); |
| } |
| |
| void ValueObject::ClearDynamicTypeInformation() { |
| m_flags.m_children_count_valid = false; |
| m_flags.m_did_calculate_complete_objc_class_type = false; |
| m_last_format_mgr_revision = 0; |
| m_override_type = CompilerType(); |
| SetValueFormat(lldb::TypeFormatImplSP()); |
| SetSummaryFormat(lldb::TypeSummaryImplSP()); |
| SetSyntheticChildren(lldb::SyntheticChildrenSP()); |
| } |
| |
| CompilerType ValueObject::MaybeCalculateCompleteType() { |
| CompilerType compiler_type(GetCompilerTypeImpl()); |
| |
| if (m_flags.m_did_calculate_complete_objc_class_type) { |
| if (m_override_type.IsValid()) |
| return m_override_type; |
| else |
| return compiler_type; |
| } |
| |
| m_flags.m_did_calculate_complete_objc_class_type = true; |
| |
| ProcessSP process_sp( |
| GetUpdatePoint().GetExecutionContextRef().GetProcessSP()); |
| |
| if (!process_sp) |
| return compiler_type; |
| |
| if (auto *runtime = |
| process_sp->GetLanguageRuntime(GetObjectRuntimeLanguage())) { |
| if (llvm::Optional<CompilerType> complete_type = |
| runtime->GetRuntimeType(compiler_type)) { |
| m_override_type = complete_type.getValue(); |
| 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); |
| 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.SetErrorString("failed to get a scalar result"); |
| return false; |
| } |
| |
| bool ret; |
| ret = scalar_value.ULongLong(1) != 0; |
| error.Clear(); |
| return ret; |
| } |
| |
| ValueObjectSP ValueObject::GetChildAtIndex(size_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 < GetNumChildren()) { |
| // 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, false, 0)); |
| } |
| |
| ValueObject *child = m_children.GetChildAtIndex(idx); |
| if (child != nullptr) |
| return child->GetSP(); |
| } |
| return child_sp; |
| } |
| |
| lldb::ValueObjectSP |
| ValueObject::GetChildAtIndexPath(llvm::ArrayRef<size_t> idxs, |
| size_t *index_of_error) { |
| if (idxs.size() == 0) |
| return GetSP(); |
| ValueObjectSP root(GetSP()); |
| for (size_t idx : idxs) { |
| root = root->GetChildAtIndex(idx, true); |
| if (!root) { |
| if (index_of_error) |
| *index_of_error = idx; |
| return root; |
| } |
| } |
| return root; |
| } |
| |
| lldb::ValueObjectSP ValueObject::GetChildAtIndexPath( |
| llvm::ArrayRef<std::pair<size_t, bool>> idxs, size_t *index_of_error) { |
| if (idxs.size() == 0) |
| return GetSP(); |
| ValueObjectSP root(GetSP()); |
| for (std::pair<size_t, bool> idx : idxs) { |
| root = root->GetChildAtIndex(idx.first, idx.second); |
| if (!root) { |
| if (index_of_error) |
| *index_of_error = idx.first; |
| return root; |
| } |
| } |
| return root; |
| } |
| |
| lldb::ValueObjectSP |
| ValueObject::GetChildAtNamePath(llvm::ArrayRef<ConstString> names, |
| ConstString *name_of_error) { |
| if (names.size() == 0) |
| return GetSP(); |
| ValueObjectSP root(GetSP()); |
| for (ConstString name : names) { |
| root = root->GetChildMemberWithName(name, true); |
| if (!root) { |
| if (name_of_error) |
| *name_of_error = name; |
| return root; |
| } |
| } |
| return root; |
| } |
| |
| lldb::ValueObjectSP ValueObject::GetChildAtNamePath( |
| llvm::ArrayRef<std::pair<ConstString, bool>> names, |
| ConstString *name_of_error) { |
| if (names.size() == 0) |
| return GetSP(); |
| ValueObjectSP root(GetSP()); |
| for (std::pair<ConstString, bool> name : names) { |
| root = root->GetChildMemberWithName(name.first, name.second); |
| if (!root) { |
| if (name_of_error) |
| *name_of_error = name.first; |
| return root; |
| } |
| } |
| return root; |
| } |
| |
| size_t ValueObject::GetIndexOfChildWithName(ConstString name) { |
| bool omit_empty_base_classes = true; |
| return GetCompilerType().GetIndexOfChildWithName(name.GetCString(), |
| omit_empty_base_classes); |
| } |
| |
| ValueObjectSP ValueObject::GetChildMemberWithName(ConstString 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.GetCString(), 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; |
| } |
| |
| size_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) { |
| SetNumChildren(CalculateNumChildren()); |
| } |
| return m_children.GetChildrenCount(); |
| } |
| |
| 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 = GetNumChildren() > 0; |
| } |
| return has_children; |
| } |
| |
| // Should only be called by ValueObject::GetNumChildren() |
| void ValueObject::SetNumChildren(size_t num_children) { |
| m_flags.m_children_count_valid = true; |
| m_children.SetChildrenCount(num_children); |
| } |
| |
| ValueObject *ValueObject::CreateChildAtIndex(size_t idx, |
| bool synthetic_array_member, |
| int32_t synthetic_index) { |
| ValueObject *valobj = nullptr; |
| |
| bool omit_empty_base_classes = true; |
| bool ignore_array_bounds = synthetic_array_member; |
| std::string child_name_str; |
| uint32_t child_byte_size = 0; |
| int32_t child_byte_offset = 0; |
| uint32_t child_bitfield_bit_size = 0; |
| uint32_t child_bitfield_bit_offset = 0; |
| bool child_is_base_class = false; |
| bool child_is_deref_of_parent = false; |
| uint64_t language_flags = 0; |
| |
| const bool transparent_pointers = !synthetic_array_member; |
| CompilerType child_compiler_type; |
| |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| |
| child_compiler_type = GetCompilerType().GetChildCompilerTypeAtIndex( |
| &exe_ctx, idx, transparent_pointers, omit_empty_base_classes, |
| ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset, |
| child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, |
| child_is_deref_of_parent, this, language_flags); |
| if (child_compiler_type) { |
| if (synthetic_index) |
| child_byte_offset += child_byte_size * synthetic_index; |
| |
| ConstString child_name; |
| if (!child_name_str.empty()) |
| child_name.SetCString(child_name_str.c_str()); |
| |
| valobj = new ValueObjectChild( |
| *this, child_compiler_type, child_name, child_byte_size, |
| child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, |
| child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid, |
| language_flags); |
| } |
| |
| // In case of an incomplete type, try to use the ValueObject's |
| // synthetic value to create the child ValueObject. |
| if (!valobj && synthetic_array_member) { |
| if (ValueObjectSP synth_valobj_sp = GetSyntheticValue()) { |
| valobj = synth_valobj_sp |
| ->GetChildAtIndex(synthetic_index, synthetic_array_member) |
| .get(); |
| } |
| } |
| |
| return valobj; |
| } |
| |
| 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(); |
| |
| // 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%#}) |
| summary_ptr->FormatObject(this, destination, actual_options); |
| } |
| m_flags.m_is_getting_summary = false; |
| return !destination.empty(); |
| } |
| |
| const char *ValueObject::GetSummaryAsCString(lldb::LanguageType lang) { |
| if (UpdateValueIfNeeded(true) && m_summary_str.empty()) { |
| TypeSummaryOptions summary_options; |
| summary_options.SetLanguage(lang); |
| GetSummaryAsCString(GetSummaryFormat().get(), m_summary_str, |
| summary_options); |
| } |
| if (m_summary_str.empty()) |
| return nullptr; |
| return m_summary_str.c_str(); |
| } |
| |
| bool ValueObject::GetSummaryAsCString(std::string &destination, |
| const TypeSummaryOptions &options) { |
| return GetSummaryAsCString(GetSummaryFormat().get(), destination, options); |
| } |
| |
| bool ValueObject::IsCStringContainer(bool check_pointer) { |
| CompilerType pointee_or_element_compiler_type; |
| const Flags type_flags(GetTypeInfo(&pointee_or_element_compiler_type)); |
| bool is_char_arr_ptr(type_flags.AnySet(eTypeIsArray | eTypeIsPointer) && |
| pointee_or_element_compiler_type.IsCharType()); |
| if (!is_char_arr_ptr) |
| return false; |
| if (!check_pointer) |
| return true; |
| if (type_flags.Test(eTypeIsArray)) |
| return true; |
| addr_t cstr_address = LLDB_INVALID_ADDRESS; |
| AddressType cstr_address_type = eAddressTypeInvalid; |
| cstr_address = GetPointerValue(&cstr_address_type); |
| return (cstr_address != LLDB_INVALID_ADDRESS); |
| } |
| |
| size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, |
| uint32_t item_count) { |
| CompilerType pointee_or_element_compiler_type; |
| const uint32_t type_info = GetTypeInfo(&pointee_or_element_compiler_type); |
| const bool is_pointer_type = type_info & eTypeIsPointer; |
| const bool is_array_type = type_info & eTypeIsArray; |
| if (!(is_pointer_type || is_array_type)) |
| return 0; |
| |
| if (item_count == 0) |
| return 0; |
| |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| |
| llvm::Optional<uint64_t> item_type_size = |
| 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, true); |
| if (child_sp.get() == nullptr) |
| return 0; |
| Status error; |
| return child_sp->GetData(data, error); |
| } |
| return true; |
| } else /* (items > 1) */ |
| { |
| Status error; |
| lldb_private::DataBufferHeap *heap_buf_ptr = nullptr; |
| lldb::DataBufferSP data_sp(heap_buf_ptr = |
| new lldb_private::DataBufferHeap()); |
| |
| AddressType addr_type; |
| lldb::addr_t addr = is_pointer_type ? GetPointerValue(&addr_type) |
| : GetAddressOf(true, &addr_type); |
| |
| switch (addr_type) { |
| case eAddressTypeFile: { |
| ModuleSP module_sp(GetModule()); |
| if (module_sp) { |
| addr = addr + offset; |
| Address so_addr; |
| module_sp->ResolveFileAddress(addr, so_addr); |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| Target *target = exe_ctx.GetTargetPtr(); |
| if (target) { |
| heap_buf_ptr->SetByteSize(bytes); |
| size_t bytes_read = target->ReadMemory( |
| so_addr, heap_buf_ptr->GetBytes(), bytes, error, true); |
| if (error.Success()) { |
| data.SetData(data_sp); |
| return bytes_read; |
| } |
| } |
| } |
| } break; |
| case eAddressTypeLoad: { |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| Process *process = exe_ctx.GetProcessPtr(); |
| if (process) { |
| heap_buf_ptr->SetByteSize(bytes); |
| size_t bytes_read = process->ReadMemory( |
| addr + offset, heap_buf_ptr->GetBytes(), bytes, error); |
| if (error.Success() || bytes_read > 0) { |
| data.SetData(data_sp); |
| return bytes_read; |
| } |
| } |
| } break; |
| case eAddressTypeHost: { |
| auto max_bytes = |
| GetCompilerType().GetByteSize(exe_ctx.GetBestExecutionContextScope()); |
| if (max_bytes && *max_bytes > offset) { |
| size_t bytes_read = std::min<uint64_t>(*max_bytes - offset, bytes); |
| addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); |
| if (addr == 0 || addr == LLDB_INVALID_ADDRESS) |
| break; |
| heap_buf_ptr->CopyData((uint8_t *)(addr + offset), bytes_read); |
| data.SetData(data_sp); |
| return bytes_read; |
| } |
| } break; |
| case eAddressTypeInvalid: |
| break; |
| } |
| } |
| return 0; |
| } |
| |
| uint64_t ValueObject::GetData(DataExtractor &data, Status &error) { |
| UpdateValueIfNeeded(false); |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| error = m_value.GetValueAsData(&exe_ctx, data, GetModule().get()); |
| if (error.Fail()) { |
| if (m_data.GetByteSize()) { |
| data = m_data; |
| error.Clear(); |
| return data.GetByteSize(); |
| } else { |
| return 0; |
| } |
| } |
| data.SetAddressByteSize(m_data.GetAddressByteSize()); |
| data.SetByteOrder(m_data.GetByteOrder()); |
| return data.GetByteSize(); |
| } |
| |
| bool ValueObject::SetData(DataExtractor &data, Status &error) { |
| error.Clear(); |
| // Make sure our value is up to date first so that our location and location |
| // type is valid. |
| if (!UpdateValueIfNeeded(false)) { |
| error.SetErrorString("unable to read value"); |
| return false; |
| } |
| |
| uint64_t count = 0; |
| const Encoding encoding = GetCompilerType().GetEncoding(count); |
| |
| const size_t byte_size = GetByteSize().getValueOr(0); |
| |
| Value::ValueType value_type = m_value.GetValueType(); |
| |
| switch (value_type) { |
| case Value::ValueType::Invalid: |
| error.SetErrorString("invalid location"); |
| return false; |
| case Value::ValueType::Scalar: { |
| Status set_error = |
| m_value.GetScalar().SetValueFromData(data, encoding, byte_size); |
| |
| if (!set_error.Success()) { |
| error.SetErrorStringWithFormat("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.SetErrorString("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; |
| } |
| |
| static bool CopyStringDataToBufferSP(const StreamString &source, |
| lldb::DataBufferSP &destination) { |
| llvm::StringRef src = source.GetString(); |
| src.consume_back(llvm::StringRef("\0", 1)); |
| 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::DataBufferSP &buffer_sp, Status &error, |
| uint32_t max_length, bool honor_array, |
| Format item_format) { |
| bool was_capped = false; |
| StreamString s; |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| Target *target = exe_ctx.GetTargetPtr(); |
| |
| if (!target) { |
| s << "<no target to read from>"; |
| error.SetErrorString("no target to read from"); |
| CopyStringDataToBufferSP(s, buffer_sp); |
| return {0, was_capped}; |
| } |
| |
| if (max_length == 0) |
| max_length = target->GetMaximumSizeOfStringSummary(); |
| |
| size_t bytes_read = 0; |
| size_t total_bytes_read = 0; |
| |
| CompilerType compiler_type = GetCompilerType(); |
| CompilerType elem_or_pointee_compiler_type; |
| const Flags type_flags(GetTypeInfo(&elem_or_pointee_compiler_type)); |
| if (type_flags.AnySet(eTypeIsArray | eTypeIsPointer) && |
| elem_or_pointee_compiler_type.IsCharType()) { |
| addr_t cstr_address = LLDB_INVALID_ADDRESS; |
| AddressType cstr_address_type = eAddressTypeInvalid; |
| |
| size_t cstr_len = 0; |
| bool capped_data = false; |
| const bool is_array = type_flags.Test(eTypeIsArray); |
| if (is_array) { |
| // We have an array |
| uint64_t array_size = 0; |
| if (compiler_type.IsArrayType(nullptr, &array_size)) { |
| cstr_len = array_size; |
| if (cstr_len > max_length) { |
| capped_data = true; |
| cstr_len = max_length; |
| } |
| } |
| cstr_address = GetAddressOf(true, &cstr_address_type); |
| } else { |
| // We have a pointer |
| cstr_address = GetPointerValue(&cstr_address_type); |
| } |
| |
| if (cstr_address == 0 || cstr_address == LLDB_INVALID_ADDRESS) { |
| if (cstr_address_type == eAddressTypeHost && is_array) { |
| const char *cstr = GetDataExtractor().PeekCStr(0); |
| if (cstr == nullptr) { |
| s << "<invalid address>"; |
| error.SetErrorString("invalid address"); |
| CopyStringDataToBufferSP(s, buffer_sp); |
| return {0, was_capped}; |
| } |
| s << llvm::StringRef(cstr, cstr_len); |
| CopyStringDataToBufferSP(s, buffer_sp); |
| return {cstr_len, was_capped}; |
| } else { |
| s << "<invalid address>"; |
| error.SetErrorString("invalid address"); |
| CopyStringDataToBufferSP(s, buffer_sp); |
| return {0, was_capped}; |
| } |
| } |
| |
| Address cstr_so_addr(cstr_address); |
| DataExtractor data; |
| if (cstr_len > 0 && honor_array) { |
| // I am using GetPointeeData() here to abstract the fact that some |
| // ValueObjects are actually frozen pointers in the host but the pointed- |
| // to data lives in the debuggee, and GetPointeeData() automatically |
| // takes care of this |
| GetPointeeData(data, 0, cstr_len); |
| |
| if ((bytes_read = data.GetByteSize()) > 0) { |
| total_bytes_read = bytes_read; |
| for (size_t offset = 0; offset < bytes_read; offset++) |
| s.Printf("%c", *data.PeekData(offset, 1)); |
| if (capped_data) |
| was_capped = true; |
| } |
| } else { |
| cstr_len = max_length; |
| const size_t k_max_buf_size = 64; |
| |
| size_t offset = 0; |
| |
| int cstr_len_displayed = -1; |
| bool capped_cstr = false; |
| // I am using GetPointeeData() here to abstract the fact that some |
| // ValueObjects are actually frozen pointers in the host but the pointed- |
| // to data lives in the debuggee, and GetPointeeData() automatically |
| // takes care of this |
| while ((bytes_read = GetPointeeData(data, offset, k_max_buf_size)) > 0) { |
| total_bytes_read += bytes_read; |
| const char *cstr = data.PeekCStr(0); |
| size_t len = strnlen(cstr, k_max_buf_size); |
| if (cstr_len_displayed < 0) |
| cstr_len_displayed = len; |
| |
| if (len == 0) |
| break; |
| cstr_len_displayed += len; |
| if (len > bytes_read) |
| len = bytes_read; |
| if (len > cstr_len) |
| len = cstr_len; |
| |
| for (size_t offset = 0; offset < bytes_read; offset++) |
| s.Printf("%c", *data.PeekData(offset, 1)); |
| |
| if (len < k_max_buf_size) |
| break; |
| |
| if (len >= cstr_len) { |
| capped_cstr = true; |
| break; |
| } |
| |
| cstr_len -= len; |
| offset += len; |
| } |
| |
| if (cstr_len_displayed >= 0) { |
| if (capped_cstr) |
| was_capped = true; |
| } |
| } |
| } else { |
| error.SetErrorString("not a string object"); |
| s << "<not a string object>"; |
| } |
| CopyStringDataToBufferSP(s, buffer_sp); |
| return {total_bytes_read, was_capped}; |
| } |
| |
| const char *ValueObject::GetObjectDescription() { |
| if (!UpdateValueIfNeeded(true)) |
| return nullptr; |
| |
| // Return cached value. |
| if (!m_object_desc_str.empty()) |
| return m_object_desc_str.c_str(); |
| |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| Process *process = exe_ctx.GetProcessPtr(); |
| if (!process) |
| return nullptr; |
| |
| // Returns the object description produced by one language runtime. |
| auto get_object_description = [&](LanguageType language) -> const char * { |
| if (LanguageRuntime *runtime = process->GetLanguageRuntime(language)) { |
| StreamString s; |
| if (runtime->GetObjectDescription(s, *this)) { |
| m_object_desc_str.append(std::string(s.GetString())); |
| return m_object_desc_str.c_str(); |
| } |
| } |
| return nullptr; |
| }; |
| |
| // Try the native language runtime first. |
| LanguageType native_language = GetObjectRuntimeLanguage(); |
| if (const char *desc = get_object_description(native_language)) |
| 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)) |
| return get_object_description(eLanguageTypeObjC); |
| return nullptr; |
| } |
| |
| 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; |
| } |
| |
| // 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) { |
| |
| 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::DataBufferSP buffer_sp; |
| std::pair<size_t, bool> read_string = ReadPointedString( |
| buffer_sp, error, 0, (custom_format == eFormatVectorOfChar) || |
| (custom_format == eFormatCharArray)); |
| lldb_private::formatters::StringPrinter:: |
| ReadBufferAndDumpToStreamOptions options(*this); |
| options.SetData(DataExtractor( |
| buffer_sp, lldb::eByteOrderInvalid, |
| 8)); // none of this matters for a string - pass some defaults |
| options.SetStream(&s); |
| options.SetPrefixToken(nullptr); |
| options.SetQuote('"'); |
| options.SetSourceSize(buffer_sp->GetByteSize()); |
| options.SetIsTruncated(read_string.second); |
| options.SetBinaryZeroIsTerminator(custom_format != eFormatVectorOfChar); |
| formatters::StringPrinter::ReadBufferAndDumpToStream< |
| lldb_private::formatters::StringPrinter::StringElementType::ASCII>( |
| options); |
| return !error.Fail(); |
| } |
| |
| if (custom_format == eFormatEnum) |
| return false; |
| |
| // this only works for arrays, because I have no way to know when the |
| // pointed memory ends, and no special \0 end of data marker |
| if (flags.Test(eTypeIsArray)) { |
| if ((custom_format == eFormatBytes) || |
| (custom_format == eFormatBytesWithASCII)) { |
| const size_t count = GetNumChildren(); |
| |
| s << '['; |
| for (size_t low = 0; low < count; low++) { |
| |
| if (low) |
| s << ','; |
| |
| ValueObjectSP child = GetChildAtIndex(low, true); |
| 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 = GetNumChildren(); |
| |
| Format format = FormatManager::GetSingleItemFormat(custom_format); |
| |
| s << '['; |
| for (size_t low = 0; low < count; low++) { |
| |
| if (low) |
| s << ','; |
| |
| ValueObjectSP child = GetChildAtIndex(low, true); |
| if (!child.get()) { |
| s << "<invalid child>"; |
| continue; |
| } |
| child->DumpPrintableRepresentation( |
| s, ValueObject::eValueObjectRepresentationStyleValue, format); |
| } |
| |
| s << ']'; |
| |
| return true; |
| } |
| } |
| |
| if ((custom_format == eFormatBoolean) || |
| (custom_format == eFormatBinary) || (custom_format == eFormatChar) || |
| (custom_format == eFormatCharPrintable) || |
| (custom_format == eFormatComplexFloat) || |
| (custom_format == eFormatDecimal) || (custom_format == eFormatHex) || |
| (custom_format == eFormatHexUppercase) || |
| (custom_format == eFormatFloat) || (custom_format == eFormatOctal) || |
| (custom_format == eFormatOSType) || |
| (custom_format == eFormatUnicode16) || |
| (custom_format == eFormatUnicode32) || |
| (custom_format == eFormatUnsigned) || |
| (custom_format == eFormatPointer) || |
| (custom_format == eFormatComplexInteger) || |
| (custom_format == eFormatComplex) || |
| (custom_format == eFormatDefault)) // use the [] operator |
| return false; |
| } |
| } |
| |
| if (only_special) |
| return false; |
| |
| bool var_success = false; |
| |
| { |
| llvm::StringRef str; |
| |
| // this is a local stream that we are using to ensure that the data pointed |
| // to by cstr survives long enough for us to copy it to its destination - |
| // it is necessary to have this temporary storage area for cases where our |
| // desired output is not backed by some other longer-term storage |
| StreamString strm; |
| |
| if (custom_format != eFormatInvalid) |
| SetFormat(custom_format); |
| |
| switch (val_obj_display) { |
| case eValueObjectRepresentationStyleValue: |
| str = GetValueAsCString(); |
| break; |
| |
| case eValueObjectRepresentationStyleSummary: |
| str = GetSummaryAsCString(); |
| break; |
| |
| case eValueObjectRepresentationStyleLanguageSpecific: |
| str = GetObjectDescription(); |
| break; |
| |
| case eValueObjectRepresentationStyleLocation: |
| str = GetLocationAsCString(); |
| break; |
| |
| case eValueObjectRepresentationStyleChildrenCount: |
| strm.Printf("%" PRIu64 "", (uint64_t)GetNumChildren()); |
| 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 (str.empty()) { |
| if (val_obj_display == eValueObjectRepresentationStyleValue) |
| str = GetSummaryAsCString(); |
| else if (val_obj_display == eValueObjectRepresentationStyleSummary) { |
| if (!CanProvideValue()) { |
| strm.Printf("%s @ %s", GetTypeName().AsCString(), |
| GetLocationAsCString()); |
| str = strm.GetString(); |
| } else |
| str = GetValueAsCString(); |
| } |
| } |
| |
| if (!str.empty()) |
| s << str; |
| else { |
| if (m_error.Fail()) { |
| if (do_dump_error) |
| s.Printf("<%s>", m_error.AsCString()); |
| else |
| return false; |
| } else if (val_obj_display == eValueObjectRepresentationStyleSummary) |
| s.PutCString("<no summary available>"); |
| else if (val_obj_display == eValueObjectRepresentationStyleValue) |
| s.PutCString("<no value available>"); |
| else if (val_obj_display == |
| eValueObjectRepresentationStyleLanguageSpecific) |
| s.PutCString("<not a valid Objective-C object>"); // edit this if we |
| // have other runtimes |
| // that support a |
| // description |
| else |
| s.PutCString("<no printable representation>"); |
| } |
| |
| // we should only return false here if we could not do *anything* even if |
| // we have an error message as output, that's a success from our callers' |
| // perspective, so return true |
| var_success = true; |
| |
| if (custom_format != eFormatInvalid) |
| SetFormat(eFormatDefault); |
| } |
| |
| return var_success; |
| } |
| |
| addr_t ValueObject::GetAddressOf(bool scalar_is_load_address, |
| AddressType *address_type) { |
| // Can't take address of a bitfield |
| if (IsBitfield()) |
| return LLDB_INVALID_ADDRESS; |
| |
| if (!UpdateValueIfNeeded(false)) |
| return LLDB_INVALID_ADDRESS; |
| |
| switch (m_value.GetValueType()) { |
| case Value::ValueType::Invalid: |
| return LLDB_INVALID_ADDRESS; |
| case Value::ValueType::Scalar: |
| if (scalar_is_load_address) { |
| if (address_type) |
| *address_type = eAddressTypeLoad; |
| return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); |
| } |
| break; |
| |
| case Value::ValueType::LoadAddress: |
| case Value::ValueType::FileAddress: { |
| if (address_type) |
| *address_type = m_value.GetValueAddressType(); |
| return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); |
| } break; |
| case Value::ValueType::HostAddress: { |
| if (address_type) |
| *address_type = m_value.GetValueAddressType(); |
| return LLDB_INVALID_ADDRESS; |
| } break; |
| } |
| if (address_type) |
| *address_type = eAddressTypeInvalid; |
| return LLDB_INVALID_ADDRESS; |
| } |
| |
| addr_t ValueObject::GetPointerValue(AddressType *address_type) { |
| addr_t address = LLDB_INVALID_ADDRESS; |
| if (address_type) |
| *address_type = eAddressTypeInvalid; |
| |
| if (!UpdateValueIfNeeded(false)) |
| return address; |
| |
| switch (m_value.GetValueType()) { |
| case Value::ValueType::Invalid: |
| return LLDB_INVALID_ADDRESS; |
| case Value::ValueType::Scalar: |
| address = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); |
| break; |
| |
| case Value::ValueType::HostAddress: |
| case Value::ValueType::LoadAddress: |
| case Value::ValueType::FileAddress: { |
| lldb::offset_t data_offset = 0; |
| address = m_data.GetAddress(&data_offset); |
| } break; |
| } |
| |
| if (address_type) |
| *address_type = GetAddressTypeOfChildren(); |
| |
| return address; |
| } |
| |
| bool ValueObject::SetValueFromCString(const char *value_str, Status &error) { |
| error.Clear(); |
| // Make sure our value is up to date first so that our location and location |
| // type is valid. |
| if (!UpdateValueIfNeeded(false)) { |
| error.SetErrorString("unable to read value"); |
| return false; |
| } |
| |
| uint64_t count = 0; |
| const Encoding encoding = GetCompilerType().GetEncoding(count); |
| |
| const size_t byte_size = GetByteSize().getValueOr(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 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.SetErrorString("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.SetErrorString("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.SetErrorString("unable to write aggregate data type"); |
| return false; |
| } |
| |
| // If we have reached this point, then we have successfully changed the |
| // value. |
| SetNeedsUpdate(); |
| return true; |
| } |
| |
| bool ValueObject::GetDeclaration(Declaration &decl) { |
| decl.Clear(); |
| return false; |
| } |
| |
| void ValueObject::AddSyntheticChild(ConstString key, |
| ValueObject *valobj) { |
| m_synthetic_children[key] = valobj; |
| } |
| |
| ValueObjectSP ValueObject::GetSyntheticChild(ConstString key) const { |
| ValueObjectSP synthetic_child_sp; |
| std::map<ConstString, ValueObject *>::const_iterator pos = |
| m_synthetic_children.find(key); |
| if (pos != m_synthetic_children.end()) |
| synthetic_child_sp = pos->second->GetSP(); |
| return synthetic_child_sp; |
| } |
| |
| bool ValueObject::IsPossibleDynamicType() { |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| Process *process = exe_ctx.GetProcessPtr(); |
| if (process) |
| return process->IsPossibleDynamicValue(*this); |
| else |
| return GetCompilerType().IsPossibleDynamicType(nullptr, true, true); |
| } |
| |
| bool ValueObject::IsRuntimeSupportValue() { |
| Process *process(GetProcessSP().get()); |
| if (!process) |
| return false; |
| |
| // We trust that the compiler did the right thing and marked runtime support |
| // values as artificial. |
| if (!GetVariable() || !GetVariable()->IsArtificial()) |
| return false; |
| |
| if (auto *runtime = process->GetLanguageRuntime(GetVariable()->GetLanguage())) |
| if (runtime->IsAllowedRuntimeValue(GetName())) |
| return false; |
| |
| return true; |
| } |
| |
| bool ValueObject::IsNilReference() { |
| if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) { |
| return language->IsNilReference(*this); |
| } |
| return false; |
| } |
| |
| bool ValueObject::IsUninitializedReference() { |
| if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) { |
| return language->IsUninitializedReference(*this); |
| } |
| return false; |
| } |
| |
| // This allows you to create an array member using and index that doesn't not |
| // fall in the normal bounds of the array. Many times structure can be defined |
| // as: struct Collection { |
| // uint32_t item_count; |
| // Item item_array[0]; |
| // }; |
| // The size of the "item_array" is 1, but many times in practice there are more |
| // items in "item_array". |
| |
| ValueObjectSP ValueObject::GetSyntheticArrayMember(size_t index, |
| bool can_create) { |
| ValueObjectSP synthetic_child_sp; |
| if (IsPointerType() || IsArrayType()) { |
| std::string index_str = llvm::formatv("[{0}]", index); |
| ConstString index_const_str(index_str); |
| // Check if we have already created a synthetic array member in this valid |
| // object. If we have we will re-use it. |
| synthetic_child_sp = GetSyntheticChild(index_const_str); |
| if (!synthetic_child_sp) { |
| ValueObject *synthetic_child; |
| // We haven't made a synthetic array member for INDEX yet, so lets make |
| // one and cache it for any future reference. |
| synthetic_child = CreateChildAtIndex(0, true, index); |
| |
| // Cache the value if we got one back... |
| if (synthetic_child) { |
| AddSyntheticChild(index_const_str, synthetic_child); |
| synthetic_child_sp = synthetic_child->GetSP(); |
| synthetic_child_sp->SetName(ConstString(index_str)); |
| synthetic_child_sp->m_flags.m_is_array_item_for_pointer = true; |
| } |
| } |
| } |
| return synthetic_child_sp; |
| } |
| |
| ValueObjectSP ValueObject::GetSyntheticBitFieldChild(uint32_t from, uint32_t to, |
| bool can_create) { |
| ValueObjectSP synthetic_child_sp; |
| if (IsScalarType()) { |
| std::string index_str = llvm::formatv("[{0}-{1}]", from, to); |
| ConstString index_const_str(index_str); |
| // Check if we have already created a synthetic array member in this valid |
| // object. If we have we will re-use it. |
| synthetic_child_sp = GetSyntheticChild(index_const_str); |
| if (!synthetic_child_sp) { |
| uint32_t bit_field_size = to - from + 1; |
| uint32_t bit_field_offset = from; |
| if (GetDataExtractor().GetByteOrder() == eByteOrderBig) |
| bit_field_offset = |
| GetByteSize().getValueOr(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, |
| GetByteSize().getValueOr(0), 0, bit_field_size, bit_field_offset, |
| false, false, eAddressTypeInvalid, 0); |
| |
| // Cache the value if we got one back... |
| if (synthetic_child) { |
| AddSyntheticChild(index_const_str, synthetic_child); |
| synthetic_child_sp = synthetic_child->GetSP(); |
| synthetic_child_sp->SetName(ConstString(index_str)); |
| synthetic_child_sp->m_flags.m_is_bitfield_for_scalar = true; |
| } |
| } |
| } |
| return synthetic_child_sp; |
| } |
| |
| ValueObjectSP ValueObject::GetSyntheticChildAtOffset( |
| uint32_t offset, const CompilerType &type, bool can_create, |
| ConstString name_const_str) { |
| |
| ValueObjectSP synthetic_child_sp; |
| |
| if (name_const_str.IsEmpty()) { |
| name_const_str.SetString("@" + std::to_string(offset)); |
| } |
| |
| // Check if we have already created a synthetic array member in this valid |
| // object. If we have we will re-use it. |
| synthetic_child_sp = GetSyntheticChild(name_const_str); |
| |
| if (synthetic_child_sp.get()) |
| return synthetic_child_sp; |
| |
| if (!can_create) |
| return {}; |
| |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| llvm::Optional<uint64_t> size = |
| type.GetByteSize(exe_ctx.GetBestExecutionContextScope()); |
| if (!size) |
| return {}; |
| ValueObjectChild *synthetic_child = |
| new ValueObjectChild(*this, type, name_const_str, *size, offset, 0, 0, |
| false, false, eAddressTypeInvalid, 0); |
| if (synthetic_child) { |
| AddSyntheticChild(name_const_str, synthetic_child); |
| synthetic_child_sp = synthetic_child->GetSP(); |
| synthetic_child_sp->SetName(name_const_str); |
| synthetic_child_sp->m_flags.m_is_child_at_offset = true; |
| } |
| return synthetic_child_sp; |
| } |
| |
| ValueObjectSP ValueObject::GetSyntheticBase(uint32_t offset, |
| const CompilerType &type, |
| bool can_create, |
| ConstString name_const_str) { |
| ValueObjectSP synthetic_child_sp; |
| |
| if (name_const_str.IsEmpty()) { |
| char name_str[128]; |
| snprintf(name_str, sizeof(name_str), "base%s@%i", |
| type.GetTypeName().AsCString("<unknown>"), offset); |
| name_const_str.SetCString(name_str); |
| } |
| |
| // Check if we have already created a synthetic array member in this valid |
| // object. If we have we will re-use it. |
| synthetic_child_sp = GetSyntheticChild(name_const_str); |
| |
| if (synthetic_child_sp.get()) |
| return synthetic_child_sp; |
| |
| if (!can_create) |
| return {}; |
| |
| const bool is_base_class = true; |
| |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| llvm::Optional<uint64_t> size = |
| type.GetByteSize(exe_ctx.GetBestExecutionContextScope()); |
| if (!size) |
| return {}; |
| ValueObjectChild *synthetic_child = |
| new ValueObjectChild(*this, type, name_const_str, *size, offset, 0, 0, |
| is_base_class, false, eAddressTypeInvalid, 0); |
| if (synthetic_child) { |
| AddSyntheticChild(name_const_str, synthetic_child); |
| synthetic_child_sp = synthetic_child->GetSP(); |
| synthetic_child_sp->SetName(name_const_str); |
| } |
| return synthetic_child_sp; |
| } |
| |
| // your expression path needs to have a leading . or -> (unless it somehow |
| // "looks like" an array, in which case it has a leading [ symbol). while the [ |
| // is meaningful and should be shown to the user, . and -> are just parser |
| // design, but by no means added information for the user.. strip them off |
| static const char *SkipLeadingExpressionPathSeparators(const char *expression) { |
| if (!expression || !expression[0]) |
| return expression; |
| if (expression[0] == '.') |
| return expression + 1; |
| if (expression[0] == '-' && expression[1] == '>') |
| return expression + 2; |
| return expression; |
| } |
| |
| ValueObjectSP |
| ValueObject::GetSyntheticExpressionPathChild(const char *expression, |
| bool can_create) { |
| ValueObjectSP synthetic_child_sp; |
| ConstString name_const_string(expression); |
| // Check if we have already created a synthetic array member in this valid |
| // object. If we have we will re-use it. |
| synthetic_child_sp = GetSyntheticChild(name_const_string); |
| if (!synthetic_child_sp) { |
| // We haven't made a synthetic array member for expression yet, so lets |
| // make one and cache it for any future reference. |
| synthetic_child_sp = GetValueForExpressionPath( |
| expression, nullptr, nullptr, |
| GetValueForExpressionPathOptions().SetSyntheticChildrenTraversal( |
| GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: |
| None)); |
| |
| // Cache the value if we got one back... |
| if (synthetic_child_sp.get()) { |
| // FIXME: this causes a "real" child to end up with its name changed to |
| // the contents of expression |
| AddSyntheticChild(name_const_string, synthetic_child_sp.get()); |
| synthetic_child_sp->SetName( |
| ConstString(SkipLeadingExpressionPathSeparators(expression))); |
| } |
| } |
| return synthetic_child_sp; |
| } |
| |
| void ValueObject::CalculateSyntheticValue() { |
| TargetSP target_sp(GetTargetSP()); |
| if (target_sp && !target_sp->GetEnableSyntheticValue()) { |
| m_synthetic_value = nullptr; |
| return; |
| } |
| |
| lldb::SyntheticChildrenSP current_synth_sp(m_synthetic_children_sp); |
| |
| if (!UpdateFormatsIfNeeded() && m_synthetic_value) |
| return; |
| |
| if (m_synthetic_children_sp.get() == nullptr) |
| return; |
| |
| if (current_synth_sp == m_synthetic_children_sp && m_synthetic_value) |
| return; |
| |
| m_synthetic_value = new ValueObjectSynthetic(*this, m_synthetic_children_sp); |
| } |
| |
| void ValueObject::CalculateDynamicValue(DynamicValueType use_dynamic) { |
| if (use_dynamic == eNoDynamicValues) |
| return; |
| |
| if (!m_dynamic_value && !IsDynamic()) { |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| Process *process = exe_ctx.GetProcessPtr(); |
| if (process && process->IsPossibleDynamicValue(*this)) { |
| ClearDynamicTypeInformation(); |
| m_dynamic_value = new ValueObjectDynamicValue(*this, use_dynamic); |
| } |
| } |
| } |
| |
| ValueObjectSP ValueObject::GetDynamicValue(DynamicValueType use_dynamic) { |
| if (use_dynamic == eNoDynamicValues) |
| return ValueObjectSP(); |
| |
| if (!IsDynamic() && m_dynamic_value == nullptr) { |
| CalculateDynamicValue(use_dynamic); |
| } |
| if (m_dynamic_value) |
| return m_dynamic_value->GetSP(); |
| else |
| return ValueObjectSP(); |
| } |
| |
| ValueObjectSP ValueObject::GetSyntheticValue() { |
| CalculateSyntheticValue(); |
| |
| if (m_synthetic_value) |
| return m_synthetic_value->GetSP(); |
| else |
| return ValueObjectSP(); |
| } |
| |
| bool ValueObject::HasSyntheticValue() { |
| UpdateFormatsIfNeeded(); |
| |
| if (m_synthetic_children_sp.get() == nullptr) |
| return false; |
| |
| CalculateSyntheticValue(); |
| |
| return m_synthetic_value != nullptr; |
| } |
| |
| ValueObject *ValueObject::GetNonBaseClassParent() { |
| if (GetParent()) { |
| if (GetParent()->IsBaseClass()) |
| return GetParent()->GetNonBaseClassParent(); |
| else |
| return GetParent(); |
| } |
| return nullptr; |
| } |
| |
| bool ValueObject::IsBaseClass(uint32_t &depth) { |
| if (!IsBaseClass()) { |
| depth = 0; |
| return false; |
| } |
| if (GetParent()) { |
| GetParent()->IsBaseClass(depth); |
| depth = depth + 1; |
| return true; |
| } |
| // TODO: a base of no parent? weird.. |
| depth = 1; |
| return true; |
| } |
| |
| void ValueObject::GetExpressionPath(Stream &s, |
| GetExpressionPathFormat epformat) { |
| // synthetic children do not actually "exist" as part of the hierarchy, and |
| // sometimes they are consed up in ways that don't make sense from an |
| // underlying language/API standpoint. So, use a special code path here to |
| // return something that can hopefully be used in expression |
| if (m_flags.m_is_synthetic_children_generated) { |
| UpdateValueIfNeeded(); |
| |
| if (m_value.GetValueType() == Value::ValueType::LoadAddress) { |
| if (IsPointerOrReferenceType()) { |
| s.Printf("((%s)0x%" PRIx64 ")", GetTypeName().AsCString("void"), |
| GetValueAsUnsigned(0)); |
| return; |
| } else { |
| uint64_t load_addr = |
| m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); |
| if (load_addr != LLDB_INVALID_ADDRESS) { |
| s.Printf("(*( (%s *)0x%" PRIx64 "))", GetTypeName().AsCString("void"), |
| load_addr); |
| return; |
| } |
| } |
| } |
| |
| if (CanProvideValue()) { |
| s.Printf("((%s)%s)", GetTypeName().AsCString("void"), |
| GetValueAsCString()); |
| return; |
| } |
| |
| return; |
| } |
| |
| const bool is_deref_of_parent = IsDereferenceOfParent(); |
| |
| if (is_deref_of_parent && |
| epformat == eGetExpressionPathFormatDereferencePointers) { |
| // this is the original format of GetExpressionPath() producing code like |
| // *(a_ptr).memberName, which is entirely fine, until you put this into |
| // StackFrame::GetValueForVariableExpressionPath() which prefers to see |
| // a_ptr->memberName. the eHonorPointers mode is meant to produce strings |
| // in this latter format |
| s.PutCString("*("); |
| } |
| |
| ValueObject *parent = GetParent(); |
| |
| if (parent) |
| parent->GetExpressionPath(s, epformat); |
| |
| // if we are a deref_of_parent just because we are synthetic array members |
| // made up to allow ptr[%d] syntax to work in variable printing, then add our |
| // name ([%d]) to the expression path |
| if (m_flags.m_is_array_item_for_pointer && |
| epformat == eGetExpressionPathFormatHonorPointers) |
| s.PutCString(m_name.GetStringRef()); |
| |
| if (!IsBaseClass()) { |
| if (!is_deref_of_parent) { |
| ValueObject *non_base_class_parent = GetNonBaseClassParent(); |
| if (non_base_class_parent && |
| !non_base_class_parent->GetName().IsEmpty()) { |
| CompilerType non_base_class_parent_compiler_type = |
| non_base_class_parent->GetCompilerType(); |
| if (non_base_class_parent_compiler_type) { |
| if (parent && parent->IsDereferenceOfParent() && |
| epformat == eGetExpressionPathFormatHonorPointers) { |
| s.PutCString("->"); |
| } else { |
| const uint32_t non_base_class_parent_type_info = |
| non_base_class_parent_compiler_type.GetTypeInfo(); |
| |
| if (non_base_class_parent_type_info & eTypeIsPointer) { |
| s.PutCString("->"); |
| } else if ((non_base_class_parent_type_info & eTypeHasChildren) && |
| !(non_base_class_parent_type_info & eTypeIsArray)) { |
| s.PutChar('.'); |
| } |
| } |
| } |
| } |
| |
| const char *name = GetName().GetCString(); |
| if (name) |
| s.PutCString(name); |
| } |
| } |
| |
| if (is_deref_of_parent && |
| epformat == eGetExpressionPathFormatDereferencePointers) { |
| s.PutChar(')'); |
| } |
| } |
| |
| ValueObjectSP ValueObject::GetValueForExpressionPath( |
| llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop, |
| ExpressionPathEndResultType *final_value_type, |
| const GetValueForExpressionPathOptions &options, |
| ExpressionPathAftermath *final_task_on_target) { |
| |
| ExpressionPathScanEndReason dummy_reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonUnknown; |
| ExpressionPathEndResultType dummy_final_value_type = |
| ValueObject::eExpressionPathEndResultTypeInvalid; |
| ExpressionPathAftermath dummy_final_task_on_target = |
| ValueObject::eExpressionPathAftermathNothing; |
| |
| ValueObjectSP ret_val = GetValueForExpressionPath_Impl( |
| expression, reason_to_stop ? reason_to_stop : &dummy_reason_to_stop, |
| final_value_type ? final_value_type : &dummy_final_value_type, options, |
| final_task_on_target ? final_task_on_target |
| : &dummy_final_task_on_target); |
| |
| if (!final_task_on_target || |
| *final_task_on_target == ValueObject::eExpressionPathAftermathNothing) |
| return ret_val; |
| |
| if (ret_val.get() && |
| ((final_value_type ? *final_value_type : dummy_final_value_type) == |
| eExpressionPathEndResultTypePlain)) // I can only deref and takeaddress |
| // of plain objects |
| { |
| if ((final_task_on_target ? *final_task_on_target |
| : dummy_final_task_on_target) == |
| ValueObject::eExpressionPathAftermathDereference) { |
| Status error; |
| ValueObjectSP final_value = ret_val->Dereference(error); |
| if (error.Fail() || !final_value.get()) { |
| if (reason_to_stop) |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonDereferencingFailed; |
| if (final_value_type) |
| *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return ValueObjectSP(); |
| } else { |
| if (final_task_on_target) |
| *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; |
| return final_value; |
| } |
| } |
| if (*final_task_on_target == |
| ValueObject::eExpressionPathAftermathTakeAddress) { |
| Status error; |
| ValueObjectSP final_value = ret_val->AddressOf(error); |
| if (error.Fail() || !final_value.get()) { |
| if (reason_to_stop) |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonTakingAddressFailed; |
| if (final_value_type) |
| *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return ValueObjectSP(); |
| } else { |
| if (final_task_on_target) |
| *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; |
| return final_value; |
| } |
| } |
| } |
| return ret_val; // final_task_on_target will still have its original value, so |
| // you know I did not do it |
| } |
| |
| ValueObjectSP ValueObject::GetValueForExpressionPath_Impl( |
| llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop, |
| ExpressionPathEndResultType *final_result, |
| const GetValueForExpressionPathOptions &options, |
| ExpressionPathAftermath *what_next) { |
| ValueObjectSP root = GetSP(); |
| |
| if (!root) |
| return nullptr; |
| |
| llvm::StringRef remainder = expression; |
| |
| while (true) { |
| llvm::StringRef temp_expression = remainder; |
| |
| CompilerType root_compiler_type = root->GetCompilerType(); |
| CompilerType pointee_compiler_type; |
| Flags pointee_compiler_type_info; |
| |
| Flags root_compiler_type_info( |
| root_compiler_type.GetTypeInfo(&pointee_compiler_type)); |
| if (pointee_compiler_type) |
| pointee_compiler_type_info.Reset(pointee_compiler_type.GetTypeInfo()); |
| |
| if (temp_expression.empty()) { |
| *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; |
| return root; |
| } |
| |
| switch (temp_expression.front()) { |
| case '-': { |
| temp_expression = temp_expression.drop_front(); |
| if (options.m_check_dot_vs_arrow_syntax && |
| root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to |
| // use -> on a |
| // non-pointer and I |
| // must catch the error |
| { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonArrowInsteadOfDot; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return ValueObjectSP(); |
| } |
| if (root_compiler_type_info.Test(eTypeIsObjC) && // if yo are trying to |
| // extract an ObjC IVar |
| // when this is forbidden |
| root_compiler_type_info.Test(eTypeIsPointer) && |
| options.m_no_fragile_ivar) { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonFragileIVarNotAllowed; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return ValueObjectSP(); |
| } |
| if (!temp_expression.startswith(">")) { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return ValueObjectSP(); |
| } |
| } |
| LLVM_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); |
| ConstString child_name; |
| if (next_sep_pos == llvm::StringRef::npos) // if no other separator just |
| // expand this last layer |
| { |
| child_name.SetString(temp_expression); |
| ValueObjectSP child_valobj_sp = |
| root->GetChildMemberWithName(child_name, true); |
| |
| if (child_valobj_sp.get()) // we know we are done, so just return |
| { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonEndOfString; |
| *final_result = ValueObject::eExpressionPathEndResultTypePlain; |
| return child_valobj_sp; |
| } else { |
| switch (options.m_synthetic_children_traversal) { |
| case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: |
| None: |
| break; |
| case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: |
| FromSynthetic: |
| if (root->IsSynthetic()) { |
| child_valobj_sp = root->GetNonSyntheticValue(); |
| if (child_valobj_sp.get()) |
| child_valobj_sp = |
| child_valobj_sp->GetChildMemberWithName(child_name, true); |
| } |
| break; |
| case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: |
| ToSynthetic: |
| if (!root->IsSynthetic()) { |
| child_valobj_sp = root->GetSyntheticValue(); |
| if (child_valobj_sp.get()) |
| child_valobj_sp = |
| child_valobj_sp->GetChildMemberWithName(child_name, true); |
| } |
| break; |
| case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: |
| Both: |
| if (root->IsSynthetic()) { |
| child_valobj_sp = root->GetNonSyntheticValue(); |
| if (child_valobj_sp.get()) |
| child_valobj_sp = |
| child_valobj_sp->GetChildMemberWithName(child_name, true); |
| } else { |
| child_valobj_sp = root->GetSyntheticValue(); |
| if (child_valobj_sp.get()) |
| child_valobj_sp = |
| child_valobj_sp->GetChildMemberWithName(child_name, true); |
| } |
| break; |
| } |
| } |
| |
| // if we are here and options.m_no_synthetic_children is true, |
| // child_valobj_sp is going to be a NULL SP, so we hit the "else" |
| // branch, and return an error |
| if (child_valobj_sp.get()) // if it worked, just return |
| { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonEndOfString; |
| *final_result = ValueObject::eExpressionPathEndResultTypePlain; |
| return child_valobj_sp; |
| } else { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonNoSuchChild; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return nullptr; |
| } |
| } else // other layers do expand |
| { |
| llvm::StringRef next_separator = temp_expression.substr(next_sep_pos); |
| |
| child_name.SetString(temp_expression.slice(0, next_sep_pos)); |
| |
| ValueObjectSP child_valobj_sp = |
| root->GetChildMemberWithName(child_name, true); |
| if (child_valobj_sp.get()) // store the new root and move on |
| { |
| root = child_valobj_sp; |
| remainder = next_separator; |
| *final_result = ValueObject::eExpressionPathEndResultTypePlain; |
| continue; |
| } else { |
| switch (options.m_synthetic_children_traversal) { |
| case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: |
| None: |
| break; |
| case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: |
| FromSynthetic: |
| if (root->IsSynthetic()) { |
| child_valobj_sp = root->GetNonSyntheticValue(); |
| if (child_valobj_sp.get()) |
| child_valobj_sp = |
| child_valobj_sp->GetChildMemberWithName(child_name, true); |
| } |
| break; |
| case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: |
| ToSynthetic: |
| if (!root->IsSynthetic()) { |
| child_valobj_sp = root->GetSyntheticValue(); |
| if (child_valobj_sp.get()) |
| child_valobj_sp = |
| child_valobj_sp->GetChildMemberWithName(child_name, true); |
| } |
| break; |
| case GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: |
| Both: |
| if (root->IsSynthetic()) { |
| child_valobj_sp = root->GetNonSyntheticValue(); |
| if (child_valobj_sp.get()) |
| child_valobj_sp = |
| child_valobj_sp->GetChildMemberWithName(child_name, true); |
| } else { |
| child_valobj_sp = root->GetSyntheticValue(); |
| if (child_valobj_sp.get()) |
| child_valobj_sp = |
| child_valobj_sp->GetChildMemberWithName(child_name, true); |
| } |
| break; |
| } |
| } |
| |
| // if we are here and options.m_no_synthetic_children is true, |
| // child_valobj_sp is going to be a NULL SP, so we hit the "else" |
| // branch, and return an error |
| if (child_valobj_sp.get()) // if it worked, move on |
| { |
| root = child_valobj_sp; |
| remainder = next_separator; |
| *final_result = ValueObject::eExpressionPathEndResultTypePlain; |
| continue; |
| } else { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonNoSuchChild; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return nullptr; |
| } |
| } |
| break; |
| } |
| case '[': { |
| if (!root_compiler_type_info.Test(eTypeIsArray) && |
| !root_compiler_type_info.Test(eTypeIsPointer) && |
| !root_compiler_type_info.Test( |
| eTypeIsVector)) // if this is not a T[] nor a T* |
| { |
| if (!root_compiler_type_info.Test( |
| eTypeIsScalar)) // if this is not even a scalar... |
| { |
| if (options.m_synthetic_children_traversal == |
| GetValueForExpressionPathOptions::SyntheticChildrenTraversal:: |
| None) // ...only chance left is synthetic |
| { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return ValueObjectSP(); |
| } |
| } else if (!options.m_allow_bitfields_syntax) // if this is a scalar, |
| // check that we can |
| // expand bitfields |
| { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return ValueObjectSP(); |
| } |
| } |
| if (temp_expression[1] == |
| ']') // if this is an unbounded range it only works for arrays |
| { |
| if (!root_compiler_type_info.Test(eTypeIsArray)) { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return nullptr; |
| } else // even if something follows, we cannot expand unbounded ranges, |
| // just let the caller do it |
| { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; |
| *final_result = |
| ValueObject::eExpressionPathEndResultTypeUnboundedRange; |
| return root; |
| } |
| } |
| |
| size_t close_bracket_position = temp_expression.find(']', 1); |
| if (close_bracket_position == |
| llvm::StringRef::npos) // if there is no ], this is a syntax error |
| { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return nullptr; |
| } |
| |
| llvm::StringRef bracket_expr = |
| temp_expression.slice(1, close_bracket_position); |
| |
| // If this was an empty expression it would have been caught by the if |
| // above. |
| assert(!bracket_expr.empty()); |
| |
| if (!bracket_expr.contains('-')) { |
| // if no separator, this is of the form [N]. Note that this cannot be |
| // an unbounded range of the form [], because that case was handled |
| // above with an unconditional return. |
| unsigned long index = 0; |
| if (bracket_expr.getAsInteger(0, index)) { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return nullptr; |
| } |
| |
| // from here on we do have a valid index |
| if (root_compiler_type_info.Test(eTypeIsArray)) { |
| ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true); |
| if (!child_valobj_sp) |
| child_valobj_sp = root->GetSyntheticArrayMember(index, true); |
| if (!child_valobj_sp) |
| if (root->HasSyntheticValue() && |
| root->GetSyntheticValue()->GetNumChildren() > index) |
| child_valobj_sp = |
| root->GetSyntheticValue()->GetChildAtIndex(index, true); |
| if (child_valobj_sp) { |
| root = child_valobj_sp; |
| remainder = |
| temp_expression.substr(close_bracket_position + 1); // skip ] |
| *final_result = ValueObject::eExpressionPathEndResultTypePlain; |
| continue; |
| } else { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonNoSuchChild; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return nullptr; |
| } |
| } else if (root_compiler_type_info.Test(eTypeIsPointer)) { |
| if (*what_next == |
| ValueObject:: |
| eExpressionPathAftermathDereference && // if this is a |
| // ptr-to-scalar, I |
| // am accessing it |
| // by index and I |
| // would have |
| // deref'ed anyway, |
| // then do it now |
| // and use this as |
| // a bitfield |
| pointee_compiler_type_info.Test(eTypeIsScalar)) { |
| Status error; |
| root = root->Dereference(error); |
| if (error.Fail() || !root) { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonDereferencingFailed; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return nullptr; |
| } else { |
| *what_next = eExpressionPathAftermathNothing; |
| continue; |
| } |
| } else { |
| if (root->GetCompilerType().GetMinimumLanguage() == |
| eLanguageTypeObjC && |
| pointee_compiler_type_info.AllClear(eTypeIsPointer) && |
| root->HasSyntheticValue() && |
| (options.m_synthetic_children_traversal == |
| GetValueForExpressionPathOptions:: |
| SyntheticChildrenTraversal::ToSynthetic || |
| options.m_synthetic_children_traversal == |
| GetValueForExpressionPathOptions:: |
| SyntheticChildrenTraversal::Both)) { |
| root = root->GetSyntheticValue()->GetChildAtIndex(index, true); |
| } 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, true); |
| 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, 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 { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonNoSuchChild; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return nullptr; |
| } |
| } else { |
| // we have a low and a high index |
| llvm::StringRef sleft, sright; |
| unsigned long low_index, high_index; |
| std::tie(sleft, sright) = bracket_expr.split('-'); |
| if (sleft.getAsInteger(0, low_index) || |
| sright.getAsInteger(0, high_index)) { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return nullptr; |
| } |
| |
| if (low_index > high_index) // swap indices if required |
| std::swap(low_index, high_index); |
| |
| if (root_compiler_type_info.Test( |
| eTypeIsScalar)) // expansion only works for scalars |
| { |
| root = root->GetSyntheticBitFieldChild(low_index, high_index, true); |
| if (!root) { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonNoSuchChild; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return nullptr; |
| } else { |
| *reason_to_stop = ValueObject:: |
| eExpressionPathScanEndReasonBitfieldRangeOperatorMet; |
| *final_result = ValueObject::eExpressionPathEndResultTypeBitfield; |
| return root; |
| } |
| } else if (root_compiler_type_info.Test( |
| eTypeIsPointer) && // if this is a ptr-to-scalar, I am |
| // accessing it by index and I would |
| // have deref'ed anyway, then do it |
| // now and use this as a bitfield |
| *what_next == |
| ValueObject::eExpressionPathAftermathDereference && |
| pointee_compiler_type_info.Test(eTypeIsScalar)) { |
| Status error; |
| root = root->Dereference(error); |
| if (error.Fail() || !root) { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonDereferencingFailed; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return nullptr; |
| } else { |
| *what_next = ValueObject::eExpressionPathAftermathNothing; |
| continue; |
| } |
| } else { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; |
| *final_result = ValueObject::eExpressionPathEndResultTypeBoundedRange; |
| return root; |
| } |
| } |
| break; |
| } |
| default: // some non-separator is in the way |
| { |
| *reason_to_stop = |
| ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; |
| *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; |
| return nullptr; |
| } |
| } |
| } |
| } |
| |
| void ValueObject::Dump(Stream &s) { Dump(s, DumpValueObjectOptions(*this)); } |
| |
| void ValueObject::Dump(Stream &s, const DumpValueObjectOptions &options) { |
| ValueObjectPrinter printer(this, &s, options); |
| printer.PrintValueObject(); |
| } |
| |
| ValueObjectSP ValueObject::CreateConstantValue(ConstString name) { |
| ValueObjectSP valobj_sp; |
| |
| if (UpdateValueIfNeeded(false) && m_error.Success()) { |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| |
| DataExtractor data; |
| data.SetByteOrder(m_data.GetByteOrder()); |
| data.SetAddressByteSize(m_data.GetAddressByteSize()); |
| |
| if (IsBitfield()) { |
| Value v(Scalar(GetValueAsUnsigned(UINT64_MAX))); |
| m_error = v.GetValueAsData(&exe_ctx, data, GetModule().get()); |
| } else |
| m_error = m_value.GetValueAsData(&exe_ctx, data, GetModule().get()); |
| |
| valobj_sp = ValueObjectConstResult::Create( |
| exe_ctx.GetBestExecutionContextScope(), GetCompilerType(), name, data, |
| GetAddressOf()); |
| } |
| |
| if (!valobj_sp) { |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| valobj_sp = ValueObjectConstResult::Create( |
| exe_ctx.GetBestExecutionContextScope(), m_error); |
| } |
| return valobj_sp; |
| } |
| |
| ValueObjectSP ValueObject::GetQualifiedRepresentationIfAvailable( |
| lldb::DynamicValueType dynValue, bool synthValue) { |
| ValueObjectSP result_sp(GetSP()); |
| |
| switch (dynValue) { |
| case lldb::eDynamicCanRunTarget: |
| case lldb::eDynamicDontRunTarget: { |
| if (!result_sp->IsDynamic()) { |
| if (result_sp->GetDynamicValue(dynValue)) |
| result_sp = result_sp->GetDynamicValue(dynValue); |
| } |
| } break; |
| case lldb::eNoDynamicValues: { |
| if (result_sp->IsDynamic()) { |
| if (result_sp->GetStaticValue()) |
| result_sp = result_sp->GetStaticValue(); |
| } |
| } break; |
| } |
| |
| if (synthValue) { |
| if (!result_sp->IsSynthetic()) { |
| if (result_sp->GetSyntheticValue()) |
| result_sp = result_sp->GetSyntheticValue(); |
| } |
| } else { |
| if (result_sp->IsSynthetic()) { |
| if (result_sp->GetNonSyntheticValue()) |
| result_sp = result_sp->GetNonSyntheticValue(); |
| } |
| } |
| |
| return result_sp; |
| } |
| |
| ValueObjectSP ValueObject::Dereference(Status &error) { |
| if (m_deref_valobj) |
| return m_deref_valobj->GetSP(); |
| |
| const bool is_pointer_or_reference_type = IsPointerOrReferenceType(); |
| if (is_pointer_or_reference_type) { |
| bool omit_empty_base_classes = true; |
| bool ignore_array_bounds = false; |
| |
| std::string child_name_str; |
| uint32_t child_byte_size = 0; |
| int32_t child_byte_offset = 0; |
| uint32_t child_bitfield_bit_size = 0; |
| uint32_t child_bitfield_bit_offset = 0; |
| bool child_is_base_class = false; |
| bool child_is_deref_of_parent = false; |
| const bool transparent_pointers = false; |
| CompilerType compiler_type = GetCompilerType(); |
| CompilerType child_compiler_type; |
| uint64_t language_flags = 0; |
| |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| |
| child_compiler_type = compiler_type.GetChildCompilerTypeAtIndex( |
| &exe_ctx, 0, transparent_pointers, omit_empty_base_classes, |
| ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset, |
| child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, |
| child_is_deref_of_parent, this, language_flags); |
| if (child_compiler_type && child_byte_size) { |
| ConstString child_name; |
| if (!child_name_str.empty()) |
| child_name.SetCString(child_name_str.c_str()); |
| |
| m_deref_valobj = new ValueObjectChild( |
| *this, child_compiler_type, child_name, child_byte_size, |
| child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, |
| child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid, |
| language_flags); |
| } |
| |
| // In case of incomplete child compiler type, use the pointee type and try |
| // to recreate a new ValueObjectChild using it. |
| if (!m_deref_valobj) { |
| if (HasSyntheticValue()) { |
| child_compiler_type = compiler_type.GetPointeeType(); |
| |
| if (child_compiler_type) { |
| ConstString child_name; |
| if (!child_name_str.empty()) |
| child_name.SetCString(child_name_str.c_str()); |
| |
| m_deref_valobj = new ValueObjectChild( |
| *this, child_compiler_type, child_name, child_byte_size, |
| child_byte_offset, child_bitfield_bit_size, |
| child_bitfield_bit_offset, child_is_base_class, |
| child_is_deref_of_parent, eAddressTypeInvalid, language_flags); |
| } |
| } |
| } |
| |
| } else if (HasSyntheticValue()) { |
| m_deref_valobj = |
| GetSyntheticValue() |
| ->GetChildMemberWithName(ConstString("$$dereference$$"), true) |
| .get(); |
| } else if (IsSynthetic()) { |
| m_deref_valobj = |
| GetChildMemberWithName(ConstString("$$dereference$$"), true).get(); |
| } |
| |
| if (m_deref_valobj) { |
| error.Clear(); |
| return m_deref_valobj->GetSP(); |
| } else { |
| StreamString strm; |
| GetExpressionPath(strm); |
| |
| if (is_pointer_or_reference_type) |
| error.SetErrorStringWithFormat("dereference failed: (%s) %s", |
| GetTypeName().AsCString("<invalid type>"), |
| strm.GetData()); |
| else |
| error.SetErrorStringWithFormat("not a pointer or reference type: (%s) %s", |
| GetTypeName().AsCString("<invalid type>"), |
| strm.GetData()); |
| return ValueObjectSP(); |
| } |
| } |
| |
| ValueObjectSP ValueObject::AddressOf(Status &error) { |
| if (m_addr_of_valobj_sp) |
| return m_addr_of_valobj_sp; |
| |
| AddressType address_type = eAddressTypeInvalid; |
| const bool scalar_is_load_address = false; |
| addr_t addr = GetAddressOf(scalar_is_load_address, &address_type); |
| error.Clear(); |
| if (addr != LLDB_INVALID_ADDRESS && address_type != eAddressTypeHost) { |
| switch (address_type) { |
| case eAddressTypeInvalid: { |
| StreamString expr_path_strm; |
| GetExpressionPath(expr_path_strm); |
| error.SetErrorStringWithFormat("'%s' is not in memory", |
| expr_path_strm.GetData()); |
| } break; |
| |
| case eAddressTypeFile: |
| case eAddressTypeLoad: { |
| CompilerType compiler_type = GetCompilerType(); |
| if (compiler_type) { |
| std::string name(1, '&'); |
| name.append(m_name.AsCString("")); |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| m_addr_of_valobj_sp = ValueObjectConstResult::Create( |
| exe_ctx.GetBestExecutionContextScope(), |
| compiler_type.GetPointerType(), ConstString(name.c_str()), addr, |
| eAddressTypeInvalid, m_data.GetAddressByteSize()); |
| } |
| } break; |
| default: |
| break; |
| } |
| } else { |
| StreamString expr_path_strm; |
| GetExpressionPath(expr_path_strm); |
| error.SetErrorStringWithFormat("'%s' doesn't have a valid address", |
| expr_path_strm.GetData()); |
| } |
| |
| return m_addr_of_valobj_sp; |
| } |
| |
| ValueObjectSP ValueObject::Cast(const CompilerType &compiler_type) { |
| return ValueObjectCast::Create(*this, GetName(), compiler_type); |
| } |
| |
| lldb::ValueObjectSP ValueObject::Clone(ConstString new_name) { |
| return ValueObjectCast::Create(*this, new_name, GetCompilerType()); |
| } |
| |
| ValueObjectSP ValueObject::CastPointerType(const char *name, |
| CompilerType &compiler_type) { |
| ValueObjectSP valobj_sp; |
| AddressType address_type; |
| addr_t ptr_value = GetPointerValue(&address_type); |
| |
| if (ptr_value != LLDB_INVALID_ADDRESS) { |
| Address ptr_addr(ptr_value); |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| valobj_sp = ValueObjectMemory::Create( |
| exe_ctx.GetBestExecutionContextScope(), name, ptr_addr, compiler_type); |
| } |
| return valobj_sp; |
| } |
| |
| ValueObjectSP ValueObject::CastPointerType(const char *name, TypeSP &type_sp) { |
| ValueObjectSP valobj_sp; |
| AddressType address_type; |
| addr_t ptr_value = GetPointerValue(&address_type); |
| |
| if (ptr_value != LLDB_INVALID_ADDRESS) { |
| Address ptr_addr(ptr_value); |
| ExecutionContext exe_ctx(GetExecutionContextRef()); |
| valobj_sp = ValueObjectMemory::Create( |
| exe_ctx.GetBestExecutionContextScope(), name, ptr_addr, type_sp); |
| } |
| return valobj_sp; |
| } |
| |
| 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(), m_needs_update(true) { |
| 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(); |
| } |
| 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), m_needs_update(true) {} |
| |
| 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() { |
|