* NEWS: Update.
authorTom Tromey <tromey@redhat.com>
Mon, 12 Nov 2012 17:41:59 +0000 (17:41 +0000)
committerTom Tromey <tromey@redhat.com>
Mon, 12 Nov 2012 17:41:59 +0000 (17:41 +0000)
* 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.
doc
* 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.
testsuite
* 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.

19 files changed:
gdb/ChangeLog
gdb/NEWS
gdb/data-directory/Makefile.in
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/python/lib/gdb/__init__.py
gdb/python/lib/gdb/command/type_printers.py [new file with mode: 0644]
gdb/python/lib/gdb/types.py
gdb/python/py-objfile.c
gdb/python/py-progspace.c
gdb/python/python.c
gdb/python/python.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/completion.exp
gdb/testsuite/gdb.python/py-typeprint.cc [new file with mode: 0644]
gdb/testsuite/gdb.python/py-typeprint.exp [new file with mode: 0644]
gdb/testsuite/gdb.python/py-typeprint.py [new file with mode: 0644]
gdb/typeprint.c
gdb/typeprint.h

index fcf5ffc13f11d99fe46085e7053da9e37f399d15..583f1c024800f8832a1a6eb6024b919189361469 100644 (file)
@@ -1,3 +1,43 @@
+2012-11-12  Tom Tromey  <tromey@redhat.com>
+
+       * 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.
+
 2012-11-12  Tom Tromey  <tromey@redhat.com>
 
        * c-typeprint.c (find_typedef_for_canonicalize,
index b7a92720efc2b67014f585d0f52136af2863acbf..739a7b3d75adcc656b11e00c946d859c37534d87 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -17,6 +17,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)
@@ -51,6 +53,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
index 41947c63d295ba0a5b65ad50ac2c41a34d001930..2e2fbf9eb0e50c779c36f8b489bc7b4aac12c51b 100644 (file)
@@ -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 \
index f59b001e3f6b450e06acc0bd20f904e56dfa8741..0410dbc4c4ce6ea726de62d8538fcd6734b0b52d 100644 (file)
@@ -1,3 +1,14 @@
+2012-11-12  Tom Tromey  <tromey@redhat.com>
+
+       * 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.
+
 2012-11-12  Tom Tromey  <tromey@redhat.com>
 
        * gdb.texinfo (Symbols): Document "set print type methods",
index 6f9d3f654d2a49165f51be0f7bf8878949900d65..86cfe8e2c57882ba41063c52efe4dd3174006619 100644 (file)
@@ -15218,6 +15218,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.
+
+@code{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}
@@ -22671,6 +22687,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.
@@ -23966,6 +23983,68 @@ my_library.so:
     bar
 @end smallexample
 
+@node Type Printing API
+@subsubsection Type Printing API
+@cindex type printing API for Python
+
+@value{GDBN} provides a way for Python code to customize type display.
+This is mainly useful for substituting canonical typedef names for
+types.
+
+@cindex type printer
+A @dfn{type printer} is just a Python object conforming to a certain
+protocol.  A simple base class implementing the protocol is provided;
+see @ref{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 @code{enable type-printer}
+and @code{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 @code{enable type-printer} and @code{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 described below.
+@end defmethod
+
+
+When displaying a type, say via the @code{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, stopping if the function returns a non-@code{None} value.
+The recognition function is defined as:
+
+@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.  For
+example, it can be convenient to look up type information in a type
+printer and hold it for a recognizer's lifetime; if a single pass were
+done then type printers would have to make use of the event system in
+order to avoid holding information that could become stale as the
+inferior changed.
+
 @node Inferiors In Python
 @subsubsection Inferiors In Python
 @cindex inferiors in Python
@@ -24810,6 +24889,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
 
@@ -24855,6 +24939,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 ()
@@ -26057,7 +26146,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})
@@ -26118,6 +26207,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
index 0e020fe4858180ef114dcf00076a7f32b3b809af..067152641b5d37d1ed4a1d7eaf1153a251961df7 100644 (file)
@@ -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 (file)
index 0000000..b7d6930
--- /dev/null
@@ -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()
index 66c952896bbf41289f73649a7974751af72bd177..3745383079928aa494b8364eb3276333a66246c0 100644 (file)
@@ -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)
index 9fa6813f62e74aad1749f68663a907d8a2a63e98..5d2398f547126532d1167b380adaa0c22afe564c 100644 (file)
@@ -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 }
 };
 
index e4b029b2070549864cd60a7fa5e1fa99f7d9818b..c1b1cac52a728df50a67fd6e02e90728b5fee19c 100644 (file)
@@ -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;
+}
+
 \f
 
 /* 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 }
 };
 
index cd50e509759dd7ccd4bf57b1fec855f0de67ab56..359d2385662993a86784154e61af0777c93dd5e5 100644 (file)
@@ -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 */
 
 \f
index 0d072712af4e6da693a879c75d3b20a906ba4da0..72872b0b1168d5c602be542bdd76ab5b38c048bb 100644 (file)
@@ -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 */
index 9e043268e8232b02f147e4cfcffc73adc1722b06..5293c88c090d13b540519854db25fdb8a484a96b 100644 (file)
@@ -1,3 +1,10 @@
+2012-11-12  Tom Tromey  <tromey@redhat.com>
+
+       * 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.
+
 2012-11-12  Tom Tromey  <tromey@redhat.com>
 
        * gdb.base/call-sc.exp: Use "ptype/r".
index ccdc199e670ba754af92a09276491949bfe6f7ea..8b1facb1ab383374c9d7487a733a8d373ca338d4 100644 (file)
@@ -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 (file)
index 0000000..65c06f0
--- /dev/null
@@ -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 (file)
index 0000000..ef07055
--- /dev/null
@@ -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 (file)
index 0000000..a4351cd
--- /dev/null
@@ -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())
index 0e1c93c0a8c870c30320c9635105d6878e749b05..cf3ba381a44c616600f6f99596bb1a2f2a899be0 100644 (file)
@@ -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 */
 };
 
 \f
@@ -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);
 }
 
 \f
@@ -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
index 71bac0189999f0fda913313e940a6020bb276231..a201bddd01e74d493feb25cebe15936b42f8ba2c 100644 (file)
@@ -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;