Providing a compact display mode for "po" to use where the convenience variable name and the pointer value are both hidden. This is for convenience when dealing with ObjC instances where the description often gets it right and the debugger-provided information is not useful to most people. If you need either of these, "expr" will still show them. llvm-svn: 173748
1383 lines
43 KiB
C++
1383 lines
43 KiB
C++
//===-- ValueObject.h -------------------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef liblldb_ValueObject_h_
|
|
#define liblldb_ValueObject_h_
|
|
|
|
// C Includes
|
|
// C++ Includes
|
|
#include <map>
|
|
#include <vector>
|
|
// Other libraries and framework includes
|
|
// Project includes
|
|
|
|
#include "lldb/lldb-private.h"
|
|
#include "lldb/Core/DataExtractor.h"
|
|
#include "lldb/Core/Error.h"
|
|
#include "lldb/Core/Flags.h"
|
|
#include "lldb/Core/ConstString.h"
|
|
#include "lldb/Core/UserID.h"
|
|
#include "lldb/Core/Value.h"
|
|
#include "lldb/Target/ExecutionContext.h"
|
|
#include "lldb/Target/ExecutionContextScope.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/StackID.h"
|
|
#include "lldb/Utility/SharedCluster.h"
|
|
|
|
namespace lldb_private {
|
|
|
|
/// ValueObject:
|
|
///
|
|
/// This abstract class provides an interface to a particular value, be it a register, a local or global variable,
|
|
/// that is evaluated in some particular scope. The ValueObject also has the capibility of being the "child" of
|
|
/// some other variable object, and in turn of having children.
|
|
/// If a ValueObject is a root variable object - having no parent - then it must be constructed with respect to some
|
|
/// particular ExecutionContextScope. If it is a child, it inherits the ExecutionContextScope from its parent.
|
|
/// The ValueObject will update itself if necessary before fetching its value, summary, object description, etc.
|
|
/// But it will always update itself in the ExecutionContextScope with which it was originally created.
|
|
|
|
/// A brief note on life cycle management for ValueObjects. This is a little tricky because a ValueObject can contain
|
|
/// various other ValueObjects - the Dynamic Value, its children, the dereference value, etc. Any one of these can be
|
|
/// handed out as a shared pointer, but for that contained value object to be valid, the root object and potentially other
|
|
/// of the value objects need to stay around.
|
|
/// We solve this problem by handing out shared pointers to the Value Object and any of its dependents using a shared
|
|
/// ClusterManager. This treats each shared pointer handed out for the entire cluster as a reference to the whole
|
|
/// cluster. The whole cluster will stay around until the last reference is released.
|
|
///
|
|
/// The ValueObject mostly handle this automatically, if a value object is made with a Parent ValueObject, then it adds
|
|
/// itself to the ClusterManager of the parent.
|
|
|
|
/// It does mean that external to the ValueObjects we should only ever make available ValueObjectSP's, never ValueObjects
|
|
/// or pointers to them. So all the "Root level" ValueObject derived constructors should be private, and
|
|
/// should implement a Create function that new's up object and returns a Shared Pointer that it gets from the GetSP() method.
|
|
///
|
|
/// However, if you are making an derived ValueObject that will be contained in a parent value object, you should just
|
|
/// hold onto a pointer to it internally, and by virtue of passing the parent ValueObject into its constructor, it will
|
|
/// be added to the ClusterManager for the parent. Then if you ever hand out a Shared Pointer to the contained ValueObject,
|
|
/// just do so by calling GetSP() on the contained object.
|
|
|
|
class ValueObject : public UserID
|
|
{
|
|
public:
|
|
|
|
enum GetExpressionPathFormat
|
|
{
|
|
eGetExpressionPathFormatDereferencePointers = 1,
|
|
eGetExpressionPathFormatHonorPointers
|
|
};
|
|
|
|
enum ValueObjectRepresentationStyle
|
|
{
|
|
eValueObjectRepresentationStyleValue = 1,
|
|
eValueObjectRepresentationStyleSummary,
|
|
eValueObjectRepresentationStyleLanguageSpecific,
|
|
eValueObjectRepresentationStyleLocation,
|
|
eValueObjectRepresentationStyleChildrenCount,
|
|
eValueObjectRepresentationStyleType
|
|
};
|
|
|
|
enum ExpressionPathScanEndReason
|
|
{
|
|
eExpressionPathScanEndReasonEndOfString = 1, // out of data to parse
|
|
eExpressionPathScanEndReasonNoSuchChild, // child element not found
|
|
eExpressionPathScanEndReasonEmptyRangeNotAllowed, // [] only allowed for arrays
|
|
eExpressionPathScanEndReasonDotInsteadOfArrow, // . used when -> should be used
|
|
eExpressionPathScanEndReasonArrowInsteadOfDot, // -> used when . should be used
|
|
eExpressionPathScanEndReasonFragileIVarNotAllowed, // ObjC ivar expansion not allowed
|
|
eExpressionPathScanEndReasonRangeOperatorNotAllowed, // [] not allowed by options
|
|
eExpressionPathScanEndReasonRangeOperatorInvalid, // [] not valid on objects other than scalars, pointers or arrays
|
|
eExpressionPathScanEndReasonArrayRangeOperatorMet, // [] is good for arrays, but I cannot parse it
|
|
eExpressionPathScanEndReasonBitfieldRangeOperatorMet, // [] is good for bitfields, but I cannot parse after it
|
|
eExpressionPathScanEndReasonUnexpectedSymbol, // something is malformed in the expression
|
|
eExpressionPathScanEndReasonTakingAddressFailed, // impossible to apply & operator
|
|
eExpressionPathScanEndReasonDereferencingFailed, // impossible to apply * operator
|
|
eExpressionPathScanEndReasonRangeOperatorExpanded, // [] was expanded into a VOList
|
|
eExpressionPathScanEndReasonSyntheticValueMissing, // getting the synthetic children failed
|
|
eExpressionPathScanEndReasonUnknown = 0xFFFF
|
|
};
|
|
|
|
enum ExpressionPathEndResultType
|
|
{
|
|
eExpressionPathEndResultTypePlain = 1, // anything but...
|
|
eExpressionPathEndResultTypeBitfield, // a bitfield
|
|
eExpressionPathEndResultTypeBoundedRange, // a range [low-high]
|
|
eExpressionPathEndResultTypeUnboundedRange, // a range []
|
|
eExpressionPathEndResultTypeValueObjectList, // several items in a VOList
|
|
eExpressionPathEndResultTypeInvalid = 0xFFFF
|
|
};
|
|
|
|
enum ExpressionPathAftermath
|
|
{
|
|
eExpressionPathAftermathNothing = 1, // just return it
|
|
eExpressionPathAftermathDereference, // dereference the target
|
|
eExpressionPathAftermathTakeAddress // take target's address
|
|
};
|
|
|
|
enum ClearUserVisibleDataItems
|
|
{
|
|
eClearUserVisibleDataItemsNothing = 1u << 0,
|
|
eClearUserVisibleDataItemsValue = 1u << 1,
|
|
eClearUserVisibleDataItemsSummary = 1u << 2,
|
|
eClearUserVisibleDataItemsLocation = 1u << 3,
|
|
eClearUserVisibleDataItemsDescription = 1u << 4,
|
|
eClearUserVisibleDataItemsSyntheticChildren = 1u << 5,
|
|
eClearUserVisibleDataItemsAllStrings = eClearUserVisibleDataItemsValue | eClearUserVisibleDataItemsSummary | eClearUserVisibleDataItemsLocation | eClearUserVisibleDataItemsDescription,
|
|
eClearUserVisibleDataItemsAll = 0xFFFF
|
|
};
|
|
|
|
struct GetValueForExpressionPathOptions
|
|
{
|
|
bool m_check_dot_vs_arrow_syntax;
|
|
bool m_no_fragile_ivar;
|
|
bool m_allow_bitfields_syntax;
|
|
bool m_no_synthetic_children;
|
|
|
|
GetValueForExpressionPathOptions(bool dot = false,
|
|
bool no_ivar = false,
|
|
bool bitfield = true,
|
|
bool no_synth = false) :
|
|
m_check_dot_vs_arrow_syntax(dot),
|
|
m_no_fragile_ivar(no_ivar),
|
|
m_allow_bitfields_syntax(bitfield),
|
|
m_no_synthetic_children(no_synth)
|
|
{
|
|
}
|
|
|
|
GetValueForExpressionPathOptions&
|
|
DoCheckDotVsArrowSyntax()
|
|
{
|
|
m_check_dot_vs_arrow_syntax = true;
|
|
return *this;
|
|
}
|
|
|
|
GetValueForExpressionPathOptions&
|
|
DontCheckDotVsArrowSyntax()
|
|
{
|
|
m_check_dot_vs_arrow_syntax = false;
|
|
return *this;
|
|
}
|
|
|
|
GetValueForExpressionPathOptions&
|
|
DoAllowFragileIVar()
|
|
{
|
|
m_no_fragile_ivar = false;
|
|
return *this;
|
|
}
|
|
|
|
GetValueForExpressionPathOptions&
|
|
DontAllowFragileIVar()
|
|
{
|
|
m_no_fragile_ivar = true;
|
|
return *this;
|
|
}
|
|
|
|
GetValueForExpressionPathOptions&
|
|
DoAllowBitfieldSyntax()
|
|
{
|
|
m_allow_bitfields_syntax = true;
|
|
return *this;
|
|
}
|
|
|
|
GetValueForExpressionPathOptions&
|
|
DontAllowBitfieldSyntax()
|
|
{
|
|
m_allow_bitfields_syntax = false;
|
|
return *this;
|
|
}
|
|
|
|
GetValueForExpressionPathOptions&
|
|
DoAllowSyntheticChildren()
|
|
{
|
|
m_no_synthetic_children = false;
|
|
return *this;
|
|
}
|
|
|
|
GetValueForExpressionPathOptions&
|
|
DontAllowSyntheticChildren()
|
|
{
|
|
m_no_synthetic_children = true;
|
|
return *this;
|
|
}
|
|
|
|
static const GetValueForExpressionPathOptions
|
|
DefaultOptions()
|
|
{
|
|
static GetValueForExpressionPathOptions g_default_options;
|
|
|
|
return g_default_options;
|
|
}
|
|
|
|
};
|
|
|
|
struct DumpValueObjectOptions
|
|
{
|
|
uint32_t m_max_ptr_depth;
|
|
uint32_t m_max_depth;
|
|
bool m_show_types;
|
|
bool m_show_location;
|
|
bool m_use_objc;
|
|
lldb::DynamicValueType m_use_dynamic;
|
|
bool m_use_synthetic;
|
|
bool m_scope_already_checked;
|
|
bool m_flat_output;
|
|
uint32_t m_omit_summary_depth;
|
|
bool m_ignore_cap;
|
|
lldb::Format m_format;
|
|
lldb::TypeSummaryImplSP m_summary_sp;
|
|
std::string m_root_valobj_name;
|
|
bool m_hide_root_type;
|
|
bool m_hide_name;
|
|
bool m_hide_value;
|
|
|
|
DumpValueObjectOptions() :
|
|
m_max_ptr_depth(0),
|
|
m_max_depth(UINT32_MAX),
|
|
m_show_types(false),
|
|
m_show_location(false),
|
|
m_use_objc(false),
|
|
m_use_dynamic(lldb::eNoDynamicValues),
|
|
m_use_synthetic(true),
|
|
m_scope_already_checked(false),
|
|
m_flat_output(false),
|
|
m_omit_summary_depth(0),
|
|
m_ignore_cap(false),
|
|
m_format (lldb::eFormatDefault),
|
|
m_summary_sp(),
|
|
m_root_valobj_name(),
|
|
m_hide_root_type(false), // provide a special compact display for "po"
|
|
m_hide_name(false), // provide a special compact display for "po"
|
|
m_hide_value(false) // provide a special compact display for "po"
|
|
{}
|
|
|
|
static const DumpValueObjectOptions
|
|
DefaultOptions()
|
|
{
|
|
static DumpValueObjectOptions g_default_options;
|
|
|
|
return g_default_options;
|
|
}
|
|
|
|
DumpValueObjectOptions (const DumpValueObjectOptions& rhs) :
|
|
m_max_ptr_depth(rhs.m_max_ptr_depth),
|
|
m_max_depth(rhs.m_max_depth),
|
|
m_show_types(rhs.m_show_types),
|
|
m_show_location(rhs.m_show_location),
|
|
m_use_objc(rhs.m_use_objc),
|
|
m_use_dynamic(rhs.m_use_dynamic),
|
|
m_use_synthetic(rhs.m_use_synthetic),
|
|
m_scope_already_checked(rhs.m_scope_already_checked),
|
|
m_flat_output(rhs.m_flat_output),
|
|
m_omit_summary_depth(rhs.m_omit_summary_depth),
|
|
m_ignore_cap(rhs.m_ignore_cap),
|
|
m_format(rhs.m_format),
|
|
m_summary_sp(rhs.m_summary_sp),
|
|
m_root_valobj_name(rhs.m_root_valobj_name),
|
|
m_hide_root_type(rhs.m_hide_root_type),
|
|
m_hide_name(rhs.m_hide_name),
|
|
m_hide_value(rhs.m_hide_value)
|
|
{}
|
|
|
|
DumpValueObjectOptions&
|
|
SetMaximumPointerDepth(uint32_t depth = 0)
|
|
{
|
|
m_max_ptr_depth = depth;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetMaximumDepth(uint32_t depth = 0)
|
|
{
|
|
m_max_depth = depth;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetShowTypes(bool show = false)
|
|
{
|
|
m_show_types = show;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetShowLocation(bool show = false)
|
|
{
|
|
m_show_location = show;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetUseObjectiveC(bool use = false)
|
|
{
|
|
m_use_objc = use;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetShowSummary(bool show = true)
|
|
{
|
|
if (show == false)
|
|
SetOmitSummaryDepth(UINT32_MAX);
|
|
else
|
|
SetOmitSummaryDepth(0);
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetUseDynamicType(lldb::DynamicValueType dyn = lldb::eNoDynamicValues)
|
|
{
|
|
m_use_dynamic = dyn;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetUseSyntheticValue(bool use_synthetic = true)
|
|
{
|
|
m_use_synthetic = use_synthetic;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetScopeChecked(bool check = true)
|
|
{
|
|
m_scope_already_checked = check;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetFlatOutput(bool flat = false)
|
|
{
|
|
m_flat_output = flat;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetOmitSummaryDepth(uint32_t depth = 0)
|
|
{
|
|
m_omit_summary_depth = depth;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetIgnoreCap(bool ignore = false)
|
|
{
|
|
m_ignore_cap = ignore;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetRawDisplay(bool raw = false)
|
|
{
|
|
if (raw)
|
|
{
|
|
SetUseSyntheticValue(false);
|
|
SetOmitSummaryDepth(UINT32_MAX);
|
|
SetIgnoreCap(true);
|
|
SetHideName(false);
|
|
SetHideValue(false);
|
|
}
|
|
else
|
|
{
|
|
SetUseSyntheticValue(true);
|
|
SetOmitSummaryDepth(0);
|
|
SetIgnoreCap(false);
|
|
SetHideName(false);
|
|
SetHideValue(false);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetFormat (lldb::Format format = lldb::eFormatDefault)
|
|
{
|
|
m_format = format;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetSummary (lldb::TypeSummaryImplSP summary = lldb::TypeSummaryImplSP())
|
|
{
|
|
m_summary_sp = summary;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetRootValueObjectName (const char* name = NULL)
|
|
{
|
|
if (name)
|
|
m_root_valobj_name.assign(name);
|
|
else
|
|
m_root_valobj_name.clear();
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetHideRootType (bool hide_root_type = false)
|
|
{
|
|
m_hide_root_type = hide_root_type;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetHideName (bool hide_name = false)
|
|
{
|
|
m_hide_name = hide_name;
|
|
return *this;
|
|
}
|
|
|
|
DumpValueObjectOptions&
|
|
SetHideValue (bool hide_value = false)
|
|
{
|
|
m_hide_value = hide_value;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
class EvaluationPoint
|
|
{
|
|
public:
|
|
|
|
EvaluationPoint ();
|
|
|
|
EvaluationPoint (ExecutionContextScope *exe_scope, bool use_selected = false);
|
|
|
|
EvaluationPoint (const EvaluationPoint &rhs);
|
|
|
|
~EvaluationPoint ();
|
|
|
|
const ExecutionContextRef &
|
|
GetExecutionContextRef() const
|
|
{
|
|
return m_exe_ctx_ref;
|
|
}
|
|
|
|
// Set the EvaluationPoint to the values in exe_scope,
|
|
// Return true if the Evaluation Point changed.
|
|
// Since the ExecutionContextScope is always going to be valid currently,
|
|
// the Updated Context will also always be valid.
|
|
|
|
// bool
|
|
// SetContext (ExecutionContextScope *exe_scope);
|
|
|
|
void
|
|
SetIsConstant ()
|
|
{
|
|
SetUpdated();
|
|
m_mod_id.SetInvalid();
|
|
}
|
|
|
|
bool
|
|
IsConstant () const
|
|
{
|
|
return !m_mod_id.IsValid();
|
|
}
|
|
|
|
ProcessModID
|
|
GetModID () const
|
|
{
|
|
return m_mod_id;
|
|
}
|
|
|
|
void
|
|
SetUpdateID (ProcessModID new_id)
|
|
{
|
|
m_mod_id = new_id;
|
|
}
|
|
|
|
bool
|
|
IsFirstEvaluation () const
|
|
{
|
|
return m_first_update;
|
|
}
|
|
|
|
void
|
|
SetNeedsUpdate ()
|
|
{
|
|
m_needs_update = true;
|
|
}
|
|
|
|
void
|
|
SetUpdated ();
|
|
|
|
bool
|
|
NeedsUpdating()
|
|
{
|
|
SyncWithProcessState();
|
|
return m_needs_update;
|
|
}
|
|
|
|
bool
|
|
IsValid ()
|
|
{
|
|
if (!m_mod_id.IsValid())
|
|
return false;
|
|
else if (SyncWithProcessState ())
|
|
{
|
|
if (!m_mod_id.IsValid())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
SetInvalid ()
|
|
{
|
|
// Use the stop id to mark us as invalid, leave the thread id and the stack id around for logging and
|
|
// history purposes.
|
|
m_mod_id.SetInvalid();
|
|
|
|
// Can't update an invalid state.
|
|
m_needs_update = false;
|
|
|
|
}
|
|
|
|
private:
|
|
bool
|
|
SyncWithProcessState ();
|
|
|
|
ProcessModID m_mod_id; // This is the stop id when this ValueObject was last evaluated.
|
|
ExecutionContextRef m_exe_ctx_ref;
|
|
bool m_needs_update;
|
|
bool m_first_update;
|
|
};
|
|
|
|
const EvaluationPoint &
|
|
GetUpdatePoint () const
|
|
{
|
|
return m_update_point;
|
|
}
|
|
|
|
EvaluationPoint &
|
|
GetUpdatePoint ()
|
|
{
|
|
return m_update_point;
|
|
}
|
|
|
|
const ExecutionContextRef &
|
|
GetExecutionContextRef() const
|
|
{
|
|
return m_update_point.GetExecutionContextRef();
|
|
}
|
|
|
|
lldb::TargetSP
|
|
GetTargetSP() const
|
|
{
|
|
return m_update_point.GetExecutionContextRef().GetTargetSP();
|
|
}
|
|
|
|
lldb::ProcessSP
|
|
GetProcessSP() const
|
|
{
|
|
return m_update_point.GetExecutionContextRef().GetProcessSP();
|
|
}
|
|
|
|
lldb::ThreadSP
|
|
GetThreadSP() const
|
|
{
|
|
return m_update_point.GetExecutionContextRef().GetThreadSP();
|
|
}
|
|
|
|
lldb::StackFrameSP
|
|
GetFrameSP() const
|
|
{
|
|
return m_update_point.GetExecutionContextRef().GetFrameSP();
|
|
}
|
|
|
|
void
|
|
SetNeedsUpdate ();
|
|
|
|
virtual ~ValueObject();
|
|
|
|
clang::ASTContext *
|
|
GetClangAST ();
|
|
|
|
lldb::clang_type_t
|
|
GetClangType ();
|
|
|
|
//------------------------------------------------------------------
|
|
// Sublasses must implement the functions below.
|
|
//------------------------------------------------------------------
|
|
virtual size_t
|
|
GetByteSize() = 0;
|
|
|
|
virtual lldb::ValueType
|
|
GetValueType() const = 0;
|
|
|
|
//------------------------------------------------------------------
|
|
// Sublasses can implement the functions below.
|
|
//------------------------------------------------------------------
|
|
virtual ConstString
|
|
GetTypeName();
|
|
|
|
virtual ConstString
|
|
GetQualifiedTypeName();
|
|
|
|
virtual lldb::LanguageType
|
|
GetObjectRuntimeLanguage();
|
|
|
|
virtual bool
|
|
IsPointerType ();
|
|
|
|
virtual bool
|
|
IsArrayType ();
|
|
|
|
virtual bool
|
|
IsScalarType ();
|
|
|
|
virtual bool
|
|
IsPointerOrReferenceType ();
|
|
|
|
virtual bool
|
|
IsPossibleDynamicType ();
|
|
|
|
virtual bool
|
|
IsObjCNil ();
|
|
|
|
virtual bool
|
|
IsBaseClass ()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual bool
|
|
IsDereferenceOfParent ()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
IsIntegerType (bool &is_signed);
|
|
|
|
virtual bool
|
|
GetBaseClassPath (Stream &s);
|
|
|
|
virtual void
|
|
GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat = eGetExpressionPathFormatDereferencePointers);
|
|
|
|
lldb::ValueObjectSP
|
|
GetValueForExpressionPath(const char* expression,
|
|
const char** first_unparsed = NULL,
|
|
ExpressionPathScanEndReason* reason_to_stop = NULL,
|
|
ExpressionPathEndResultType* final_value_type = NULL,
|
|
const GetValueForExpressionPathOptions& options = GetValueForExpressionPathOptions::DefaultOptions(),
|
|
ExpressionPathAftermath* final_task_on_target = NULL);
|
|
|
|
int
|
|
GetValuesForExpressionPath(const char* expression,
|
|
lldb::ValueObjectListSP& list,
|
|
const char** first_unparsed = NULL,
|
|
ExpressionPathScanEndReason* reason_to_stop = NULL,
|
|
ExpressionPathEndResultType* final_value_type = NULL,
|
|
const GetValueForExpressionPathOptions& options = GetValueForExpressionPathOptions::DefaultOptions(),
|
|
ExpressionPathAftermath* final_task_on_target = NULL);
|
|
|
|
virtual bool
|
|
IsInScope ()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
virtual off_t
|
|
GetByteOffset()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual uint32_t
|
|
GetBitfieldBitSize ()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual uint32_t
|
|
GetBitfieldBitOffset ()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
IsBitfield ()
|
|
{
|
|
return (GetBitfieldBitSize() != 0) || (GetBitfieldBitOffset() != 0);
|
|
}
|
|
|
|
virtual bool
|
|
IsArrayItemForPointer()
|
|
{
|
|
return m_is_array_item_for_pointer;
|
|
}
|
|
|
|
virtual bool
|
|
SetClangAST (clang::ASTContext *ast)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual const char *
|
|
GetValueAsCString ();
|
|
|
|
virtual bool
|
|
GetValueAsCString (lldb::Format format,
|
|
std::string& destination);
|
|
|
|
virtual uint64_t
|
|
GetValueAsUnsigned (uint64_t fail_value, bool *success = NULL);
|
|
|
|
virtual bool
|
|
SetValueFromCString (const char *value_str, Error& error);
|
|
|
|
// Return the module associated with this value object in case the
|
|
// value is from an executable file and might have its data in
|
|
// sections of the file. This can be used for variables.
|
|
virtual lldb::ModuleSP
|
|
GetModule()
|
|
{
|
|
if (m_parent)
|
|
return m_parent->GetModule();
|
|
return lldb::ModuleSP();
|
|
}
|
|
|
|
virtual bool
|
|
GetDeclaration (Declaration &decl);
|
|
|
|
//------------------------------------------------------------------
|
|
// The functions below should NOT be modified by sublasses
|
|
//------------------------------------------------------------------
|
|
const Error &
|
|
GetError();
|
|
|
|
const ConstString &
|
|
GetName() const;
|
|
|
|
virtual lldb::ValueObjectSP
|
|
GetChildAtIndex (size_t idx, bool can_create);
|
|
|
|
// this will always create the children if necessary
|
|
lldb::ValueObjectSP
|
|
GetChildAtIndexPath (const std::initializer_list<size_t> &idxs,
|
|
size_t* index_of_error = NULL);
|
|
|
|
lldb::ValueObjectSP
|
|
GetChildAtIndexPath (const std::vector<size_t> &idxs,
|
|
size_t* index_of_error = NULL);
|
|
|
|
lldb::ValueObjectSP
|
|
GetChildAtIndexPath (const std::initializer_list< std::pair<size_t, bool> > &idxs,
|
|
size_t* index_of_error = NULL);
|
|
|
|
lldb::ValueObjectSP
|
|
GetChildAtIndexPath (const std::vector< std::pair<size_t, bool> > &idxs,
|
|
size_t* index_of_error = NULL);
|
|
|
|
virtual lldb::ValueObjectSP
|
|
GetChildMemberWithName (const ConstString &name, bool can_create);
|
|
|
|
virtual size_t
|
|
GetIndexOfChildWithName (const ConstString &name);
|
|
|
|
size_t
|
|
GetNumChildren ();
|
|
|
|
const Value &
|
|
GetValue() const;
|
|
|
|
Value &
|
|
GetValue();
|
|
|
|
virtual bool
|
|
ResolveValue (Scalar &scalar);
|
|
|
|
const char *
|
|
GetLocationAsCString ();
|
|
|
|
const char *
|
|
GetSummaryAsCString ();
|
|
|
|
bool
|
|
GetSummaryAsCString (TypeSummaryImpl* summary_ptr,
|
|
std::string& destination);
|
|
|
|
const char *
|
|
GetObjectDescription ();
|
|
|
|
bool
|
|
HasSpecialPrintableRepresentation (ValueObjectRepresentationStyle val_obj_display,
|
|
lldb::Format custom_format);
|
|
|
|
enum PrintableRepresentationSpecialCases
|
|
{
|
|
ePrintableRepresentationSpecialCasesDisable = 0,
|
|
ePrintableRepresentationSpecialCasesAllow = 1,
|
|
ePrintableRepresentationSpecialCasesOnly = 3
|
|
};
|
|
|
|
bool
|
|
DumpPrintableRepresentation (Stream& s,
|
|
ValueObjectRepresentationStyle val_obj_display = eValueObjectRepresentationStyleSummary,
|
|
lldb::Format custom_format = lldb::eFormatInvalid,
|
|
PrintableRepresentationSpecialCases special = ePrintableRepresentationSpecialCasesAllow);
|
|
bool
|
|
GetValueIsValid () const;
|
|
|
|
bool
|
|
GetValueDidChange ();
|
|
|
|
bool
|
|
UpdateValueIfNeeded (bool update_format = true);
|
|
|
|
bool
|
|
UpdateFormatsIfNeeded();
|
|
|
|
lldb::ValueObjectSP
|
|
GetSP ()
|
|
{
|
|
return m_manager->GetSharedPointer(this);
|
|
}
|
|
|
|
void
|
|
SetName (const ConstString &name);
|
|
|
|
virtual lldb::addr_t
|
|
GetAddressOf (bool scalar_is_load_address = true,
|
|
AddressType *address_type = NULL);
|
|
|
|
lldb::addr_t
|
|
GetPointerValue (AddressType *address_type = NULL);
|
|
|
|
lldb::ValueObjectSP
|
|
GetSyntheticChild (const ConstString &key) const;
|
|
|
|
lldb::ValueObjectSP
|
|
GetSyntheticArrayMember (size_t index, bool can_create);
|
|
|
|
lldb::ValueObjectSP
|
|
GetSyntheticArrayMemberFromPointer (size_t index, bool can_create);
|
|
|
|
lldb::ValueObjectSP
|
|
GetSyntheticArrayMemberFromArray (size_t index, bool can_create);
|
|
|
|
lldb::ValueObjectSP
|
|
GetSyntheticBitFieldChild (uint32_t from, uint32_t to, bool can_create);
|
|
|
|
lldb::ValueObjectSP
|
|
GetSyntheticExpressionPathChild(const char* expression, bool can_create);
|
|
|
|
virtual lldb::ValueObjectSP
|
|
GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create);
|
|
|
|
virtual lldb::ValueObjectSP
|
|
GetDynamicValue (lldb::DynamicValueType valueType);
|
|
|
|
virtual lldb::DynamicValueType
|
|
GetDynamicValueType ()
|
|
{
|
|
if (m_parent)
|
|
return m_parent->GetDynamicValueType ();
|
|
else
|
|
return lldb::eNoDynamicValues;
|
|
}
|
|
|
|
virtual lldb::ValueObjectSP
|
|
GetStaticValue ();
|
|
|
|
virtual lldb::ValueObjectSP
|
|
GetNonSyntheticValue ();
|
|
|
|
lldb::ValueObjectSP
|
|
GetSyntheticValue (bool use_synthetic = true);
|
|
|
|
virtual bool
|
|
HasSyntheticValue();
|
|
|
|
virtual bool
|
|
IsSynthetic() { return false; }
|
|
|
|
virtual lldb::ValueObjectSP
|
|
CreateConstantValue (const ConstString &name);
|
|
|
|
virtual lldb::ValueObjectSP
|
|
Dereference (Error &error);
|
|
|
|
virtual lldb::ValueObjectSP
|
|
AddressOf (Error &error);
|
|
|
|
virtual lldb::addr_t
|
|
GetLiveAddress()
|
|
{
|
|
return LLDB_INVALID_ADDRESS;
|
|
}
|
|
|
|
virtual void
|
|
SetLiveAddress(lldb::addr_t addr = LLDB_INVALID_ADDRESS,
|
|
AddressType address_type = eAddressTypeLoad)
|
|
{
|
|
}
|
|
|
|
virtual lldb::ValueObjectSP
|
|
Cast (const ClangASTType &clang_ast_type);
|
|
|
|
virtual lldb::ValueObjectSP
|
|
CastPointerType (const char *name,
|
|
ClangASTType &ast_type);
|
|
|
|
virtual lldb::ValueObjectSP
|
|
CastPointerType (const char *name,
|
|
lldb::TypeSP &type_sp);
|
|
|
|
// The backing bits of this value object were updated, clear any
|
|
// descriptive string, so we know we have to refetch them
|
|
virtual void
|
|
ValueUpdated ()
|
|
{
|
|
ClearUserVisibleData(eClearUserVisibleDataItemsValue |
|
|
eClearUserVisibleDataItemsSummary |
|
|
eClearUserVisibleDataItemsDescription);
|
|
}
|
|
|
|
virtual bool
|
|
IsDynamic ()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual SymbolContextScope *
|
|
GetSymbolContextScope();
|
|
|
|
static void
|
|
DumpValueObject (Stream &s,
|
|
ValueObject *valobj);
|
|
static void
|
|
DumpValueObject (Stream &s,
|
|
ValueObject *valobj,
|
|
const DumpValueObjectOptions& options);
|
|
|
|
static lldb::ValueObjectSP
|
|
CreateValueObjectFromExpression (const char* name,
|
|
const char* expression,
|
|
const ExecutionContext& exe_ctx);
|
|
|
|
static lldb::ValueObjectSP
|
|
CreateValueObjectFromAddress (const char* name,
|
|
uint64_t address,
|
|
const ExecutionContext& exe_ctx,
|
|
ClangASTType type);
|
|
|
|
static lldb::ValueObjectSP
|
|
CreateValueObjectFromData (const char* name,
|
|
DataExtractor& data,
|
|
const ExecutionContext& exe_ctx,
|
|
ClangASTType type);
|
|
|
|
static void
|
|
LogValueObject (Log *log,
|
|
ValueObject *valobj);
|
|
|
|
static void
|
|
LogValueObject (Log *log,
|
|
ValueObject *valobj,
|
|
const DumpValueObjectOptions& options);
|
|
|
|
|
|
// returns true if this is a char* or a char[]
|
|
// if it is a char* and check_pointer is true,
|
|
// it also checks that the pointer is valid
|
|
bool
|
|
IsCStringContainer (bool check_pointer = false);
|
|
|
|
void
|
|
ReadPointedString (Stream& s,
|
|
Error& error,
|
|
uint32_t max_length = 0,
|
|
bool honor_array = true,
|
|
lldb::Format item_format = lldb::eFormatCharArray);
|
|
|
|
virtual size_t
|
|
GetPointeeData (DataExtractor& data,
|
|
uint32_t item_idx = 0,
|
|
uint32_t item_count = 1);
|
|
|
|
virtual size_t
|
|
GetData (DataExtractor& data);
|
|
|
|
bool
|
|
GetIsConstant () const
|
|
{
|
|
return m_update_point.IsConstant();
|
|
}
|
|
|
|
void
|
|
SetIsConstant ()
|
|
{
|
|
m_update_point.SetIsConstant();
|
|
}
|
|
|
|
lldb::Format
|
|
GetFormat () const
|
|
{
|
|
if (m_parent && m_format == lldb::eFormatDefault)
|
|
return m_parent->GetFormat();
|
|
return m_format;
|
|
}
|
|
|
|
void
|
|
SetFormat (lldb::Format format)
|
|
{
|
|
if (format != m_format)
|
|
ClearUserVisibleData(eClearUserVisibleDataItemsValue);
|
|
m_format = format;
|
|
}
|
|
|
|
lldb::TypeSummaryImplSP
|
|
GetSummaryFormat()
|
|
{
|
|
UpdateFormatsIfNeeded();
|
|
return m_type_summary_sp;
|
|
}
|
|
|
|
void
|
|
SetSummaryFormat(lldb::TypeSummaryImplSP format)
|
|
{
|
|
m_type_summary_sp = format;
|
|
ClearUserVisibleData(eClearUserVisibleDataItemsSummary);
|
|
}
|
|
|
|
void
|
|
SetValueFormat(lldb::TypeFormatImplSP format)
|
|
{
|
|
m_type_format_sp = format;
|
|
ClearUserVisibleData(eClearUserVisibleDataItemsValue);
|
|
}
|
|
|
|
lldb::TypeFormatImplSP
|
|
GetValueFormat()
|
|
{
|
|
UpdateFormatsIfNeeded();
|
|
return m_type_format_sp;
|
|
}
|
|
|
|
void
|
|
SetSyntheticChildren(const lldb::SyntheticChildrenSP &synth_sp)
|
|
{
|
|
if (synth_sp.get() == m_synthetic_children_sp.get())
|
|
return;
|
|
ClearUserVisibleData(eClearUserVisibleDataItemsSyntheticChildren);
|
|
m_synthetic_children_sp = synth_sp;
|
|
}
|
|
|
|
lldb::SyntheticChildrenSP
|
|
GetSyntheticChildren()
|
|
{
|
|
UpdateFormatsIfNeeded();
|
|
return m_synthetic_children_sp;
|
|
}
|
|
|
|
// Use GetParent for display purposes, but if you want to tell the parent to update itself
|
|
// then use m_parent. The ValueObjectDynamicValue's parent is not the correct parent for
|
|
// displaying, they are really siblings, so for display it needs to route through to its grandparent.
|
|
virtual ValueObject *
|
|
GetParent()
|
|
{
|
|
return m_parent;
|
|
}
|
|
|
|
virtual const ValueObject *
|
|
GetParent() const
|
|
{
|
|
return m_parent;
|
|
}
|
|
|
|
ValueObject *
|
|
GetNonBaseClassParent();
|
|
|
|
void
|
|
SetAddressTypeOfChildren(AddressType at)
|
|
{
|
|
m_address_type_of_ptr_or_ref_children = at;
|
|
}
|
|
|
|
AddressType
|
|
GetAddressTypeOfChildren()
|
|
{
|
|
if (m_address_type_of_ptr_or_ref_children == eAddressTypeInvalid)
|
|
{
|
|
if (m_parent)
|
|
return m_parent->GetAddressTypeOfChildren();
|
|
}
|
|
return m_address_type_of_ptr_or_ref_children;
|
|
}
|
|
|
|
void
|
|
SetHasCompleteType()
|
|
{
|
|
m_did_calculate_complete_objc_class_type = true;
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
/// Find out if a SBValue might have children.
|
|
///
|
|
/// This call is much more efficient than CalculateNumChildren() as
|
|
/// it doesn't need to complete the underlying type. This is designed
|
|
/// to be used in a UI environment in order to detect if the
|
|
/// disclosure triangle should be displayed or not.
|
|
///
|
|
/// This function returns true for class, union, structure,
|
|
/// pointers, references, arrays and more. Again, it does so without
|
|
/// doing any expensive type completion.
|
|
///
|
|
/// @return
|
|
/// Returns \b true if the SBValue might have children, or \b
|
|
/// false otherwise.
|
|
//------------------------------------------------------------------
|
|
virtual bool
|
|
MightHaveChildren();
|
|
|
|
protected:
|
|
typedef ClusterManager<ValueObject> ValueObjectManager;
|
|
|
|
class ChildrenManager
|
|
{
|
|
public:
|
|
ChildrenManager() :
|
|
m_mutex(Mutex::eMutexTypeRecursive),
|
|
m_children(),
|
|
m_children_count(0)
|
|
{}
|
|
|
|
bool
|
|
HasChildAtIndex (size_t idx)
|
|
{
|
|
Mutex::Locker locker(m_mutex);
|
|
ChildrenIterator iter = m_children.find(idx);
|
|
ChildrenIterator end = m_children.end();
|
|
return (iter != end);
|
|
}
|
|
|
|
ValueObject*
|
|
GetChildAtIndex (size_t idx)
|
|
{
|
|
Mutex::Locker locker(m_mutex);
|
|
ChildrenIterator iter = m_children.find(idx);
|
|
ChildrenIterator end = m_children.end();
|
|
if (iter == end)
|
|
return NULL;
|
|
else
|
|
return iter->second;
|
|
}
|
|
|
|
void
|
|
SetChildAtIndex (size_t idx, ValueObject* valobj)
|
|
{
|
|
ChildrenPair pair(idx,valobj); // we do not need to be mutex-protected to make a pair
|
|
Mutex::Locker locker(m_mutex);
|
|
m_children.insert(pair);
|
|
}
|
|
|
|
void
|
|
SetChildrenCount (size_t count)
|
|
{
|
|
m_children_count = count;
|
|
}
|
|
|
|
size_t
|
|
GetChildrenCount ()
|
|
{
|
|
return m_children_count;
|
|
}
|
|
|
|
void
|
|
Clear()
|
|
{
|
|
m_children_count = 0;
|
|
Mutex::Locker locker(m_mutex);
|
|
m_children.clear();
|
|
}
|
|
|
|
private:
|
|
typedef std::map<size_t, ValueObject*> ChildrenMap;
|
|
typedef ChildrenMap::iterator ChildrenIterator;
|
|
typedef ChildrenMap::value_type ChildrenPair;
|
|
Mutex m_mutex;
|
|
ChildrenMap m_children;
|
|
size_t m_children_count;
|
|
};
|
|
|
|
//------------------------------------------------------------------
|
|
// Classes that inherit from ValueObject can see and modify these
|
|
//------------------------------------------------------------------
|
|
ValueObject * m_parent; // The parent value object, or NULL if this has no parent
|
|
EvaluationPoint m_update_point; // Stores both the stop id and the full context at which this value was last
|
|
// updated. When we are asked to update the value object, we check whether
|
|
// the context & stop id are the same before updating.
|
|
ConstString m_name; // The name of this object
|
|
DataExtractor m_data; // A data extractor that can be used to extract the value.
|
|
Value m_value;
|
|
Error m_error; // An error object that can describe any errors that occur when updating values.
|
|
std::string m_value_str; // Cached value string that will get cleared if/when the value is updated.
|
|
std::string m_old_value_str;// Cached old value string from the last time the value was gotten
|
|
std::string m_location_str; // Cached location string that will get cleared if/when the value is updated.
|
|
std::string m_summary_str; // Cached summary string that will get cleared if/when the value is updated.
|
|
std::string m_object_desc_str; // Cached result of the "object printer". This differs from the summary
|
|
// in that the summary is consed up by us, the object_desc_string is builtin.
|
|
|
|
ClangASTType m_override_type;// If the type of the value object should be overridden, the type to impose.
|
|
|
|
ValueObjectManager *m_manager; // This object is managed by the root object (any ValueObject that gets created
|
|
// without a parent.) The manager gets passed through all the generations of
|
|
// dependent objects, and will keep the whole cluster of objects alive as long
|
|
// as a shared pointer to any of them has been handed out. Shared pointers to
|
|
// value objects must always be made with the GetSP method.
|
|
|
|
ChildrenManager m_children;
|
|
std::map<ConstString, ValueObject *> m_synthetic_children;
|
|
|
|
ValueObject* m_dynamic_value;
|
|
ValueObject* m_synthetic_value;
|
|
ValueObject* m_deref_valobj;
|
|
|
|
lldb::ValueObjectSP m_addr_of_valobj_sp; // We have to hold onto a shared pointer to this one because it is created
|
|
// as an independent ValueObjectConstResult, which isn't managed by us.
|
|
|
|
lldb::Format m_format;
|
|
uint32_t m_last_format_mgr_revision;
|
|
lldb::TypeSummaryImplSP m_type_summary_sp;
|
|
lldb::TypeFormatImplSP m_type_format_sp;
|
|
lldb::SyntheticChildrenSP m_synthetic_children_sp;
|
|
ProcessModID m_user_id_of_forced_summary;
|
|
AddressType m_address_type_of_ptr_or_ref_children;
|
|
|
|
bool m_value_is_valid:1,
|
|
m_value_did_change:1,
|
|
m_children_count_valid:1,
|
|
m_old_value_valid:1,
|
|
m_is_deref_of_parent:1,
|
|
m_is_array_item_for_pointer:1,
|
|
m_is_bitfield_for_scalar:1,
|
|
m_is_expression_path_child:1,
|
|
m_is_child_at_offset:1,
|
|
m_is_getting_summary:1,
|
|
m_did_calculate_complete_objc_class_type:1;
|
|
|
|
friend class ClangExpressionDeclMap; // For GetValue
|
|
friend class ClangExpressionVariable; // For SetName
|
|
friend class Target; // For SetName
|
|
friend class ValueObjectConstResultImpl;
|
|
|
|
//------------------------------------------------------------------
|
|
// Constructors and Destructors
|
|
//------------------------------------------------------------------
|
|
|
|
// Use the no-argument constructor to make a constant variable object (with no ExecutionContextScope.)
|
|
|
|
ValueObject();
|
|
|
|
// Use this constructor to create a "root variable object". The ValueObject will be locked to this context
|
|
// through-out its lifespan.
|
|
|
|
ValueObject (ExecutionContextScope *exe_scope,
|
|
AddressType child_ptr_or_ref_addr_type = eAddressTypeLoad);
|
|
|
|
// Use this constructor to create a ValueObject owned by another ValueObject. It will inherit the ExecutionContext
|
|
// of its parent.
|
|
|
|
ValueObject (ValueObject &parent);
|
|
|
|
ValueObjectManager *
|
|
GetManager()
|
|
{
|
|
return m_manager;
|
|
}
|
|
|
|
virtual bool
|
|
UpdateValue () = 0;
|
|
|
|
virtual void
|
|
CalculateDynamicValue (lldb::DynamicValueType use_dynamic);
|
|
|
|
virtual void
|
|
CalculateSyntheticValue (bool use_synthetic = true);
|
|
|
|
// Should only be called by ValueObject::GetChildAtIndex()
|
|
// Returns a ValueObject managed by this ValueObject's manager.
|
|
virtual ValueObject *
|
|
CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index);
|
|
|
|
// Should only be called by ValueObject::GetNumChildren()
|
|
virtual size_t
|
|
CalculateNumChildren() = 0;
|
|
|
|
void
|
|
SetNumChildren (size_t num_children);
|
|
|
|
void
|
|
SetValueDidChange (bool value_changed);
|
|
|
|
void
|
|
SetValueIsValid (bool valid);
|
|
|
|
void
|
|
ClearUserVisibleData(uint32_t items = ValueObject::eClearUserVisibleDataItemsAllStrings);
|
|
|
|
void
|
|
AddSyntheticChild (const ConstString &key,
|
|
ValueObject *valobj);
|
|
|
|
DataExtractor &
|
|
GetDataExtractor ();
|
|
|
|
void
|
|
ClearDynamicTypeInformation ();
|
|
|
|
//------------------------------------------------------------------
|
|
// Sublasses must implement the functions below.
|
|
//------------------------------------------------------------------
|
|
|
|
virtual clang::ASTContext *
|
|
GetClangASTImpl () = 0;
|
|
|
|
virtual lldb::clang_type_t
|
|
GetClangTypeImpl () = 0;
|
|
|
|
private:
|
|
//------------------------------------------------------------------
|
|
// For ValueObject only
|
|
//------------------------------------------------------------------
|
|
|
|
virtual ClangASTType
|
|
MaybeCalculateCompleteType ();
|
|
|
|
lldb::ValueObjectSP
|
|
GetValueForExpressionPath_Impl(const char* expression_cstr,
|
|
const char** first_unparsed,
|
|
ExpressionPathScanEndReason* reason_to_stop,
|
|
ExpressionPathEndResultType* final_value_type,
|
|
const GetValueForExpressionPathOptions& options,
|
|
ExpressionPathAftermath* final_task_on_target);
|
|
|
|
// this method will ONLY expand [] expressions into a VOList and return
|
|
// the number of elements it added to the VOList
|
|
// it will NOT loop through expanding the follow-up of the expression_cstr
|
|
// for all objects in the list
|
|
int
|
|
ExpandArraySliceExpression(const char* expression_cstr,
|
|
const char** first_unparsed,
|
|
lldb::ValueObjectSP root,
|
|
lldb::ValueObjectListSP& list,
|
|
ExpressionPathScanEndReason* reason_to_stop,
|
|
ExpressionPathEndResultType* final_value_type,
|
|
const GetValueForExpressionPathOptions& options,
|
|
ExpressionPathAftermath* final_task_on_target);
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN (ValueObject);
|
|
|
|
};
|
|
|
|
} // namespace lldb_private
|
|
|
|
#endif // liblldb_ValueObject_h_
|