diff options
Diffstat (limited to 'source/Core/ValueObject.cpp')
-rw-r--r-- | source/Core/ValueObject.cpp | 4199 |
1 files changed, 4199 insertions, 0 deletions
diff --git a/source/Core/ValueObject.cpp b/source/Core/ValueObject.cpp new file mode 100644 index 0000000..a30cc13 --- /dev/null +++ b/source/Core/ValueObject.cpp @@ -0,0 +1,4199 @@ +//===-- ValueObject.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/ValueObject.h" + +// C Includes +#include <stdlib.h> + +// C++ Includes +// Other libraries and framework includes +#include "llvm/Support/raw_ostream.h" +#include "clang/AST/Type.h" + +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectCast.h" +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectDynamicValue.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Core/ValueObjectSyntheticFilter.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +#include "lldb/Host/Endian.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_utility; + +static user_id_t g_value_obj_uid = 0; + +//---------------------------------------------------------------------- +// ValueObject constructor +//---------------------------------------------------------------------- +ValueObject::ValueObject (ValueObject &parent) : + UserID (++g_value_obj_uid), // Unique identifier for every value object + m_parent (&parent), + m_root (NULL), + m_update_point (parent.GetUpdatePoint ()), + m_name (), + m_data (), + m_value (), + m_error (), + m_value_str (), + m_old_value_str (), + m_location_str (), + m_summary_str (), + m_object_desc_str (), + m_manager(parent.GetManager()), + m_children (), + m_synthetic_children (), + m_dynamic_value (NULL), + m_synthetic_value(NULL), + m_deref_valobj(NULL), + m_format (eFormatDefault), + m_last_format (eFormatDefault), + m_last_format_mgr_revision(0), + m_type_summary_sp(), + m_type_format_sp(), + m_synthetic_children_sp(), + m_user_id_of_forced_summary(), + m_address_type_of_ptr_or_ref_children(eAddressTypeInvalid), + m_value_is_valid (false), + m_value_did_change (false), + m_children_count_valid (false), + m_old_value_valid (false), + m_is_deref_of_parent (false), + m_is_array_item_for_pointer(false), + m_is_bitfield_for_scalar(false), + m_is_child_at_offset(false), + m_is_getting_summary(false), + m_did_calculate_complete_objc_class_type(false) +{ + m_manager->ManageObject(this); +} + +//---------------------------------------------------------------------- +// ValueObject constructor +//---------------------------------------------------------------------- +ValueObject::ValueObject (ExecutionContextScope *exe_scope, + AddressType child_ptr_or_ref_addr_type) : + UserID (++g_value_obj_uid), // Unique identifier for every value object + m_parent (NULL), + m_root (NULL), + m_update_point (exe_scope), + m_name (), + m_data (), + m_value (), + m_error (), + m_value_str (), + m_old_value_str (), + m_location_str (), + m_summary_str (), + m_object_desc_str (), + m_manager(), + m_children (), + m_synthetic_children (), + m_dynamic_value (NULL), + m_synthetic_value(NULL), + m_deref_valobj(NULL), + m_format (eFormatDefault), + m_last_format (eFormatDefault), + m_last_format_mgr_revision(0), + m_type_summary_sp(), + m_type_format_sp(), + m_synthetic_children_sp(), + m_user_id_of_forced_summary(), + m_address_type_of_ptr_or_ref_children(child_ptr_or_ref_addr_type), + m_value_is_valid (false), + m_value_did_change (false), + m_children_count_valid (false), + m_old_value_valid (false), + m_is_deref_of_parent (false), + m_is_array_item_for_pointer(false), + m_is_bitfield_for_scalar(false), + m_is_child_at_offset(false), + m_is_getting_summary(false), + m_did_calculate_complete_objc_class_type(false) +{ + m_manager = new ValueObjectManager(); + m_manager->ManageObject (this); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ValueObject::~ValueObject () +{ +} + +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 were asked to update your formatters, but did not get a chance to do it + // clear your own values (this serves the purpose of faking a stop-id for frozen + // objects (which are regarded as constant, but could have changes behind their backs + // because of the frozen-pointer depth limit) + // TODO: decouple summary from value and then remove this code and only force-clear the summary + if (update_format && !did_change_formats) + ClearUserVisibleData(eClearUserVisibleDataItemsSummary); + return m_error.Success(); + } + + bool first_update = m_update_point.IsFirstEvaluation(); + + if (m_update_point.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_old_value_valid = false; + } + else + { + 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 success = UpdateValue (); + + SetValueIsValid (success); + + if (first_update) + SetValueDidChange (false); + else if (!m_value_did_change && success == false) + { + // 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 + { + m_error.SetErrorString("out of scope"); + } + } + return m_error.Success(); +} + +bool +ValueObject::UpdateFormatsIfNeeded() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + if (log) + log->Printf("[%s %p] checking for FormatManager revisions. ValueObject rev: %d - Global rev: %d", + GetName().GetCString(), + this, + m_last_format_mgr_revision, + DataVisualization::GetCurrentRevision()); + + bool any_change = false; + + if ( (m_last_format_mgr_revision != DataVisualization::GetCurrentRevision())) + { + SetValueFormat(DataVisualization::ValueFormats::GetFormat (*this, eNoDynamicValues)); + SetSummaryFormat(DataVisualization::GetSummaryFormat (*this, GetDynamicValueType())); +#ifndef LLDB_DISABLE_PYTHON + SetSyntheticChildren(DataVisualization::GetSyntheticChildren (*this, GetDynamicValueType())); +#endif + + m_last_format_mgr_revision = DataVisualization::GetCurrentRevision(); + + any_change = true; + } + + 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_did_calculate_complete_objc_class_type = false; + m_last_format_mgr_revision = 0; + m_override_type = ClangASTType(); + SetValueFormat(lldb::TypeFormatImplSP()); + SetSummaryFormat(lldb::TypeSummaryImplSP()); + SetSyntheticChildren(lldb::SyntheticChildrenSP()); +} + +ClangASTType +ValueObject::MaybeCalculateCompleteType () +{ + ClangASTType clang_type(GetClangTypeImpl()); + + if (m_did_calculate_complete_objc_class_type) + { + if (m_override_type.IsValid()) + return m_override_type; + else + return clang_type; + } + + ClangASTType class_type; + bool is_pointer_type = false; + + if (clang_type.IsObjCObjectPointerType(&class_type)) + { + is_pointer_type = true; + } + else if (clang_type.IsObjCObjectOrInterfaceType()) + { + class_type = clang_type; + } + else + { + return clang_type; + } + + m_did_calculate_complete_objc_class_type = true; + + if (class_type) + { + ConstString class_name (class_type.GetConstTypeName()); + + if (class_name) + { + ProcessSP process_sp(GetUpdatePoint().GetExecutionContextRef().GetProcessSP()); + + if (process_sp) + { + ObjCLanguageRuntime *objc_language_runtime(process_sp->GetObjCLanguageRuntime()); + + if (objc_language_runtime) + { + TypeSP complete_objc_class_type_sp = objc_language_runtime->LookupInCompleteClassCache(class_name); + + if (complete_objc_class_type_sp) + { + ClangASTType complete_class(complete_objc_class_type_sp->GetClangFullType()); + + if (complete_class.GetCompleteType()) + { + if (is_pointer_type) + { + m_override_type = complete_class.GetPointerType(); + } + else + { + m_override_type = complete_class; + } + + if (m_override_type.IsValid()) + return m_override_type; + } + } + } + } + } + } + return clang_type; +} + +ClangASTType +ValueObject::GetClangType () +{ + return MaybeCalculateCompleteType(); +} + +DataExtractor & +ValueObject::GetDataExtractor () +{ + UpdateValueIfNeeded(false); + return m_data; +} + +const Error & +ValueObject::GetError() +{ + UpdateValueIfNeeded(false); + return m_error; +} + +const ConstString & +ValueObject::GetName() const +{ + return m_name; +} + +const char * +ValueObject::GetLocationAsCString () +{ + return GetLocationAsCStringImpl(m_value, + m_data); +} + +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::eValueTypeScalar: + case Value::eValueTypeVector: + if (value.GetContextType() == Value::eContextTypeRegisterInfo) + { + 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 = (value_type == Value::eValueTypeVector) ? "vector" : "scalar"; + break; + + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + { + 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.swap(sstr.GetString()); + } + break; + } + } + } + return m_location_str.c_str(); +} + +Value & +ValueObject::GetValue() +{ + return m_value; +} + +const Value & +ValueObject::GetValue() const +{ + return m_value; +} + +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::GetValueIsValid () const +{ + return m_value_is_valid; +} + + +void +ValueObject::SetValueIsValid (bool b) +{ + m_value_is_valid = b; +} + +bool +ValueObject::GetValueDidChange () +{ + GetValueAsCString (); + return m_value_did_change; +} + +void +ValueObject::SetValueDidChange (bool value_changed) +{ + m_value_did_change = value_changed; +} + +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 != NULL) + return child->GetSP(); + } + return child_sp; +} + +ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::initializer_list<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; +} + +ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::initializer_list< 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::GetChildAtIndexPath (const std::vector<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 (const std::vector< 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; +} + +size_t +ValueObject::GetIndexOfChildWithName (const ConstString &name) +{ + bool omit_empty_base_classes = true; + return GetClangType().GetIndexOfChildWithName (name.GetCString(), omit_empty_base_classes); +} + +ValueObjectSP +ValueObject::GetChildMemberWithName (const ConstString &name, bool can_create) +{ + // 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 + ValueObjectSP child_sp; + + // We may need to update our value if we are dynamic + if (IsPossibleDynamicType ()) + UpdateValueIfNeeded(false); + + std::vector<uint32_t> child_indexes; + bool omit_empty_base_classes = true; + const size_t num_child_indexes = GetClangType().GetIndexOfChildMemberWithName (name.GetCString(), + omit_empty_base_classes, + child_indexes); + if (num_child_indexes > 0) + { + std::vector<uint32_t>::const_iterator pos = child_indexes.begin (); + std::vector<uint32_t>::const_iterator end = child_indexes.end (); + + child_sp = GetChildAtIndex(*pos, can_create); + for (++pos; pos != end; ++pos) + { + if (child_sp) + { + ValueObjectSP new_child_sp(child_sp->GetChildAtIndex (*pos, can_create)); + child_sp = new_child_sp; + } + else + { + child_sp.reset(); + } + + } + } + return child_sp; +} + + +size_t +ValueObject::GetNumChildren () +{ + UpdateValueIfNeeded(); + if (!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 & (ClangASTType::eTypeHasChildren | + ClangASTType::eTypeIsPointer | + ClangASTType::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_children_count_valid = true; + m_children.SetChildrenCount(num_children); +} + +void +ValueObject::SetName (const ConstString &name) +{ + m_name = name; +} + +ValueObject * +ValueObject::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObject *valobj = NULL; + + 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; + + const bool transparent_pointers = synthetic_array_member == false; + ClangASTType child_clang_type; + + ExecutionContext exe_ctx (GetExecutionContextRef()); + + child_clang_type = GetClangType().GetChildClangTypeAtIndex (&exe_ctx, + GetName().GetCString(), + 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); + if (child_clang_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_clang_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); + //if (valobj) + // valobj->SetAddressTypeOfChildren(eAddressTypeInvalid); + } + + return valobj; +} + +bool +ValueObject::GetSummaryAsCString (TypeSummaryImpl* summary_ptr, + std::string& destination) +{ + 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_is_getting_summary) + return false; + + m_is_getting_summary = true; + + // 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)) + { + if (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); + } + else + { + ClangASTType clang_type = GetClangType(); + + // Do some default printout for function pointers + if (clang_type) + { + if (clang_type.IsFunctionPointerType ()) + { + StreamString sstr; + AddressType func_ptr_address_type = eAddressTypeInvalid; + addr_t func_ptr_address = GetPointerValue (&func_ptr_address_type); + if (func_ptr_address != 0 && func_ptr_address != LLDB_INVALID_ADDRESS) + { + switch (func_ptr_address_type) + { + case eAddressTypeInvalid: + case eAddressTypeFile: + break; + + case eAddressTypeLoad: + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + + Address so_addr; + Target *target = exe_ctx.GetTargetPtr(); + if (target && target->GetSectionLoadList().IsEmpty() == false) + { + if (target->GetSectionLoadList().ResolveLoadAddress(func_ptr_address, so_addr)) + { + so_addr.Dump (&sstr, + exe_ctx.GetBestExecutionContextScope(), + Address::DumpStyleResolvedDescription, + Address::DumpStyleSectionNameOffset); + } + } + } + break; + + case eAddressTypeHost: + break; + } + } + if (sstr.GetSize() > 0) + { + destination.assign (1, '('); + destination.append (sstr.GetData(), sstr.GetSize()); + destination.append (1, ')'); + } + } + } + } + } + m_is_getting_summary = false; + return !destination.empty(); +} + +const char * +ValueObject::GetSummaryAsCString () +{ + if (UpdateValueIfNeeded(true) && m_summary_str.empty()) + { + GetSummaryAsCString(GetSummaryFormat().get(), + m_summary_str); + } + if (m_summary_str.empty()) + return NULL; + return m_summary_str.c_str(); +} + +bool +ValueObject::IsCStringContainer(bool check_pointer) +{ + ClangASTType pointee_or_element_clang_type; + const Flags type_flags (GetTypeInfo (&pointee_or_element_clang_type)); + bool is_char_arr_ptr (type_flags.AnySet (ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) && + pointee_or_element_clang_type.IsCharType ()); + if (!is_char_arr_ptr) + return false; + if (!check_pointer) + return true; + if (type_flags.Test(ClangASTType::eTypeIsArray)) + return true; + addr_t cstr_address = LLDB_INVALID_ADDRESS; + AddressType cstr_address_type = eAddressTypeInvalid; + cstr_address = GetAddressOf (true, &cstr_address_type); + return (cstr_address != LLDB_INVALID_ADDRESS); +} + +size_t +ValueObject::GetPointeeData (DataExtractor& data, + uint32_t item_idx, + uint32_t item_count) +{ + ClangASTType pointee_or_element_clang_type; + const uint32_t type_info = GetTypeInfo (&pointee_or_element_clang_type); + const bool is_pointer_type = type_info & ClangASTType::eTypeIsPointer; + const bool is_array_type = type_info & ClangASTType::eTypeIsArray; + if (!(is_pointer_type || is_array_type)) + return 0; + + if (item_count == 0) + return 0; + + const uint64_t item_type_size = pointee_or_element_clang_type.GetByteSize(); + 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) + { + Error error; + ValueObjectSP pointee_sp = Dereference(error); + if (error.Fail() || pointee_sp.get() == NULL) + return 0; + return pointee_sp->GetDataExtractor().Copy(data); + } + else + { + ValueObjectSP child_sp = GetChildAtIndex(0, true); + if (child_sp.get() == NULL) + return 0; + return child_sp->GetDataExtractor().Copy(data); + } + return true; + } + else /* (items > 1) */ + { + Error error; + lldb_private::DataBufferHeap* heap_buf_ptr = NULL; + 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, false, heap_buf_ptr->GetBytes(), bytes, error); + 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()) + { + data.SetData(data_sp); + return bytes_read; + } + } + } + break; + case eAddressTypeHost: + { + const uint64_t max_bytes = GetClangType().GetByteSize(); + if (max_bytes > offset) + { + size_t bytes_read = std::min<uint64_t>(max_bytes - offset, bytes); + 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) +{ + UpdateValueIfNeeded(false); + ExecutionContext exe_ctx (GetExecutionContextRef()); + Error error = m_value.GetValueAsData(&exe_ctx, data, 0, GetModule().get()); + if (error.Fail()) + { + if (m_data.GetByteSize()) + { + data = m_data; + return data.GetByteSize(); + } + else + { + return 0; + } + } + data.SetAddressByteSize(m_data.GetAddressByteSize()); + data.SetByteOrder(m_data.GetByteOrder()); + return data.GetByteSize(); +} + +bool +ValueObject::SetData (DataExtractor &data, Error &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 = GetClangType().GetEncoding(count); + + const size_t byte_size = GetByteSize(); + + Value::ValueType value_type = m_value.GetValueType(); + + switch (value_type) + { + case Value::eValueTypeScalar: + { + Error 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::eValueTypeLoadAddress: + { + // 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::eValueTypeHostAddress: + { + // 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::eValueTypeFileAddress: + case Value::eValueTypeVector: + break; + } + + // If we have reached this point, then we have successfully changed the value. + SetNeedsUpdate(); + return true; +} + +// will compute strlen(str), but without consuming more than +// maxlen bytes out of str (this serves the purpose of reading +// chunks of a string without having to worry about +// missing NULL terminators in the chunk) +// of course, if strlen(str) > maxlen, the function will return +// maxlen_value (which should be != maxlen, because that allows you +// to know whether strlen(str) == maxlen or strlen(str) > maxlen) +static uint32_t +strlen_or_inf (const char* str, + uint32_t maxlen, + uint32_t maxlen_value) +{ + uint32_t len = 0; + if (str) + { + while(*str) + { + len++;str++; + if (len >= maxlen) + return maxlen_value; + } + } + return len; +} + +size_t +ValueObject::ReadPointedString (Stream& s, + Error& error, + uint32_t max_length, + bool honor_array, + Format item_format) +{ + ExecutionContext exe_ctx (GetExecutionContextRef()); + Target* target = exe_ctx.GetTargetPtr(); + + if (!target) + { + s << "<no target to read from>"; + error.SetErrorString("no target to read from"); + return 0; + } + + if (max_length == 0) + max_length = target->GetMaximumSizeOfStringSummary(); + + size_t bytes_read = 0; + size_t total_bytes_read = 0; + + ClangASTType clang_type = GetClangType(); + ClangASTType elem_or_pointee_clang_type; + const Flags type_flags (GetTypeInfo (&elem_or_pointee_clang_type)); + if (type_flags.AnySet (ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) && + elem_or_pointee_clang_type.IsCharType ()) + { + addr_t cstr_address = LLDB_INVALID_ADDRESS; + AddressType cstr_address_type = eAddressTypeInvalid; + + size_t cstr_len = 0; + bool capped_data = false; + if (type_flags.Test (ClangASTType::eTypeIsArray)) + { + // We have an array + uint64_t array_size = 0; + if (clang_type.IsArrayType(NULL, &array_size, NULL)) + { + 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) + { + s << "<invalid address>"; + error.SetErrorString("invalid address"); + return 0; + } + + 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; + s << '"'; + data.Dump (&s, + 0, // Start offset in "data" + item_format, + 1, // Size of item (1 byte for a char!) + bytes_read, // How many bytes to print? + UINT32_MAX, // num per line + LLDB_INVALID_ADDRESS,// base address + 0, // bitfield bit size + 0); // bitfield bit offset + if (capped_data) + s << "..."; + s << '"'; + } + } + 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 = strlen_or_inf (cstr, k_max_buf_size, k_max_buf_size+1); + if (len > k_max_buf_size) + len = k_max_buf_size; + if (cstr && cstr_len_displayed < 0) + s << '"'; + + 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; + + data.Dump (&s, + 0, // Start offset in "data" + item_format, + 1, // Size of item (1 byte for a char!) + len, // How many bytes to print? + UINT32_MAX, // num per line + LLDB_INVALID_ADDRESS,// base address + 0, // bitfield bit size + 0); // bitfield bit offset + + 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) + { + s << '"'; + if (capped_cstr) + s << "..."; + } + } + } + else + { + error.SetErrorString("not a string object"); + s << "<not a string object>"; + } + return total_bytes_read; +} + +const char * +ValueObject::GetObjectDescription () +{ + + if (!UpdateValueIfNeeded (true)) + return NULL; + + if (!m_object_desc_str.empty()) + return m_object_desc_str.c_str(); + + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process == NULL) + return NULL; + + StreamString s; + + LanguageType language = GetObjectRuntimeLanguage(); + LanguageRuntime *runtime = process->GetLanguageRuntime(language); + + if (runtime == NULL) + { + // Aw, hell, if the things a pointer, or even just an integer, let's try ObjC anyway... + ClangASTType clang_type = GetClangType(); + if (clang_type) + { + bool is_signed; + if (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType ()) + { + runtime = process->GetLanguageRuntime(eLanguageTypeObjC); + } + } + } + + if (runtime && runtime->GetObjectDescription(s, *this)) + { + m_object_desc_str.append (s.GetData()); + } + + if (m_object_desc_str.empty()) + return NULL; + else + return m_object_desc_str.c_str(); +} + +bool +ValueObject::GetValueAsCString (lldb::Format format, + std::string& destination) +{ + if (GetClangType().IsAggregateType () == false && UpdateValueIfNeeded(false)) + { + const Value::ContextType context_type = m_value.GetContextType(); + + if (context_type == Value::eContextTypeRegisterInfo) + { + const RegisterInfo *reg_info = m_value.GetRegisterInfo(); + if (reg_info) + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + + StreamString reg_sstr; + m_data.Dump (®_sstr, + 0, + format, + reg_info->byte_size, + 1, + UINT32_MAX, + LLDB_INVALID_ADDRESS, + 0, + 0, + exe_ctx.GetBestExecutionContextScope()); + destination.swap(reg_sstr.GetString()); + } + } + else + { + ClangASTType clang_type = GetClangType (); + if (clang_type) + { + // put custom bytes to display in this DataExtractor to override the default value logic + lldb_private::DataExtractor special_format_data; + if (format == eFormatCString) + { + Flags type_flags(clang_type.GetTypeInfo(NULL)); + if (type_flags.Test(ClangASTType::eTypeIsPointer) && !type_flags.Test(ClangASTType::eTypeIsObjC)) + { + // if we are dumping a pointer as a c-string, get the pointee data as a string + TargetSP target_sp(GetTargetSP()); + if (target_sp) + { + size_t max_len = target_sp->GetMaximumSizeOfStringSummary(); + Error error; + DataBufferSP buffer_sp(new DataBufferHeap(max_len+1,0)); + Address address(GetPointerValue()); + if (target_sp->ReadCStringFromMemory(address, (char*)buffer_sp->GetBytes(), max_len, error) && error.Success()) + special_format_data.SetData(buffer_sp); + } + } + } + + StreamString sstr; + ExecutionContext exe_ctx (GetExecutionContextRef()); + clang_type.DumpTypeValue (&sstr, // The stream to use for display + format, // Format to display this type with + special_format_data.GetByteSize() ? + special_format_data: m_data, // Data to extract from + 0, // Byte offset into "m_data" + GetByteSize(), // Byte size of item in "m_data" + GetBitfieldBitSize(), // Bitfield bit size + GetBitfieldBitOffset(), // Bitfield bit offset + exe_ctx.GetBestExecutionContextScope()); + // Don't set the m_error to anything here otherwise + // we won't be able to re-format as anything else. The + // code for ClangASTType::DumpTypeValue() should always + // return something, even if that something contains + // an error messsage. "m_error" is used to detect errors + // when reading the valid object, not for formatting errors. + if (sstr.GetString().empty()) + destination.clear(); + else + destination.swap(sstr.GetString()); + } + } + return !destination.empty(); + } + else + return false; +} + +const char * +ValueObject::GetValueAsCString () +{ + if (UpdateValueIfNeeded(true)) + { + lldb::Format my_format = GetFormat(); + if (my_format == lldb::eFormatDefault) + { + if (m_type_format_sp) + my_format = m_type_format_sp->GetFormat(); + else + { + if (m_is_bitfield_for_scalar) + my_format = eFormatUnsigned; + else + { + if (m_value.GetContextType() == Value::eContextTypeRegisterInfo) + { + const RegisterInfo *reg_info = m_value.GetRegisterInfo(); + if (reg_info) + my_format = reg_info->format; + } + else + { + my_format = GetClangType().GetFormat(); + } + } + } + } + if (my_format != m_last_format || m_value_str.empty()) + { + m_last_format = my_format; + if (GetValueAsCString(my_format, m_value_str)) + { + if (!m_value_did_change && 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 NULL; + 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 (!GetClangType().IsAggregateType()) + { + Scalar scalar; + if (ResolveValue (scalar)) + { + if (success) + *success = true; + return scalar.ULongLong(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(ClangASTType::eTypeIsArray | ClangASTType::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(ClangASTType::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) +{ + + Flags flags(GetTypeInfo()); + + bool allow_special = ((special & ePrintableRepresentationSpecialCasesAllow) == ePrintableRepresentationSpecialCasesAllow); + bool only_special = ((special & ePrintableRepresentationSpecialCasesOnly) == ePrintableRepresentationSpecialCasesOnly); + + if (allow_special) + { + if (flags.AnySet(ClangASTType::eTypeIsArray | ClangASTType::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 + { + Error error; + ReadPointedString(s, + error, + 0, + (custom_format == eFormatVectorOfChar) || + (custom_format == eFormatCharArray)); + 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(ClangASTType::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; + + { + const char *cstr = NULL; + + // 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: + cstr = GetValueAsCString(); + break; + + case eValueObjectRepresentationStyleSummary: + cstr = GetSummaryAsCString(); + break; + + case eValueObjectRepresentationStyleLanguageSpecific: + cstr = GetObjectDescription(); + break; + + case eValueObjectRepresentationStyleLocation: + cstr = GetLocationAsCString(); + break; + + case eValueObjectRepresentationStyleChildrenCount: + strm.Printf("%zu", GetNumChildren()); + cstr = strm.GetString().c_str(); + break; + + case eValueObjectRepresentationStyleType: + cstr = GetTypeName().AsCString(); + break; + + case eValueObjectRepresentationStyleName: + cstr = GetName().AsCString(); + break; + + case eValueObjectRepresentationStyleExpressionPath: + GetExpressionPath(strm, false); + cstr = strm.GetString().c_str(); + break; + } + + if (!cstr) + { + if (val_obj_display == eValueObjectRepresentationStyleValue) + cstr = GetSummaryAsCString(); + else if (val_obj_display == eValueObjectRepresentationStyleSummary) + { + if (GetClangType().IsAggregateType()) + { + strm.Printf("%s @ %s", GetTypeName().AsCString(), GetLocationAsCString()); + cstr = strm.GetString().c_str(); + } + else + cstr = GetValueAsCString(); + } + } + + if (cstr) + s.PutCString(cstr); + else + { + if (m_error.Fail()) + s.Printf("<%s>", m_error.AsCString()); + 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) +{ + if (!UpdateValueIfNeeded(false)) + return LLDB_INVALID_ADDRESS; + + switch (m_value.GetValueType()) + { + case Value::eValueTypeScalar: + case Value::eValueTypeVector: + if (scalar_is_load_address) + { + if(address_type) + *address_type = eAddressTypeLoad; + return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + } + break; + + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + { + if(address_type) + *address_type = m_value.GetValueAddressType (); + return m_value.GetScalar().ULongLong(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::eValueTypeScalar: + case Value::eValueTypeVector: + address = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + break; + + case Value::eValueTypeHostAddress: + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + { + lldb::offset_t data_offset = 0; + address = m_data.GetPointer(&data_offset); + } + break; + } + + if (address_type) + *address_type = GetAddressTypeOfChildren(); + + return address; +} + +bool +ValueObject::SetValueFromCString (const char *value_str, Error& 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 = GetClangType().GetEncoding (count); + + const size_t byte_size = GetByteSize(); + + Value::ValueType value_type = m_value.GetValueType(); + + if (value_type == Value::eValueTypeScalar) + { + // 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 <= Scalar::GetMaxByteSize()) + { + // 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::eValueTypeLoadAddress: + { + // 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::eValueTypeHostAddress: + { + // 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::eValueTypeFileAddress: + case Value::eValueTypeScalar: + case Value::eValueTypeVector: + 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; +} + +ConstString +ValueObject::GetTypeName() +{ + return GetClangType().GetConstTypeName(); +} + +ConstString +ValueObject::GetQualifiedTypeName() +{ + return GetClangType().GetConstQualifiedTypeName(); +} + + +LanguageType +ValueObject::GetObjectRuntimeLanguage () +{ + return GetClangType().GetMinimumLanguage (); +} + +void +ValueObject::AddSyntheticChild (const ConstString &key, ValueObject *valobj) +{ + m_synthetic_children[key] = valobj; +} + +ValueObjectSP +ValueObject::GetSyntheticChild (const 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; +} + +uint32_t +ValueObject::GetTypeInfo (ClangASTType *pointee_or_element_clang_type) +{ + return GetClangType().GetTypeInfo (pointee_or_element_clang_type); +} + +bool +ValueObject::IsPointerType () +{ + return GetClangType().IsPointerType(); +} + +bool +ValueObject::IsArrayType () +{ + return GetClangType().IsArrayType (NULL, NULL, NULL); +} + +bool +ValueObject::IsScalarType () +{ + return GetClangType().IsScalarType (); +} + +bool +ValueObject::IsIntegerType (bool &is_signed) +{ + return GetClangType().IsIntegerType (is_signed); +} + +bool +ValueObject::IsPointerOrReferenceType () +{ + return GetClangType().IsPointerOrReferenceType (); +} + +bool +ValueObject::IsPossibleDynamicType () +{ + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process) + return process->IsPossibleDynamicValue(*this); + else + return GetClangType().IsPossibleDynamicType (NULL, true, true); +} + +bool +ValueObject::IsObjCNil () +{ + const uint32_t mask = ClangASTType::eTypeIsObjC | ClangASTType::eTypeIsPointer; + bool isObjCpointer = (((GetClangType().GetTypeInfo(NULL)) & mask) == mask); + if (!isObjCpointer) + return false; + bool canReadValue = true; + bool isZero = GetValueAsUnsigned(0,&canReadValue) == 0; + return canReadValue && isZero; +} + +ValueObjectSP +ValueObject::GetSyntheticArrayMember (size_t index, bool can_create) +{ + const uint32_t type_info = GetTypeInfo (); + if (type_info & ClangASTType::eTypeIsArray) + return GetSyntheticArrayMemberFromArray(index, can_create); + + if (type_info & ClangASTType::eTypeIsPointer) + return GetSyntheticArrayMemberFromPointer(index, can_create); + + return ValueObjectSP(); + +} + +ValueObjectSP +ValueObject::GetSyntheticArrayMemberFromPointer (size_t index, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + if (IsPointerType ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%zu]", 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_is_array_item_for_pointer = true; + } + } + } + return synthetic_child_sp; +} + +// 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::GetSyntheticArrayMemberFromArray (size_t index, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + if (IsArrayType ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%zu]", 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_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 ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%i-%i]", 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) + { + // 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, + GetClangType(), + index_const_str, + GetByteSize(), + 0, + to-from+1, + from, + false, + false, + eAddressTypeInvalid); + + // 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_is_bitfield_for_scalar = true; + } + } + } + return synthetic_child_sp; +} + +ValueObjectSP +ValueObject::GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create) +{ + + ValueObjectSP synthetic_child_sp; + + char name_str[64]; + snprintf(name_str, sizeof(name_str), "@%i", offset); + ConstString name_const_str(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 ValueObjectSP(); + + ValueObjectChild *synthetic_child = new ValueObjectChild(*this, + type, + name_const_str, + type.GetByteSize(), + offset, + 0, + 0, + false, + false, + eAddressTypeInvalid); + 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_is_child_at_offset = true; + } + 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, + NULL, NULL, NULL, + GetValueForExpressionPathOptions().DontAllowSyntheticChildren()); + + // 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 (bool use_synthetic) +{ + if (use_synthetic == false) + return; + + TargetSP target_sp(GetTargetSP()); + if (target_sp && (target_sp->GetEnableSyntheticValue() == false || target_sp->GetSuppressSyntheticValue() == true)) + { + m_synthetic_value = NULL; + return; + } + + lldb::SyntheticChildrenSP current_synth_sp(m_synthetic_children_sp); + + if (!UpdateFormatsIfNeeded() && m_synthetic_value) + return; + + if (m_synthetic_children_sp.get() == NULL) + 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 == NULL) + { + CalculateDynamicValue(use_dynamic); + } + if (m_dynamic_value) + return m_dynamic_value->GetSP(); + else + return ValueObjectSP(); +} + +ValueObjectSP +ValueObject::GetStaticValue() +{ + return GetSP(); +} + +lldb::ValueObjectSP +ValueObject::GetNonSyntheticValue () +{ + return GetSP(); +} + +ValueObjectSP +ValueObject::GetSyntheticValue (bool use_synthetic) +{ + if (use_synthetic == false) + return ValueObjectSP(); + + CalculateSyntheticValue(use_synthetic); + + if (m_synthetic_value) + return m_synthetic_value->GetSP(); + else + return ValueObjectSP(); +} + +bool +ValueObject::HasSyntheticValue() +{ + UpdateFormatsIfNeeded(); + + if (m_synthetic_children_sp.get() == NULL) + return false; + + CalculateSyntheticValue(true); + + if (m_synthetic_value) + return true; + else + return false; +} + +bool +ValueObject::GetBaseClassPath (Stream &s) +{ + if (IsBaseClass()) + { + bool parent_had_base_class = GetParent() && GetParent()->GetBaseClassPath (s); + ClangASTType clang_type = GetClangType(); + std::string cxx_class_name; + bool this_had_base_class = clang_type.GetCXXClassName (cxx_class_name); + if (this_had_base_class) + { + if (parent_had_base_class) + s.PutCString("::"); + s.PutCString(cxx_class_name.c_str()); + } + return parent_had_base_class || this_had_base_class; + } + return false; +} + + +ValueObject * +ValueObject::GetNonBaseClassParent() +{ + if (GetParent()) + { + if (GetParent()->IsBaseClass()) + return GetParent()->GetNonBaseClassParent(); + else + return GetParent(); + } + return NULL; +} + +void +ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat epformat) +{ + 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, qualify_cxx_base_classes, 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_is_array_item_for_pointer && epformat == eGetExpressionPathFormatHonorPointers) + s.PutCString(m_name.AsCString()); + + if (!IsBaseClass()) + { + if (!is_deref_of_parent) + { + ValueObject *non_base_class_parent = GetNonBaseClassParent(); + if (non_base_class_parent) + { + ClangASTType non_base_class_parent_clang_type = non_base_class_parent->GetClangType(); + if (non_base_class_parent_clang_type) + { + if (parent && parent->IsDereferenceOfParent() && epformat == eGetExpressionPathFormatHonorPointers) + { + s.PutCString("->"); + } + else + { + const uint32_t non_base_class_parent_type_info = non_base_class_parent_clang_type.GetTypeInfo(); + + if (non_base_class_parent_type_info & ClangASTType::eTypeIsPointer) + { + s.PutCString("->"); + } + else if ((non_base_class_parent_type_info & ClangASTType::eTypeHasChildren) && + !(non_base_class_parent_type_info & ClangASTType::eTypeIsArray)) + { + s.PutChar('.'); + } + } + } + } + + const char *name = GetName().GetCString(); + if (name) + { + if (qualify_cxx_base_classes) + { + if (GetBaseClassPath (s)) + s.PutCString("::"); + } + s.PutCString(name); + } + } + } + + if (is_deref_of_parent && epformat == eGetExpressionPathFormatDereferencePointers) + { + s.PutChar(')'); + } +} + +ValueObjectSP +ValueObject::GetValueForExpressionPath(const char* expression, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target) +{ + + const char* dummy_first_unparsed; + 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, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + 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) + { + Error 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) + { + Error 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 +} + +int +ValueObject::GetValuesForExpressionPath(const char* expression, + ValueObjectListSP& list, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target) +{ + const char* dummy_first_unparsed; + ExpressionPathScanEndReason dummy_reason_to_stop; + ExpressionPathEndResultType dummy_final_value_type; + ExpressionPathAftermath dummy_final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + + ValueObjectSP ret_val = GetValueForExpressionPath_Impl(expression, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + 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 (!ret_val.get()) // if there are errors, I add nothing to the list + return 0; + + if ( (reason_to_stop ? *reason_to_stop : dummy_reason_to_stop) != eExpressionPathScanEndReasonArrayRangeOperatorMet) + { + // I need not expand a range, just post-process the final value and return + if (!final_task_on_target || *final_task_on_target == ValueObject::eExpressionPathAftermathNothing) + { + list->Append(ret_val); + return 1; + } + 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 == ValueObject::eExpressionPathAftermathDereference) + { + Error 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 0; + } + else + { + *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + list->Append(final_value); + return 1; + } + } + if (*final_task_on_target == ValueObject::eExpressionPathAftermathTakeAddress) + { + Error 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 0; + } + else + { + *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + list->Append(final_value); + return 1; + } + } + } + } + else + { + return ExpandArraySliceExpression(first_unparsed ? *first_unparsed : dummy_first_unparsed, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + ret_val, + list, + 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); + } + // in any non-covered case, just do the obviously right thing + list->Append(ret_val); + return 1; +} + +ValueObjectSP +ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_result, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* what_next) +{ + ValueObjectSP root = GetSP(); + + if (!root.get()) + return ValueObjectSP(); + + *first_unparsed = expression_cstr; + + while (true) + { + + const char* expression_cstr = *first_unparsed; // hide the top level expression_cstr + + ClangASTType root_clang_type = root->GetClangType(); + ClangASTType pointee_clang_type; + Flags pointee_clang_type_info; + + Flags root_clang_type_info(root_clang_type.GetTypeInfo(&pointee_clang_type)); + if (pointee_clang_type) + pointee_clang_type_info.Reset(pointee_clang_type.GetTypeInfo()); + + if (!expression_cstr || *expression_cstr == '\0') + { + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + return root; + } + + switch (*expression_cstr) + { + case '-': + { + if (options.m_check_dot_vs_arrow_syntax && + root_clang_type_info.Test(ClangASTType::eTypeIsPointer) ) // if you are trying to use -> on a non-pointer and I must catch the error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrowInsteadOfDot; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (root_clang_type_info.Test(ClangASTType::eTypeIsObjC) && // if yo are trying to extract an ObjC IVar when this is forbidden + root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && + options.m_no_fragile_ivar) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonFragileIVarNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (expression_cstr[1] != '>') + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + expression_cstr++; // skip the - + } + case '.': // or fallthrough from -> + { + if (options.m_check_dot_vs_arrow_syntax && *expression_cstr == '.' && + root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) // if you are trying to use . on a pointer and I must catch the error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDotInsteadOfArrow; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + expression_cstr++; // skip . + const char *next_separator = strpbrk(expression_cstr+1,"-.["); + ConstString child_name; + if (!next_separator) // if no other separator just expand this last layer + { + child_name.SetCString (expression_cstr); + ValueObjectSP child_valobj_sp = root->GetChildMemberWithName(child_name, true); + + if (child_valobj_sp.get()) // we know we are done, so just return + { + *first_unparsed = ""; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + return child_valobj_sp; + } + else if (options.m_no_synthetic_children == false) // let's try with synthetic children + { + if (root->IsSynthetic()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + + child_valobj_sp = root->GetSyntheticValue(); + if (child_valobj_sp.get()) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + + // 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 + { + *first_unparsed = ""; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + return child_valobj_sp; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else // other layers do expand + { + child_name.SetCStringWithLength(expression_cstr, next_separator - expression_cstr); + 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; + *first_unparsed = next_separator; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + else if (options.m_no_synthetic_children == false) // let's try with synthetic children + { + if (root->IsSynthetic()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + + child_valobj_sp = root->GetSyntheticValue(true); + if (child_valobj_sp) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + + // 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; + *first_unparsed = next_separator; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + break; + } + case '[': + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray) && !root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && !root_clang_type_info.Test(ClangASTType::eTypeIsVector)) // if this is not a T[] nor a T* + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // if this is not even a scalar... + { + if (options.m_no_synthetic_children) // ...only chance left is synthetic + { + *first_unparsed = expression_cstr; + *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 + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else // even if something follows, we cannot expand unbounded ranges, just let the caller do it + { + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeUnboundedRange; + return root; + } + } + const char *separator_position = ::strchr(expression_cstr+1,'-'); + const char *close_bracket_position = ::strchr(expression_cstr+1,']'); + if (!close_bracket_position) // if there is no ], this is a syntax error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (!separator_position || separator_position > close_bracket_position) // if no separator, this is either [] or [N] + { + char *end = NULL; + unsigned long index = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays + { + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeUnboundedRange; + return root; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + // from here on we do have a valid index + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true); + if (!child_valobj_sp) + child_valobj_sp = root->GetSyntheticArrayMemberFromArray(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; + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else if (root_clang_type_info.Test(ClangASTType::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_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *what_next = eExpressionPathAftermathNothing; + continue; + } + } + else + { + if (root->GetClangType().GetMinimumLanguage() == eLanguageTypeObjC + && pointee_clang_type_info.AllClear(ClangASTType::eTypeIsPointer) + && root->HasSyntheticValue() + && options.m_no_synthetic_children == false) + { + root = root->GetSyntheticValue()->GetChildAtIndex(index, true); + } + else + root = root->GetSyntheticArrayMemberFromPointer(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + } + } + else if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + root = root->GetSyntheticBitFieldChild(index, index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else // we do not know how to expand members of bitfields, so we just return and let the caller do any further processing + { + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonBitfieldRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeBitfield; + return root; + } + } + else if (root_clang_type_info.Test(ClangASTType::eTypeIsVector)) + { + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + } + else if (options.m_no_synthetic_children == false) + { + if (root->HasSyntheticValue()) + root = root->GetSyntheticValue(); + else if (!root->IsSynthetic()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + // if we are here, then root itself is a synthetic VO.. should be good to go + + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else // we have a low and a high index + { + char *end = NULL; + unsigned long index_lower = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != separator_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + unsigned long index_higher = ::strtoul (separator_position+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (index_lower > index_higher) // swap indices if required + { + unsigned long temp = index_lower; + index_lower = index_higher; + index_higher = temp; + } + if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // expansion only works for scalars + { + root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonBitfieldRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeBitfield; + return root; + } + } + else if (root_clang_type_info.Test(ClangASTType::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_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *what_next = ValueObject::eExpressionPathAftermathNothing; + continue; + } + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeBoundedRange; + return root; + } + } + break; + } + default: // some non-separator is in the way + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + break; + } + } + } +} + +int +ValueObject::ExpandArraySliceExpression(const char* expression_cstr, + const char** first_unparsed, + ValueObjectSP root, + ValueObjectListSP& list, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_result, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* what_next) +{ + if (!root.get()) + return 0; + + *first_unparsed = expression_cstr; + + while (true) + { + + const char* expression_cstr = *first_unparsed; // hide the top level expression_cstr + + ClangASTType root_clang_type = root->GetClangType(); + ClangASTType pointee_clang_type; + Flags pointee_clang_type_info; + Flags root_clang_type_info(root_clang_type.GetTypeInfo(&pointee_clang_type)); + if (pointee_clang_type) + pointee_clang_type_info.Reset(pointee_clang_type.GetTypeInfo()); + + if (!expression_cstr || *expression_cstr == '\0') + { + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + list->Append(root); + return 1; + } + + switch (*expression_cstr) + { + case '[': + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray) && !root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) // if this is not a T[] nor a T* + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // if this is not even a scalar, this syntax is just plain wrong! + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else if (!options.m_allow_bitfields_syntax) // if this is a scalar, check that we can expand bitfields + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + } + if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else // expand this into list + { + const size_t max_index = root->GetNumChildren() - 1; + for (size_t index = 0; index < max_index; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return max_index; // tell me number of items I added to the VOList + } + } + const char *separator_position = ::strchr(expression_cstr+1,'-'); + const char *close_bracket_position = ::strchr(expression_cstr+1,']'); + if (!close_bracket_position) // if there is no ], this is a syntax error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + if (!separator_position || separator_position > close_bracket_position) // if no separator, this is either [] or [N] + { + char *end = NULL; + unsigned long index = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays + { + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + const size_t max_index = root->GetNumChildren() - 1; + for (size_t index = 0; index < max_index; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return max_index; // tell me number of items I added to the VOList + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + } + // from here on we do have a valid index + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + else if (root_clang_type_info.Test(ClangASTType::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_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + *what_next = eExpressionPathAftermathNothing; + continue; + } + } + else + { + root = root->GetSyntheticArrayMemberFromPointer(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + } + else /*if (ClangASTContext::IsScalarType(root_clang_type))*/ + { + root = root->GetSyntheticBitFieldChild(index, index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else // we do not know how to expand members of bitfields, so we just return and let the caller do any further processing + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + } + else // we have a low and a high index + { + char *end = NULL; + unsigned long index_lower = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != separator_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + unsigned long index_higher = ::strtoul (separator_position+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + if (index_lower > index_higher) // swap indices if required + { + unsigned long temp = index_lower; + index_lower = index_higher; + index_higher = temp; + } + if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // expansion only works for scalars + { + root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + else if (root_clang_type_info.Test(ClangASTType::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_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + *what_next = ValueObject::eExpressionPathAftermathNothing; + continue; + } + } + else + { + for (unsigned long index = index_lower; + index <= index_higher; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = end+1; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return index_higher-index_lower+1; // tell me number of items I added to the VOList + } + } + break; + } + default: // some non-[ separator, or something entirely wrong, is in the way + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + break; + } + } + } +} + +static void +DumpValueObject_Impl (Stream &s, + ValueObject *valobj, + const ValueObject::DumpValueObjectOptions& options, + uint32_t ptr_depth, + uint32_t curr_depth) +{ + if (valobj) + { + bool update_success = valobj->UpdateValueIfNeeded (true); + + const char *root_valobj_name = + options.m_root_valobj_name.empty() ? + valobj->GetName().AsCString() : + options.m_root_valobj_name.c_str(); + + if (update_success && options.m_use_dynamic != eNoDynamicValues) + { + ValueObject *dynamic_value = valobj->GetDynamicValue(options.m_use_dynamic).get(); + if (dynamic_value) + valobj = dynamic_value; + } + + ClangASTType clang_type = valobj->GetClangType(); + const Flags type_flags (clang_type.GetTypeInfo ()); + const char *err_cstr = NULL; + const bool has_children = type_flags.Test (ClangASTType::eTypeHasChildren); + const bool has_value = type_flags.Test (ClangASTType::eTypeHasValue); + + const bool print_valobj = options.m_flat_output == false || has_value; + + if (print_valobj) + { + if (options.m_show_location) + { + s.Printf("%s: ", valobj->GetLocationAsCString()); + } + + s.Indent(); + + bool show_type = true; + // if we are at the root-level and been asked to hide the root's type, then hide it + if (curr_depth == 0 && options.m_hide_root_type) + show_type = false; + else + // otherwise decide according to the usual rules (asked to show types - always at the root level) + show_type = options.m_show_types || (curr_depth == 0 && !options.m_flat_output); + + if (show_type) + { + // Some ValueObjects don't have types (like registers sets). Only print + // the type if there is one to print + ConstString qualified_type_name(valobj->GetQualifiedTypeName()); + if (qualified_type_name) + s.Printf("(%s) ", qualified_type_name.GetCString()); + } + + if (options.m_flat_output) + { + // If we are showing types, also qualify the C++ base classes + const bool qualify_cxx_base_classes = options.m_show_types; + if (!options.m_hide_name) + { + valobj->GetExpressionPath(s, qualify_cxx_base_classes); + s.PutCString(" ="); + } + } + else if (!options.m_hide_name) + { + const char *name_cstr = root_valobj_name ? root_valobj_name : valobj->GetName().AsCString(""); + s.Printf ("%s =", name_cstr); + } + + if (!options.m_scope_already_checked && !valobj->IsInScope()) + { + err_cstr = "out of scope"; + } + } + + std::string summary_str; + std::string value_str; + const char *val_cstr = NULL; + const char *sum_cstr = NULL; + TypeSummaryImpl* entry = options.m_summary_sp ? options.m_summary_sp.get() : valobj->GetSummaryFormat().get(); + + if (options.m_omit_summary_depth > 0) + entry = NULL; + + bool is_nil = valobj->IsObjCNil(); + + if (err_cstr == NULL) + { + if (options.m_format != eFormatDefault && options.m_format != valobj->GetFormat()) + { + valobj->GetValueAsCString(options.m_format, + value_str); + } + else + { + val_cstr = valobj->GetValueAsCString(); + if (val_cstr) + value_str = val_cstr; + } + err_cstr = valobj->GetError().AsCString(); + } + + if (err_cstr) + { + s.Printf (" <%s>\n", err_cstr); + } + else + { + const bool is_ref = type_flags.Test (ClangASTType::eTypeIsReference); + if (print_valobj) + { + if (is_nil) + sum_cstr = "nil"; + else if (options.m_omit_summary_depth == 0) + { + if (options.m_summary_sp) + { + valobj->GetSummaryAsCString(entry, summary_str); + sum_cstr = summary_str.c_str(); + } + else + sum_cstr = valobj->GetSummaryAsCString(); + } + + // Make sure we have a value and make sure the summary didn't + // specify that the value should not be printed - and do not print + // the value if this thing is nil + // (but show the value if the user passes a format explicitly) + if (!is_nil && !value_str.empty() && (entry == NULL || (entry->DoesPrintValue() || options.m_format != eFormatDefault) || sum_cstr == NULL) && !options.m_hide_value) + s.Printf(" %s", value_str.c_str()); + + if (sum_cstr) + s.Printf(" %s", sum_cstr); + + // let's avoid the overly verbose no description error for a nil thing + if (options.m_use_objc && !is_nil) + { + if (!options.m_hide_value || !options.m_hide_name) + s.Printf(" "); + const char *object_desc = valobj->GetObjectDescription(); + if (object_desc) + s.Printf("%s\n", object_desc); + else + s.Printf ("[no Objective-C description available]\n"); + return; + } + } + + if (curr_depth < options.m_max_depth) + { + // We will show children for all concrete types. We won't show + // pointer contents unless a pointer depth has been specified. + // We won't reference contents unless the reference is the + // root object (depth of zero). + bool print_children = true; + + // Use a new temporary pointer depth in case we override the + // current pointer depth below... + uint32_t curr_ptr_depth = ptr_depth; + + const bool is_ptr = type_flags.Test (ClangASTType::eTypeIsPointer); + if (is_ptr || is_ref) + { + // We have a pointer or reference whose value is an address. + // Make sure that address is not NULL + AddressType ptr_address_type; + if (valobj->GetPointerValue (&ptr_address_type) == 0) + print_children = false; + + else if (is_ref && curr_depth == 0) + { + // If this is the root object (depth is zero) that we are showing + // and it is a reference, and no pointer depth has been supplied + // print out what it references. Don't do this at deeper depths + // otherwise we can end up with infinite recursion... + curr_ptr_depth = 1; + } + + if (curr_ptr_depth == 0) + print_children = false; + } + + if (print_children && (!entry || entry->DoesPrintChildren() || !sum_cstr)) + { + ValueObjectSP synth_valobj_sp = valobj->GetSyntheticValue (options.m_use_synthetic); + ValueObject* synth_valobj = (synth_valobj_sp ? synth_valobj_sp.get() : valobj); + + size_t num_children = synth_valobj->GetNumChildren(); + bool print_dotdotdot = false; + if (num_children) + { + if (options.m_flat_output) + { + if (print_valobj) + s.EOL(); + } + else + { + if (print_valobj) + s.PutCString(is_ref ? ": {\n" : " {\n"); + s.IndentMore(); + } + + const size_t max_num_children = valobj->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + + if (num_children > max_num_children && !options.m_ignore_cap) + { + num_children = max_num_children; + print_dotdotdot = true; + } + + ValueObject::DumpValueObjectOptions child_options(options); + child_options.SetFormat(options.m_format).SetSummary().SetRootValueObjectName(); + child_options.SetScopeChecked(true).SetHideName(options.m_hide_name).SetHideValue(options.m_hide_value) + .SetOmitSummaryDepth(child_options.m_omit_summary_depth > 1 ? child_options.m_omit_summary_depth - 1 : 0); + for (size_t idx=0; idx<num_children; ++idx) + { + ValueObjectSP child_sp(synth_valobj->GetChildAtIndex(idx, true)); + if (child_sp.get()) + { + DumpValueObject_Impl (s, + child_sp.get(), + child_options, + (is_ptr || is_ref) ? curr_ptr_depth - 1 : curr_ptr_depth, + curr_depth + 1); + } + } + + if (!options.m_flat_output) + { + if (print_dotdotdot) + { + ExecutionContext exe_ctx (valobj->GetExecutionContextRef()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + target->GetDebugger().GetCommandInterpreter().ChildrenTruncated(); + s.Indent("...\n"); + } + s.IndentLess(); + s.Indent("}\n"); + } + } + else if (has_children) + { + // Aggregate, no children... + if (print_valobj) + s.PutCString(" {}\n"); + } + else + { + if (print_valobj) + s.EOL(); + } + + } + else + { + s.EOL(); + } + } + else + { + if (has_children && print_valobj) + { + s.PutCString("{...}\n"); + } + } + } + } +} + +void +ValueObject::LogValueObject (Log *log, + ValueObject *valobj) +{ + if (log && valobj) + return LogValueObject (log, valobj, DumpValueObjectOptions::DefaultOptions()); +} + +void +ValueObject::LogValueObject (Log *log, + ValueObject *valobj, + const DumpValueObjectOptions& options) +{ + if (log && valobj) + { + StreamString s; + ValueObject::DumpValueObject (s, valobj, options); + if (s.GetSize()) + log->PutCString(s.GetData()); + } +} + +void +ValueObject::DumpValueObject (Stream &s, + ValueObject *valobj) +{ + + if (!valobj) + return; + + DumpValueObject_Impl(s, + valobj, + DumpValueObjectOptions::DefaultOptions(), + 0, + 0); +} + +void +ValueObject::DumpValueObject (Stream &s, + ValueObject *valobj, + const DumpValueObjectOptions& options) +{ + DumpValueObject_Impl(s, + valobj, + options, + options.m_max_ptr_depth, // max pointer depth allowed, we will go down from here + 0 // current object depth is 0 since we are just starting + ); +} + +ValueObjectSP +ValueObject::CreateConstantValue (const 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, 0, GetModule().get()); + } + else + m_error = m_value.GetValueAsData (&exe_ctx, data, 0, GetModule().get()); + + valobj_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + GetClangType(), + name, + data, + GetAddressOf()); + } + + if (!valobj_sp) + { + valobj_sp = ValueObjectConstResult::Create (NULL, m_error); + } + return valobj_sp; +} + +ValueObjectSP +ValueObject::Dereference (Error &error) +{ + if (m_deref_valobj) + return m_deref_valobj->GetSP(); + + const bool is_pointer_type = IsPointerType(); + if (is_pointer_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; + ClangASTType clang_type = GetClangType(); + ClangASTType child_clang_type; + + ExecutionContext exe_ctx (GetExecutionContextRef()); + + child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx, + GetName().GetCString(), + 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); + if (child_clang_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_clang_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); + } + } + + if (m_deref_valobj) + { + error.Clear(); + return m_deref_valobj->GetSP(); + } + else + { + StreamString strm; + GetExpressionPath(strm, true); + + if (is_pointer_type) + error.SetErrorStringWithFormat("dereference failed: (%s) %s", GetTypeName().AsCString("<invalid type>"), strm.GetString().c_str()); + else + error.SetErrorStringWithFormat("not a pointer type: (%s) %s", GetTypeName().AsCString("<invalid type>"), strm.GetString().c_str()); + return ValueObjectSP(); + } +} + +ValueObjectSP +ValueObject::AddressOf (Error &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) + { + switch (address_type) + { + case eAddressTypeInvalid: + { + StreamString expr_path_strm; + GetExpressionPath(expr_path_strm, true); + error.SetErrorStringWithFormat("'%s' is not in memory", expr_path_strm.GetString().c_str()); + } + break; + + case eAddressTypeFile: + case eAddressTypeLoad: + case eAddressTypeHost: + { + ClangASTType clang_type = GetClangType(); + if (clang_type) + { + std::string name (1, '&'); + name.append (m_name.AsCString("")); + ExecutionContext exe_ctx (GetExecutionContextRef()); + m_addr_of_valobj_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + clang_type.GetPointerType(), + ConstString (name.c_str()), + addr, + eAddressTypeInvalid, + m_data.GetAddressByteSize()); + } + } + break; + } + } + else + { + StreamString expr_path_strm; + GetExpressionPath(expr_path_strm, true); + error.SetErrorStringWithFormat("'%s' doesn't have a valid address", expr_path_strm.GetString().c_str()); + } + + return m_addr_of_valobj_sp; +} + +ValueObjectSP +ValueObject::Cast (const ClangASTType &clang_ast_type) +{ + return ValueObjectCast::Create (*this, GetName(), clang_ast_type); +} + +ValueObjectSP +ValueObject::CastPointerType (const char *name, ClangASTType &clang_ast_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, + clang_ast_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(), + m_needs_update (true), + m_first_update (true) +{ +} + +ValueObject::EvaluationPoint::EvaluationPoint (ExecutionContextScope *exe_scope, bool use_selected): + m_mod_id(), + m_exe_ctx_ref(), + m_needs_update (true), + m_first_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), + m_first_update (true) +{ +} + +ValueObject::EvaluationPoint::~EvaluationPoint () +{ +} + +// 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() +{ + + // Start with the target, if it is NULL, then we're obviously not going to get any further: + ExecutionContext exe_ctx(m_exe_ctx_ref.Lock()); + + if (exe_ctx.GetTargetPtr() == NULL) + return false; + + // If we don't have a process nothing can change. + Process *process = exe_ctx.GetProcessPtr(); + if (process == NULL) + 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 (m_exe_ctx_ref.HasThreadRef()) + { + ThreadSP thread_sp (m_exe_ctx_ref.GetThreadSP()); + if (thread_sp) + { + if (m_exe_ctx_ref.HasFrameRef()) + { + StackFrameSP frame_sp (m_exe_ctx_ref.GetFrameSP()); + if (!frame_sp) + { + // We used to have a frame, but now it is gone + SetInvalid(); + changed = was_valid; + } + } + } + else + { + // We used to have a thread, but now it is gone + SetInvalid(); + changed = was_valid; + } + + } + return changed; +} + +void +ValueObject::EvaluationPoint::SetUpdated () +{ + ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP()); + if (process_sp) + m_mod_id = process_sp->GetModID(); + m_first_update = false; + m_needs_update = false; +} + + + +void +ValueObject::ClearUserVisibleData(uint32_t clear_mask) +{ + if ((clear_mask & eClearUserVisibleDataItemsValue) == eClearUserVisibleDataItemsValue) + m_value_str.clear(); + + if ((clear_mask & eClearUserVisibleDataItemsLocation) == eClearUserVisibleDataItemsLocation) + m_location_str.clear(); + + if ((clear_mask & eClearUserVisibleDataItemsSummary) == eClearUserVisibleDataItemsSummary) + { + m_summary_str.clear(); + } + + if ((clear_mask & eClearUserVisibleDataItemsDescription) == eClearUserVisibleDataItemsDescription) + m_object_desc_str.clear(); + + if ((clear_mask & eClearUserVisibleDataItemsSyntheticChildren) == eClearUserVisibleDataItemsSyntheticChildren) + { + if (m_synthetic_value) + m_synthetic_value = NULL; + } +} + +SymbolContextScope * +ValueObject::GetSymbolContextScope() +{ + if (m_parent) + { + if (!m_parent->IsPointerOrReferenceType()) + return m_parent->GetSymbolContextScope(); + } + return NULL; +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromExpression (const char* name, + const char* expression, + const ExecutionContext& exe_ctx) +{ + lldb::ValueObjectSP retval_sp; + lldb::TargetSP target_sp(exe_ctx.GetTargetSP()); + if (!target_sp) + return retval_sp; + if (!expression || !*expression) + return retval_sp; + target_sp->EvaluateExpression (expression, + exe_ctx.GetFrameSP().get(), + retval_sp); + if (retval_sp && name && *name) + retval_sp->SetName(ConstString(name)); + return retval_sp; +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromAddress (const char* name, + uint64_t address, + const ExecutionContext& exe_ctx, + ClangASTType type) +{ + if (type) + { + ClangASTType pointer_type(type.GetPointerType()); + if (pointer_type) + { + lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(&address,sizeof(lldb::addr_t))); + lldb::ValueObjectSP ptr_result_valobj_sp(ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + pointer_type, + ConstString(name), + buffer, + lldb::endian::InlHostByteOrder(), + exe_ctx.GetAddressByteSize())); + if (ptr_result_valobj_sp) + { + ptr_result_valobj_sp->GetValue().SetValueType(Value::eValueTypeLoadAddress); + Error err; + ptr_result_valobj_sp = ptr_result_valobj_sp->Dereference(err); + if (ptr_result_valobj_sp && name && *name) + ptr_result_valobj_sp->SetName(ConstString(name)); + } + return ptr_result_valobj_sp; + } + } + return lldb::ValueObjectSP(); +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromData (const char* name, + DataExtractor& data, + const ExecutionContext& exe_ctx, + ClangASTType type) +{ + lldb::ValueObjectSP new_value_sp; + new_value_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + type, + ConstString(name), + data, + LLDB_INVALID_ADDRESS); + new_value_sp->SetAddressTypeOfChildren(eAddressTypeLoad); + if (new_value_sp && name && *name) + new_value_sp->SetName(ConstString(name)); + return new_value_sp; +} + +ModuleSP +ValueObject::GetModule () +{ + ValueObject* root(GetRoot()); + if (root != this) + return root->GetModule(); + return lldb::ModuleSP(); +} + +ValueObject* +ValueObject::GetRoot () +{ + if (m_root) + return m_root; + ValueObject* parent = m_parent; + if (!parent) + return (m_root = this); + while (parent->m_parent) + { + if (parent->m_root) + return (m_root = parent->m_root); + parent = parent->m_parent; + } + return (m_root = parent); +} + +AddressType +ValueObject::GetAddressTypeOfChildren() +{ + if (m_address_type_of_ptr_or_ref_children == eAddressTypeInvalid) + { + ValueObject* root(GetRoot()); + if (root != this) + return root->GetAddressTypeOfChildren(); + } + return m_address_type_of_ptr_or_ref_children; +} + +lldb::DynamicValueType +ValueObject::GetDynamicValueType () +{ + ValueObject* with_dv_info = this; + while (with_dv_info) + { + if (with_dv_info->HasDynamicValueTypeInfo()) + return with_dv_info->GetDynamicValueTypeImpl(); + with_dv_info = with_dv_info->m_parent; + } + return lldb::eNoDynamicValues; +} + +lldb::Format +ValueObject::GetFormat () const +{ + const ValueObject* with_fmt_info = this; + while (with_fmt_info) + { + if (with_fmt_info->m_format != lldb::eFormatDefault) + return with_fmt_info->m_format; + with_fmt_info = with_fmt_info->m_parent; + } + return m_format; +} |