[Lldb-commits] [lldb] 0478ead - [lldb/DataFormatters] Fix the `$$deference$$` synthetic child

Fred Riss via lldb-commits lldb-commits at lists.llvm.org
Tue Jan 21 13:37:01 PST 2020


Author: Fred Riss
Date: 2020-01-21T13:35:55-08:00
New Revision: 0478eadf73c191199cba12c85785cfafb8bfa174

URL: https://github.com/llvm/llvm-project/commit/0478eadf73c191199cba12c85785cfafb8bfa174
DIFF: https://github.com/llvm/llvm-project/commit/0478eadf73c191199cba12c85785cfafb8bfa174.diff

LOG: [lldb/DataFormatters] Fix the `$$deference$$` synthetic child

Summary:
The ValueObject code checks for a special `$$dereference$$` synthetic
child to allow formatter providers to implement a natural
dereferencing behavior in `frame variable` for objects like smart
pointers.

This support was broken when used directly throught the Python API and
not trhough `frame variable`. The reason is that
SBFrame.FindVariable() will return by default the synthetic variable
if it exists, while `frame variable` will not do this eagerly. The
code in `ValueObject::Dereference()` accounted for the latter but not
for the former. The fix is trivial. The test change includes
additional covergage for the already-working bahevior as it wasn't
covered by the testsuite before.

This commit also adds a short piece of documentatione explaining that
it is possible (even advisable) to provide this synthetic child
outstide of the range of the normal children.

Reviewers: jingham

Subscribers: lldb-commits

Tags: #lldb

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

Added: 
    

Modified: 
    lldb/docs/use/variable.rst
    lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/TestDataFormatterPythonSynth.py
    lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/fooSynthProvider.py
    lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/main.cpp
    lldb/source/Core/ValueObject.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/docs/use/variable.rst b/lldb/docs/use/variable.rst
index 13a56637ecea..4e3f25eb6a4a 100644
--- a/lldb/docs/use/variable.rst
+++ b/lldb/docs/use/variable.rst
@@ -846,7 +846,7 @@ adheres to a given interface (the word is italicized because Python has no
 explicit notion of interface, by that word we mean a given set of methods must
 be implemented by the Python class):
 
-::
+.. code-block:: python
 
    class SyntheticChildrenProvider:
       def __init__(self, valobj, internal_dict):
@@ -885,7 +885,28 @@ returning default no-children responses.
 
 If a synthetic child provider supplies a special child named
 ``$$dereference$$`` then it will be used when evaluating ``operator *`` and
-``operator ->`` in the frame variable command and related SB API functions.
+``operator ->`` in the frame variable command and related SB API
+functions. It is possible to declare this synthetic child without
+including it in the range of children displayed by LLDB. For example,
+this subset of a synthetic children provider class would allow the
+synthetic value to be dereferenced without actually showing any
+synthtic children in the UI:
+
+.. code-block:: python
+
+      class SyntheticChildrenProvider:
+          [...]
+          def num_children(self):
+              return 0
+          def get_child_index(self, name):
+              if name == '$$dereference$$':
+                  return 0
+              return -1
+          def get_child_at_index(self, index):
+              if index == 0:
+                  return <valobj resulting from dereference>
+              return None
+
 
 For examples of how synthetic children are created, you are encouraged to look
 at examples/synthetic in the LLDB trunk. Please, be aware that the code in

diff  --git a/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/TestDataFormatterPythonSynth.py b/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/TestDataFormatterPythonSynth.py
index 5f908f76b0ab..9d4759100ce2 100644
--- a/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/TestDataFormatterPythonSynth.py
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/TestDataFormatterPythonSynth.py
@@ -38,19 +38,9 @@ def setUp(self):
 
     def data_formatter_commands(self):
         """Test using Python synthetic children provider."""
-        self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
-
-        lldbutil.run_break_set_by_file_and_line(
-            self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True)
-
-        self.runCmd("run", RUN_SUCCEEDED)
-
-        process = self.dbg.GetSelectedTarget().GetProcess()
 
-        # The stop reason of the thread should be breakpoint.
-        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
-                    substrs=['stopped',
-                             'stop reason = breakpoint'])
+        _, process, thread, _ = lldbutil.run_to_line_breakpoint(
+            self, lldb.SBFileSpec("main.cpp"), self.line)
 
         # This is the function to remove the custom formats in order to have a
         # clean slate for the next test case.
@@ -72,6 +62,7 @@ def cleanup():
         # now set up the synth
         self.runCmd("script from fooSynthProvider import *")
         self.runCmd("type synth add -l fooSynthProvider foo")
+        self.runCmd("type synth add -l wrapfooSynthProvider wrapfoo")
         self.expect("type synthetic list foo", substrs=['fooSynthProvider'])
 
         # note that the value of fake_a depends on target byte order
