This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH 9/9] add python layer


>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:

Tom> Even with the previous patches in place, some types aren't printed as
Tom> nicely as we would like them to be.  For example, std::string is still
Tom> printed as "std::basic_string<char, std::char_traits<char>,
Tom> std::allocator<char> >".

Here's a new version of this patch which fixes a bug that I found
through further testing.  The new test file has a test for this bug.

Built and regtested on x86-64 F16.

Tom

	* NEWS: Update.
	* data-directory/Makefile.in (PYTHON_FILES): Add
	type_printers.py.
	* python/lib/gdb/command/type_printers.py: New file.
	* python/lib/gdb/command/types.py (TypePrinter): New class.
	(_get_some_type_recognizers, get_type_recognizers,
	apply_type_recognizers, register_type_printer): New
	functions.
	* python/py-objfile.c (objfile_object) <type_printers>: New
	field.
	(objfpy_dealloc): Decref new field.
	(objfpy_new): Set new field.
	(objfpy_get_type_printers, objfpy_set_type_printers): New
	functions.
	(objfile_to_objfile_object): Set new field.
	(objfile_getset): Add "type_printers".
	* python/py-progspace.c (pspace_object) <type_printers>: New
	field.
	(pspy_dealloc): Decref new field.
	(pspy_new): Set new field.
	(pspy_get_type_printers, pspy_set_type_printers): New functions.
	(pspace_to_pspace_object): Set new field.
	(pspace_getset): Add "type_printers".
	* python/python.c (start_type_printers, apply_type_printers,
	free_type_printers): New functions.
	(_initialize_python): Set gdb.type_printers.
	* python/python.h (start_type_printers, apply_type_printers,
	free_type_printers): Declare.
	* typeprint.c (type_print_raw_options, default_ptype_flags):
	Update for new fields.
	(do_free_global_table, create_global_typedef_table,
	find_global_typedef): New functions.
	(find_typedef_in_hash): Use find_global_typedef.
	(whatis_exp): Use create_global_typedef_table.  Change cleanup
	handling.
	* typeprint.h (struct type_print_options) <global_typedefs,
	global_printers>: New fields.

	* gdb.texinfo (Symbols): Document "info type-printers",
	"enable type-printer" and "disable type-printer".
	(Python API): Add new node to menu.
	(Type Printing API): New node.
	(Progspaces In Python): Document type_printers field.
	(Objfiles In Python): Likewise.
	(gdb.types) <get_type_recognizers, apply_type_recognizers,
	register_type_printer, TypePrinter>: Document.

	* gdb.base/completion.exp: Update for "info type-printers".
	* gdb.python/py-typeprint.cc: New file.
	* gdb.python/py-typeprint.exp: New file.
	* gdb.python/py-typeprint.py: New file.
---
 gdb/NEWS                                    |    6 +
 gdb/data-directory/Makefile.in              |    1 +
 gdb/doc/gdb.texinfo                         |  115 ++++++++++++++++++++++-
 gdb/python/lib/gdb/__init__.py              |    3 +
 gdb/python/lib/gdb/command/type_printers.py |  125 ++++++++++++++++++++++++
 gdb/python/lib/gdb/types.py                 |   65 +++++++++++++
 gdb/python/py-objfile.c                     |   62 ++++++++++++
 gdb/python/py-progspace.c                   |   62 ++++++++++++
 gdb/python/python.c                         |  136 +++++++++++++++++++++++++++
 gdb/python/python.h                         |    6 +
 gdb/testsuite/gdb.base/completion.exp       |    6 +-
 gdb/testsuite/gdb.python/py-typeprint.cc    |   37 +++++++
 gdb/testsuite/gdb.python/py-typeprint.exp   |   53 +++++++++++
 gdb/testsuite/gdb.python/py-typeprint.py    |   35 +++++++
 gdb/typeprint.c                             |  106 ++++++++++++++++++---
 gdb/typeprint.h                             |    8 ++
 16 files changed, 809 insertions(+), 17 deletions(-)
 create mode 100644 gdb/python/lib/gdb/command/type_printers.py
 create mode 100644 gdb/testsuite/gdb.python/py-typeprint.cc
 create mode 100644 gdb/testsuite/gdb.python/py-typeprint.exp
 create mode 100644 gdb/testsuite/gdb.python/py-typeprint.py

diff --git a/gdb/NEWS b/gdb/NEWS
index d77de8d..997bcf6 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -12,6 +12,8 @@
 
   ** Python's atexit.register now works in GDB.
 
+  ** Types can be pretty-printed via a Python API.
+
 * New Python-based convenience functions:
 
   ** $_memeq(buf1, buf2, length)
@@ -44,6 +46,10 @@ pi [command]
 py [command]
   "py" is a new alias for "python".
 
+enable type-printer [name]...
+disable type-printer [name]...
+  Enable or disable type printers.
+
 * Removed commands
 
   ** For the Renesas Super-H architecture, the "regs" command has been removed
diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
index 41947c6..2e2fbf9 100644
--- a/gdb/data-directory/Makefile.in
+++ b/gdb/data-directory/Makefile.in
@@ -57,6 +57,7 @@ PYTHON_FILES = \
 	gdb/printing.py \
 	gdb/prompt.py \
 	gdb/command/__init__.py \
+	gdb/command/type_printers.py \
 	gdb/command/pretty_printers.py \
 	gdb/command/prompt.py \
 	gdb/command/explore.py \
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 19ef59f..529a3bb 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -15205,6 +15205,22 @@ This command differs from @code{ptype} in two ways: first, like
 @code{whatis}, it does not print a detailed description; second, it
 lists all source files where a type is defined.
 
+@kindex info type-printers
+@item info type-printers
+Versions of @value{GDBN} that ship with Python scripting enabled may
+have ``type printers'' available.  When using @command{ptype} or
+@command{whatis}, these printers are consulted when the name of a type
+is needed.  @xref{Type Printing API}, for more information on writing
+type printers.
+
+@command{info type-printers} displays all the available type printers.
+
+@kindex enable type-printer
+@kindex disable type-printer
+@item enable type-printer @var{name}@dots{}
+@item disable type-printer @var{name}@dots{}
+These commands can be used to enable or disable type printers.
+
 @kindex info scope
 @cindex local variables
 @item info scope @var{location}
@@ -22658,6 +22674,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Pretty Printing API::         Pretty-printing values.
 * Selecting Pretty-Printers::   How GDB chooses a pretty-printer.
 * Writing a Pretty-Printer::    Writing a Pretty-Printer.
+* Type Printing API::		Pretty-printing types.
 * Inferiors In Python::         Python representation of inferiors (processes)
 * Events In Python::            Listening for events from @value{GDBN}.
 * Threads In Python::           Accessing inferior threads from Python.
@@ -23953,6 +23970,61 @@ my_library.so:
     bar
 @end smallexample
 
+@node Type Printing API
+@subsubsection Type Printing API
+
+@value{GDBN} provides a way for Python code to customize type display.
+This is mainly useful for substituting canonical typedef names for
+types.
+
+A @dfn{type printer} is just a Python object conforming to a certain
+protocol.  A simple base class implementing the protocol is provided;
+@xref{gdb.types}.  A type printer must supply at least:
+
+@defivar type_printer enabled
+A boolean which is True if the printer is enabled, and False
+otherwise.  This is manipulated by the @command{enable type-printer}
+and @command{disable type-printer} commands.
+@end defivar
+
+@defivar type_printer name
+The name of the type printer.  This must be a string.  This is used by
+the @command{enable type-printer} and @command{disable type-printer}
+commands.
+@end defivar
+
+@defmethod type_printer instantiate (self)
+This is called by @value{GDBN} at the start of type-printing.  It is
+only called if the type printer is enabled.  This method must return a
+new object that supplies a @code{recognize} method, as describe below.
+@end defmethod
+
+
+When displaying a type, say via the @command{ptype} command,
+@value{GDBN} will compute a list of type recognizers.  This is done by
+iterating first over the per-objfile type printers (@pxref{Objfiles In
+Python}), followed by the per-progspace type printers
+(@pxref{Progspaces In Python}), and finally the global type printers.
+
+@value{GDBN} will call the @code{instantiate} method of each enabled
+type printer.  If this method returns @code{None}, then the result is
+ignored; otherwise, it is appended to the list of recognizers.
+
+Then, when @value{GDBN} is going to display a type name, it iterates
+over the list of recognizers.  For each one, it calls the recognition
+function:
+
+@defmethod type_recognizer recognize (self, type)
+If @var{type} is not recognized, return @code{None}.  Otherwise,
+return a string which is to be printed as the name of @var{type}.
+@var{type} will be an instance of @code{gdb.Type} (@pxref{Types In
+Python}).
+@end defmethod
+
+@value{GDBN} uses this two-pass approach so that type printers can
+efficiently cache information without holding on to it too long.
+
+
 @node Inferiors In Python
 @subsubsection Inferiors In Python
 @cindex inferiors in Python
@@ -24790,6 +24862,11 @@ which is used to format the value.  @xref{Pretty Printing API}, for more
 information.
 @end defvar
 
+@defvar Progspace.type_printers
+The @code{type_printers} attribute is a list of type printer objects.
+@xref{Type Printing API}, for more information.
+@end defvar
+
 @node Objfiles In Python
 @subsubsection Objfiles In Python
 
@@ -24835,6 +24912,11 @@ which is used to format the value.  @xref{Pretty Printing API}, for more
 information.
 @end defvar
 
+@defvar Objfile.type_printers
+The @code{type_printers} attribute is a list of type printer objects.
+@xref{Type Printing API}, for more information.
+@end defvar
+
 A @code{gdb.Objfile} object has the following methods:
 
 @defun Objfile.is_valid ()
@@ -26037,7 +26119,7 @@ if a printer with the same name already exists.
 @cindex gdb.types
 
 This module provides a collection of utilities for working with
