| //===-- ValueObjectDynamicValue.cpp ---------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/ValueObject/ValueObjectDynamicValue.h" | |
| #include "lldb/Core/Value.h" | |
| #include "lldb/Symbol/CompilerType.h" | |
| #include "lldb/Symbol/Type.h" | |
| #include "lldb/Target/ExecutionContext.h" | |
| #include "lldb/Target/LanguageRuntime.h" | |
| #include "lldb/Target/Process.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Utility/DataExtractor.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "lldb/Utility/Scalar.h" | |
| #include "lldb/Utility/Status.h" | |
| #include "lldb/ValueObject/ValueObject.h" | |
| #include "lldb/lldb-types.h" | |
| #include <cstring> | |
| #include <optional> | |
| namespace lldb_private { | |
| class Declaration; | |
| } | |
| using namespace lldb_private; | |
| ValueObjectDynamicValue::ValueObjectDynamicValue( | |
| ValueObject &parent, lldb::DynamicValueType use_dynamic) | |
| : ValueObject(parent), m_address(), m_dynamic_type_info(), | |
| m_use_dynamic(use_dynamic) { | |
| SetName(parent.GetName()); | |
| } | |
| CompilerType ValueObjectDynamicValue::GetCompilerTypeImpl() { | |
| const bool success = UpdateValueIfNeeded(false); | |
| if (success) { | |
| if (m_dynamic_type_info.HasType()) | |
| return m_value.GetCompilerType(); | |
| else | |
| return m_parent->GetCompilerType(); | |
| } | |
| return m_parent->GetCompilerType(); | |
| } | |
| ConstString ValueObjectDynamicValue::GetTypeName() { | |
| const bool success = UpdateValueIfNeeded(false); | |
| if (success) { | |
| if (m_dynamic_type_info.HasName()) | |
| return m_dynamic_type_info.GetName(); | |
| } | |
| return m_parent->GetTypeName(); | |
| } | |
| TypeImpl ValueObjectDynamicValue::GetTypeImpl() { | |
| const bool success = UpdateValueIfNeeded(false); | |
| if (success && m_type_impl.IsValid()) { | |
| return m_type_impl; | |
| } | |
| return m_parent->GetTypeImpl(); | |
| } | |
| ConstString ValueObjectDynamicValue::GetQualifiedTypeName() { | |
| const bool success = UpdateValueIfNeeded(false); | |
| if (success) { | |
| if (m_dynamic_type_info.HasName()) | |
| return m_dynamic_type_info.GetName(); | |
| } | |
| return m_parent->GetQualifiedTypeName(); | |
| } | |
| ConstString ValueObjectDynamicValue::GetDisplayTypeName() { | |
| const bool success = UpdateValueIfNeeded(false); | |
| if (success) { | |
| if (m_dynamic_type_info.HasType()) | |
| return GetCompilerType().GetDisplayTypeName(); | |
| if (m_dynamic_type_info.HasName()) | |
| return m_dynamic_type_info.GetName(); | |
| } | |
| return m_parent->GetDisplayTypeName(); | |
| } | |
| llvm::Expected<uint32_t> | |
| ValueObjectDynamicValue::CalculateNumChildren(uint32_t max) { | |
| const bool success = UpdateValueIfNeeded(false); | |
| if (success && m_dynamic_type_info.HasType()) { | |
| ExecutionContext exe_ctx(GetExecutionContextRef()); | |
| auto children_count = GetCompilerType().GetNumChildren(true, &exe_ctx); | |
| if (!children_count) | |
| return children_count; | |
| return *children_count <= max ? *children_count : max; | |
| } else | |
| return m_parent->GetNumChildren(max); | |
| } | |
| llvm::Expected<uint64_t> ValueObjectDynamicValue::GetByteSize() { | |
| const bool success = UpdateValueIfNeeded(false); | |
| if (success && m_dynamic_type_info.HasType()) { | |
| ExecutionContext exe_ctx(GetExecutionContextRef()); | |
| return m_value.GetValueByteSize(nullptr, &exe_ctx); | |
| } else | |
| return m_parent->GetByteSize(); | |
| } | |
| lldb::ValueType ValueObjectDynamicValue::GetValueType() const { | |
| return m_parent->GetValueType(); | |
| } | |
| bool ValueObjectDynamicValue::UpdateValue() { | |
| SetValueIsValid(false); | |
| m_error.Clear(); | |
| if (!m_parent->UpdateValueIfNeeded(false)) { | |
| // The dynamic value failed to get an error, pass the error along | |
| if (m_error.Success() && m_parent->GetError().Fail()) | |
| m_error = m_parent->GetError().Clone(); | |
| return false; | |
| } | |
| // Setting our type_sp to NULL will route everything back through our parent | |
| // which is equivalent to not using dynamic values. | |
| if (m_use_dynamic == lldb::eNoDynamicValues) { | |
| m_dynamic_type_info.Clear(); | |
| return true; | |
| } | |
| ExecutionContext exe_ctx(GetExecutionContextRef()); | |
| Target *target = exe_ctx.GetTargetPtr(); | |
| if (target) { | |
| m_data.SetByteOrder(target->GetArchitecture().GetByteOrder()); | |
| m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); | |
| } | |
| // First make sure our Type and/or Address haven't changed: | |
| Process *process = exe_ctx.GetProcessPtr(); | |
| if (!process) | |
| return false; | |
| TypeAndOrName class_type_or_name; | |
| Address dynamic_address; | |
| bool found_dynamic_type = false; | |
| Value::ValueType value_type; | |
| llvm::ArrayRef<uint8_t> local_buffer; | |
| LanguageRuntime *runtime = nullptr; | |
| lldb::LanguageType known_type = m_parent->GetObjectRuntimeLanguage(); | |
| if (known_type != lldb::eLanguageTypeUnknown && | |
| known_type != lldb::eLanguageTypeC) { | |
| runtime = process->GetLanguageRuntime(known_type); | |
| if (auto *preferred_runtime = | |
| runtime->GetPreferredLanguageRuntime(*m_parent)) { | |
| // Try the preferred runtime first. | |
| found_dynamic_type = preferred_runtime->GetDynamicTypeAndAddress( | |
| *m_parent, m_use_dynamic, class_type_or_name, dynamic_address, | |
| value_type, local_buffer); | |
| if (found_dynamic_type) | |
| // Set the operative `runtime` for later use in this function. | |
| runtime = preferred_runtime; | |
| } | |
| if (!found_dynamic_type) | |
| // Fallback to the runtime for `known_type`. | |
| found_dynamic_type = runtime->GetDynamicTypeAndAddress( | |
| *m_parent, m_use_dynamic, class_type_or_name, dynamic_address, | |
| value_type, local_buffer); | |
| } else { | |
| runtime = process->GetLanguageRuntime(lldb::eLanguageTypeC_plus_plus); | |
| if (runtime) | |
| found_dynamic_type = runtime->GetDynamicTypeAndAddress( | |
| *m_parent, m_use_dynamic, class_type_or_name, dynamic_address, | |
| value_type, local_buffer); | |
| if (!found_dynamic_type) { | |
| runtime = process->GetLanguageRuntime(lldb::eLanguageTypeObjC); | |
| if (runtime) | |
| found_dynamic_type = runtime->GetDynamicTypeAndAddress( | |
| *m_parent, m_use_dynamic, class_type_or_name, dynamic_address, | |
| value_type, local_buffer); | |
| } | |
| } | |
| // Getting the dynamic value may have run the program a bit, and so marked us | |
| // as needing updating, but we really don't... | |
| m_update_point.SetUpdated(); | |
| if (runtime && found_dynamic_type) { | |
| if (class_type_or_name.HasType()) { | |
| m_type_impl = | |
| TypeImpl(m_parent->GetCompilerType(), | |
| runtime->FixUpDynamicType(class_type_or_name, *m_parent) | |
| .GetCompilerType()); | |
| } else { | |
| m_type_impl.Clear(); | |
| } | |
| } else { | |
| m_type_impl.Clear(); | |
| } | |
| // If we don't have a dynamic type, set ourselves to be invalid and return | |
| // false. We used to try to produce a dynamic ValueObject that behaved "like" | |
| // its parent, but that failed for ValueObjectConstResult, which is too | |
| // complex a beast to try to emulate. If we return an invalid ValueObject, | |
| // clients will end up getting the static value instead, which behaves | |
| // correctly. | |
| if (!found_dynamic_type) { | |
| if (m_dynamic_type_info) | |
| SetValueDidChange(true); | |
| ClearDynamicTypeInformation(); | |
| m_dynamic_type_info.Clear(); | |
| m_error = Status::FromErrorString("no dynamic type found"); | |
| return false; | |
| } | |
| Value old_value(m_value); | |
| Log *log = GetLog(LLDBLog::Types); | |
| bool has_changed_type = false; | |
| if (!m_dynamic_type_info) { | |
| m_dynamic_type_info = class_type_or_name; | |
| has_changed_type = true; | |
| } else if (class_type_or_name != m_dynamic_type_info) { | |
| // We are another type, we need to tear down our children... | |
| m_dynamic_type_info = class_type_or_name; | |
| SetValueDidChange(true); | |
| has_changed_type = true; | |
| } | |
| if (has_changed_type) | |
| ClearDynamicTypeInformation(); | |
| if (!m_address.IsValid() || m_address != dynamic_address) { | |
| if (m_address.IsValid()) | |
| SetValueDidChange(true); | |
| // If we found a host address, and the dynamic type fits in the local buffer | |
| // that was found, point to that buffer. Later on this function will copy | |
| // the buffer over. | |
| if (value_type == Value::ValueType::HostAddress && !local_buffer.empty()) { | |
| auto *exe_scope = exe_ctx.GetBestExecutionContextScope(); | |
| // If we found a host address but it doesn't fit in the buffer, there's | |
| // nothing we can do. | |
| if (local_buffer.size() < | |
| llvm::expectedToOptional( | |
| m_dynamic_type_info.GetCompilerType().GetByteSize(exe_scope))) { | |
| SetValueIsValid(false); | |
| return false; | |
| } | |
| m_value.GetScalar() = (uint64_t)local_buffer.data(); | |
| m_address = Address(); | |
| } else { | |
| // Otherwise we have a legitimate address on the target. Point to the load | |
| // address. | |
| m_address = dynamic_address; | |
| lldb::TargetSP target_sp(GetTargetSP()); | |
| lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get()); | |
| m_value.GetScalar() = load_address; | |
| } | |
| } | |
| if (runtime) | |
| m_dynamic_type_info = | |
| runtime->FixUpDynamicType(m_dynamic_type_info, *m_parent); | |
| m_value.SetCompilerType(m_dynamic_type_info.GetCompilerType()); | |
| m_value.SetValueType(value_type); | |
| if (has_changed_type && log) | |
| LLDB_LOGF(log, "[%s %p] has a new dynamic type %s", GetName().GetCString(), | |
| static_cast<void *>(this), GetTypeName().GetCString()); | |
| // m_address could be invalid but we could still have a local buffer | |
| // containing the dynamic value. | |
| if ((m_address.IsValid() || | |
| m_value.GetValueType() == Value::ValueType::HostAddress) && | |
| m_dynamic_type_info) { | |
| // The variable value is in the Scalar value inside the m_value. We can | |
| // point our m_data right to it. | |
| m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get()); | |
| if (m_error.Success()) { | |
| if (!CanProvideValue()) { | |
| // this value object represents an aggregate type whose children have | |
| // values, but this object does not. So we say we are changed if our | |
| // location has changed. | |
| SetValueDidChange(m_value.GetValueType() != old_value.GetValueType() || | |
| m_value.GetScalar() != old_value.GetScalar()); | |
| } | |
| SetValueIsValid(true); | |
| return true; | |
| } | |
| } | |
| // We get here if we've failed above... | |
| SetValueIsValid(false); | |
| return false; | |
| } | |
| bool ValueObjectDynamicValue::IsInScope() { return m_parent->IsInScope(); } | |
| bool ValueObjectDynamicValue::SetValueFromCString(const char *value_str, | |
| Status &error) { | |
| if (!UpdateValueIfNeeded(false)) { | |
| error = Status::FromErrorString("unable to read value"); | |
| return false; | |
| } | |
| uint64_t my_value = GetValueAsUnsigned(UINT64_MAX); | |
| uint64_t parent_value = m_parent->GetValueAsUnsigned(UINT64_MAX); | |
| if (my_value == UINT64_MAX || parent_value == UINT64_MAX) { | |
| error = Status::FromErrorString("unable to read value"); | |
| return false; | |
| } | |
| // if we are at an offset from our parent, in order to set ourselves | |
| // correctly we would need to change the new value so that it refers to the | |
| // correct dynamic type. we choose not to deal with that - if anything more | |
| // than a value overwrite is required, you should be using the expression | |
| // parser instead of the value editing facility | |
| if (my_value != parent_value) { | |
| // but NULL'ing out a value should always be allowed | |
| if (strcmp(value_str, "0")) { | |
| error = Status::FromErrorString( | |
| "unable to modify dynamic value, use 'expression' command"); | |
| return false; | |
| } | |
| } | |
| bool ret_val = m_parent->SetValueFromCString(value_str, error); | |
| SetNeedsUpdate(); | |
| return ret_val; | |
| } | |
| bool ValueObjectDynamicValue::SetData(DataExtractor &data, Status &error) { | |
| if (!UpdateValueIfNeeded(false)) { | |
| error = Status::FromErrorString("unable to read value"); | |
| return false; | |
| } | |
| uint64_t my_value = GetValueAsUnsigned(UINT64_MAX); | |
| uint64_t parent_value = m_parent->GetValueAsUnsigned(UINT64_MAX); | |
| if (my_value == UINT64_MAX || parent_value == UINT64_MAX) { | |
| error = Status::FromErrorString("unable to read value"); | |
| return false; | |
| } | |
| // if we are at an offset from our parent, in order to set ourselves | |
| // correctly we would need to change the new value so that it refers to the | |
| // correct dynamic type. we choose not to deal with that - if anything more | |
| // than a value overwrite is required, you should be using the expression | |
| // parser instead of the value editing facility | |
| if (my_value != parent_value) { | |
| // but NULL'ing out a value should always be allowed | |
| lldb::offset_t offset = 0; | |
| if (data.GetAddress(&offset) != 0) { | |
| error = Status::FromErrorString( | |
| "unable to modify dynamic value, use 'expression' command"); | |
| return false; | |
| } | |
| } | |
| bool ret_val = m_parent->SetData(data, error); | |
| SetNeedsUpdate(); | |
| return ret_val; | |
| } | |
| void ValueObjectDynamicValue::SetPreferredDisplayLanguage( | |
| lldb::LanguageType lang) { | |
| this->ValueObject::SetPreferredDisplayLanguage(lang); | |
| if (m_parent) | |
| m_parent->SetPreferredDisplayLanguage(lang); | |
| } | |
| lldb::LanguageType ValueObjectDynamicValue::GetPreferredDisplayLanguage() { | |
| if (m_preferred_display_language == lldb::eLanguageTypeUnknown) { | |
| if (m_parent) | |
| return m_parent->GetPreferredDisplayLanguage(); | |
| return lldb::eLanguageTypeUnknown; | |
| } else | |
| return m_preferred_display_language; | |
| } | |
| bool ValueObjectDynamicValue::IsSyntheticChildrenGenerated() { | |
| if (m_parent) | |
| return m_parent->IsSyntheticChildrenGenerated(); | |
| return false; | |
| } | |
| void ValueObjectDynamicValue::SetSyntheticChildrenGenerated(bool b) { | |
| if (m_parent) | |
| m_parent->SetSyntheticChildrenGenerated(b); | |
| this->ValueObject::SetSyntheticChildrenGenerated(b); | |
| } | |
| bool ValueObjectDynamicValue::GetDeclaration(Declaration &decl) { | |
| if (m_parent) | |
| return m_parent->GetDeclaration(decl); | |
| return ValueObject::GetDeclaration(decl); | |
| } | |
| uint64_t ValueObjectDynamicValue::GetLanguageFlags() { | |
| if (m_parent) | |
| return m_parent->GetLanguageFlags(); | |
| return this->ValueObject::GetLanguageFlags(); | |
| } | |
| void ValueObjectDynamicValue::SetLanguageFlags(uint64_t flags) { | |
| if (m_parent) | |
| m_parent->SetLanguageFlags(flags); | |
| else | |
| this->ValueObject::SetLanguageFlags(flags); | |
| } |