Index: include/lldb/Core/ValueObject.h =================================================================== --- include/lldb/Core/ValueObject.h +++ include/lldb/Core/ValueObject.h @@ -374,6 +374,8 @@ virtual bool IsPointerOrReferenceType(); + virtual bool IsPointerLikeObject(); + virtual bool IsPossibleDynamicType(); bool IsNilReference(); Index: include/lldb/DataFormatters/ValueObjectPrinter.h =================================================================== --- include/lldb/DataFormatters/ValueObjectPrinter.h +++ include/lldb/DataFormatters/ValueObjectPrinter.h @@ -83,6 +83,8 @@ bool IsInstancePointer(); + bool IsPointerLikeObject(); + bool IsAggregate(); bool PrintValidationMarkerIfNeeded(); @@ -148,6 +150,7 @@ LazyBool m_is_ptr; LazyBool m_is_ref; LazyBool m_is_aggregate; + LazyBool m_is_pointer_like; LazyBool m_is_instance_ptr; std::pair m_summary_formatter; std::string m_value; Index: include/lldb/Target/Language.h =================================================================== --- include/lldb/Target/Language.h +++ include/lldb/Target/Language.h @@ -1,5 +1,4 @@ -//===-- Language.h ---------------------------------------------------*- C++ -//-*-===// +//===-- Language.h ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -221,6 +220,10 @@ virtual void GetExceptionResolverDescription(bool catch_on, bool throw_on, Stream &s); + // Returns true if the specified value object is a class type what behaves + // like a pointer in the language (e.g. std::shared_ptr in c++). + virtual bool IsPointerLikeObject(ValueObject &valobj); + static void GetDefaultExceptionResolverDescription(bool catch_on, bool throw_on, Stream &s); Index: packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/TestDataFormatterStdUniquePtr.py =================================================================== --- packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/TestDataFormatterStdUniquePtr.py +++ packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/TestDataFormatterStdUniquePtr.py @@ -40,7 +40,7 @@ self.expect("frame variable ndp", substrs=['ndp = nullptr']) self.expect("frame variable idp", substrs=['idp = 0x', 'object = 456', 'deleter = ', 'a = 1', 'b = 2']) self.expect("frame variable sdp", substrs=['sdp = 0x', 'object = "baz"', 'deleter = ', 'a = 3', 'b = 4']) - + self.assertEqual(123, frame.GetValueForVariablePath("iup.object").GetValueAsUnsigned()) self.assertFalse(frame.GetValueForVariablePath("iup.deleter").IsValid()) @@ -59,3 +59,31 @@ self.assertTrue(sdp_deleter.IsValid()) self.assertEqual(3, sdp_deleter.GetChildMemberWithName("a").GetValueAsUnsigned()) self.assertEqual(4, sdp_deleter.GetChildMemberWithName("b").GetValueAsUnsigned()) + + @skipIfFreeBSD + @skipIfWindows # libstdcpp not ported to Windows + @skipIfDarwin # doesn't compile on Darwin + def test_recursive_unique_ptr(self): + # Tests that LLDB can handle when we have a loop in the unique_ptr + # reference chain and that it correctly handles the different options + # for the frame variable command in this case. + self.build() + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp( + self, "Set break point at this line.") + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', 'stop reason = breakpoint']) + + self.expect("frame variable f1->fp", + substrs=['data = 2', 'fp = 0x']) + self.expect("frame variable --ptr-depth=2 f1->fp", + substrs=['data = 2', 'fp = 0x', 'object = {', 'data = 1']) + self.expect("frame variable f1->fp->data", + substrs=['f1->fp->data = 2']) + self.expect("frame variable *f1->fp", + substrs=['(Foo) *f1->fp', 'data = 2', 'fp = 0x']) + Index: packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/main.cpp =================================================================== --- packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/main.cpp +++ packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/main.cpp @@ -8,6 +8,11 @@ int b; }; +struct Foo { + int data; + std::unique_ptr fp; +}; + int main() { std::unique_ptr nup; std::unique_ptr iup(new int{123}); @@ -18,5 +23,11 @@ std::unique_ptr sdp(new std::string("baz"), Deleter{3, 4}); + // Set up a structure where we have a loop in the unique_ptr chain. + Foo* f1 = new Foo{1}; + Foo* f2 = new Foo{2}; + f1->fp.reset(f2); + f2->fp.reset(f1); + return 0; // Set break point at this line. } Index: source/Core/ValueObject.cpp =================================================================== --- source/Core/ValueObject.cpp +++ source/Core/ValueObject.cpp @@ -1709,6 +1709,13 @@ return GetCompilerType().IsPointerOrReferenceType(); } +bool ValueObject::IsPointerLikeObject() { + Language *language = Language::FindPlugin(GetObjectRuntimeLanguage()); + if (!language) + return false; + return language->IsPointerLikeObject(*this); +} + bool ValueObject::IsPossibleDynamicType() { ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); @@ -2889,6 +2896,8 @@ child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid, language_flags); } + } else if (IsPointerLikeObject() && HasSyntheticValue()) { + m_deref_valobj = GetSyntheticValue()->GetChildAtIndex(0, true).get(); } if (m_deref_valobj) { Index: source/DataFormatters/ValueObjectPrinter.cpp =================================================================== --- source/DataFormatters/ValueObjectPrinter.cpp +++ source/DataFormatters/ValueObjectPrinter.cpp @@ -63,6 +63,7 @@ m_is_ptr = eLazyBoolCalculate; m_is_ref = eLazyBoolCalculate; m_is_aggregate = eLazyBoolCalculate; + m_is_pointer_like = eLazyBoolCalculate; m_is_instance_ptr = eLazyBoolCalculate; m_summary_formatter = {nullptr, false}; m_value.assign(""); @@ -208,6 +209,13 @@ return m_is_aggregate == eLazyBoolYes; } +bool ValueObjectPrinter::IsPointerLikeObject() { + if (m_is_pointer_like == eLazyBoolCalculate) + m_is_pointer_like = + m_valobj->IsPointerLikeObject() ? eLazyBoolYes : eLazyBoolNo; + return m_is_pointer_like == eLazyBoolYes; +} + bool ValueObjectPrinter::IsInstancePointer() { // you need to do this check on the value's clang type if (m_is_instance_ptr == eLazyBoolCalculate) @@ -503,8 +511,8 @@ bool ValueObjectPrinter::ShouldPrintChildren( bool is_failed_description, DumpValueObjectOptions::PointerDepth &curr_ptr_depth) { - const bool is_ref = IsRef(); const bool is_ptr = IsPtr(); + const bool is_ref_or_ptr_like = IsRef() || IsPointerLikeObject(); const bool is_uninit = IsUninitialized(); if (is_uninit) @@ -529,7 +537,7 @@ // Use a new temporary pointer depth in case we override the // current pointer depth below... - if (is_ptr || is_ref) { + if (is_ptr || is_ref_or_ptr_like) { // We have a pointer or reference whose value is an address. // Make sure that address is not NULL AddressType ptr_address_type; @@ -538,7 +546,7 @@ const bool is_root_level = m_curr_depth == 0; - if (is_ref && is_root_level) { + if (is_ref_or_ptr_like && is_root_level) { // If this is the root object (depth is zero) that we are showing // and it is a reference, and no pointer depth has been supplied // print out what it references. Don't do this at deeper depths @@ -584,7 +592,8 @@ const DumpValueObjectOptions::PointerDepth &curr_ptr_depth) { const uint32_t consumed_depth = (!m_options.m_pointer_as_array) ? 1 : 0; const bool does_consume_ptr_depth = - ((IsPtr() && !m_options.m_pointer_as_array) || IsRef()); + ((IsPtr() && !m_options.m_pointer_as_array) || IsRef() || + IsPointerLikeObject()); DumpValueObjectOptions child_options(m_options); child_options.SetFormat(m_options.m_format) Index: source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h =================================================================== --- source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -101,6 +101,8 @@ HardcodedFormatters::HardcodedSyntheticFinder GetHardcodedSynthetics() override; + bool IsPointerLikeObject(ValueObject &valobj) override; + //------------------------------------------------------------------ // Static Functions //------------------------------------------------------------------ Index: source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp =================================================================== --- source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1164,3 +1164,12 @@ return g_formatters; } + +bool CPlusPlusLanguage::IsPointerLikeObject(ValueObject &valobj) { + static RegularExpression unique_ptr_regex("^std::unique_ptr<.+>(( )?&)?$"); + + llvm::StringRef name(valobj.GetCompilerType().GetTypeName().GetStringRef()); + if (unique_ptr_regex.Execute(name)) + return true; + return false; +} Index: source/Target/Language.cpp =================================================================== --- source/Target/Language.cpp +++ source/Target/Language.cpp @@ -413,6 +413,9 @@ s.Printf("Exception breakpoint (catch: %s throw: %s)", catch_on ? "on" : "off", throw_on ? "on" : "off"); } + +bool Language::IsPointerLikeObject(ValueObject &valobj) { return false; } + //---------------------------------------------------------------------- // Constructor //---------------------------------------------------------------------- Index: source/Target/StackFrame.cpp =================================================================== --- source/Target/StackFrame.cpp +++ source/Target/StackFrame.cpp @@ -604,8 +604,10 @@ // Calculate the next separator index ahead of time ValueObjectSP child_valobj_sp; const char separator_type = var_expr[0]; + bool expr_is_ptr = false; switch (separator_type) { case '-': + expr_is_ptr = true; if (var_expr.size() >= 2 && var_expr[1] != '>') return ValueObjectSP(); @@ -622,11 +624,26 @@ return ValueObjectSP(); } } + + // If we have operator-> and it is a pointer like value object then we + // dereference it and then handle it as operator. + if (valobj_sp->IsPointerLikeObject()) { + Error deref_error; + ValueObjectSP deref_sp = valobj_sp->Dereference(deref_error); + if (deref_sp && error.Success()) { + valobj_sp = deref_sp; + expr_is_ptr = false; + } else { + error.SetErrorStringWithFormat( + "Failed to dereference a pointer like object: %s", + deref_error.AsCString()); + return ValueObjectSP(); + } + } + var_expr = var_expr.drop_front(); // Remove the '-' LLVM_FALLTHROUGH; case '.': { - const bool expr_is_ptr = var_expr[0] == '>'; - var_expr = var_expr.drop_front(); // Remove the '.' or '>' separator_idx = var_expr.find_first_of(".-["); ConstString child_name(var_expr.substr(0, var_expr.find_first_of(".-[")));