-@code{gdb.Types} objects.
+@code{gdb.Type} objects.
 
 @table @code
 @item get_basic_type (@var{type})
@@ -26098,6 +26180,37 @@ Then in @value{GDBN}:
 @{['a', 'b0', 'b1']@}
 @end smallexample
 
+@item get_type_recognizers ()
+Return a list of the enabled type recognizers for the current context.
+This is called by @value{GDBN} during the type-printing process
+(@pxref{Type Printing API}).
+
+@item apply_type_recognizers (recognizers, type_obj)
+Apply the type recognizers, @var{recognizers}, to the type object
+@var{type_obj}.  If any recognizer returns a string, return that
+string.  Otherwise, return @code{None}.  This is called by
+@value{GDBN} during the type-printing process (@pxref{Type Printing
+API}).
+
+@item register_type_printer (locus, printer)
+This is a convenience function to register a type printer.
+@var{printer} is the type printer to register.  It must implement the
+type printer protocol.  @var{locus} is either a @code{gdb.Objfile}, in
+which case the printer is registered with that objfile; a
+@code{gdb.Progspace}, in which case the printer is registered with
+that progspace; or @code{None}, in which case the printer is
+registered globally.
+
+@item TypePrinter
+This is a base class that implements the type printer protocol.  Type
+printers are encouraged, but not required, to derive from this class.
+It defines a constructor:
+
+@defmethod TypePrinter __init__ (self, name)
+Initialize the type printer with the given name.  The new printer
+starts in the enabled state.
+@end defmethod
+
 @end table
 
 @node gdb.prompt
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
index 0e020fe..0671526 100644
--- a/gdb/python/lib/gdb/__init__.py
+++ b/gdb/python/lib/gdb/__init__.py
@@ -70,6 +70,9 @@ sys.argv = ['']
 # Initial pretty printers.
 pretty_printers = []
 
+# Initial type printers.
+type_printers = []
+
 # Convenience variable to GDB's python directory
 PYTHONDIR = os.path.dirname(os.path.dirname(__file__))
 
diff --git a/gdb/python/lib/gdb/command/type_printers.py b/gdb/python/lib/gdb/command/type_printers.py
new file mode 100644
index 0000000..b7d6930
--- /dev/null
+++ b/gdb/python/lib/gdb/command/type_printers.py
@@ -0,0 +1,125 @@
+# Type printer commands.
+# Copyright (C) 2010-2012 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import copy
+import gdb
+
+"""GDB commands for working with type-printers."""
+
+class InfoTypePrinter(gdb.Command):
+    """GDB command to list all registered type-printers.
+
+    Usage: info type-printers
+    """
+
+    def __init__ (self):
+        super(InfoTypePrinter, self).__init__("info type-printers",
+                                              gdb.COMMAND_DATA)
+
+    def list_type_printers(self, type_printers):
+        """Print a list of type printers."""
+        # A potential enhancement is to provide an option to list printers in
+        # "lookup order" (i.e. unsorted).
+        sorted_type_printers = copy.copy(type_printers)
+        sorted_type_printers.sort(lambda x, y: cmp(x.name, y.name))
+        for printer in sorted_type_printers:
+            if printer.enabled:
+                enabled = ''
+            else:
+                enabled = " [disabled]"
+            print "  %s%s" % (printer.name, enabled)
+
+    def invoke(self, arg, from_tty):
+        """GDB calls this to perform the command."""
+        sep = ''
+        for objfile in gdb.objfiles():
+            if objfile.type_printers:
+                print "%sType printers for %s:" % (sep, objfile.name)
+                self.list_type_printers(objfile.type_printers)
+                sep = '\n'
+        if gdb.current_progspace().type_printers:
+            print "%sType printers for program space:" % sep
+            self.list_type_printers(gdb.current_progspace().type_printers)
+            sep = '\n'
+        if gdb.type_printers:
+            print "%sGlobal type printers:" % sep
+            self.list_type_printers(gdb.type_printers)
+
+class _EnableOrDisableCommand(gdb.Command):
+    def __init__(self, setting, name):
+        super(_EnableOrDisableCommand, self).__init__(name, gdb.COMMAND_DATA)
+        self.setting = setting
+
+    def set_some(self, name, printers):
+        result = False
+        for p in printers:
+            if name == p.name:
+                p.enabled = self.setting
+                result = True
+        return result
+
+    def invoke(self, arg, from_tty):
+        """GDB calls this to perform the command."""
+        for name in arg.split():
+            ok = False
+            for objfile in gdb.objfiles():
+                if self.set_some(name, objfile.type_printers):
+                    ok = True
+            if self.set_some(name, gdb.current_progspace().type_printers):
+                ok = True
+            if self.set_some(name, gdb.type_printers):
+                ok = True
+            if not ok:
+                print "No type printer named '%s'" % name
+
+    def add_some(self, result, word, printers):
+        for p in printers:
+            if p.name.startswith(word):
+                result.append(p.name)
+
+    def complete(self, text, word):
+        result = []
+        for objfile in gdb.objfiles():
+            self.add_some(result, word, objfile.type_printers)
+        self.add_some(result, word, gdb.current_progspace().type_printers)
+        self.add_some(result, word, gdb.type_printers)
+        return result
+
+class EnableTypePrinter(_EnableOrDisableCommand):
+    """GDB command to enable the specified type printer.
+
+    Usage: enable type-printer NAME
+
+    NAME is the name of the type-printer.
+    """
+
+    def __init__(self):
+        super(EnableTypePrinter, self).__init__(True, "enable type-printer")
+
+class DisableTypePrinter(_EnableOrDisableCommand):
+    """GDB command to disable the specified type-printer.
+
+    Usage: disable type-printer NAME
+
+    NAME is the name of the type-printer.
+    """
+
+    def __init__(self):
+        super(DisableTypePrinter, self).__init__(False, "disable type-printer")
+
+InfoTypePrinter()
+EnableTypePrinter()
+DisableTypePrinter()
diff --git a/gdb/python/lib/gdb/types.py b/gdb/python/lib/gdb/types.py
index 66c9528..3745383 100644
--- a/gdb/python/lib/gdb/types.py
+++ b/gdb/python/lib/gdb/types.py
@@ -109,3 +109,68 @@ def deep_items (type_):
         else:
             for i in deep_items (v.type):
                 yield i
