mirror of
https://github.com/intel/llvm.git
synced 2026-01-16 05:32:28 +08:00
[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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -38,19 +38,9 @@ class PythonSynthDataFormatterTestCase(TestBase):
|
||||
|
||||
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 @@ class PythonSynthDataFormatterTestCase(TestBase):
|
||||
# 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 @@ class PythonSynthDataFormatterTestCase(TestBase):
|
||||
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 @@ class PythonSynthDataFormatterTestCase(TestBase):
|
||||
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 @@ class PythonSynthDataFormatterTestCase(TestBase):
|
||||
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 @@ class PythonSynthDataFormatterTestCase(TestBase):
|
||||
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',
|
||||
|
||||
@@ -28,3 +28,29 @@ class fooSynthProvider:
|
||||
|
||||
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
|
||||
|
||||
@@ -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' +
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user