diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 3eb5b0de1971..17d48d3b1082 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -486,7 +486,7 @@ public: { public: static bool - Get(ValueObject& vobj, ValueFormat::SharedPointer &entry); + Get(ValueObject& vobj, lldb::DynamicValueType use_dynamic, ValueFormat::SharedPointer &entry); static void Add(const ConstString &type, const ValueFormat::SharedPointer &entry); @@ -509,9 +509,11 @@ public: static bool GetSummaryFormat(ValueObject& vobj, + lldb::DynamicValueType use_dynamic, lldb::SummaryFormatSP& entry); static bool GetSyntheticFilter(ValueObject& vobj, + lldb::DynamicValueType use_dynamic, lldb::SyntheticChildrenSP& entry); class NamedSummaryFormats diff --git a/lldb/include/lldb/Core/FormatManager.h b/lldb/include/lldb/Core/FormatManager.h index c8a2fdddcadc..14bd709fa98a 100644 --- a/lldb/include/lldb/Core/FormatManager.h +++ b/lldb/include/lldb/Core/FormatManager.h @@ -55,7 +55,9 @@ namespace std #include "lldb/Core/ValueObject.h" #include "lldb/Interpreter/ScriptInterpreterPython.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/TargetList.h" @@ -189,6 +191,8 @@ private: typedef FormatMap BackEndType; BackEndType m_format_map; + + std::string m_name; public: typedef typename BackEndType::MapType MapType; @@ -201,8 +205,10 @@ public: friend class FormatCategory; - FormatNavigator(IFormatChangeListener* lst = NULL) : - m_format_map(lst) + FormatNavigator(std::string name, + IFormatChangeListener* lst = NULL) : + m_format_map(lst), + m_name(name) { } @@ -223,11 +229,12 @@ public: bool Get(ValueObject& vobj, MapValueType& entry, + lldb::DynamicValueType use_dynamic, uint32_t* why = NULL) { uint32_t value = lldb::eFormatterChoiceCriterionDirectChoice; clang::QualType type = clang::QualType::getFromOpaquePtr(vobj.GetClangType()); - bool ret = Get(vobj, type, entry, value); + bool ret = Get(vobj, type, entry, use_dynamic, value); if (ret) entry = MapValueType(entry); else @@ -267,9 +274,64 @@ private: return m_format_map.Get(type, entry); } + bool Get_ObjC(ValueObject& vobj, + ObjCLanguageRuntime::ObjCISA isa, + MapValueType& entry, + uint32_t& reason) + { + LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + if (log) + log->Printf("going to an Objective-C dynamic scanning"); + Process* process = vobj.GetUpdatePoint().GetProcessSP().get(); + ObjCLanguageRuntime* runtime = process->GetObjCLanguageRuntime(); + if (runtime == NULL) + { + if (log) + log->Printf("no valid ObjC runtime, bailing out"); + return false; + } + if (runtime->IsValidISA(isa) == false) + { + if (log) + log->Printf("invalid ISA, bailing out"); + return false; + } + ConstString name = runtime->GetActualTypeName(isa); + if (log) + log->Printf("looking for formatter for %s", name.GetCString()); + if (Get(name.GetCString(), entry)) + { + if (log) + log->Printf("direct match found, returning"); + return true; + } + if (log) + log->Printf("no direct match"); + ObjCLanguageRuntime::ObjCISA parent = runtime->GetParentClass(isa); + if (runtime->IsValidISA(parent) == false) + { + if (log) + log->Printf("invalid parent ISA, bailing out"); + return false; + } + if (parent == isa) + { + if (log) + log->Printf("parent-child loop, bailing out"); + return false; + } + if (Get_ObjC(vobj, parent, entry, reason)) + { + reason |= lldb::eFormatterChoiceCriterionNavigatedBaseClasses; + return true; + } + return false; + } + bool Get(ValueObject& vobj, clang::QualType type, MapValueType& entry, + lldb::DynamicValueType use_dynamic, uint32_t& reason) { LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); @@ -299,7 +361,10 @@ private: log->Printf("appended bitfield info, final result is %s", name.GetCString()); } if (log) - log->Printf("trying to get format for VO name %s of type %s",vobj.GetName().AsCString(),name.AsCString()); + log->Printf("trying to get %s for VO name %s of type %s", + m_name.c_str(), + vobj.GetName().AsCString(), + name.AsCString()); if (Get(name.GetCString(), entry)) { if (log) @@ -313,17 +378,48 @@ private: { if (log) log->Printf("stripping reference"); - if (Get(vobj,type.getNonReferenceType(),entry, reason) && !entry->m_skip_references) + if (Get(vobj,type.getNonReferenceType(),entry, use_dynamic, reason) && !entry->m_skip_references) { reason |= lldb::eFormatterChoiceCriterionStrippedPointerReference; return true; } } + if (use_dynamic != lldb::eNoDynamicValues && + typePtr == vobj.GetClangAST()->ObjCBuiltinIdTy.getTypePtr()) + { + if (log) + log->Printf("this is an ObjC 'id', let's do dynamic search"); + Process* process = vobj.GetUpdatePoint().GetProcessSP().get(); + ObjCLanguageRuntime* runtime = process->GetObjCLanguageRuntime(); + if (runtime == NULL) + { + if (log) + log->Printf("no valid ObjC runtime, skipping dynamic"); + } + else + { + if (Get_ObjC(vobj, runtime->GetISA(vobj), entry, reason)) + { + reason |= lldb::eFormatterChoiceCriterionDynamicObjCHierarchy; + return true; + } + } + } + else if (use_dynamic != lldb::eNoDynamicValues && log) + { + log->Printf("typename: %s, typePtr = %p, id = %p", + name.AsCString(), typePtr, vobj.GetClangAST()->ObjCBuiltinIdTy.getTypePtr()); + } + else if (log) + { + log->Printf("no dynamic"); + } if (typePtr->isPointerType()) { if (log) log->Printf("stripping pointer"); - if (Get(vobj, typePtr->getPointeeType(), entry, reason) && !entry->m_skip_pointers) + clang::QualType pointee = typePtr->getPointeeType(); + if (Get(vobj, pointee, entry, use_dynamic, reason) && !entry->m_skip_pointers) { reason |= lldb::eFormatterChoiceCriterionStrippedPointerReference; return true; @@ -331,6 +427,27 @@ private: } if (typePtr->isObjCObjectPointerType()) { + if (use_dynamic != lldb::eNoDynamicValues && + name.GetCString() == ConstString("id").GetCString()) + { + if (log) + log->Printf("this is an ObjC 'id', let's do dynamic search"); + Process* process = vobj.GetUpdatePoint().GetProcessSP().get(); + ObjCLanguageRuntime* runtime = process->GetObjCLanguageRuntime(); + if (runtime == NULL) + { + if (log) + log->Printf("no valid ObjC runtime, skipping dynamic"); + } + else + { + if (Get_ObjC(vobj, runtime->GetISA(vobj), entry, reason)) + { + reason |= lldb::eFormatterChoiceCriterionDynamicObjCHierarchy; + return true; + } + } + } if (log) log->Printf("stripping ObjC pointer"); /* @@ -343,7 +460,7 @@ private: ValueObject* target = vobj.Dereference(error).get(); if (error.Fail() || !target) return false; - if (Get(*target, typePtr->getPointeeType(), entry, reason) && !entry->m_skip_pointers) + if (Get(*target, typePtr->getPointeeType(), entry, use_dynamic, reason) && !entry->m_skip_pointers) { reason |= lldb::eFormatterChoiceCriterionStrippedPointerReference; return true; @@ -368,7 +485,7 @@ private: if (log) log->Printf("got a parent class for this ObjC class"); clang::QualType ivar_qual_type(ast->getObjCInterfaceType(superclass_interface_decl)); - if (Get(vobj, ivar_qual_type, entry, reason) && entry->m_cascades) + if (Get(vobj, ivar_qual_type, entry, use_dynamic, reason) && entry->m_cascades) { reason |= lldb::eFormatterChoiceCriterionNavigatedBaseClasses; return true; @@ -397,7 +514,7 @@ private: end = record->bases_end(); for (pos = record->bases_begin(); pos != end; pos++) { - if ((Get(vobj, pos->getType(), entry, reason)) && entry->m_cascades) + if ((Get(vobj, pos->getType(), entry, use_dynamic, reason)) && entry->m_cascades) { reason |= lldb::eFormatterChoiceCriterionNavigatedBaseClasses; return true; @@ -411,7 +528,7 @@ private: end = record->vbases_end(); for (pos = record->vbases_begin(); pos != end; pos++) { - if ((Get(vobj, pos->getType(), entry, reason)) && entry->m_cascades) + if ((Get(vobj, pos->getType(), entry, use_dynamic, reason)) && entry->m_cascades) { reason |= lldb::eFormatterChoiceCriterionNavigatedBaseClasses; return true; @@ -427,7 +544,7 @@ private: { if (log) log->Printf("stripping typedef"); - if ((Get(vobj, type_tdef->getDecl()->getUnderlyingType(), entry, reason)) && entry->m_cascades) + if ((Get(vobj, type_tdef->getDecl()->getUnderlyingType(), entry, use_dynamic, reason)) && entry->m_cascades) { reason |= lldb::eFormatterChoiceCriterionNavigatedTypedefs; return true; @@ -495,9 +612,9 @@ public: FormatCategory(IFormatChangeListener* clist, std::string name) : - m_summary_nav(new SummaryNavigator(clist)), - m_regex_summary_nav(new RegexSummaryNavigator(clist)), - m_filter_nav(new FilterNavigator(clist)), + m_summary_nav(new SummaryNavigator("summary",clist)), + m_regex_summary_nav(new RegexSummaryNavigator("regex-summary",clist)), + m_filter_nav(new FilterNavigator("filter",clist)), m_enabled(false), m_change_listener(clist), m_mutex(Mutex::eMutexTypeRecursive), @@ -531,13 +648,14 @@ public: bool Get(ValueObject& vobj, lldb::SummaryFormatSP& entry, + lldb::DynamicValueType use_dynamic, uint32_t* reason = NULL) { if (!IsEnabled()) return false; - if (Summary()->Get(vobj, entry, reason)) + if (Summary()->Get(vobj, entry, use_dynamic, reason)) return true; - bool regex = RegexSummary()->Get(vobj, entry, reason); + bool regex = RegexSummary()->Get(vobj, entry, use_dynamic, reason); if (regex && reason) *reason |= lldb::eFormatterChoiceCriterionRegularExpressionSummary; return regex; @@ -546,11 +664,12 @@ public: bool Get(ValueObject& vobj, lldb::SyntheticChildrenSP& entry, + lldb::DynamicValueType use_dynamic, uint32_t* reason = NULL) { if (!IsEnabled()) return false; - return (Filter()->Get(vobj, entry, reason)); + return (Filter()->Get(vobj, entry, use_dynamic, reason)); } // just a shortcut for Summary()->Clear; RegexSummary()->Clear() @@ -760,43 +879,44 @@ public: bool Get(ValueObject& vobj, - lldb::SummaryFormatSP& entry) + lldb::SummaryFormatSP& entry, + lldb::DynamicValueType use_dynamic) { Mutex::Locker(m_map_mutex); - uint32_t reason_why; - bool first = true; - + uint32_t reason_why; ActiveCategoriesIterator begin, end = m_active_categories.end(); for (begin = m_active_categories.begin(); begin != end; begin++) { FormatCategory::SharedPointer category = *begin; lldb::SummaryFormatSP current_format; - if (!category->Get(vobj, current_format, &reason_why)) + if (!category->Get(vobj, current_format, use_dynamic, &reason_why)) continue; - if (reason_why == lldb::eFormatterChoiceCriterionDirectChoice) - { - entry = current_format; - return true; - } - else if (first) - { - entry = current_format; - first = false; - } + /*if (reason_why == lldb::eFormatterChoiceCriterionDirectChoice) + { + entry = current_format; + return true; + } + else if (first) + { + entry = current_format; + first = false; + }*/ + entry = current_format; + return true; } - return !first; + return false; } bool Get(ValueObject& vobj, - lldb::SyntheticChildrenSP& entry) + lldb::SyntheticChildrenSP& entry, + lldb::DynamicValueType use_dynamic) { Mutex::Locker(m_map_mutex); uint32_t reason_why; - bool first = true; ActiveCategoriesIterator begin, end = m_active_categories.end(); @@ -804,9 +924,9 @@ public: { FormatCategory::SharedPointer category = *begin; lldb::SyntheticChildrenSP current_format; - if (!category->Get(vobj, current_format, &reason_why)) + if (!category->Get(vobj, current_format, use_dynamic, &reason_why)) continue; - if (reason_why == lldb::eFormatterChoiceCriterionDirectChoice) + /*if (reason_why == lldb::eFormatterChoiceCriterionDirectChoice) { entry = current_format; return true; @@ -815,9 +935,11 @@ public: { entry = current_format; first = false; - } + }*/ + entry = current_format; + return true; } - return !first; + return false; } }; @@ -847,7 +969,7 @@ public: typedef bool (*CategoryCallback)(void*, const char*, const FormatCategory::SharedPointer&); FormatManager() : - m_value_nav(this), + m_value_nav("format",this), m_named_summaries_map(this), m_last_revision(0), m_categories_map(this) @@ -941,15 +1063,17 @@ public: bool Get(ValueObject& vobj, - lldb::SummaryFormatSP& entry) + lldb::SummaryFormatSP& entry, + lldb::DynamicValueType use_dynamic) { - return m_categories_map.Get(vobj, entry); + return m_categories_map.Get(vobj, entry, use_dynamic); } bool Get(ValueObject& vobj, - lldb::SyntheticChildrenSP& entry) + lldb::SyntheticChildrenSP& entry, + lldb::DynamicValueType use_dynamic) { - return m_categories_map.Get(vobj, entry); + return m_categories_map.Get(vobj, entry, use_dynamic); } static bool diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index da75b46be153..1bff914ed3fd 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -443,6 +443,9 @@ public: virtual const char * GetValueAsCString (); + + virtual unsigned long long + GetValueAsUnsigned(); virtual bool SetValueFromCString (const char *value_str); @@ -513,8 +516,11 @@ public: bool UpdateValueIfNeeded (bool update_format = true); + bool + UpdateValueIfNeeded (lldb::DynamicValueType use_dynamic, bool update_format = true); + void - UpdateFormatsIfNeeded(); + UpdateFormatsIfNeeded(lldb::DynamicValueType use_dynamic = lldb::eNoDynamicValues); DataExtractor & GetDataExtractor (); @@ -639,6 +645,18 @@ public: return m_format; } + void + SetIsExpressionResult(bool expr) + { + m_is_expression_result = expr; + } + + bool + GetIsExpressionResult() + { + return m_is_expression_result; + } + void SetFormat (lldb::Format format) { @@ -677,7 +695,7 @@ public: lldb::SummaryFormatSP GetSummaryFormat() { - UpdateFormatsIfNeeded(); + UpdateFormatsIfNeeded(m_last_format_mgr_dynamic); if (HasCustomSummaryFormat()) return m_forced_summary_format; return m_last_summary_format; @@ -744,6 +762,7 @@ protected: lldb::Format m_format; uint32_t m_last_format_mgr_revision; + lldb::DynamicValueType m_last_format_mgr_dynamic; lldb::SummaryFormatSP m_last_summary_format; lldb::SummaryFormatSP m_forced_summary_format; lldb::ValueFormatSP m_last_value_format; @@ -759,7 +778,8 @@ protected: 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_child_at_offset:1, + m_is_expression_result:1; // used to prevent endless looping into GetpPrintableRepresentation() uint32_t m_dump_printable_counter; diff --git a/lldb/include/lldb/Symbol/ClangASTType.h b/lldb/include/lldb/Symbol/ClangASTType.h index b19e2726ac79..e50735beb7b1 100644 --- a/lldb/include/lldb/Symbol/ClangASTType.h +++ b/lldb/include/lldb/Symbol/ClangASTType.h @@ -98,7 +98,8 @@ public: GetMinimumLanguage (); static lldb::LanguageType - GetMinimumLanguage (lldb::clang_type_t clang_type); + GetMinimumLanguage (clang::ASTContext *ctx, + lldb::clang_type_t clang_type); void DumpValue (ExecutionContext *exe_ctx, diff --git a/lldb/include/lldb/Target/ObjCLanguageRuntime.h b/lldb/include/lldb/Target/ObjCLanguageRuntime.h index e508da9e7a46..5dc88b6bda02 100644 --- a/lldb/include/lldb/Target/ObjCLanguageRuntime.h +++ b/lldb/include/lldb/Target/ObjCLanguageRuntime.h @@ -72,6 +72,20 @@ public: { return eObjC_VersionUnknown; } + + typedef lldb::addr_t ObjCISA; + + virtual bool + IsValidISA(ObjCISA isa) = 0; + + virtual ObjCISA + GetISA(ValueObject& valobj) = 0; + + virtual ConstString + GetActualTypeName(ObjCISA isa) = 0; + + virtual ObjCISA + GetParentClass(ObjCISA isa) = 0; // Finds the byte offset of the child_type ivar in parent_type. If it can't find the // offset, returns LLDB_INVALID_IVAR_OFFSET. diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index b9c504b34375..34e9a6cc3efd 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -505,7 +505,8 @@ namespace lldb { eFormatterChoiceCriterionStrippedPointerReference = 0x00000001, eFormatterChoiceCriterionNavigatedTypedefs = 0x00000002, eFormatterChoiceCriterionNavigatedBaseClasses = 0x00000004, - eFormatterChoiceCriterionRegularExpressionSummary = 0x00000008 + eFormatterChoiceCriterionRegularExpressionSummary = 0x00000008, + eFormatterChoiceCriterionDynamicObjCHierarchy = 0x00000010 } FormatterChoiceCriterion; //---------------------------------------------------------------------- diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index 6109b643fdcb..86c2fd7d9a91 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -321,6 +321,9 @@ CommandObjectExpression::EvaluateExpression { if (result_valobj_sp->GetError().Success()) { + + result_valobj_sp.get()->SetIsExpressionResult(true); + if (m_options.format != eFormatDefault) result_valobj_sp->SetFormat (m_options.format); diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index a217ddb36ec4..9442600a4d78 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -1010,22 +1010,35 @@ Debugger::FormatPrompt if (!vobj) break; + if (log) + log->Printf("initial string: %s",var_name_begin); + // check for *var and *svar if (*var_name_begin == '*') { do_deref_pointer = true; var_name_begin++; } + + if (log) + log->Printf("initial string: %s",var_name_begin); + if (*var_name_begin == 's') { vobj = vobj->GetSyntheticValue(lldb::eUseSyntheticFilter).get(); var_name_begin++; } + if (log) + log->Printf("initial string: %s",var_name_begin); + // should be a 'v' by now if (*var_name_begin != 'v') break; + if (log) + log->Printf("initial string: %s",var_name_begin); + ValueObject::ExpressionPathAftermath what_next = (do_deref_pointer ? ValueObject::eDereference : ValueObject::eNothing); ValueObject::GetValueForExpressionPathOptions options; @@ -1765,9 +1778,9 @@ Debugger::Formatting::ForceUpdate() } bool -Debugger::Formatting::ValueFormats::Get(ValueObject& vobj, ValueFormat::SharedPointer &entry) +Debugger::Formatting::ValueFormats::Get(ValueObject& vobj, lldb::DynamicValueType use_dynamic, ValueFormat::SharedPointer &entry) { - return GetFormatManager().Value().Get(vobj,entry); + return GetFormatManager().Value().Get(vobj,entry, use_dynamic); } void @@ -1808,15 +1821,17 @@ Debugger::Formatting::ValueFormats::GetCount() bool Debugger::Formatting::GetSummaryFormat(ValueObject& vobj, + lldb::DynamicValueType use_dynamic, lldb::SummaryFormatSP& entry) { - return GetFormatManager().Get(vobj, entry); + return GetFormatManager().Get(vobj, entry, use_dynamic); } bool Debugger::Formatting::GetSyntheticFilter(ValueObject& vobj, + lldb::DynamicValueType use_dynamic, lldb::SyntheticChildrenSP& entry) { - return GetFormatManager().Get(vobj, entry); + return GetFormatManager().Get(vobj, entry, use_dynamic); } bool diff --git a/lldb/source/Core/FormatClasses.cpp b/lldb/source/Core/FormatClasses.cpp index 046cc705d00e..480970872536 100644 --- a/lldb/source/Core/FormatClasses.cpp +++ b/lldb/source/Core/FormatClasses.cpp @@ -21,6 +21,7 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatClasses.h" #include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/ClangASTType.h" #include "lldb/Target/StackFrame.h" @@ -121,8 +122,29 @@ StringSummaryFormat::GetDescription() std::string ScriptSummaryFormat::FormatObject(lldb::ValueObjectSP object) { + lldb::ValueObjectSP target_object; + if (object->GetIsExpressionResult() && + ClangASTContext::IsPointerType(object->GetClangType()) && + object->GetValue().GetValueType() == Value::eValueTypeHostAddress) + { + // when using the expression parser, an additional layer of "frozen data" + // can be created, which is basically a byte-exact copy of the data returned + // by the expression, but in host memory. because Python code might need to read + // into the object memory in non-obvious ways, we need to hand it the target version + // of the expression output + lldb::addr_t tgt_address = object->GetValueAsUnsigned(); + target_object = ValueObjectConstResult::Create (object->GetExecutionContextScope(), + object->GetClangAST(), + object->GetClangType(), + object->GetName(), + tgt_address, + eAddressTypeLoad, + object->GetUpdatePoint().GetProcessSP()->GetAddressByteSize()); + } + else + target_object = object; return std::string(ScriptInterpreterPython::CallPythonScriptFunction(m_function_name.c_str(), - object).c_str()); + target_object).c_str()); } std::string @@ -171,7 +193,26 @@ m_python_class(pclass) return; } - m_interpreter = be->GetUpdatePoint().GetTargetSP()->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (be->GetIsExpressionResult() && + ClangASTContext::IsPointerType(be->GetClangType()) && + be->GetValue().GetValueType() == Value::eValueTypeHostAddress) + { + // when using the expression parser, an additional layer of "frozen data" + // can be created, which is basically a byte-exact copy of the data returned + // by the expression, but in host memory. because Python code might need to read + // into the object memory in non-obvious ways, we need to hand it the target version + // of the expression output + lldb::addr_t tgt_address = be->GetValueAsUnsigned(); + m_backend = ValueObjectConstResult::Create (be->GetExecutionContextScope(), + be->GetClangAST(), + be->GetClangType(), + be->GetName(), + tgt_address, + eAddressTypeLoad, + be->GetUpdatePoint().GetProcessSP()->GetAddressByteSize()); + } + + m_interpreter = m_backend->GetUpdatePoint().GetTargetSP()->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); if (m_interpreter == NULL) m_wrapper = NULL; diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index fce3646c47e4..77ee5c14e529 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -39,6 +39,7 @@ #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Target.h" @@ -76,6 +77,7 @@ ValueObject::ValueObject (ValueObject &parent) : m_deref_valobj(NULL), m_format (eFormatDefault), m_last_format_mgr_revision(0), + m_last_format_mgr_dynamic(lldb::eNoDynamicValues), m_last_summary_format(), m_forced_summary_format(), m_last_value_format(), @@ -91,6 +93,7 @@ ValueObject::ValueObject (ValueObject &parent) : m_is_bitfield_for_scalar(false), m_is_expression_path_child(false), m_is_child_at_offset(false), + m_is_expression_result(false), m_dump_printable_counter(0) { m_manager->ManageObject(this); @@ -120,6 +123,7 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope) : m_deref_valobj(NULL), m_format (eFormatDefault), m_last_format_mgr_revision(0), + m_last_format_mgr_dynamic(lldb::eNoDynamicValues), m_last_summary_format(), m_forced_summary_format(), m_last_value_format(), @@ -135,6 +139,7 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope) : m_is_bitfield_for_scalar(false), m_is_expression_path_child(false), m_is_child_at_offset(false), + m_is_expression_result(false), m_dump_printable_counter(0) { m_manager = new ValueObjectManager(); @@ -150,10 +155,16 @@ ValueObject::~ValueObject () bool ValueObject::UpdateValueIfNeeded (bool update_format) +{ + return UpdateValueIfNeeded(m_last_format_mgr_dynamic, update_format); +} + +bool +ValueObject::UpdateValueIfNeeded (lldb::DynamicValueType use_dynamic, bool update_format) { if (update_format) - UpdateFormatsIfNeeded(); + UpdateFormatsIfNeeded(use_dynamic); // If this is a constant value, then our success is predicated on whether // we have an error or not @@ -204,7 +215,7 @@ ValueObject::UpdateValueIfNeeded (bool update_format) } void -ValueObject::UpdateFormatsIfNeeded() +ValueObject::UpdateFormatsIfNeeded(lldb::DynamicValueType use_dynamic) { LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); if (log) @@ -217,7 +228,8 @@ ValueObject::UpdateFormatsIfNeeded() ClearCustomSummaryFormat(); m_summary_str.clear(); } - if (m_last_format_mgr_revision != Debugger::Formatting::ValueFormats::GetCurrentRevision()) + if ( (m_last_format_mgr_revision != Debugger::Formatting::ValueFormats::GetCurrentRevision()) || + m_last_format_mgr_dynamic != use_dynamic) { if (m_last_summary_format.get()) m_last_summary_format.reset((StringSummaryFormat*)NULL); @@ -228,11 +240,12 @@ ValueObject::UpdateFormatsIfNeeded() m_synthetic_value = NULL; - Debugger::Formatting::ValueFormats::Get(*this, m_last_value_format); - Debugger::Formatting::GetSummaryFormat(*this, m_last_summary_format); - Debugger::Formatting::GetSyntheticFilter(*this, m_last_synthetic_filter); + Debugger::Formatting::ValueFormats::Get(*this, use_dynamic, m_last_value_format); + Debugger::Formatting::GetSummaryFormat(*this, use_dynamic, m_last_summary_format); + Debugger::Formatting::GetSyntheticFilter(*this, use_dynamic, m_last_synthetic_filter); m_last_format_mgr_revision = Debugger::Formatting::ValueFormats::GetCurrentRevision(); + m_last_format_mgr_dynamic = use_dynamic; ClearUserVisibleData(); } @@ -241,14 +254,14 @@ ValueObject::UpdateFormatsIfNeeded() DataExtractor & ValueObject::GetDataExtractor () { - UpdateValueIfNeeded(); + UpdateValueIfNeeded(false); return m_data; } const Error & ValueObject::GetError() { - UpdateValueIfNeeded(); + UpdateValueIfNeeded(false); return m_error; } @@ -261,7 +274,7 @@ ValueObject::GetName() const const char * ValueObject::GetLocationAsCString () { - if (UpdateValueIfNeeded()) + if (UpdateValueIfNeeded(false)) { if (m_location_str.empty()) { @@ -358,7 +371,7 @@ ValueObject::GetChildAtIndex (uint32_t idx, bool can_create) ValueObjectSP child_sp; // We may need to update our value if we are dynamic if (IsPossibleDynamicType ()) - UpdateValueIfNeeded(); + UpdateValueIfNeeded(false); if (idx < GetNumChildren()) { // Check if we have already made the child value object? @@ -395,7 +408,7 @@ ValueObject::GetChildMemberWithName (const ConstString &name, bool can_create) // We may need to update our value if we are dynamic if (IsPossibleDynamicType ()) - UpdateValueIfNeeded(); + UpdateValueIfNeeded(false); std::vector child_indexes; clang::ASTContext *clang_ast = GetClangAST(); @@ -519,7 +532,7 @@ ValueObject::CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int3 const char * ValueObject::GetSummaryAsCString () { - if (UpdateValueIfNeeded ()) + if (UpdateValueIfNeeded (m_last_format_mgr_dynamic, true)) { if (m_summary_str.empty()) { @@ -775,7 +788,7 @@ const char * ValueObject::GetObjectDescription () { - if (!UpdateValueIfNeeded ()) + if (!UpdateValueIfNeeded (m_last_format_mgr_dynamic, true)) return NULL; if (!m_object_desc_str.empty()) @@ -826,7 +839,7 @@ ValueObject::GetValueAsCString () // If our byte size is zero this is an aggregate type that has children if (ClangASTContext::IsAggregateType (GetClangType()) == false) { - if (UpdateValueIfNeeded()) + if (UpdateValueIfNeeded(true)) { if (m_value_str.empty()) { @@ -841,7 +854,7 @@ ValueObject::GetValueAsCString () clang_type_t clang_type = GetClangType (); if (clang_type) { - if (m_last_value_format) + if (m_format == lldb::eFormatDefault && m_last_value_format) { m_value_str = m_last_value_format->FormatObject(GetSP()); } @@ -905,6 +918,24 @@ ValueObject::GetValueAsCString () return m_value_str.c_str(); } +// if > 8bytes, 0 is returned. this method should mostly be used +// to read address values out of pointers +unsigned long long +ValueObject::GetValueAsUnsigned() +{ + // If our byte size is zero this is an aggregate type that has children + if (ClangASTContext::IsAggregateType (GetClangType()) == false) + { + if (UpdateValueIfNeeded(true)) + { + uint32_t offset = 0; + return m_data.GetMaxU64(&offset, + m_data.GetByteSize()); + } + } + return 0; +} + // this call should only return pointers to data that needs no special memory management // (either because they are hardcoded strings, or because they are backed by some other // object); returning any new()-ed or malloc()-ed data here, will lead to leaks! @@ -1092,7 +1123,7 @@ ValueObject::DumpPrintableRepresentation(Stream& s, addr_t ValueObject::GetAddressOf (AddressType &address_type, bool scalar_is_load_address) { - if (!UpdateValueIfNeeded()) + if (!UpdateValueIfNeeded(false)) return LLDB_INVALID_ADDRESS; switch (m_value.GetValueType()) @@ -1124,7 +1155,7 @@ ValueObject::GetPointerValue (AddressType &address_type, bool scalar_is_load_add lldb::addr_t address = LLDB_INVALID_ADDRESS; address_type = eAddressTypeInvalid; - if (!UpdateValueIfNeeded()) + if (!UpdateValueIfNeeded(false)) return address; switch (m_value.GetValueType()) @@ -1161,7 +1192,7 @@ ValueObject::SetValueFromCString (const char *value_str) { // Make sure our value is up to date first so that our location and location // type is valid. - if (!UpdateValueIfNeeded()) + if (!UpdateValueIfNeeded(false)) return false; uint32_t count = 0; @@ -1256,7 +1287,8 @@ ValueObject::Write () lldb::LanguageType ValueObject::GetObjectRuntimeLanguage () { - return ClangASTType::GetMinimumLanguage (GetClangType()); + return ClangASTType::GetMinimumLanguage (GetClangAST(), + GetClangType()); } void @@ -1521,7 +1553,7 @@ ValueObject::CalculateSyntheticValue (lldb::SyntheticValueType use_synthetic) if (use_synthetic == lldb::eNoSyntheticFilter) return; - UpdateFormatsIfNeeded(); + UpdateFormatsIfNeeded(m_last_format_mgr_dynamic); if (m_last_synthetic_filter.get() == NULL) return; @@ -1601,7 +1633,7 @@ ValueObject::GetSyntheticValue (SyntheticValueType use_synthetic) if (use_synthetic == lldb::eNoSyntheticFilter) return GetSP(); - UpdateFormatsIfNeeded(); + UpdateFormatsIfNeeded(m_last_format_mgr_dynamic); if (m_last_synthetic_filter.get() == NULL) return GetSP(); @@ -2537,7 +2569,7 @@ ValueObject::DumpValueObject { if (valobj) { - bool update_success = valobj->UpdateValueIfNeeded (); + bool update_success = valobj->UpdateValueIfNeeded (use_dynamic, true); if (update_success && use_dynamic != lldb::eNoDynamicValues) { @@ -2566,7 +2598,33 @@ ValueObject::DumpValueObject // Always show the type for the top level items. if (show_types || (curr_depth == 0 && !flat_output)) - s.Printf("(%s) ", valobj->GetTypeName().AsCString("")); + { + s.Printf("(%s", valobj->GetTypeName().AsCString("")); + if (use_dynamic != lldb::eNoDynamicValues && + strcmp(valobj->GetTypeName().AsCString("NULL"), "id") == 0) + { + Process* process = valobj->GetUpdatePoint().GetProcessSP().get(); + if (process == NULL) + s.Printf(") "); + else + { + ObjCLanguageRuntime *runtime = process->GetObjCLanguageRuntime(); + if (runtime == NULL) + s.Printf(") "); + else + { + ObjCLanguageRuntime::ObjCISA isa = runtime->GetISA(*valobj); + if (!runtime->IsValidISA(isa)) + s.Printf(") "); + else + s.Printf(", dynamic type: %s) ", + runtime->GetActualTypeName(isa).GetCString()); + } + } + } + else + s.Printf(") "); + } if (flat_output) @@ -2758,7 +2816,7 @@ ValueObject::CreateConstantValue (const ConstString &name) { ValueObjectSP valobj_sp; - if (UpdateValueIfNeeded() && m_error.Success()) + if (UpdateValueIfNeeded(false) && m_error.Success()) { ExecutionContextScope *exe_scope = GetExecutionContextScope(); if (exe_scope) @@ -2957,7 +3015,6 @@ ValueObject::CastPointerType (const char *name, TypeSP &type_sp) return valobj_sp; } - ValueObject::EvaluationPoint::EvaluationPoint () : m_thread_id (LLDB_INVALID_UID), m_stop_id (0) diff --git a/lldb/source/Core/ValueObjectChild.cpp b/lldb/source/Core/ValueObjectChild.cpp index 8ddf195b51f3..2ded7b50e82a 100644 --- a/lldb/source/Core/ValueObjectChild.cpp +++ b/lldb/source/Core/ValueObjectChild.cpp @@ -97,7 +97,7 @@ ValueObjectChild::UpdateValue () ValueObject* parent = m_parent; if (parent) { - if (parent->UpdateValueIfNeeded()) + if (parent->UpdateValueIfNeeded(false)) { m_value.SetContext(Value::eContextTypeClangType, m_clang_type); diff --git a/lldb/source/Core/ValueObjectDynamicValue.cpp b/lldb/source/Core/ValueObjectDynamicValue.cpp index 885e68fd3bd8..beecaa95238e 100644 --- a/lldb/source/Core/ValueObjectDynamicValue.cpp +++ b/lldb/source/Core/ValueObjectDynamicValue.cpp @@ -60,7 +60,7 @@ ValueObjectDynamicValue::GetClangType () ConstString ValueObjectDynamicValue::GetTypeName() { - const bool success = UpdateValueIfNeeded(); + const bool success = UpdateValueIfNeeded(false); if (success && m_type_sp) return ClangASTType::GetConstTypeName (GetClangType()); else @@ -70,7 +70,7 @@ ValueObjectDynamicValue::GetTypeName() uint32_t ValueObjectDynamicValue::CalculateNumChildren() { - const bool success = UpdateValueIfNeeded(); + const bool success = UpdateValueIfNeeded(false); if (success && m_type_sp) return ClangASTContext::GetNumChildren (GetClangAST (), GetClangType(), true); else @@ -90,7 +90,7 @@ ValueObjectDynamicValue::GetClangAST () size_t ValueObjectDynamicValue::GetByteSize() { - const bool success = UpdateValueIfNeeded(); + const bool success = UpdateValueIfNeeded(false); if (success && m_type_sp) return m_value.GetValueByteSize(GetClangAST(), NULL); else @@ -109,7 +109,7 @@ ValueObjectDynamicValue::UpdateValue () SetValueIsValid (false); m_error.Clear(); - if (!m_parent->UpdateValueIfNeeded()) + if (!m_parent->UpdateValueIfNeeded(false)) { // The dynamic value failed to get an error, pass the error along if (m_error.Success() && m_parent->GetError().Fail()) diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h index c0d8dcdfc94d..431787729558 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h @@ -63,6 +63,30 @@ public: virtual lldb::ThreadPlanSP GetStepThroughTrampolinePlan (Thread &thread, bool stop_others); + virtual bool + IsValidISA(ObjCISA isa) + { + return false; + } + + virtual ObjCISA + GetISA(ValueObject& valobj) + { + return 0; + } + + virtual ConstString + GetActualTypeName(ObjCISA isa) + { + return ConstString(NULL); + } + + virtual ObjCISA + GetParentClass(ObjCISA isa) + { + return 0; + } + //------------------------------------------------------------------ // Static Functions //------------------------------------------------------------------ diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h index 5596b179929d..0e7d87ce2f2b 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h @@ -72,6 +72,31 @@ public: { return eAppleObjC_V1; } + + virtual bool + IsValidISA(ObjCISA isa) + { + return false; + } + + virtual ObjCISA + GetISA(ValueObject& valobj) + { + return 0; + } + + virtual ConstString + GetActualTypeName(ObjCISA isa) + { + return ConstString(NULL); + } + + virtual ObjCISA + GetParentClass(ObjCISA isa) + { + return 0; + } + protected: private: diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index 9f2a93c9e06b..753d1e290552 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -563,4 +563,90 @@ AppleObjCRuntimeV2::GetByteOffsetForIvar (ClangASTType &parent_ast_type, const c return ivar_offset; } +lldb_private::ObjCLanguageRuntime::ObjCISA +AppleObjCRuntimeV2::GetISA(ValueObject& valobj) +{ + if (ClangASTType::GetMinimumLanguage(valobj.GetClangAST(),valobj.GetClangType()) != lldb::eLanguageTypeObjC) + return 0; + + uint32_t offset = 0; + uint64_t isa_pointer = valobj.GetDataExtractor().GetPointer(&offset); + + uint8_t pointer_size = valobj.GetUpdatePoint().GetProcessSP()->GetAddressByteSize(); + + Error error; + lldb_private::ObjCLanguageRuntime::ObjCISA isa = + valobj.GetUpdatePoint().GetProcessSP()->ReadUnsignedIntegerFromMemory(isa_pointer, + pointer_size, + 0, + error); + return isa; +} + +ConstString +AppleObjCRuntimeV2::GetActualTypeName(lldb_private::ObjCLanguageRuntime::ObjCISA isa) +{ + if (!IsValidISA(isa)) + return ConstString(NULL); + + uint8_t pointer_size = m_process->GetAddressByteSize(); + Error error; + lldb::addr_t rw_pointer = isa + (4 * pointer_size); + //printf("rw_pointer: %llx\n", rw_pointer); + + uint64_t data_pointer = m_process->ReadUnsignedIntegerFromMemory(rw_pointer, + pointer_size, + 0, + error); + if (error.Fail()) + return ConstString("unknown"); + + data_pointer += 8; + //printf("data_pointer: %llx\n", data_pointer); + uint64_t ro_pointer = m_process->ReadUnsignedIntegerFromMemory(data_pointer, + pointer_size, + 0, + error); + if (error.Fail()) + return ConstString("unknown"); + + ro_pointer += 12; + if (pointer_size == 8) + ro_pointer += 4; + ro_pointer += pointer_size; + //printf("ro_pointer: %llx\n", ro_pointer); + uint64_t name_pointer = m_process->ReadUnsignedIntegerFromMemory(ro_pointer, + pointer_size, + 0, + error); + if (error.Fail()) + return ConstString("unknown"); + + //printf("name_pointer: %llx\n", name_pointer); + char* cstr = new char[512]; + if (m_process->ReadCStringFromMemory(name_pointer, cstr, 512) > 0) + return ConstString(cstr); + else + return ConstString("unknown"); +} + +lldb_private::ObjCLanguageRuntime::ObjCISA +AppleObjCRuntimeV2::GetParentClass(lldb_private::ObjCLanguageRuntime::ObjCISA isa) +{ + if (!IsValidISA(isa)) + return 0; + + uint8_t pointer_size = m_process->GetAddressByteSize(); + Error error; + lldb::addr_t parent_pointer = isa + pointer_size; + //printf("rw_pointer: %llx\n", rw_pointer); + + uint64_t parent_isa = m_process->ReadUnsignedIntegerFromMemory(parent_pointer, + pointer_size, + 0, + error); + if (error.Fail()) + return 0; + return parent_isa; +} diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h index b6bd5b8fbd51..63bb994f28d7 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h @@ -76,7 +76,21 @@ public: virtual size_t GetByteOffsetForIvar (ClangASTType &parent_qual_type, const char *ivar_name); - + + virtual bool + IsValidISA(ObjCISA isa) + { + return (isa != 0); + } + + virtual ObjCISA + GetISA(ValueObject& valobj); + + virtual ConstString + GetActualTypeName(ObjCISA isa); + + virtual ObjCISA + GetParentClass(ObjCISA isa); protected: diff --git a/lldb/source/Symbol/ClangASTType.cpp b/lldb/source/Symbol/ClangASTType.cpp index 1de409d8cff5..6f57347b2569 100644 --- a/lldb/source/Symbol/ClangASTType.cpp +++ b/lldb/source/Symbol/ClangASTType.cpp @@ -159,11 +159,13 @@ ClangASTType::GetEncoding (uint32_t &count) lldb::LanguageType ClangASTType::GetMinimumLanguage () { - return ClangASTType::GetMinimumLanguage (m_type); + return ClangASTType::GetMinimumLanguage (m_ast, + m_type); } lldb::LanguageType -ClangASTType::GetMinimumLanguage (lldb::clang_type_t clang_type) +ClangASTType::GetMinimumLanguage (clang::ASTContext *ctx, + lldb::clang_type_t clang_type) { if (clang_type == NULL) return lldb::eLanguageTypeC; @@ -182,6 +184,8 @@ ClangASTType::GetMinimumLanguage (lldb::clang_type_t clang_type) return lldb::eLanguageTypeObjC; if (pointee_type->isObjCClassType()) return lldb::eLanguageTypeObjC; + if (pointee_type.getTypePtr() == ctx->ObjCBuiltinIdTy.getTypePtr()) + return lldb::eLanguageTypeObjC; } else { @@ -238,7 +242,8 @@ ClangASTType::GetMinimumLanguage (lldb::clang_type_t clang_type) } break; case clang::Type::Typedef: - return GetMinimumLanguage(llvm::cast(qual_type)->getDecl()->getUnderlyingType().getAsOpaquePtr()); + return GetMinimumLanguage(ctx, + llvm::cast(qual_type)->getDecl()->getUnderlyingType().getAsOpaquePtr()); } } return lldb::eLanguageTypeC; diff --git a/lldb/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py b/lldb/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py index ac370f70a184..fea3e669abdf 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py +++ b/lldb/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py @@ -286,7 +286,7 @@ class DataFormatterTestCase(TestBase): substrs = ['ACircle', 'ARectangle', 'ACircle', - 'ARectangleStar']) + 'ARectangle']) # Check that abruptly deleting an enabled category does not crash us self.runCmd("type category delete RectangleCategory") diff --git a/lldb/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py b/lldb/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py index e17b6fc195b1..31a64a439e8e 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py +++ b/lldb/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py @@ -164,7 +164,7 @@ class DataFormatterTestCase(TestBase): 'inline = ', 'explicit = ', 'content = ', - 'a very much boring task to write a string this way!!\\xe4\\x8c\\xb3']) + 'a very much boring task to write a string this way!!\\xcf\\x83']) self.expect('frame variable str10 -P 1 -Y', substrs = ['mutable =', @@ -215,7 +215,7 @@ class DataFormatterTestCase(TestBase): self.expect('frame variable str8', substrs = ['hasVeryLongExtensionThisTime']) self.expect('frame variable str9', - substrs = ['a very much boring task to write a string this way!!\\xe4\\x8c\\xb3']) + substrs = ['a very much boring task to write a string this way!!\\xcf\\x83']) self.expect('frame variable str10', substrs = ['This is a Unicode string \\xcf\\x83 number 4 right here']) self.expect('frame variable str11', @@ -224,6 +224,25 @@ class DataFormatterTestCase(TestBase): substrs = ['a.out']) self.expect('frame variable str12', substrs = ['Process Name: a.out Process Id:']) + self.expect('frame variable dyn_test', matching=False, + substrs = ['Process Name: a.out Process Id:']) + self.expect('frame variable dyn_test -d run-target', + substrs = ['(id, dynamic type:', + 'Process Name: a.out Process Id:']) + + + # check that we can format stuff out of the expression parser + self.expect('expression ((id)@"Hello")', matching=False, + substrs = ['Hello']) + + self.expect('expression -d true -- ((id)@"Hello")', + substrs = ['Hello']) + + self.expect('expr -d true -- label1', + substrs = ['Process Name']) + + self.expect('expr -d true -- @"Hello"', + substrs = ['Hello']) if __name__ == '__main__': import atexit diff --git a/lldb/test/functionalities/data-formatter/data-formatter-objc/main.m b/lldb/test/functionalities/data-formatter/data-formatter-objc/main.m index 132d70b53824..326e49d5b3d3 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-objc/main.m +++ b/lldb/test/functionalities/data-formatter/data-formatter-objc/main.m @@ -117,6 +117,8 @@ int main (int argc, const char * argv[]) NSString *processID = [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]]; NSString *str12 = [NSString stringWithFormat:@"%@ %@ %@ %@", label1, processName, label2, processID]; + id dyn_test = str12; + // Set break point at this line. [pool drain]; return 0;