+
+class TypePrinter(object):
+    """The base class for type printers.
+
+    Instances of this type can be used to substitute type names during
+    'ptype'.
+
+    A type printer must have at least 'name' and 'enabled' attributes,
+    and supply an 'instantiate' method.
+
+    The 'instantiate' method must either return None, or return an
+    object which has a 'recognize' method.  This method must accept a
+    gdb.Type argument and either return None, meaning that the type
+    was not recognized, or a string naming the type.
+    """
+
+    def __init__(self, name):
+        self.name = name
+        self.enabled = True
+
+    def instantiate(self):
+        return None
+
+# Helper function for computing the list of type recognizers.
+def _get_some_type_recognizers(result, plist):
+    for printer in plist:
+        if printer.enabled:
+            inst = printer.instantiate()
+            if inst is not None:
+                result.append(inst)
+    return None
+
+def get_type_recognizers():
+    "Return a list of the enabled type recognizers for the current context."
+    result = []
+
+    # First try the objfiles.
+    for objfile in gdb.objfiles():
+        _get_some_type_recognizers(result, objfile.type_printers)
+    # Now try the program space.
+    _get_some_type_recognizers(result, gdb.current_progspace().type_printers)
+    # Finally, globals.
+    _get_some_type_recognizers(result, gdb.type_printers)
+
+    return result
+
+def apply_type_recognizers(recognizers, type_obj):
+    """Apply the given list of type recognizers to the type TYPE_OBJ.
+    If any recognizer in the list recognizes TYPE_OBJ, returns the name
+    given by the recognizer.  Otherwise, this returns None."""
+    for r in recognizers:
+        result = r.recognize(type_obj)
+        if result is not None:
+            return result
+    return None
+
+def register_type_printer(locus, printer):
+    """Register a type printer.
+    PRINTER is the type printer instance.
+    LOCUS is either an objfile, a program space, or None, indicating
+    global registration."""
+
+    if locus is None:
+        locus = gdb
+    locus.type_printers.insert(0, printer)
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index 9fa6813..5d2398f 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -32,6 +32,9 @@ typedef struct
 
   /* The pretty-printer list of functions.  */
   PyObject *printers;
+
+  /* The type-printer list.  */
+  PyObject *type_printers;
 } objfile_object;
 
 static PyTypeObject objfile_object_type;
@@ -58,6 +61,7 @@ objfpy_dealloc (PyObject *o)
   objfile_object *self = (objfile_object *) o;
 
   Py_XDECREF (self->printers);
+  Py_XDECREF (self->type_printers);
   self->ob_type->tp_free ((PyObject *) self);
 }
 
@@ -76,6 +80,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
 	  Py_DECREF (self);
 	  return NULL;
 	}
+
+      self->type_printers = PyList_New (0);
+      if (!self->type_printers)
+	{
+	  Py_DECREF (self);
+	  return NULL;
+	}
     }
   return (PyObject *) self;
 }
@@ -118,6 +129,48 @@ objfpy_set_printers (PyObject *o, PyObject *value, void *ignore)
   return 0;
 }
 
