[RFC,2/2] gdb: improve equal check used by the pretty-printer [] support

Message ID 3be0ad8136fc64fd1e9987409dc8c2a699dc0c22.1619100479.git.andrew.burgess@embecosm.com
State New
Headers show
Series
  • Using pretty-printers for [] indexing
Related show

Commit Message

Andrew Burgess April 22, 2021, 2:10 p.m.
When performing a BINOP_SUBSCRIPT via the pretty printers, if the
pretty printer represents a map, then the index field could be any
type.

This commit extends GDB so that we can use any compiled in equality
operators, or any available xmethods when comparing the index as
returned by the pretty-printer to the user supplied index, this allows
for better handling of C++ string based maps.

gdb/ChangeLog:

	* python/py-prettyprint.c: Add 'expop.h' include.
	(gdbpy_val_pretty_printer_find_child): Call eval_op_equal instead
	of value_equal.

gdb/testsuite/ChangeLog:

	* gdb.python/py-prettyprint-subscript.cc (class string_type): New
	class.
	(dump_bool): New global.
	(obj_str_int): New global.
	(main): Fill new global.
	* gdb.python/py-prettyprint-subscript.exp (run_tests): Add new
	tests.
	* gdb.python/py-prettyprint-subscript.py (class pp_string_type):
	New class.
	(class pretty_printer_finder): Add lookup for string to int type.
	(generic_array_subscript::worker::__call__): Add comment.
	(generic_map_subscript::worker::__call__): Add comment.
	(class string_type_xmethod): New class.
---
 gdb/ChangeLog                                 |  6 ++
 gdb/python/py-prettyprint.c                   | 11 +++-
 gdb/testsuite/ChangeLog                       | 16 ++++++
 .../gdb.python/py-prettyprint-subscript.cc    | 56 +++++++++++++++++++
 .../gdb.python/py-prettyprint-subscript.exp   | 12 ++++
 .../gdb.python/py-prettyprint-subscript.py    | 52 +++++++++++++++++
 6 files changed, 152 insertions(+), 1 deletion(-)

-- 
2.25.4

Patch

diff --git a/gdb/python/py-prettyprint.c b/gdb/python/py-prettyprint.c
index effbfcbfe28..2e083375f28 100644
--- a/gdb/python/py-prettyprint.c
+++ b/gdb/python/py-prettyprint.c
@@ -26,6 +26,7 @@ 
 #include "python.h"
 #include "python-internal.h"
 #include "cli/cli-style.h"
+#include "expop.h"
 
 /* Return type of print_string_repr.  */
 
