[lldb] Add deref support to libc++ unique_ptr synthetic
authorDave Lee <davelee.com@gmail.com>
Thu, 25 Feb 2021 21:27:22 +0000 (13:27 -0800)
committerDave Lee <davelee.com@gmail.com>
Fri, 26 Feb 2021 15:39:01 +0000 (07:39 -0800)
Add frame variable dereference suppport to libc++ `std::unique_ptr`.

This change allows for commands like `v *thing_up` and `v thing_up->m_id`. These commands now work the same way they would with raw pointers, and as they would with expression. This is done by adding an unaccounted for child member named `$$dereference$$`.

Without this change, the command would have to be written as `v *thing_up.__value_` or v thing_up.__value_->m_id` which exposes internal structure and is more clumsy to type.

Additionally, the existing tests were updated. See also https://reviews.llvm.org/D97165 which added deref support for `std::shared_ptr`.

Differential Revision: https://reviews.llvm.org/D97524

lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp

index 925076f..8eda422 100644 (file)
@@ -463,7 +463,7 @@ lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator(
 
 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
     LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
-    : SyntheticChildrenFrontEnd(*valobj_sp), m_compressed_pair_sp() {
+    : SyntheticChildrenFrontEnd(*valobj_sp) {
   if (valobj_sp)
     Update();
 }
@@ -480,19 +480,27 @@ lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator(
 
 size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
     CalculateNumChildren() {
-  return (m_compressed_pair_sp ? 1 : 0);
+  return (m_value_ptr_sp ? 1 : 0);
 }
 
 lldb::ValueObjectSP
 lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex(
     size_t idx) {
-  if (!m_compressed_pair_sp)
+  if (!m_value_ptr_sp)
     return lldb::ValueObjectSP();
 
-  if (idx != 0)
-    return lldb::ValueObjectSP();
+  if (idx == 0)
+    return m_value_ptr_sp;
 
-  return m_compressed_pair_sp;
+  if (idx == 1) {
+    Status status;
+    auto value_sp = m_value_ptr_sp->Dereference(status);
+    if (status.Success()) {
+      return value_sp;
+    }
+  }
+
+  return lldb::ValueObjectSP();
 }
 
 bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() {
@@ -505,7 +513,7 @@ bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() {
   if (!ptr_sp)
     return false;
 
-  m_compressed_pair_sp = GetValueOfLibCXXCompressedPair(*ptr_sp);
+  m_value_ptr_sp = GetValueOfLibCXXCompressedPair(*ptr_sp);
 
   return false;
 }
@@ -519,6 +527,8 @@ size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
     GetIndexOfChildWithName(ConstString name) {
   if (name == "__value_")
     return 0;
+  if (name == "$$dereference$$")
+    return 1;
   return UINT32_MAX;
 }
 
index 907025f..99e2065 100644 (file)
@@ -124,7 +124,7 @@ public:
   ~LibcxxUniquePtrSyntheticFrontEnd() override;
 
 private:
-  lldb::ValueObjectSP m_compressed_pair_sp;
+  lldb::ValueObjectSP m_value_ptr_sp;
 };
 
 SyntheticChildrenFrontEnd *
index b91e494..825513a 100644 (file)
@@ -3,45 +3,85 @@ Test lldb data formatter for libc++ std::unique_ptr.
 """
 
 
-
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 
-class LibcxUniquePtrDataFormatterTestCase(TestBase):
+
+class TestCase(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
     @add_test_categories(["libc++"])
-    def test_with_run_command(self):
-        """Test that that file and class static variables display correctly."""
+    def test_unique_ptr_variables(self):
+        """Test `frame variable` output for `std::unique_ptr` types."""
         self.build()
 
-        (self.target, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, '// break here',
-                lldb.SBFileSpec("main.cpp", False))
-
-        self.expect("frame variable up_empty",
-            substrs=['(std::unique_ptr<int, std::default_delete<int> >) up_empty = nullptr {',
-                               '__value_ = ',
-                               '}'])
-
-        self.expect("frame variable up_int",
-            substrs=['(std::unique_ptr<int, std::default_delete<int> >) up_int = 10 {',
-                               '__value_ = ',
-                               '}'])
-
-        self.expect("frame variable up_int_ref",
-            substrs=['(std::unique_ptr<int, std::default_delete<int> > &) up_int_ref = 10: {',
-                               '__value_ = ',
-                               '}'])
-
-        self.expect("frame variable up_int_ref_ref",
-            substrs=['(std::unique_ptr<int, std::default_delete<int> > &&) up_int_ref_ref = 10: {',
-                               '__value_ = ',
-                               '}'])
-
-        self.expect("frame variable up_str",
-            substrs=['up_str = "hello" {',
-                               '__value_ = ',
-                               '}'])
+        lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.cpp")
+        )
+
+        valobj = self.expect_var_path(
+            "up_empty",
+            type="std::unique_ptr<int, std::default_delete<int> >",
+            summary="nullptr",
+            children=[ValueCheck(name="__value_")],
+        )
+        self.assertEqual(
+            valobj.child[0].GetValueAsUnsigned(lldb.LLDB_INVALID_ADDRESS), 0
+        )
+
+        self.expect(
+            "frame variable *up_empty", substrs=["(int) *up_empty = <parent is NULL>"]
+        )
+
+        valobj = self.expect_var_path(
+            "up_int",
+            type="std::unique_ptr<int, std::default_delete<int> >",
+            summary="10",
+            children=[ValueCheck(name="__value_")],
+        )
+        self.assertNotEqual(valobj.child[0].unsigned, 0)
+
+        valobj = self.expect_var_path(
+            "up_int_ref",
+            type="std::unique_ptr<int, std::default_delete<int> > &",
+            summary="10",
+            children=[ValueCheck(name="__value_")],
+        )
+        self.assertNotEqual(valobj.child[0].unsigned, 0)
+
+        valobj = self.expect_var_path(
+            "up_int_ref_ref",
+            type="std::unique_ptr<int, std::default_delete<int> > &&",
+            summary="10",
+            children=[ValueCheck(name="__value_")],
+        )
+        self.assertNotEqual(valobj.child[0].unsigned, 0)
+
+        valobj = self.expect_var_path(
+            "up_str",
+            type="std::unique_ptr<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::default_delete<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >",
+            summary='"hello"',
+            children=[ValueCheck(name="__value_", summary='"hello"')],
+        )
+
+        valobj = self.expect_var_path(
+            "up_user", type="std::unique_ptr<User, std::default_delete<User> >"
+        )
+        self.assertRegex(valobj.summary, "^User @ 0x0*[1-9a-f][0-9a-f]+$")
+        self.assertNotEqual(valobj.child[0].unsigned, 0)
+
+        valobj = self.expect_var_path(
+            "*up_user",
+            type="User",
+            children=[
+                ValueCheck(name="id", value="30"),
+                ValueCheck(name="name", summary='"steph"'),
+            ],
+        )
+        self.assertEqual(str(valobj), '(User) *__value_ = (id = 30, name = "steph")')
+
+        self.expect_var_path("up_user->id", type="int", value="30")
+        self.expect_var_path("up_user->name", type="std::string", summary='"steph"')
index 4ccffe2..5201b2a 100644 (file)
@@ -1,13 +1,18 @@
-#include <cstdio>
 #include <memory>
 #include <string>
 
+struct User {
+  int id = 30;
+  std::string name = "steph";
+};
+
 int main() {
   std::unique_ptr<int> up_empty;
   std::unique_ptr<int> up_int = std::make_unique<int>(10);
   std::unique_ptr<std::string> up_str = std::make_unique<std::string>("hello");
   std::unique_ptr<int> &up_int_ref = up_int;
   std::unique_ptr<int> &&up_int_ref_ref = std::make_unique<int>(10);
+  std::unique_ptr<User> up_user = std::make_unique<User>();
 
   return 0; // break here
 }