+/* Get the 'type_printers' attribute.  */
+
+static PyObject *
+objfpy_get_type_printers (PyObject *o, void *ignore)
+{
+  objfile_object *self = (objfile_object *) o;
+
+  Py_INCREF (self->type_printers);
+  return self->type_printers;
+}
+
+/* Set the 'type_printers' attribute.  */
+
+static int
+objfpy_set_type_printers (PyObject *o, PyObject *value, void *ignore)
+{
+  PyObject *tmp;
+  objfile_object *self = (objfile_object *) o;
+
+  if (! value)
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("Cannot delete the type_printers attribute."));
+      return -1;
+    }
+
+  if (! PyList_Check (value))
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("The type_printers attribute must be a list."));
+      return -1;
+    }
+
+  /* Take care in case the LHS and RHS are related somehow.  */
+  tmp = self->type_printers;
+  Py_INCREF (value);
+  self->type_printers = value;
+  Py_XDECREF (tmp);
+
+  return 0;
+}
+
 /* Implementation of gdb.Objfile.is_valid (self) -> Boolean.
    Returns True if this object file still exists in GDB.  */
 
@@ -172,6 +225,13 @@ objfile_to_objfile_object (struct objfile *objfile)
 	      return NULL;
 	    }
 
+	  object->type_printers = PyList_New (0);
+	  if (!object->type_printers)
+	    {
+	      Py_DECREF (object);
+	      return NULL;
+	    }
+
 	  set_objfile_data (objfile, objfpy_objfile_data_key, object);
 	}
     }
@@ -210,6 +270,8 @@ static PyGetSetDef objfile_getset[] =
     "The objfile's filename, or None.", NULL },
   { "pretty_printers", objfpy_get_printers, objfpy_set_printers,
     "Pretty printers.", NULL },
+  { "type_printers", objfpy_get_type_printers, objfpy_set_type_printers,
+    "Type printers.", NULL },
   { NULL }
 };
 
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index e4b029b..c1b1cac 100644
--- a/gdb/python/py-progspace.c
+++ b/gdb/python/py-progspace.c
@@ -34,6 +34,9 @@ typedef struct
 
   /* The pretty-printer list of functions.  */
   PyObject *printers;
+
+  /* The type-printer list.  */
+  PyObject *type_printers;
 } pspace_object;
 
 static PyTypeObject pspace_object_type;
@@ -66,6 +69,7 @@ pspy_dealloc (PyObject *self)
   pspace_object *ps_self = (pspace_object *) self;
 
   Py_XDECREF (ps_self->printers);
+  Py_XDECREF (ps_self->type_printers);
   self->ob_type->tp_free (self);
 }
 
@@ -84,6 +88,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
 	  Py_DECREF (self);
 	  return NULL;
 	}
+
+      self->type_printers = PyList_New (0);
+      if (!self->type_printers)
+	{
+	  Py_DECREF (self);
+	  return NULL;
+	}
     }
   return (PyObject *) self;
 }
@@ -126,6 +137,48 @@ pspy_set_printers (PyObject *o, PyObject *value, void *ignore)
   return 0;
 }
 
+/* Get the 'type_printers' attribute.  */
+
+static PyObject *
+pspy_get_type_printers (PyObject *o, void *ignore)
+{
+  pspace_object *self = (pspace_object *) o;
+
+  Py_INCREF (self->type_printers);
+  return self->type_printers;
+}
+
+/* Set the 'type_printers' attribute.  */
+
+static int
+pspy_set_type_printers (PyObject *o, PyObject *value, void *ignore)
+{
+  PyObject *tmp;
+  pspace_object *self = (pspace_object *) o;
+
+  if (! value)
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       "cannot delete the type_printers attribute");
+      return -1;
+    }
+
+  if (! PyList_Check (value))
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       "the type_printers attribute must be a list");
+      return -1;
+    }
+
+  /* Take care in case the LHS and RHS are related somehow.  */
+  tmp = self->type_printers;
+  Py_INCREF (value);
+  self->type_printers = value;
+  Py_XDECREF (tmp);
+
+  return 0;
+}
+
 
 
 /* Clear the PSPACE pointer in a Pspace object and remove the reference.  */
@@ -168,6 +221,13 @@ pspace_to_pspace_object (struct program_space *pspace)
 	      return NULL;
 	    }
 
+	  object->type_printers = PyList_New (0);
+	  if (!object->type_printers)
+	    {
+	      Py_DECREF (object);
+	      return NULL;
+	    }
+
 	  set_program_space_data (pspace, pspy_pspace_data_key, object);
 	}
     }
@@ -197,6 +257,8 @@ static PyGetSetDef pspace_getset[] =
     "The progspace's main filename, or None.", NULL },
   { "pretty_printers", pspy_get_printers, pspy_set_printers,
     "Pretty printers.", NULL },
+  { "type_printers", pspy_get_type_printers, pspy_set_type_printers,
+    "Type printers.", NULL },
   { NULL }
 };
 
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 97f8506..c25aa16 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1181,6 +1181,125 @@ gdbpy_objfiles (PyObject *unused1, PyObject *unused2)
   return list;
 }
 