@@ -817,7 +818,15 @@  gdbpy_val_pretty_printer_find_child (const extension_language_defn *extlang,
 		 pull from the iterator.  Check to see if this is the name
 		 we're looking for.  */
 	      struct value *tmp = convert_value_from_python (py_v);
-	      prev_matched_as_name = value_equal (idx, tmp);
+	      struct expression exp (language, gdbarch);
+
+	      /* Call through eval_op_equal here so that we get the chance
+		 to use the compiled in operators, or any xmethods that are
+		 available.  This call will fall back to value_equal if
+		 there's no special handling available.  */
+	      struct value *res = eval_op_equal (nullptr, &exp, EVAL_NORMAL,
+						 BINOP_EQUAL, tmp, idx);
+	      prev_matched_as_name = (bool) value_as_long (res);
 	    }
 	  else if (prev_matched_as_name)
 	    {
diff --git a/gdb/testsuite/gdb.python/py-prettyprint-subscript.cc b/gdb/testsuite/gdb.python/py-prettyprint-subscript.cc
index c446fc82d67..cfda4dc127f 100644
--- a/gdb/testsuite/gdb.python/py-prettyprint-subscript.cc
+++ b/gdb/testsuite/gdb.python/py-prettyprint-subscript.cc
@@ -19,6 +19,52 @@ 
 #include <cstdlib>
 #include <stdexcept>
 
+/* A very minimal string like class.  This is used instead of std::string
+   so that we can provide known pretty-printers and xmethods for this
+   class.  */
+
+class string_type
+{
+public:
+  string_type ()
+  {
+    m_s = strdup ("");
+  }
+
+  string_type (const char *s)
+  {
+    m_s = strdup (s);
+  }
+
+  string_type (const string_type &other)
+  {
+    m_s = strdup (other.m_s);
+  }
+
+  string_type &
+  operator= (const string_type &other)
+  {
+    free (m_s);
+    m_s = strdup (other.m_s);
+    return *this;
+  }
+
+  bool
+  operator== (const string_type &other)
+  {
+    return strcmp (m_s, other.m_s) == 0;
+  }
+
+  bool
+  operator== (const char *other)
+  {
+    return other != nullptr && strcmp (m_s, other) == 0;
+  }
+
+private:
+  char *m_s = nullptr;
+};
+
 template<typename TYPE>
 class generic_array
 {
@@ -99,6 +145,7 @@  private:
 
 volatile int dump_int;
 volatile float dump_float;
+volatile bool dump_bool;
 
 int
 main ()
@@ -106,6 +153,7 @@  main ()
   generic_array<int> obj_int ("first int array");
   generic_array<float> obj_float ("first float array");
   generic_map<int,int> obj_int_int ("int to int map");
+  generic_map<string_type,int> obj_str_int ("string to int map");
 
   obj_int.push_back (3);
   obj_int.push_back (6);
@@ -122,6 +170,14 @@  main ()
   obj_int_int.insert (8, 16);
   obj_int_int.insert (9, 42);
 
+  string_type arg1 = "abc";
+  string_type arg2 = "def";
+  string_type arg3 = "ghi";
+
+  obj_str_int.insert ("abc", 1);
+  obj_str_int.insert ("def", 2);
+  obj_str_int.insert ("ghi", 3);
+
 #ifdef DEFINE_SUBSCRIPT_OPERATOR
   dump_int = obj_int[0];
   dump_float = obj_float[0];
diff --git a/gdb/testsuite/gdb.python/py-prettyprint-subscript.exp b/gdb/testsuite/gdb.python/py-prettyprint-subscript.exp
index d53aadc2643..89436f97d5a 100644
--- a/gdb/testsuite/gdb.python/py-prettyprint-subscript.exp
+++ b/gdb/testsuite/gdb.python/py-prettyprint-subscript.exp
@@ -155,6 +155,18 @@  proc run_tests {lang use_subscript_operators use_xmethods} {
     gdb_test "p obj_int_int\[2\]" ${err_msg}
     set err_msg [build_map_key_error 10 $use_xmethods]
     gdb_test "p obj_int_int\[10\]" ${err_msg}
+
+    # The following tests are only available for C++.
+    if { $lang == "c++" } {
+	gdb_test "p obj_str_int" \
+	    " = string_type -> int map \"string to int map\" with 3 elements = \\{\\\[\"abc\"\\\] = 1, \\\[\"def\"\\\] = 2, \\\[\"ghi\"\\\] = 3\\}"
+	gdb_test "p obj_str_int\[arg1\]" " = 1"
+	gdb_test "p obj_str_int\[arg2\]" " = 2"
+	gdb_test "p obj_str_int\[arg3\]" " = 3"
+	gdb_test "p obj_str_int\[\"abc\"\]" " = 1"
+	gdb_test "p obj_str_int\[\"def\"\]" " = 2"
+	gdb_test "p obj_str_int\[\"ghi\"\]" " = 3"
+    }
 }
 
 # Each test-spec is {
diff --git a/gdb/testsuite/gdb.python/py-prettyprint-subscript.py b/gdb/testsuite/gdb.python/py-prettyprint-subscript.py
index 79eae646240..33c19efb6f0 100644
--- a/gdb/testsuite/gdb.python/py-prettyprint-subscript.py
+++ b/gdb/testsuite/gdb.python/py-prettyprint-subscript.py
@@ -65,6 +65,16 @@  class pp_generic_map:
     def display_hint (self):
         return 'map'
 
+class pp_string_type:
+    def __init__ (self, val):
+        self.val = val
+
+    def to_string (self):
+        return self.val['m_s'].string ()
+
+    def display_hint (self):
+        return 'string'
+
 class pretty_printer_finder:
     def __init__ (self):
         self.dict = {}
@@ -80,6 +90,9 @@  class pretty_printer_finder:
             = lambda v : pp_generic_array ('float', v)
         self.dict[re.compile ('^generic_map<int, int>$')] \
             = lambda v : pp_generic_map ('int', 'int', v)
+        self.dict[re.compile ('^generic_map<string_type, int>$')] \
+            = lambda v : pp_generic_map ('string_type', 'int', v)
+        self.dict[re.compile ('^string_type$')] = pp_string_type
 
     def lookup (self, value):
         type = value.type
@@ -125,6 +138,7 @@  class generic_array_subscript (gdb.xmethod.XMethod):
             if idx < 0 or idx >= obj['m_nitems']:
                 raise gdb.GdbError ("array index %d out of bounds" % idx)
             ptr = obj['m_items'] + idx
+            # This output is checked for by the test.
             print ("xmethod found it")
             return ptr.dereference ()
 
@@ -180,6 +194,7 @@  class generic_map_subscript (gdb.xmethod.XMethod):
             while pointer != end:
                 child = pointer.dereference ()
                 if (child['key'] == idx):
+                    # This output is checked for by the test.
                     print ("xmethod found it")
                     return child['value']
                 pointer += 1
@@ -217,6 +232,43 @@  class generic_map_matcher (gdb.xmethod.XMethodMatcher):
                     workers.append (worker)
         return workers
 
+class string_type_xmethod (gdb.xmethod.XMethod):
+
+    class worker (gdb.xmethod.XMethodWorker):
+        def get_arg_types (self):
+            return [gdb.lookup_type ('char').pointer ()]
+
+        def get_result_type(self, obj):
+            return gdb.lookup_type('bool')
+
+        def __call__(self, obj, other):
+            return gdb.Value (obj['m_s'].string () == other.string ())
+
+    def get_worker (self, method_name):
+        # Method name is always 'operator=='.
+        return self.worker ()
+
+    def __init__ (self):
+        gdb.xmethod.XMethod.__init__ (self, "string_type::operator==")
+
+class string_type_matcher (gdb.xmethod.XMethodMatcher):
+    def __init__ (self):
+        gdb.xmethod.XMethodMatcher.__init__ (self, 'string_type_matcher')
+        self.methods = [string_type_xmethod ()]
+
+    def match (self, class_type, method_name):
+        workers = []
+        for method in self.methods:
+            if (method.enabled and class_type.tag == "string_type"
+                and method_name == "operator=="):
+                worker = method.get_worker (method_name)
+                if worker:
+                    workers.append (worker)
+        return workers
+
 def register_xmethods ():
     gdb.xmethod.register_xmethod_matcher (None, generic_array_matcher ())
     gdb.xmethod.register_xmethod_matcher (None, generic_map_matcher ())
+
+# Always register the string_type xmethod support.
+gdb.xmethod.register_xmethod_matcher (None, string_type_matcher ())