| //===-- ValueObjectDynamicValue.cpp ------------------------------*- C++-*-===// | |
| // | |
| // 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/ValueObjectDynamicValue.h" | |
| #include "lldb/Core/Value.h" | |
| #include "lldb/Core/ValueObject.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/Log.h" | |
| #include "lldb/Utility/Logging.h" | |
| #include "lldb/Utility/Scalar.h" | |
| #include "lldb/Utility/Status.h" | |
| #include "lldb/lldb-types.h" | |
| #include <string.h> | |
| 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()); | |
| } | |
| ValueObjectDynamicValue::~ValueObjectDynamicValue() { | |
| m_owning_valobj_sp.reset(); | |
| } | |
| 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(); | |
| } | |
| size_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); | |
| return children_count <= max ? children_count : max; | |
| } else | |
| return m_parent->GetNumChildren(max); | |
| } | |
| 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(); | |
| 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; | |
| 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 (runtime) | |
| found_dynamic_type = runtime->GetDynamicTypeAndAddress( | |
| *m_parent, m_use_dynamic, class_type_or_name, dynamic_address, | |
| value_type); | |
| } 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); | |
| 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); | |
| } | |
| } | |
| // 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, then make ourselves just a echo of our | |
| // parent. Or we could return false, and make ourselves an echo of our | |
| // parent? | |
| if (!found_dynamic_type) { | |
| if (m_dynamic_type_info) | |
| SetValueDidChange(true); | |
| ClearDynamicTypeInformation(); | |
| m_dynamic_type_info.Clear(); | |
| m_value = m_parent->GetValue(); | |
| m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get()); | |
| return m_error.Success(); | |
| } | |
| Value old_value(m_value); | |
| Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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); | |
| // We've moved, so we should be fine... | |
| 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.SetContext (Value::eContextTypeClangType, corrected_type); | |
| 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()); | |
| if (m_address.IsValid() && 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.SetErrorString("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.SetErrorString("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.SetErrorString( | |
| "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.SetErrorString("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.SetErrorString("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.GetPointer(&offset) != 0) { | |
| error.SetErrorString( | |
| "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); | |
| } |