+/* Compute the list of active type printers and return it.  The result
+   of this function can be passed to apply_type_printers, and should
+   be freed by free_type_printers.  */
+
+void *
+start_type_printers (void)
+{
+  struct cleanup *cleanups;
+  PyObject *type_module, *func, *result_obj;
+
+  cleanups = ensure_python_env (get_current_arch (), current_language);
+
+  type_module = PyImport_ImportModule ("gdb.types");
+  if (type_module == NULL)
+    {
+      gdbpy_print_stack ();
+      goto done;
+    }
+  make_cleanup_py_decref (type_module);
+
+  func = PyObject_GetAttrString (type_module, "get_type_recognizers");
+  if (func == NULL)
+    {
+      gdbpy_print_stack ();
+      goto done;
+    }
+  make_cleanup_py_decref (func);
+
+  result_obj = PyObject_CallFunctionObjArgs (func, (char *) NULL);
+  if (result_obj == NULL)
+    gdbpy_print_stack ();
+
+ done:
+  do_cleanups (cleanups);
+  return result_obj;
+}
+
+/* If TYPE is recognized by some type printer, return a newly
+   allocated string holding the type's replacement name.  The caller
+   is responsible for freeing the string.  Otherwise, return NULL.
+
+   This function has a bit of a funny name, since it actually applies
+   recognizers, but this seemed clearer given the start_type_printers
+   and free_type_printers functions.  */
+
+char *
+apply_type_printers (void *printers, struct type *type)
+{
+  struct cleanup *cleanups;
+  PyObject *type_obj, *type_module, *func, *result_obj;
+  PyObject *printers_obj = printers;
+  char *result = NULL;
+
+  if (printers_obj == NULL)
+    return NULL;
+
+  cleanups = ensure_python_env (get_current_arch (), current_language);
+
+  type_obj = type_to_type_object (type);
+  if (type_obj == NULL)
+    {
+      gdbpy_print_stack ();
+      goto done;
+    }
+  make_cleanup_py_decref (type_obj);
+
+  type_module = PyImport_ImportModule ("gdb.types");
+  if (type_module == NULL)
+    {
+      gdbpy_print_stack ();
+      goto done;
+    }
+  make_cleanup_py_decref (type_module);
+
+  func = PyObject_GetAttrString (type_module, "apply_type_recognizers");
+  if (func == NULL)
+    {
+      gdbpy_print_stack ();
+      goto done;
+    }
+  make_cleanup_py_decref (func);
+
+  result_obj = PyObject_CallFunctionObjArgs (func, printers_obj,
+					     type_obj, (char *) NULL);
+  if (result_obj == NULL)
+    {
+      gdbpy_print_stack ();
+      goto done;
+    }
+  make_cleanup_py_decref (result_obj);
+
+  if (result_obj != Py_None)
+    {
+      result = python_string_to_host_string (result_obj);
+      if (result == NULL)
+	gdbpy_print_stack ();
+    }
+
+ done:
+  do_cleanups (cleanups);
+  return result;
+}
+
+/* Free the result of start_type_printers.  */
+
+void
+free_type_printers (void *arg)
+{
+  struct cleanup *cleanups;
+  PyObject *printers = arg;
+
+  if (printers == NULL)
+    return;
+
+  cleanups = ensure_python_env (get_current_arch (), current_language);
+  Py_DECREF (printers);
+  do_cleanups (cleanups);
+}
+
 #else /* HAVE_PYTHON */
 
 /* Dummy implementation of the gdb "python-interactive" and "python"
@@ -1238,6 +1357,23 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
 		    "scripting is not supported."));
 }
 
+void *
+start_type_printers (void)
+{
+  return NULL;
+}
+
+char *
+apply_type_printers (void *ignore, struct type *type)
+{
+  return NULL;
+}
+
+void
+free_type_printers (void *arg)
+{
+}
+
 #endif /* HAVE_PYTHON */
 
 
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 0d07271..72872b0 100644
--- a/gdb/python/python.h
+++ b/gdb/python/python.h
@@ -49,4 +49,10 @@ int gdbpy_should_stop (struct breakpoint_object *bp_obj);
 
 int gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj);
 
+void *start_type_printers (void);
+
+char *apply_type_printers (void *, struct type *type);
+
+void free_type_printers (void *arg);
+
 #endif /* GDB_PYTHON_H */
diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp
index ccdc199..8b1facb 100644
--- a/gdb/testsuite/gdb.base/completion.exp
+++ b/gdb/testsuite/gdb.base/completion.exp
@@ -161,7 +161,7 @@ gdb_test_multiple "" "$test" {
     -re "^info t foo\\\x07$" {
 	send_gdb "\n"
 	gdb_test_multiple "" "$test" {
-	    -re "Ambiguous info command \"t foo\": target, tasks, terminal, threads, tp, tracepoints, tvariables, types\\..*$gdb_prompt $" {
+	    -re "Ambiguous info command \"t foo\": target, tasks, terminal, threads, tp, tracepoints, tvariables, (type-printers, )?types\\..*$gdb_prompt $" {
 		pass "$test"
 	    }
 	}
@@ -174,7 +174,7 @@ gdb_test_multiple "" "$test" {
     -re "^info t\\\x07$" {
 	send_gdb "\n"
 	gdb_test_multiple "" "$test" {
-	    -re "Ambiguous info command \"t\": target, tasks, terminal, threads, tp, tracepoints, tvariables, types\\..*$gdb_prompt $" {
+	    -re "Ambiguous info command \"t\": target, tasks, terminal, threads, tp, tracepoints, tvariables, (type-printers, )?types\\..*$gdb_prompt $" {
 		pass "$test"
 	    }
 	}
@@ -187,7 +187,7 @@ gdb_test_multiple "" "$test" {
     -re "^info t \\\x07$" {
 	send_gdb "\n"
 	gdb_test_multiple "" "$test" {
-	    -re "Ambiguous info command \"t \": target, tasks, terminal, threads, tp, tracepoints, tvariables, types\\..*$gdb_prompt $" {
+	    -re "Ambiguous info command \"t \": target, tasks, terminal, threads, tp, tracepoints, tvariables, (type-printers, )?types\\..*$gdb_prompt $" {
 		pass "$test"
 	    }
 	}
diff --git a/gdb/testsuite/gdb.python/py-typeprint.cc b/gdb/testsuite/gdb.python/py-typeprint.cc
new file mode 100644
index 0000000..65c06f0
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-typeprint.cc
@@ -0,0 +1,37 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2008-2012 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+class basic_string
+{
+};
+
+template<typename T>
+class templ
+{
+public:
+  T x;
+  templ<T> *value;
+};
+
+templ<basic_string> s;
+
+basic_string bs;
+
+int main()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.python/py-typeprint.exp b/gdb/testsuite/gdb.python/py-typeprint.exp
new file mode 100644
index 0000000..ef07055
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-typeprint.exp
@@ -0,0 +1,53 @@
+# Copyright (C) 2012 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+if { [skip_cplus_tests] } { continue }
+
+load_lib gdb-python.exp
+load_lib cp-support.exp
+
+standard_testfile .cc
+
+if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
+    return -1
+}
+
+if { [skip_python_tests] } { continue }
+
+set remote_python_file [remote_download host ${srcdir}/${subdir}/${testfile}.py]
+
+gdb_test_no_output "python execfile ('${remote_python_file}')"
+
+cp_test_ptype_class s "basic test" "class" "templ<string>" {
+    { field public "T x;" }
+    { field public "templ<T> *value;" }
+} "" {} ""
+
+cp_test_ptype_class s "raw test" "class" "templ<basic_string>" {
+    { field public "basic_string x;" }
+    { field public "templ<basic_string> *value;" }
+} "" {} "/r"
+
+gdb_test_no_output "disable type-printer string"
+gdb_test "whatis bs" "basic_string" "whatis with disabled printer"
+
+gdb_test "info type-printers" "Global type printers:.*string.*disabled.*"
+
+gdb_test_no_output "enable type-printer string"
+gdb_test "whatis bs" "string" "whatis with enabled printer"
+
+gdb_test "whatis s" "templ<string>"
+
+remote_file host delete ${remote_python_file}
diff --git a/gdb/testsuite/gdb.python/py-typeprint.py b/gdb/testsuite/gdb.python/py-typeprint.py
new file mode 100644
index 0000000..a4351cd
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-typeprint.py
@@ -0,0 +1,35 @@
+# Copyright (C) 2012 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import gdb
+
+class Recognizer(object):
+    def __init__(self):
+        self.enabled = True
+
+    def recognize(self, type_obj):
+        if type_obj.tag == 'basic_string':
+            return 'string'
+        return None
+
+class StringTypePrinter(object):
+    def __init__(self):
+        self.name = 'string'
+        self.enabled = True
+
+    def instantiate(self):
+        return Recognizer()
+
+gdb.type_printers.append(StringTypePrinter())
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index 0e1c93c..cf3ba38 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -38,6 +38,7 @@
 #include <errno.h>
 #include <ctype.h>
 #include "cli/cli-utils.h"
+#include "python/python.h"
 
 extern void _initialize_typeprint (void);
 
@@ -52,7 +53,9 @@ const struct type_print_options type_print_raw_options =
   1,				/* raw */
   1,				/* print_methods */
   1,				/* print_typedefs */
-  NULL				/* local_typedefs */
+  NULL,				/* local_typedefs */
+  NULL,				/* global_table */
+  NULL				/* global_printers */
 };
 
 /* The default flags for 'ptype' and 'whatis'.  */
@@ -62,7 +65,9 @@ static struct type_print_options default_ptype_flags =
   0,				/* raw */
   1,				/* print_methods */
   1,				/* print_typedefs */
-  NULL				/* local_typedefs */
+  NULL,				/* local_typedefs */
+  NULL,				/* global_table */
+  NULL				/* global_printers */
 };
 
 
@@ -235,6 +240,74 @@ copy_typedef_hash (struct typedef_hash_table *table)
   return result;
 }
 
+/* A cleanup to free the global typedef hash.  */
+
+static void
+do_free_global_table (void *arg)
+{
+  struct type_print_options *flags = arg;
+
+  free_typedef_hash (flags->global_typedefs);
+  free_type_printers (flags->global_printers);
+}
+
+/* Create the global typedef hash.  */
+
+static struct cleanup *
+create_global_typedef_table (struct type_print_options *flags)
+{
+  gdb_assert (flags->global_typedefs == NULL && flags->global_printers == NULL);
+  flags->global_typedefs = create_typedef_hash ();
+  flags->global_printers = start_type_printers ();
+  return make_cleanup (do_free_global_table, flags);
+}
+
+/* Look up the type T in the global typedef hash.  If it is found,
+   return the typedef name.  If it is not found, apply the
+   type-printers, if any, given by start_type_printers and return the
+   result.  A NULL return means that the name was not found.  */
+
+static const char *
+find_global_typedef (const struct type_print_options *flags,
+		     struct type *t)
+{
+  char *applied;
+  void **slot;
+  struct typedef_field tf, *new_tf;
+
+  if (flags->global_typedefs == NULL)
+    return NULL;
+
+  tf.name = NULL;
+  tf.type = t;
+
+  slot = htab_find_slot (flags->global_typedefs->table, &tf, INSERT);
+  if (*slot != NULL)
+    {
+      new_tf = *slot;
+      return new_tf->name;
+    }
+
+  /* Put an entry into the hash table now, in case apply_type_printers
+     recurses.  */
+  new_tf = XOBNEW (&flags->global_typedefs->storage, struct typedef_field);
+  new_tf->name = NULL;
+  new_tf->type = t;
+
+  *slot = new_tf;
+
+  applied = apply_type_printers (flags->global_printers, t);
+
+  if (applied != NULL)
+    {
+      new_tf->name = obstack_copy0 (&flags->global_typedefs->storage, applied,
+				    strlen (applied));
+      xfree (applied);
+    }
+
+  return new_tf->name;
+}
+
 /* Look up the type T in the typedef hash table in with FLAGS.  If T
    is in the table, return its short (class-relative) typedef name.
    Otherwise return NULL.  If the table is NULL, this always returns
@@ -243,16 +316,19 @@ copy_typedef_hash (struct typedef_hash_table *table)
 const char *
 find_typedef_in_hash (const struct type_print_options *flags, struct type *t)
 {
-  struct typedef_field tf, *found;
+  if (flags->local_typedefs != NULL)
+    {
+      struct typedef_field tf, *found;
 
-  if (flags->local_typedefs == NULL)
-    return NULL;
+      tf.name = NULL;
+      tf.type = t;
+      found = htab_find (flags->local_typedefs->table, &tf);
 
-  tf.name = NULL;
-  tf.type = t;
-  found = htab_find (flags->local_typedefs->table, &tf);
+      if (found != NULL)
+	return found->name;
+    }
 
-  return found == NULL ? NULL : found->name;
+  return find_global_typedef (flags, t);
 }
 
 
@@ -325,7 +401,7 @@ whatis_exp (char *exp, int show)
 {
   struct expression *expr;
   struct value *val;
-  struct cleanup *old_chain = NULL;
+  struct cleanup *old_chain;
   struct type *real_type = NULL;
   struct type *type;
   int full = 0;
@@ -334,6 +410,8 @@ whatis_exp (char *exp, int show)
   struct value_print_options opts;
   struct type_print_options flags = default_ptype_flags;
 
+  old_chain = make_cleanup (null_cleanup, NULL);
+
   if (exp)
     {
       if (*exp == '/')
@@ -373,7 +451,7 @@ whatis_exp (char *exp, int show)
 	}
 
       expr = parse_expression (exp);
-      old_chain = make_cleanup (free_current_contents, &expr);
+      make_cleanup (free_current_contents, &expr);
       val = evaluate_type (expr);
     }
   else
@@ -394,6 +472,9 @@ whatis_exp (char *exp, int show)
 
   printf_filtered ("type = ");
 
+  if (!flags.raw)
+    create_global_typedef_table (&flags);
+
   if (real_type)
     {
       printf_filtered ("/* real type = ");
@@ -406,8 +487,7 @@ whatis_exp (char *exp, int show)
   LA_PRINT_TYPE (type, "", gdb_stdout, show, 0, &flags);
   printf_filtered ("\n");
 
-  if (exp)
-    do_cleanups (old_chain);
+  do_cleanups (old_chain);
 }
 
 static void
diff --git a/gdb/typeprint.h b/gdb/typeprint.h
index 71bac01..a201bdd 100644
--- a/gdb/typeprint.h
+++ b/gdb/typeprint.h
@@ -38,6 +38,14 @@ struct type_print_options
   /* If not NULL, a local typedef hash table used when printing a
      type.  */
   struct typedef_hash_table *local_typedefs;
+
+  /* If not NULL, a global typedef hash table used when printing a
+     type.  */
+  struct typedef_hash_table *global_typedefs;
+
+  /* The list of type printers associated with the global typedef
+     table.  This is intentionally opaque.  */
+  void *global_printers;
 };
 
 extern const struct type_print_options type_print_raw_options;
-- 
1.7.7.6


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]