summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lldb/include/lldb/Core/Debugger.h26
-rw-r--r--lldb/include/lldb/Core/FormatManager.h198
-rw-r--r--lldb/include/lldb/Core/ValueObject.h85
-rw-r--r--lldb/include/lldb/Interpreter/OptionGroupVariable.h1
-rw-r--r--lldb/include/lldb/Symbol/ClangASTContext.h3
-rw-r--r--lldb/include/lldb/Utility/CleanUp.h136
-rw-r--r--lldb/source/Commands/CommandObjectFrame.cpp11
-rw-r--r--lldb/source/Commands/CommandObjectType.cpp69
-rw-r--r--lldb/source/Core/Debugger.cpp42
-rw-r--r--lldb/source/Core/FormatManager.cpp19
-rw-r--r--lldb/source/Core/ValueObject.cpp615
-rw-r--r--lldb/source/Interpreter/CommandInterpreter.cpp2
-rw-r--r--lldb/source/Interpreter/OptionGroupVariable.cpp13
-rw-r--r--lldb/source/Symbol/ClangASTContext.cpp29
-rw-r--r--lldb/source/Symbol/SymbolContext.cpp2
-rw-r--r--lldb/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py31
-rw-r--r--lldb/test/functionalities/data-formatter/data-formatter-cpp/main.cpp3
17 files changed, 1156 insertions, 129 deletions
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index 924fada5cc3..f43b842fd3b 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -550,6 +550,32 @@ public:
static uint32_t
GetCount();
};
+
+ class NamedSummaryFormats
+ {
+ public:
+
+ static bool
+ Get(const ConstString &type, SummaryFormat::SharedPointer &entry);
+
+ static void
+ Add(const ConstString &type, const SummaryFormat::SharedPointer &entry);
+
+ static bool
+ Delete(const ConstString &type);
+
+ static void
+ Clear();
+
+ static void
+ LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton);
+
+ static uint32_t
+ GetCurrentRevision();
+
+ static uint32_t
+ GetCount();
+ };
};
diff --git a/lldb/include/lldb/Core/FormatManager.h b/lldb/include/lldb/Core/FormatManager.h
index 01f4f4e6169..bd408a5697c 100644
--- a/lldb/include/lldb/Core/FormatManager.h
+++ b/lldb/include/lldb/Core/FormatManager.h
@@ -142,53 +142,66 @@ struct ValueFormat
}
};
+
+template<typename KeyType, typename ValueType>
+class FormatNavigator;
-template<typename MapType, typename CallbackType>
-class FormatNavigator
+template<typename KeyType, typename ValueType>
+class FormatMap
{
+private:
+ typedef typename ValueType::SharedPointer ValueSP;
+ Mutex m_map_mutex;
+ IFormatChangeListener* listener;
+
+ friend class FormatNavigator<KeyType, ValueType>;
+
+public:
+ typedef std::map<KeyType, ValueSP> MapType;
+
+private:
+ MapType m_map;
+
+ MapType& map()
+ {
+ return m_map;
+ }
+
+ Mutex& mutex()
+ {
+ return m_map_mutex;
+ }
+
public:
typedef typename MapType::iterator MapIterator;
- typedef typename MapType::key_type MapKeyType;
- typedef typename MapType::mapped_type MapValueType;
+ typedef bool(*CallbackType)(void*, KeyType, const ValueSP&);
- FormatNavigator(IFormatChangeListener* lst = NULL) :
+ FormatMap(IFormatChangeListener* lst = NULL) :
+ m_map(),
m_map_mutex(Mutex::eMutexTypeRecursive),
- m_map(MapType()),
listener(lst)
{
}
-
- bool
- Get(ValueObject& vobj, MapValueType& entry)
- {
- Mutex::Locker(m_map_mutex);
- clang::QualType type = clang::QualType::getFromOpaquePtr(vobj.GetClangType());
- bool ret = Get(vobj, type, entry);
- if(ret)
- entry = MapValueType(entry);
- else
- entry = MapValueType();
- return ret;
- }
void
- Add(const MapKeyType &type, const MapValueType& entry)
+ Add(KeyType name,
+ const ValueSP& entry)
{
Mutex::Locker(m_map_mutex);
- m_map[type] = MapValueType(entry);
- if(listener)
+ m_map[name] = entry;
+ if (listener)
listener->Changed();
}
bool
- Delete(const char* type)
+ Delete(KeyType name)
{
Mutex::Locker(m_map_mutex);
- MapIterator iter = m_map.find(type);
+ MapIterator iter = m_map.find(name);
if (iter == m_map.end())
return false;
- m_map.erase(type);
+ m_map.erase(name);
if(listener)
listener->Changed();
return true;
@@ -203,6 +216,18 @@ public:
listener->Changed();
}
+ bool
+ Get(KeyType name,
+ ValueSP& entry)
+ {
+ Mutex::Locker(m_map_mutex);
+ MapIterator iter = m_map.find(name);
+ if (iter == m_map.end())
+ return false;
+ entry = iter->second;
+ return true;
+ }
+
void
LoopThrough(CallbackType callback, void* param)
{
@@ -212,8 +237,8 @@ public:
MapIterator pos, end = m_map.end();
for (pos = m_map.begin(); pos != end; pos++)
{
- MapKeyType type = pos->first;
- if(!callback(param, type, MapValueType(pos->second)))
+ KeyType type = pos->first;
+ if(!callback(param, type, pos->second))
break;
}
}
@@ -225,36 +250,91 @@ public:
return m_map.size();
}
- ~FormatNavigator()
+};
+
+template<typename KeyType, typename ValueType>
+class FormatNavigator
+{
+private:
+ typedef FormatMap<KeyType,ValueType> BackEndType;
+
+ BackEndType m_format_map;
+
+public:
+ typedef typename BackEndType::MapType MapType;
+ typedef typename MapType::iterator MapIterator;
+ typedef typename MapType::key_type MapKeyType;
+ typedef typename MapType::mapped_type MapValueType;
+ typedef typename BackEndType::CallbackType CallbackType;
+
+ FormatNavigator(IFormatChangeListener* lst = NULL) :
+ m_format_map(lst)
{
}
-private:
+ void
+ Add(const MapKeyType &type, const MapValueType& entry)
+ {
+ m_format_map.Add(type,entry);
+ }
- Mutex m_map_mutex;
- MapType m_map;
- IFormatChangeListener* listener;
+ // using const char* instead of MapKeyType is necessary here
+ // to make the partial template specializations below work
+ bool
+ Delete(const char *type)
+ {
+ return m_format_map.Delete(type);
+ }
+
+ bool
+ Get(ValueObject& vobj, MapValueType& entry)
+ {
+ clang::QualType type = clang::QualType::getFromOpaquePtr(vobj.GetClangType());
+ bool ret = Get(vobj, type, entry);
+ if(ret)
+ entry = MapValueType(entry);
+ else
+ entry = MapValueType();
+ return ret;
+ }
+
+ void
+ Clear()
+ {
+ m_format_map.Clear();
+ }
+
+ void
+ LoopThrough(CallbackType callback, void* param)
+ {
+ m_format_map.LoopThrough(callback,param);
+ }
+
+ uint32_t
+ GetCount()
+ {
+ return m_format_map.GetCount();
+ }
+
+private:
DISALLOW_COPY_AND_ASSIGN(FormatNavigator);
+ // using const char* instead of MapKeyType is necessary here
+ // to make the partial template specializations below work
bool
Get(const char* type, MapValueType& entry)
{
- Mutex::Locker(m_map_mutex);
- MapIterator iter = m_map.find(type);
- if (iter == m_map.end())
- return false;
- entry = iter->second;
- return true;
+ return m_format_map.Get(type, entry);
}
bool Get(ValueObject& vobj,
- const clang::QualType& q_type,
+ clang::QualType type,
MapValueType& entry)
{
- if (q_type.isNull())
+ if (type.isNull())
return false;
- clang::QualType type = q_type.getUnqualifiedType();
+ // clang::QualType type = q_type.getUnqualifiedType();
type.removeLocalConst(); type.removeLocalVolatile(); type.removeLocalRestrict();
const clang::Type* typePtr = type.getTypePtrOrNull();
if (!typePtr)
@@ -354,40 +434,41 @@ private:
// try to strip typedef chains
const clang::TypedefType* type_tdef = type->getAs<clang::TypedefType>();
if (type_tdef)
+ {
if ((Get(vobj, type_tdef->getDecl()->getUnderlyingType(), entry)) && entry->m_cascades)
return true;
+ }
return false;
}
-
};
-
+
template<>
bool
-FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Get(const char* key,
- SummaryFormat::SharedPointer& value);
+FormatNavigator<lldb::RegularExpressionSP, SummaryFormat>::Get(const char* key, SummaryFormat::SharedPointer& value);
+
template<>
bool
-FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Delete(const char* type);
-
+FormatNavigator<lldb::RegularExpressionSP, SummaryFormat>::Delete(const char* type);
+
class FormatManager : public IFormatChangeListener
{
-
-public:
-
private:
- typedef std::map<const char*, ValueFormat::SharedPointer> ValueMap;
- typedef std::map<const char*, SummaryFormat::SharedPointer> SummaryMap;
- typedef std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer> RegexSummaryMap;
+ typedef FormatNavigator<const char*, ValueFormat> ValueNavigator;
+ typedef FormatNavigator<const char*, SummaryFormat> SummaryNavigator;
+ typedef FormatNavigator<lldb::RegularExpressionSP, SummaryFormat> RegexSummaryNavigator;
- typedef FormatNavigator<ValueMap, ValueFormat::ValueCallback> ValueNavigator;
- typedef FormatNavigator<SummaryMap, SummaryFormat::SummaryCallback> SummaryNavigator;
- typedef FormatNavigator<RegexSummaryMap, SummaryFormat::RegexSummaryCallback> RegexSummaryNavigator;
+ typedef ValueNavigator::MapType ValueMap;
+ typedef SummaryNavigator::MapType SummaryMap;
+ typedef RegexSummaryNavigator::MapType RegexSummaryMap;
+ typedef FormatMap<const char*, SummaryFormat> NamedSummariesMap;
ValueNavigator m_value_nav;
SummaryNavigator m_summary_nav;
RegexSummaryNavigator m_regex_summary_nav;
-
+
+ NamedSummariesMap m_named_summaries_map;
+
uint32_t m_last_revision;
public:
@@ -396,6 +477,7 @@ public:
m_value_nav(this),
m_summary_nav(this),
m_regex_summary_nav(this),
+ m_named_summaries_map(this),
m_last_revision(0)
{
}
@@ -404,7 +486,7 @@ public:
ValueNavigator& Value() { return m_value_nav; }
SummaryNavigator& Summary() { return m_summary_nav; }
RegexSummaryNavigator& RegexSummary() { return m_regex_summary_nav; }
-
+ NamedSummariesMap& NamedSummary() { return m_named_summaries_map; }
static bool
GetFormatFromCString (const char *format_cstr,
diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h
index e87948c4711..182d078d9b3 100644
--- a/lldb/include/lldb/Core/ValueObject.h
+++ b/lldb/include/lldb/Core/ValueObject.h
@@ -93,6 +93,7 @@ public:
eUnexpectedSymbol, // something is malformed in the expression
eTakingAddressFailed, // impossible to apply & operator
eDereferencingFailed, // impossible to apply * operator
+ eRangeOperatorExpanded, // [] was expanded into a VOList
eUnknown = 0xFFFF
};
@@ -102,6 +103,7 @@ public:
eBitfield, // a bitfield
eBoundedRange, // a range [low-high]
eUnboundedRange, // a range []
+ eValueObjectList, // several items in a VOList
eInvalid = 0xFFFF
};
@@ -392,6 +394,15 @@ public:
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 ()
{
@@ -581,6 +592,17 @@ public:
lldb::DynamicValueType use_dynamic,
bool scope_already_checked,
bool flat_output);
+
+ // 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
GetIsConstant () const
@@ -609,6 +631,42 @@ public:
m_value_str.clear();
m_format = format;
}
+
+ void
+ SetCustomSummaryFormat(lldb::SummaryFormatSP format)
+ {
+ m_forced_summary_format = format;
+ m_user_id_of_forced_summary = m_update_point.GetUpdateID();
+ m_summary_str.clear();
+ }
+
+ lldb::SummaryFormatSP
+ GetCustomSummaryFormat()
+ {
+ return m_forced_summary_format;
+ }
+
+ void
+ ClearCustomSummaryFormat()
+ {
+ m_forced_summary_format.reset();
+ m_summary_str.clear();
+ }
+
+ bool
+ HasCustomSummaryFormat()
+ {
+ return (m_forced_summary_format.get());
+ }
+
+ lldb::SummaryFormatSP
+ GetSummaryFormat()
+ {
+ UpdateFormatsIfNeeded();
+ if (HasCustomSummaryFormat())
+ return m_forced_summary_format;
+ return m_last_summary_format;
+ }
// 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
@@ -668,10 +726,12 @@ protected:
// as an independent ValueObjectConstResult, which isn't managed by us.
ValueObject *m_deref_valobj;
- lldb::Format m_format;
- uint32_t m_last_format_mgr_revision;
- lldb::SummaryFormatSP m_last_summary_format;
- lldb::ValueFormatSP m_last_value_format;
+ lldb::Format m_format;
+ uint32_t m_last_format_mgr_revision;
+ lldb::SummaryFormatSP m_last_summary_format;
+ lldb::ValueFormatSP m_last_value_format;
+ lldb::SummaryFormatSP m_forced_summary_format;
+ lldb::user_id_t m_user_id_of_forced_summary;
bool m_value_is_valid:1,
m_value_did_change:1,
m_children_count_valid:1,
@@ -753,12 +813,27 @@ private:
//------------------------------------------------------------------
lldb::ValueObjectSP
- GetValueForExpressionPath_Impl(const char* expression,
+ 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);
diff --git a/lldb/include/lldb/Interpreter/OptionGroupVariable.h b/lldb/include/lldb/Interpreter/OptionGroupVariable.h
index e7dac2afc3e..387eeba36c5 100644
--- a/lldb/include/lldb/Interpreter/OptionGroupVariable.h
+++ b/lldb/include/lldb/Interpreter/OptionGroupVariable.h
@@ -53,6 +53,7 @@ namespace lldb_private {
show_scope:1,
show_decl:1;
lldb::Format format;
+ std::string summary;
private:
DISALLOW_COPY_AND_ASSIGN(OptionGroupVariable);
diff --git a/lldb/include/lldb/Symbol/ClangASTContext.h b/lldb/include/lldb/Symbol/ClangASTContext.h
index 7159793133d..dd3c2f49d63 100644
--- a/lldb/include/lldb/Symbol/ClangASTContext.h
+++ b/lldb/include/lldb/Symbol/ClangASTContext.h
@@ -49,7 +49,8 @@ public:
eTypeIsStructUnion = (1u << 13),
eTypeIsTemplate = (1u << 14),
eTypeIsTypedef = (1u << 15),
- eTypeIsVector = (1u << 16)
+ eTypeIsVector = (1u << 16),
+ eTypeIsScalar = (1u << 17),
};
typedef void (*CompleteTagDeclCallback)(void *baton, clang::TagDecl *);
diff --git a/lldb/include/lldb/Utility/CleanUp.h b/lldb/include/lldb/Utility/CleanUp.h
index 9f7b169f0ac..8597293e331 100644
--- a/lldb/include/lldb/Utility/CleanUp.h
+++ b/lldb/include/lldb/Utility/CleanUp.h
@@ -52,7 +52,7 @@ namespace lldb_utility {
// // malloc/free example
// CleanUp <void *, void> malloced_bytes(malloc(32), NULL, free);
//----------------------------------------------------------------------
-template <typename T, typename R>
+template <typename T, typename R = void>
class CleanUp
{
public:
@@ -182,6 +182,140 @@ private:
// Outlaw default constructor, copy constructor and the assignment operator
DISALLOW_COPY_AND_ASSIGN (CleanUp);
};
+
+template <typename T, typename R, typename A0>
+class CleanUp2
+{
+public:
+ typedef T value_type;
+ typedef R (*CallbackType)(value_type, A0);
+
+ //----------------------------------------------------------------------
+ // Constructor that sets the current value only. No values are
+ // considered to be invalid and the cleanup function will be called
+ // regardless of the value of m_current_value.
+ //----------------------------------------------------------------------
+ CleanUp2 (value_type value, CallbackType callback, A0 arg) :
+ m_current_value (value),
+ m_invalid_value (),
+ m_callback (callback),
+ m_callback_called (false),
+ m_invalid_value_is_valid (false),
+ m_argument(arg)
+ {
+ }
+
+ //----------------------------------------------------------------------
+ // Constructor that sets the current value and also the invalid value.
+ // The cleanup function will be called on "m_value" as long as it isn't
+ // equal to "m_invalid_value".
+ //----------------------------------------------------------------------
+ CleanUp2 (value_type value, value_type invalid, CallbackType callback, A0 arg) :
+ m_current_value (value),
+ m_invalid_value (invalid),
+ m_callback (callback),
+ m_callback_called (false),
+ m_invalid_value_is_valid (true),
+ m_argument(arg)
+ {
+ }
+
+ //----------------------------------------------------------------------
+ // Automatically cleanup when this object goes out of scope.
+ //----------------------------------------------------------------------
+ ~CleanUp2 ()
+ {
+ clean();
+ }
+
+ //----------------------------------------------------------------------
+ // Access the value stored in this class
+ //----------------------------------------------------------------------
+ value_type get()
+ {
+ return m_current_value;
+ }
+
+ //----------------------------------------------------------------------
+ // Access the value stored in this class
+ //----------------------------------------------------------------------
+ const value_type
+ get() const
+ {
+ return m_current_value;
+ }
+
+ //----------------------------------------------------------------------
+ // Reset the owned value to "value". If a current value is valid and
+ // the cleanup callback hasn't been called, the previous value will
+ // be cleaned up (see void CleanUp::clean()).
+ //----------------------------------------------------------------------
+ void
+ set (const value_type value)
+ {
+ // Cleanup the current value if needed
+ clean ();
+ // Now set the new value and mark our callback as not called
+ m_callback_called = false;
+ m_current_value = value;
+ }
+
+ //----------------------------------------------------------------------
+ // Checks is "m_current_value" is valid. The value is considered valid
+ // no invalid value was supplied during construction of this object or
+ // if an invalid value was supplied and "m_current_value" is not equal
+ // to "m_invalid_value".
+ //
+ // Returns true if "m_current_value" is valid, false otherwise.
+ //----------------------------------------------------------------------
+ bool
+ is_valid() const
+ {
+ if (m_invalid_value_is_valid)
+ return m_current_value != m_invalid_value;
+ return true;
+ }
+
+ //----------------------------------------------------------------------
+ // This function will call the cleanup callback provided in the
+ // constructor one time if the value is considered valid (See is_valid()).
+ // This function sets m_callback_called to true so we don't call the
+ // cleanup callback multiple times on the same value.
+ //----------------------------------------------------------------------
+ void
+ clean()
+ {
+ if (m_callback && !m_callback_called)
+ {
+ m_callback_called = true;
+ if (is_valid())
+ m_callback(m_current_value, m_argument);
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Cancels the cleanup that would have been called on "m_current_value"
+ // if it was valid. This function can be used to release the value
+ // contained in this object so ownership can be transfered to the caller.
+ //----------------------------------------------------------------------
+ value_type
+ release ()
+ {
+ m_callback_called = true;
+ return m_current_value;
+ }
+
+private:
+ value_type m_current_value;
+ const value_type m_invalid_value;
+ CallbackType m_callback;
+ bool m_callback_called;
+ bool m_invalid_value_is_valid;
+ A0 m_argument;
+
+ // Outlaw default constructor, copy constructor and the assignment operator
+ DISALLOW_COPY_AND_ASSIGN (CleanUp2);
+};
} // namespace lldb_utility
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index f82c06bcc15..9d8f429cabc 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -439,6 +439,10 @@ public:
const char *name_cstr = NULL;
size_t idx;
+
+ SummaryFormatSP summary_format_sp;
+ if (!m_option_variable.summary.empty())
+ Debugger::NamedSummaryFormats::Get(ConstString(m_option_variable.summary.c_str()), summary_format_sp);
if (variable_list)
{
@@ -484,7 +488,8 @@ public:
if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module))
s.PutCString (": ");
}
-
+ if (summary_format_sp)
+ valobj_sp->SetCustomSummaryFormat(summary_format_sp);
ValueObject::DumpValueObject (result.GetOutputStream(),
valobj_sp.get(),
var_sp->GetName().AsCString(),
@@ -534,6 +539,8 @@ public:
var_sp->GetDeclaration ().DumpStopContext (&s, false);
s.PutCString (": ");
}
+ if (summary_format_sp)
+ valobj_sp->SetCustomSummaryFormat(summary_format_sp);
ValueObject::DumpValueObject (result.GetOutputStream(),
valobj_sp.get(),
valobj_sp->GetParent() ? name_cstr : NULL,
@@ -622,6 +629,8 @@ public:
var_sp->GetDeclaration ().DumpStopContext (&s, false);
s.PutCString (": ");
}
+ if (summary_format_sp)
+ valobj_sp->SetCustomSummaryFormat(summary_format_sp);
ValueObject::DumpValueObject (result.GetOutputStream(),
valobj_sp.get(),
name_cstr,
diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp
index 2e0aa5c0749..c0ffddb62ae 100644
--- a/lldb/source/Commands/CommandObjectType.cpp
+++ b/lldb/source/Commands/CommandObjectType.cpp
@@ -468,6 +468,9 @@ private:
case 'x':
m_regex = true;
break;
+ case 'n':
+ m_name = new ConstString(option_arg);
+ break;
default:
error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option);
break;
@@ -486,6 +489,7 @@ private:
m_skip_references = false;
m_skip_pointers = false;
m_regex = false;
+ m_name = NULL;
}
const OptionDefinition*
@@ -508,6 +512,7 @@ private:
bool m_skip_pointers;
bool m_regex;
std::string m_format_string;
+ ConstString* m_name;
};
CommandOptions m_options;
@@ -601,7 +606,7 @@ public:
{
const size_t argc = command.GetArgumentCount();
- if (argc < 1)
+ if (argc < 1 && !m_options.m_name)
{
result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
@@ -636,14 +641,14 @@ public:
for(int i = 0; i < argc; i++) {
const char* typeA = command.GetArgumentAtIndex(i);
- if(!typeA || typeA[0] == '\0')
+ if (!typeA || typeA[0] == '\0')
{
result.AppendError("empty typenames not allowed");
result.SetStatus(eReturnStatusFailed);
return false;
}
ConstString typeCS(typeA);
- if(!m_options.m_regex)
+ if (!m_options.m_regex)
{
Debugger::SummaryFormats::Add(typeCS, entry);
}
@@ -660,6 +665,21 @@ public:
Debugger::RegexSummaryFormats::Add(typeRX, entry);
}
}
+
+ if (m_options.m_name)
+ {
+ if( (bool)(*(m_options.m_name)) )
+ {
+ Debugger::NamedSummaryFormats::Add(*(m_options.m_name), entry);
+ }
+ else
+ {
+ result.AppendError("added to types, but not given a name");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
result.SetStatus(eReturnStatusSuccessFinishNoResult);
return result.Succeeded();
}
@@ -676,7 +696,8 @@ CommandObjectTypeSummaryAdd::CommandOptions::g_option_table[] =
{ LLDB_OPT_SET_ALL, false, "regex", 'x', no_argument, NULL, 0, eArgTypeBoolean, "Type names are actually regular expressions."},
{ LLDB_OPT_SET_1 , true, "inline-children", 'c', no_argument, NULL, 0, eArgTypeBoolean, "If true, inline all child values into summary string."},
{ LLDB_OPT_SET_2 , true, "format-string", 'f', required_argument, NULL, 0, eArgTypeSummaryString, "Format string used to display text and object contents."},
- { LLDB_OPT_SET_2, false, "expand", 'e', no_argument, NULL, 0, eArgTypeBoolean, "Expand aggregate data types to show children on separate lines."},
+ { LLDB_OPT_SET_2, false, "expand", 'e', no_argument, NULL, 0, eArgTypeBoolean, "Expand aggregate data types to show children on separate lines."},
+ { LLDB_OPT_SET_2, false, "name", 'n', required_argument, NULL, 0, eArgTypeName, "A name for this summary string."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};
@@ -734,7 +755,9 @@ public:
bool delete_summary = Debugger::SummaryFormats::Delete(typeCS);
bool delete_regex = Debugger::RegexSummaryFormats::Delete(typeCS);
- if (delete_summary || delete_regex)
+ bool delete_named = Debugger::NamedSummaryFormats::Delete(typeCS);
+
+ if (delete_summary || delete_regex || delete_named)
{
result.SetStatus(eReturnStatusSuccessFinishNoResult);
return result.Succeeded();
@@ -774,6 +797,7 @@ public:
{
Debugger::SummaryFormats::Clear();
Debugger::RegexSummaryFormats::Clear();
+ Debugger::NamedSummaryFormats::Clear();
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
@@ -847,22 +871,33 @@ public:
Debugger::SummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param);
delete param;
- if(Debugger::RegexSummaryFormats::GetCount() == 0)
+ if(Debugger::RegexSummaryFormats::GetCount() > 0)
{
- result.SetStatus(eReturnStatusSuccessFinishResult);
- return result.Succeeded();
+ result.GetOutputStream().Printf("Regex-based summaries (slower):\n");
+ if (argc == 1) {
+ RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0));
+ regex->Compile(command.GetArgumentAtIndex(0));
+ rxparam = new CommandObjectTypeRXSummaryList_LoopCallbackParam(this,&result,regex);
+ }
+ else
+ rxparam = new CommandObjectTypeRXSummaryList_LoopCallbackParam(this,&result);
+ Debugger::RegexSummaryFormats::LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, rxparam);
+ delete rxparam;
}
- result.GetOutputStream().Printf("Regex-based summaries (slower):\n");
- if (argc == 1) {
- RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0));
- regex->Compile(command.GetArgumentAtIndex(0));
- rxparam = new CommandObjectTypeRXSummaryList_LoopCallbackParam(this,&result,regex);
+ if(Debugger::NamedSummaryFormats::GetCount() > 0)
+ {
+ result.GetOutputStream().Printf("Named summaries:\n");
+ if (argc == 1) {
+ RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0));
+ regex->Compile(command.GetArgumentAtIndex(0));
+ param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result,regex);
+ }
+ else
+ param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result);
+ Debugger::NamedSummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param);
+ delete param;
}
- else
- rxparam = new CommandObjectTypeRXSummaryList_LoopCallbackParam(this,&result);
- Debugger::RegexSummaryFormats::LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, rxparam);
- delete rxparam;
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index e7b97a59077..9b34331e84f 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -1821,6 +1821,48 @@ Debugger::RegexSummaryFormats::GetCount()
return GetFormatManager().RegexSummary().GetCount();
}
+bool
+Debugger::NamedSummaryFormats::Get(const ConstString &type, SummaryFormat::SharedPointer &entry)
+{
+ return GetFormatManager().NamedSummary().Get(type.AsCString(),entry);
+}
+
+void
+Debugger::NamedSummaryFormats::Add(const ConstString &type, const SummaryFormat::SharedPointer &entry)
+{
+ GetFormatManager().NamedSummary().Add(type.AsCString(),entry);
+}
+
+bool
+Debugger::NamedSummaryFormats::Delete(const ConstString &type)
+{
+ return GetFormatManager().NamedSummary().Delete(type.AsCString());
+}
+
+void
+Debugger::NamedSummaryFormats::Clear()
+{
+ GetFormatManager().NamedSummary().Clear();
+}
+
+void
+Debugger::NamedSummaryFormats::LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton)
+{
+ GetFormatManager().NamedSummary().LoopThrough(callback, callback_baton);
+}
+
+uint32_t
+Debugger::NamedSummaryFormats::GetCurrentRevision()
+{
+ return GetFormatManager().GetCurrentRevision();
+}
+
+uint32_t
+Debugger::NamedSummaryFormats::GetCount()
+{
+ return GetFormatManager().NamedSummary().GetCount();
+}
+
#pragma mark Debugger::SettingsController
//--------------------------------------------------
diff --git a/lldb/source/Core/FormatManager.cpp b/lldb/source/Core/FormatManager.cpp
index 24b565af996..ece30068e4d 100644
--- a/lldb/source/Core/FormatManager.cpp
+++ b/lldb/source/Core/FormatManager.cpp
@@ -154,12 +154,11 @@ FormatManager::GetFormatAsCString (Format format)
template<>
bool
-FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Get(const char* key,
- SummaryFormat::SharedPointer& value)
+FormatNavigator<lldb::RegularExpressionSP, SummaryFormat>::Get(const char* key, SummaryFormat::SharedPointer& value)
{
- Mutex::Locker(m_map_mutex);
- MapIterator pos, end = m_map.end();
- for (pos = m_map.begin(); pos != end; pos++)
+ Mutex::Locker(m_format_map.mutex());
+ MapIterator pos, end = m_format_map.map().end();
+ for (pos = m_format_map.map().begin(); pos != end; pos++)
{
lldb::RegularExpressionSP regex = pos->first;
if (regex->Execute(key))
@@ -173,16 +172,16 @@ FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer
template<>
bool
-FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Delete(const char* type)
+FormatNavigator<lldb::RegularExpressionSP, SummaryFormat>::Delete(const char* type)
{
- Mutex::Locker(m_map_mutex);
- MapIterator pos, end = m_map.end();
- for (pos = m_map.begin(); pos != end; pos++)
+ Mutex::Locker(m_format_map.mutex());
+ MapIterator pos, end = m_format_map.map().end();
+ for (pos = m_format_map.map().begin(); pos != end; pos++)
{
lldb::RegularExpressionSP regex = pos->first;
if ( ::strcmp(type,regex->GetText()) == 0)
{
- m_map.erase(pos);
+ m_format_map.map().erase(pos);
return true;
}
}
diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp
index 5c548dfbb97..d39b836f021 100644
--- a/lldb/source/Core/ValueObject.cpp
+++ b/lldb/source/Core/ValueObject.cpp
@@ -77,7 +77,8 @@ ValueObject::ValueObject (ValueObject &parent) :
m_is_bitfield_for_scalar(false),
m_last_format_mgr_revision(0),
m_last_summary_format(),
- m_last_value_format()
+ m_last_value_format(),
+ m_forced_summary_format()
{
m_manager->ManageObject(this);
}
@@ -114,7 +115,8 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope) :
m_is_bitfield_for_scalar(false),
m_last_format_mgr_revision(0),
m_last_summary_format(),
- m_last_value_format()
+ m_last_value_format(),
+ m_forced_summary_format()
{
m_manager = new ValueObjectManager();
m_manager->ManageObject (this);
@@ -189,6 +191,11 @@ ValueObject::UpdateFormatsIfNeeded()
/*printf("CHECKING FOR UPDATES. I am at revision %d, while the format manager is at revision %d\n",
m_last_format_mgr_revision,
Debugger::ValueFormats::GetCurrentRevision());*/
+ if (HasCustomSummaryFormat() && m_update_point.GetUpdateID() != m_user_id_of_forced_summary)
+ {
+ ClearCustomSummaryFormat();
+ m_summary_str.clear();
+ }
if (m_last_format_mgr_revision != Debugger::ValueFormats::GetCurrentRevision())
{
if (m_last_summary_format.get())
@@ -495,7 +502,9 @@ ValueObject::GetSummaryAsCString ()
{
if (m_summary_str.empty())
{
- if (m_last_summary_format.get())
+ SummaryFormat* summary_format = GetSummaryFormat().get();
+
+ if (summary_format)
{
StreamString s;
ExecutionContext exe_ctx;
@@ -504,7 +513,7 @@ ValueObject::GetSummaryAsCString ()
if (exe_ctx.frame)
sc = exe_ctx.frame->GetSymbolContext(eSymbolContextEverything);
- if (m_last_summary_format->m_show_members_oneliner)
+ if (summary_format->m_show_members_oneliner)
{
const uint32_t num_children = GetNumChildren();
if (num_children)
@@ -536,7 +545,7 @@ ValueObject::GetSummaryAsCString ()
}
else
{
- if (Debugger::FormatPrompt(m_last_summary_format->m_format.c_str(), &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, NULL, this))
+ if (Debugger::FormatPrompt(summary_format->m_format.c_str(), &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, NULL, this))
{
m_summary_str.swap(s.GetString());
return m_summary_str.c_str();
@@ -719,6 +728,157 @@ ValueObject::GetSummaryAsCString ()
return m_summary_str.c_str();
}
+bool
+ValueObject::IsCStringContainer(bool check_pointer)
+{
+ clang_type_t elem_or_pointee_clang_type;
+ const Flags type_flags (ClangASTContext::GetTypeInfo (GetClangType(),
+ GetClangAST(),
+ &elem_or_pointee_clang_type));
+ bool is_char_arr_ptr (type_flags.AnySet (ClangASTContext::eTypeIsArray | ClangASTContext::eTypeIsPointer) &&
+ ClangASTContext::IsCharType (elem_or_pointee_clang_type));
+ if (!is_char_arr_ptr)
+ return false;
+ if (!check_pointer)
+ return true;
+ if (type_flags.Test(ClangASTContext::eTypeIsArray))
+ return true;
+ lldb::addr_t cstr_address = LLDB_INVALID_ADDRESS;
+ AddressType cstr_address_type = eAddressTypeInvalid;
+ cstr_address = GetAddressOf (cstr_address_type, true);
+ return (cstr_address != LLDB_INVALID_ADDRESS);
+}
+
+void
+ValueObject::ReadPointedString(Stream& s,
+ Error& error,
+ uint32_t max_length)
+{
+
+ if (max_length == 0)
+ max_length = 128; // this should be a setting, or a formatting parameter
+
+ clang_type_t clang_type = GetClangType();
+ clang_type_t elem_or_pointee_clang_type;
+ const Flags type_flags (ClangASTContext::GetTypeInfo (clang_type,
+ GetClangAST(),
+ &elem_or_pointee_clang_type));
+ if (type_flags.AnySet (ClangASTContext::eTypeIsArray | ClangASTContext::eTypeIsPointer) &&
+ ClangASTContext::IsCharType (elem_or_pointee_clang_type))
+ {
+ ExecutionContextScope *exe_scope = GetExecutionContextScope();
+ if (exe_scope)
+ {
+ Target *target = exe_scope->CalculateTarget();
+ if (target != NULL)
+ {
+ lldb::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 (ClangASTContext::eTypeIsArray))
+ {
+ // We have an array
+ cstr_len = ClangASTContext::GetArraySize (clang_type);
+ if (cstr_len > max_length) // TODO: make cap a setting
+ {
+ cstr_len = ClangASTContext::GetArraySize (clang_type);
+ if (cstr_len > max_length) // TODO: make cap a setting
+ {
+ capped_data = true;
+ cstr_len = max_length;
+ }
+ }
+ cstr_address = GetAddressOf (cstr_address_type, true);
+ }
+ else
+ {
+ // We have a pointer
+ cstr_address = GetPointerValue (cstr_address_type, true);
+ }
+ if (cstr_address != LLDB_INVALID_ADDRESS)
+ {
+ Address cstr_so_addr (NULL, cstr_address);
+ DataExtractor data;
+ size_t bytes_read = 0;
+ std::vector<char> data_buffer;
+ Error error;
+ bool prefer_file_cache = false;
+ if (cstr_len > 0)
+ {
+ data_buffer.resize(cstr_len);
+ data.SetData (&data_buffer.front(), data_buffer.size(), lldb::endian::InlHostByteOrder());
+ bytes_read = target->ReadMemory (cstr_so_addr,
+ prefer_file_cache,
+ &data_buffer.front(),
+ cstr_len,
+ error);
+ if (bytes_read > 0)
+ {
+ s << '"';
+ data.Dump (&s,
+ 0, // Start offset in "data"
+ eFormatCharArray, // Print as characters
+ 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
+ {
+ const size_t k_max_buf_size = 256;
+ data_buffer.resize (k_max_buf_size + 1);
+ // NULL terminate in case we don't get the entire C string
+ data_buffer.back() = '\0';
+
+ s << '"';
+
+ data.SetData (&data_buffer.front(), data_buffer.size(), endian::InlHostByteOrder());
+ while ((bytes_read = target->ReadMemory (cstr_so_addr,
+ prefer_file_cache,
+ &data_buffer.front(),
+ k_max_buf_size,
+ error)) > 0)
+ {
+ size_t len = strlen(&data_buffer.front());
+ if (len == 0)
+ break;
+ if (len > bytes_read)
+ len = bytes_read;
+
+ data.Dump (&s,
+ 0, // Start offset in "data"
+ eFormatCharArray, // Print as characters
+ 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;
+ cstr_so_addr.Slide (k_max_buf_size);
+ }
+ s << '"';
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ error.SetErrorString("impossible to read a string from this object");
+ }
+}
+
const char *
ValueObject::GetObjectDescription ()
{
@@ -894,13 +1054,25 @@ ValueObject::DumpPrintableRepresentation(Stream& s,
ValueObjectRepresentationStyle val_obj_display,
lldb::Format custom_format)
{
- const char *targetvalue = GetPrintableRepresentation(val_obj_display, custom_format);
- if(targetvalue)
- s.PutCString(targetvalue);
- bool var_success = (targetvalue != NULL);
- if(custom_format != eFormatInvalid)
- SetFormat(eFormatDefault);
- return var_success;
+
+ if (IsCStringContainer(true) &&
+ val_obj_display == ValueObject::eDisplayValue &&
+ custom_format == lldb::eFormatCString)
+ {
+ Error error;
+ ReadPointedString(s, error);
+ return error.Success();
+ }
+ else
+ {
+ const char *targetvalue = GetPrintableRepresentation(val_obj_display, custom_format);
+ if(targetvalue)
+ s.PutCString(targetvalue);
+ bool var_success = (targetvalue != NULL);
+ if(custom_format != eFormatInvalid)
+ SetFormat(eFormatDefault);
+ return var_success;
+ }
}
addr_t
@@ -1503,6 +1675,92 @@ ValueObject::GetValueForExpressionPath(const char* expression,
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,
+ lldb::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::eNothing;
+
+ 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 != eArrayRangeOperatorMet)
+ {
+ // I need not expand a range, just post-process the final value and return
+ if (!final_task_on_target || *final_task_on_target == ValueObject::eNothing)
+ {
+ list->Append(ret_val);
+ return 1;
+ }
+ if (ret_val.get() && *final_value_type == ePlain) // I can only deref and takeaddress of plain objects
+ {
+ if (*final_task_on_target == ValueObject::eDereference)
+ {
+ Error error;
+ ValueObjectSP final_value = ret_val->Dereference(error);
+ if (error.Fail() || !final_value.get())
+ {
+ *reason_to_stop = ValueObject::eDereferencingFailed;
+ *final_value_type = ValueObject::eInvalid;
+ return 0;
+ }
+ else
+ {
+ *final_task_on_target = ValueObject::eNothing;
+ list->Append(final_value);
+ return 1;
+ }
+ }
+ if (*final_task_on_target == ValueObject::eTakeAddress)
+ {
+ Error error;
+ ValueObjectSP final_value = ret_val->AddressOf(error);
+ if (error.Fail() || !final_value.get())
+ {
+ *reason_to_stop = ValueObject::eTakingAddressFailed;
+ *final_value_type = ValueObject::eInvalid;
+ return 0;
+ }
+ else
+ {
+ *final_task_on_target = ValueObject::eNothing;
+ 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;
+}
+
lldb::ValueObjectSP
ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr,
const char** first_unparsed,
@@ -1524,6 +1782,12 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr,
const char* expression_cstr = *first_unparsed; // hide the top level expression_cstr
lldb::clang_type_t root_clang_type = root->GetClangType();
+ lldb::clang_type_t pointee_clang_type;
+ Flags root_clang_type_info,pointee_clang_type_info;
+
+ root_clang_type_info = Flags(ClangASTContext::GetTypeInfo(root_clang_type, GetClangAST(), &pointee_clang_type));
+ if (pointee_clang_type)
+ pointee_clang_type_info = Flags(ClangASTContext::GetTypeInfo(pointee_clang_type, GetClangAST(), NULL));
if (!expression_cstr || *expression_cstr == '\0')
{
@@ -1536,16 +1800,15 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr,
case '-':
{
if (options.m_check_dot_vs_arrow_syntax &&
- !ClangASTContext::IsPointerType(root_clang_type)) // if you are trying to use -> on a non-pointer and I must catch the error
+ root_clang_type_info.Test(ClangASTContext::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::eArrowInsteadOfDot;
*final_result = ValueObject::eInvalid;
return ValueObjectSP();
}
- const uint32_t pointer_type_flags = ClangASTContext::GetTypeInfo (root_clang_type, NULL, NULL);
- if ((pointer_type_flags & ClangASTContext::eTypeIsObjC) && // if yo are trying to extract an ObjC IVar when this is forbidden
- (pointer_type_flags & ClangASTContext::eTypeIsPointer) &&
+ if (root_clang_type_info.Test(ClangASTContext::eTypeIsObjC) && // if yo are trying to extract an ObjC IVar when this is forbidden
+ root_clang_type_info.Test(ClangASTContext::eTypeIsPointer) &&
options.m_no_fragile_ivar)
{
*first_unparsed = expression_cstr;
@@ -1565,7 +1828,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr,
case '.': // or fallthrough from ->
{
if (options.m_check_dot_vs_arrow_syntax && *expression_cstr == '.' &&
- ClangASTContext::IsPointerType(root_clang_type)) // if you are trying to use . on a pointer and I must catch the error
+ root_clang_type_info.Test(ClangASTContext::eTypeIsPointer)) // if you are trying to use . on a pointer and I must catch the error
{
*first_unparsed = expression_cstr;
*reason_to_stop = ValueObject::eDotInsteadOfArrow;
@@ -1616,9 +1879,9 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr,
}
case '[':
{
- if (!ClangASTContext::IsArrayType(root_clang_type) && !ClangASTContext::IsPointerType(root_clang_type)) // if this is not a T[] nor a T*
+ if (!root_clang_type_info.Test(ClangASTContext::eTypeIsArray) && !root_clang_type_info.Test(ClangASTContext::eTypeIsPointer)) // if this is not a T[] nor a T*
{
- if (!ClangASTContext::IsScalarType(root_clang_type)) // if this is not even a scalar, this syntax is just plain wrong!
+ if (!root_clang_type_info.Test(ClangASTContext::eTypeIsScalar)) // if this is not even a scalar, this syntax is just plain wrong!
{
*first_unparsed = expression_cstr;
*reason_to_stop = ValueObject::eRangeOperatorInvalid;
@@ -1635,7 +1898,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr,
}
if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays
{
- if (!ClangASTContext::IsArrayType(root_clang_type))
+ if (!root_clang_type_info.Test(ClangASTContext::eTypeIsArray))
{
*first_unparsed = expression_cstr;
*reason_to_stop = ValueObject::eEmptyRangeNotAllowed;
@@ -1672,7 +1935,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr,
}
if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays
{
- if (ClangASTContext::IsArrayType(root_clang_type))
+ if (root_clang_type_info.Test(ClangASTContext::eTypeIsArray))
{
*first_unparsed = expression_cstr+2;
*reason_to_stop = ValueObject::eArrayRangeOperatorMet;
@@ -1688,7 +1951,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr,
}
}
// from here on we do have a valid index
- if (ClangASTContext::IsArrayType(root_clang_type))
+ if (root_clang_type_info.Test(ClangASTContext::eTypeIsArray))
{
ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true);
if (!child_valobj_sp)
@@ -1708,10 +1971,10 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr,
return ValueObjectSP();
}
}
- else if (ClangASTContext::IsPointerType(root_clang_type))
+ else if (root_clang_type_info.Test(ClangASTContext::eTypeIsPointer))
{
if (*what_next == ValueObject::eDereference && // 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
- ClangASTContext::IsScalarType(clang::QualType::getFromOpaquePtr(root_clang_type).getTypePtr()->getPointeeType().getAsOpaquePtr()))
+ pointee_clang_type_info.Test(ClangASTContext::eTypeIsScalar))
{
Error error;
root = root->Dereference(error);
@@ -1790,7 +2053,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr,
index_lower = index_higher;
index_higher = temp;
}
- if (ClangASTContext::IsScalarType(root_clang_type)) // expansion only works for scalars
+ if (root_clang_type_info.Test(ClangASTContext::eTypeIsScalar)) // expansion only works for scalars
{
root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true);
if (!root.get())
@@ -1808,9 +2071,9 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr,
return root;
}
}
- else if (ClangASTContext::IsPointerType(root_clang_type) && // 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
+ else if (root_clang_type_info.Test(ClangASTContext::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::eDereference &&
- ClangASTContext::IsScalarType(clang::QualType::getFromOpaquePtr(root_clang_type).getTypePtr()->getPointeeType().getAsOpaquePtr()))
+ pointee_clang_type_info.Test(ClangASTContext::eTypeIsScalar))
{
Error error;
root = root->Dereference(error);
@@ -1849,6 +2112,302 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr,
}
}
+int
+ValueObject::ExpandArraySliceExpression(const char* expression_cstr,
+ const char** first_unparsed,
+ lldb::ValueObjectSP root,
+ lldb::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
+
+ lldb::clang_type_t root_clang_type = root->GetClangType();
+ lldb::clang_type_t pointee_clang_type;
+ Flags root_clang_type_info,pointee_clang_type_info;
+
+ root_clang_type_info = Flags(ClangASTContext::GetTypeInfo(root_clang_type, GetClangAST(), &pointee_clang_type));
+ if (pointee_clang_type)
+ pointee_clang_type_info = Flags(ClangASTContext::GetTypeInfo(pointee_clang_type, GetClangAST(), NULL));
+
+ if (!expression_cstr || *expression_cstr == '\0')
+ {
+ *reason_to_stop = ValueObject::eEndOfString;
+ list->Append(root);
+ return 1;
+ }
+
+ switch (*expression_cstr)
+ {
+ case '[':
+ {
+ if (!root_clang_type_info.Test(ClangASTContext::eTypeIsArray) && !root_clang_type_info.Test(ClangASTContext::eTypeIsPointer)) // if this is not a T[] nor a T*
+ {
+ if (!root_clang_type_info.Test(ClangASTContext::eTypeIsScalar)) // if this is not even a scalar, this syntax is just plain wrong!
+ {
+ *first_unparsed = expression_cstr;
+ *reason_to_stop = ValueObject::eRangeOperatorInvalid;
+ *final_result = ValueObject::eInvalid;
+ 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::eRangeOperatorNotAllowed;
+ *final_result = ValueObject::eInvalid;
+ return 0;
+ }
+ }
+ if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays
+ {
+ if (!root_clang_type_info.Test(ClangASTContext::eTypeIsArray))
+ {
+ *first_unparsed = expression_cstr;
+ *reason_to_stop = ValueObject::eEmptyRangeNotAllowed;
+ *final_result = ValueObject::eInvalid;
+ return 0;
+ }
+ else // expand this into list
+ {
+ int max_index = root->GetNumChildren() - 1;
+ for (int index = 0; index < max_index; index++)
+ {
+ ValueObjectSP child =
+ root->GetChildAtIndex(index, true);
+ list->Append(child);
+ }
+ *first_unparsed = expression_cstr+2;
+ *reason_to_stop = ValueObject::eRangeOperatorExpanded;
+ *final_result = ValueObject::eValueObjectList;
+ 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::eUnexpectedSymbol;
+ *final_result = ValueObject::eInvalid;
+ 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::eUnexpectedSymbol;
+ *final_result = ValueObject::eInvalid;
+ return 0;
+ }
+ if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays
+ {
+ if (root_clang_type_info.Test(ClangASTContext::eTypeIsArray))
+ {
+ int max_index = root->GetNumChildren() - 1;
+ for (int index = 0; index < max_index; index++)
+ {
+ ValueObjectSP child =
+ root->GetChildAtIndex(index, true);
+ list->Append(child);
+ }
+ *first_unparsed = expression_cstr+2;
+ *reason_to_stop = ValueObject::eRangeOperatorExpanded;
+ *final_result = ValueObject::eValueObjectList;
+ return max_index; // tell me number of items I added to the VOList
+ }
+ else
+ {
+ *first_unparsed = expression_cstr;
+ *reason_to_stop = ValueObject::eEmptyRangeNotAllowed;
+ *final_result = ValueObject::eInvalid;
+ return 0;
+ }
+ }
+ // from here on we do have a valid index
+ if (root_clang_type_info.Test(ClangASTContext::eTypeIsArray))
+ {
+ root = root->GetChildAtIndex(index, true);
+ if (!root.get())
+ {
+ *first_unparsed = expression_cstr;
+ *reason_to_stop = ValueObject::eNoSuchChild;
+ *final_result = ValueObject::eInvalid;
+ return 0;
+ }
+ else
+ {
+ list->Append(root);
+ *first_unparsed = end+1; // skip ]
+ *reason_to_stop = ValueObject::eRangeOperatorExpanded;
+ *final_result = ValueObject::eValueObjectList;
+ return 1;
+ }
+ }
+ else if (root_clang_type_info.Test(ClangASTContext::eTypeIsPointer))
+ {
+ if (*what_next == ValueObject::eDereference && // 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(ClangASTContext::eTypeIsScalar))
+ {
+ Error error;
+ root = root->Dereference(error);
+ if (error.Fail() || !root.get())
+ {
+ *first_unparsed = expression_cstr;
+ *reason_to_stop = ValueObject::eDereferencingFailed;
+ *final_result = ValueObject::eInvalid;
+ return 0;
+ }
+ else
+ {
+ *what_next = eNothing;
+ continue;
+ }
+ }
+ else
+ {
+ root = root->GetSyntheticArrayMemberFromPointer(index, true);
+ if (!root.get())
+ {
+ *first_unparsed = expression_cstr;
+ *reason_to_stop = ValueObject::eNoSuchChild;
+ *final_result = ValueObject::eInvalid;
+ return 0;
+ }
+ else
+ {
+ list->Append(root);
+ *first_unparsed = end+1; // skip ]
+ *reason_to_stop = ValueObject::eRangeOperatorExpanded;
+ *final_result = ValueObject::eValueObjectList;
+ 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::eNoSuchChild;
+ *final_result = ValueObject::eInvalid;
+ 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::eRangeOperatorExpanded;
+ *final_result = ValueObject::eValueObjectList;
+ 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::eUnexpectedSymbol;
+ *final_result = ValueObject::eInvalid;
+ 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::eUnexpectedSymbol;
+ *final_result = ValueObject::eInvalid;
+ 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(ClangASTContext::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::eNoSuchChild;
+ *final_result = ValueObject::eInvalid;
+ return 0;
+ }
+ else
+ {
+ list->Append(root);
+ *first_unparsed = end+1; // skip ]
+ *reason_to_stop = ValueObject::eRangeOperatorExpanded;
+ *final_result = ValueObject::eValueObjectList;
+ return 1;
+ }
+ }
+ else if (root_clang_type_info.Test(ClangASTContext::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::eDereference &&
+ pointee_clang_type_info.Test(ClangASTContext::eTypeIsScalar))
+ {
+ Error error;
+ root = root->Dereference(error);
+ if (error.Fail() || !root.get())
+ {
+ *first_unparsed = expression_cstr;
+ *reason_to_stop = ValueObject::eDereferencingFailed;
+ *final_result = ValueObject::eInvalid;
+ return 0;
+ }
+ else
+ {
+ *what_next = ValueObject::eNothing;
+ continue;
+ }
+ }
+ else
+ {
+ for (int index = index_lower;
+ index <= index_higher; index++)
+ {
+ ValueObjectSP child =
+ root->GetChildAtIndex(index, true);
+ list->Append(child);
+ }
+ *first_unparsed = end+1;
+ *reason_to_stop = ValueObject::eRangeOperatorExpanded;
+ *final_result = ValueObject::eValueObjectList;
+ 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::eUnexpectedSymbol;
+ *final_result = ValueObject::eInvalid;
+ return 0;
+ break;
+ }
+ }
+ }
+}
+
void
ValueObject::DumpValueObject
(
@@ -1921,7 +2480,7 @@ ValueObject::DumpValueObject
const char *val_cstr = NULL;
const char *sum_cstr = NULL;
- SummaryFormat* entry = valobj->m_last_summary_format.get();
+ SummaryFormat* entry = valobj->GetSummaryFormat().get();
if (err_cstr == NULL)
{
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 8e499d7de43..bd934c3aa07 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -935,7 +935,7 @@ CommandInterpreter::HandleCommand (const char *command_line,
// Make a scoped cleanup object that will clear the crash description string
// on exit of this function.
- lldb_utility::CleanUp <const char *, void> crash_description_cleanup(NULL, Host::SetCrashDescription);
+ lldb_utility::CleanUp <const char *> crash_description_cleanup(NULL, Host::SetCrashDescription);
if (log)
log->Printf ("Processing command: %s", command_line);
diff --git a/lldb/source/Interpreter/OptionGroupVariable.cpp b/lldb/source/Interpreter/OptionGroupVariable.cpp
index e553e1bea47..9cf8d417610 100644
--- a/lldb/source/Interpreter/OptionGroupVariable.cpp
+++ b/lldb/source/Interpreter/OptionGroupVariable.cpp
@@ -19,6 +19,7 @@
using namespace lldb;
using namespace lldb_private;
+// if you add any options here, remember to update the counters in OptionGroupVariable::GetNumDefinitions()
static OptionDefinition
g_option_table[] =
{
@@ -28,7 +29,8 @@ g_option_table[] =
{ LLDB_OPT_SET_1, false, "show-declaration",'c', no_argument, NULL, 0, eArgTypeNone, "Show variable declaration information (source file and line where the variable was declared)."},
{ LLDB_OPT_SET_1, false, "format", 'f', required_argument, NULL, 0, eArgTypeExprFormat, "Specify the format that the variable output should use."},
{ LLDB_OPT_SET_1, false, "regex", 'r', no_argument, NULL, 0, eArgTypeRegularExpression, "The <variable-name> argument for name lookups are regular expressions."},
- { LLDB_OPT_SET_1, false, "scope", 's', no_argument, NULL, 0, eArgTypeNone, "Show variable scope (argument, local, global, static)."}
+ { LLDB_OPT_SET_1, false, "scope", 's', no_argument, NULL, 0, eArgTypeNone, "Show variable scope (argument, local, global, static)."},
+ { LLDB_OPT_SET_1, false, "summary", 'y', required_argument, NULL, 0, eArgTypeName, "Specify the summary that the variable output should use."},
};
@@ -62,7 +64,9 @@ OptionGroupVariable::SetOptionValue (CommandInterpreter &interpreter,
case 's':
show_scope = true;
break;
-
+ case 'y':
+ summary = std::string(option_arg);
+ break;
default:
error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option);
break;
@@ -81,6 +85,7 @@ OptionGroupVariable::OptionParsingStarting (CommandInterpreter &interpreter)
format = lldb::eFormatDefault;
use_regex = false;
show_scope = false;
+ summary = "";
}
@@ -101,9 +106,9 @@ uint32_t
OptionGroupVariable::GetNumDefinitions ()
{
if (include_frame_options)
- return 7;
+ return 8;
else
- return 4;
+ return 5;
}
diff --git a/lldb/source/Symbol/ClangASTContext.cpp b/lldb/source/Symbol/ClangASTContext.cpp
index 6e3a83e162c..8ccde5ce1e9 100644
--- a/lldb/source/Symbol/ClangASTContext.cpp
+++ b/lldb/source/Symbol/ClangASTContext.cpp
@@ -2077,7 +2077,30 @@ ClangASTContext::GetTypeInfo
if (ast && pointee_or_element_clang_type)
*pointee_or_element_clang_type = ast->ObjCBuiltinClassTy.getAsOpaquePtr();
return eTypeIsBuiltIn | eTypeIsPointer | eTypeHasValue;
-
+ break;
+ case clang::BuiltinType::Bool:
+ case clang::BuiltinType::Char_U:
+ case clang::BuiltinType::UChar:
+ case clang::BuiltinType::WChar_U:
+ case clang::BuiltinType::Char16:
+ case clang::BuiltinType::Char32:
+ case clang::BuiltinType::UShort:
+ case clang::BuiltinType::UInt:
+ case clang::BuiltinType::ULong:
+ case clang::BuiltinType::ULongLong:
+ case clang::BuiltinType::UInt128:
+ case clang::BuiltinType::Char_S:
+ case clang::BuiltinType::SChar:
+ case clang::BuiltinType::WChar_S:
+ case clang::BuiltinType::Short:
+ case clang::BuiltinType::Int:
+ case clang::BuiltinType::Long:
+ case clang::BuiltinType::LongLong:
+ case clang::BuiltinType::Int128:
+ case clang::BuiltinType::Float:
+ case clang::BuiltinType::Double:
+ case clang::BuiltinType::LongDouble:
+ return eTypeIsBuiltIn | eTypeHasValue | eTypeIsScalar;
default:
break;
}
@@ -4645,6 +4668,10 @@ ClangASTContext::GetArraySize (clang_type_t clang_type)
case clang::Type::Typedef:
return ClangASTContext::GetArraySize(cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType().getAsOpaquePtr());
+ break;
+
+ default:
+ break;
}
}
return 0;
diff --git a/lldb/source/Symbol/SymbolContext.cpp b/lldb/source/Symbol/SymbolContext.cpp
index 1f24f1339bd..0898b812552 100644
--- a/lldb/source/Symbol/SymbolContext.cpp
+++ b/lldb/source/Symbol/SymbolContext.cpp
@@ -164,7 +164,7 @@ SymbolContext::DumpStopContext
dumped_something = true;
s->PutCString(" at ");
if (line_entry.DumpStopContext(s, show_fullpaths))
- return;
+ return dumped_something;
}
}
}
diff --git a/lldb/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py b/lldb/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py
index 68eab1897bd..12f6b6d0ba9 100644
--- a/lldb/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py
+++ b/lldb/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py
@@ -73,7 +73,16 @@ class DataFormatterTestCase(TestBase):
self.expect("frame variable",
patterns = ['\(Speed\) SPILookHex = 0x[0-9a-f]+' # Speed should look hex-ish now.
]);
-
+
+ # gcc4.2 on Mac OS X skips typedef chains in the DWARF output
+ if self.getCompiler() in ['clang', 'llvm-gcc']:
+ self.expect("frame variable",
+ patterns = ['\(SignalMask\) SMILookHex = 0x[0-9a-f]+' # SignalMask should look hex-ish now.
+ ]);
+ self.expect("frame variable", matching=False,
+ patterns = ['\(Type4\) T4ILookChar = 0x[0-9a-f]+' # Type4 should NOT look hex-ish now.
+ ]);
+
# Now let's delete the 'Speed' custom format.
self.runCmd("type format delete Speed")
@@ -85,6 +94,26 @@ class DataFormatterTestCase(TestBase):
self.expect("type format delete Speed", error=True,
substrs = ['no custom format for Speed'])
+ self.runCmd("type summary add -f \"arr = ${var%s}\" -x \"char \\[[0-9]+\\]\" -v")
+
+ self.expect("frame variable strarr",
+ substrs = ['arr = "Hello world!\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0"'])
+
+ self.runCmd("type summary clear")
+
+ self.runCmd("type summary add -f \"ptr = ${var%s}\" \"char *\" -v")
+
+ self.expect("frame variable strptr",
+ substrs = ['ptr = "Hello world!"'])
+
+ self.runCmd("type summary add -f \"arr = ${var%s}\" -x \"char \\[[0-9]+\\]\" -v")
+
+ self.expect("frame variable strarr",
+ substrs = ['arr = "Hello world!\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0"'])
+
+ self.expect("frame variable strptr",
+ substrs = ['ptr = "Hello world!"'])
+
self.runCmd("type summary add -c Point")
self.expect("frame variable iAmSomewhere",
diff --git a/lldb/test/functionalities/data-formatter/data-formatter-cpp/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-cpp/main.cpp
index 20cdc2c900e..9224cb8a0a0 100644
--- a/lldb/test/functionalities/data-formatter/data-formatter-cpp/main.cpp
+++ b/lldb/test/functionalities/data-formatter/data-formatter-cpp/main.cpp
@@ -107,6 +107,9 @@ int main (int argc, const char * argv[])
int int_array[] = {1,2,3,4,5};
IUseCharStar iEncapsulateCharStar;
+
+ char strarr[32] = "Hello world!";
+ char* strptr = "Hello world!";
return 0; // Set break point at this line.
}
OpenPOWER on IntegriCloud