diff --git a/lldb/docs/use/variable.rst b/lldb/docs/use/variable.rst --- a/lldb/docs/use/variable.rst +++ b/lldb/docs/use/variable.rst @@ -846,7 +846,7 @@ 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 @@ 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 + 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 --- 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 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 @@ # 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 @@ 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 @@ 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 different 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 @@ 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 @@ 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 --- 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 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 --- 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 @@ 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 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -2859,6 +2859,9 @@ GetSyntheticValue() ->GetChildMemberWithName(ConstString("$$dereference$$"), true) .get(); + } else if (IsSynthetic()) { + m_deref_valobj = + GetChildMemberWithName(ConstString("$$dereference$$"), true).get(); } if (m_deref_valobj) {