@@ -147,6 +138,10 @@ def cleanup():
                     substrs=['r = 45',
                              'fake_a = %d' % fake_a_val,
                              'a = 12'])
+        self.expect("frame variable --ptr-depth 1 wrapper",
+                    substrs=['r = 45',
+                             'fake_a = %d' % fake_a_val,
+                             'a = 12'])
 
         # now add a filter.. it should fail
         self.expect("type filter add foo --child b --child j", error=True,
@@ -160,9 +155,24 @@ def cleanup():
                     substrs=['r = 45',
                              'fake_a = %d' % fake_a_val,
                              'a = 12'])
+        self.expect("frame variable --ptr-depth 1 wrapper",
+                    substrs=['r = 45',
+                             'fake_a = %d' % fake_a_val,
+                             'a = 12'])
+
+        # Test that the custom dereference operator for `wrapfoo` works through
+        # the Python API. The synthetic children provider gets queried at
+        # slightly 
diff erent times in this case.
+        wrapper_var = thread.GetSelectedFrame().FindVariable('wrapper')
+        foo_var = wrapper_var.Dereference()
+        self.assertEqual(foo_var.GetNumChildren(), 3)
+        self.assertEqual(foo_var.GetChildAtIndex(0).GetName(), 'a')
+        self.assertEqual(foo_var.GetChildAtIndex(1).GetName(), 'fake_a')
+        self.assertEqual(foo_var.GetChildAtIndex(2).GetName(), 'r')
 
         # now delete the synth and add the filter
         self.runCmd("type synth delete foo")
+        self.runCmd("type synth delete wrapfoo")
         self.runCmd("type filter add foo --child b --child j")
 
         self.expect('frame variable f00_1',
@@ -172,6 +182,10 @@ def cleanup():
                     substrs=['r = 45',
                              'fake_a = %d' % fake_a_val,
                              'a = 12'])
+        self.expect("frame variable --ptr-depth 1 wrapper", matching=False,
+                    substrs=['r = 45',
+                             'fake_a = %d' % fake_a_val,
+                             'a = 12'])
 
         # now add the synth and it should fail
         self.expect("type synth add -l fooSynthProvider foo", error=True,
@@ -197,6 +211,10 @@ def cleanup():
                     substrs=['r = 45',
                              'fake_a = %d' % fake_a_val,
                              'a = 12'])
+        self.expect("frame variable --ptr-depth 1 wrapper",
+                    substrs=['r = 45',
+                             'fake_a = %d' % fake_a_val,
+                             'a = 12'])
 
         # check the listing
         self.expect('type synth list',

diff  --git a/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/fooSynthProvider.py b/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/fooSynthProvider.py
index 45fb00468e08..6ee749b720b2 100644
--- a/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/fooSynthProvider.py
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/fooSynthProvider.py
@@ -28,3 +28,29 @@ def get_child_index(self, name):
 
     def update(self):
         return True
+
+
+class wrapfooSynthProvider:
+
+    def __init__(self, valobj, dict):
+        self.valobj = valobj
+
+    def num_children(self):
+        return 1
+
+    def get_child_at_index(self, index):
+        if index == 0:
+            return self.valobj.GetChildMemberWithName('ptr')
+        if index == 1:
+            return self.valobj.GetChildMemberWithName('ptr').Dereference()
+        return None
+
+    def get_child_index(self, name):
+        if name == 'ptr':
+            return 0
+        if name == '$$dereference$$':
+            return 1
+        return -1
+
+    def update(self):
+        return True

diff  --git a/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/main.cpp b/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/main.cpp
index f45a2abfb9f1..5cf4b6345927 100644
--- a/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/main.cpp
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-python-synth/main.cpp
@@ -46,11 +46,17 @@ struct wrapint
     wrapint(int X) : x(X) {}
 };
 
+struct wrapfoo
+{
+    foo *ptr;
+};
+
 int main()
 {
     foo f00_1(1);
     foo *f00_ptr = new foo(12);
-    
+    wrapfoo wrapper{f00_ptr};
+
     f00_1.a++; // Set break point at this line.
     
     wrapint test_cast('A' +

diff  --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp
index 1dd9a6cf62c3..1e553596fcfc 100644
--- a/lldb/source/Core/ValueObject.cpp
+++ b/lldb/source/Core/ValueObject.cpp
@@ -2859,6 +2859,9 @@ ValueObjectSP ValueObject::Dereference(Status &error) {
         GetSyntheticValue()
             ->GetChildMemberWithName(ConstString("$$dereference$$"), true)
             .get();
+  } else if (IsSynthetic()) {
+    m_deref_valobj =
+        GetChildMemberWithName(ConstString("$$dereference$$"), true).get();
   }
 
   if (m_deref_valobj) {


        


More information about the lldb-commits mailing list