Commit | Line | Data |
---|---|---|
7b51bc51 DE |
1 | # Pretty-printer utilities. |
2 | # Copyright (C) 2010 Free Software Foundation, Inc. | |
3 | ||
4 | # This program is free software; you can redistribute it and/or modify | |
5 | # it under the terms of the GNU General Public License as published by | |
6 | # the Free Software Foundation; either version 3 of the License, or | |
7 | # (at your option) any later version. | |
8 | # | |
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. | |
13 | # | |
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | """Utilities for working with pretty-printers.""" | |
18 | ||
19 | import gdb | |
20 | import gdb.types | |
21 | import re | |
22 | ||
23 | ||
24 | class PrettyPrinter(object): | |
25 | """A basic pretty-printer. | |
26 | ||
27 | Attributes: | |
28 | name: A unique string among all printers for the context in which | |
29 | it is defined (objfile, progspace, or global(gdb)), and should | |
30 | meaningfully describe what can be pretty-printed. | |
31 | E.g., "StringPiece" or "protobufs". | |
32 | subprinters: An iterable object with each element having a `name' | |
33 | attribute, and, potentially, "enabled" attribute. | |
34 | Or this is None if there are no subprinters. | |
35 | enabled: A boolean indicating if the printer is enabled. | |
36 | ||
37 | Subprinters are for situations where "one" pretty-printer is actually a | |
38 | collection of several printers. E.g., The libstdc++ pretty-printer has | |
39 | a pretty-printer for each of several different types, based on regexps. | |
40 | """ | |
41 | ||
42 | # While one might want to push subprinters into the subclass, it's | |
43 | # present here to formalize such support to simplify | |
44 | # commands/pretty_printers.py. | |
45 | ||
46 | def __init__(self, name, subprinters=None): | |
47 | self.name = name | |
48 | self.subprinters = subprinters | |
49 | self.enabled = True | |
50 | ||
51 | def __call__(self, val): | |
52 | # The subclass must define this. | |
53 | raise NotImplementedError("PrettyPrinter __call__") | |
54 | ||
55 | ||
56 | class SubPrettyPrinter(object): | |
57 | """Baseclass for sub-pretty-printers. | |
58 | ||
59 | Sub-pretty-printers needn't use this, but it formalizes what's needed. | |
60 | ||
61 | Attributes: | |
62 | name: The name of the subprinter. | |
63 | enabled: A boolean indicating if the subprinter is enabled. | |
64 | """ | |
65 | ||
66 | def __init__(self, name): | |
67 | self.name = name | |
68 | self.enabled = True | |
69 | ||
70 | ||
71 | def register_pretty_printer(obj, printer): | |
72 | """Register pretty-printer PRINTER with OBJ. | |
73 | ||
74 | The printer is added to the front of the search list, thus one can override | |
75 | an existing printer if one needs to. | |
76 | ||
77 | Arguments: | |
78 | obj: Either an objfile, progspace, or None (in which case the printer | |
79 | is registered globally). | |
80 | printer: Either a function of one argument (old way) or any object | |
81 | which has attributes: name, enabled, __call__. | |
82 | ||
83 | Returns: | |
84 | Nothing. | |
85 | ||
86 | Raises: | |
87 | TypeError: A problem with the type of the printer. | |
88 | ValueError: The printer's name contains a colon ":". | |
89 | ||
90 | If the caller wants the printer to be listable and disableable, it must | |
91 | follow the PrettyPrinter API. This applies to the old way (functions) too. | |
92 | If printer is an object, __call__ is a method of two arguments: | |
93 | self, and the value to be pretty-printed. See PrettyPrinter. | |
94 | """ | |
95 | ||
96 | # Watch for both __name__ and name. | |
97 | # Functions get the former for free, but we don't want to use an | |
98 | # attribute named __foo__ for pretty-printers-as-objects. | |
99 | # If printer has both, we use `name'. | |
100 | if not hasattr(printer, "__name__") and not hasattr(printer, "name"): | |
101 | raise TypeError("printer missing attribute: name") | |
102 | if hasattr(printer, "name") and not hasattr(printer, "enabled"): | |
103 | raise TypeError("printer missing attribute: enabled") | |
104 | if not hasattr(printer, "__call__"): | |
105 | raise TypeError("printer missing attribute: __call__") | |
106 | ||
107 | if obj is None: | |
108 | if gdb.parameter("verbose"): | |
109 | gdb.write("Registering global %s pretty-printer ...\n" % name) | |
110 | obj = gdb | |
111 | else: | |
112 | if gdb.parameter("verbose"): | |
113 | gdb.write("Registering %s pretty-printer for %s ...\n" % | |
114 | (printer.name, obj.filename)) | |
115 | ||
116 | if hasattr(printer, "name"): | |
117 | if not isinstance(printer.name, basestring): | |
118 | raise TypeError("printer name is not a string") | |
119 | # If printer provides a name, make sure it doesn't contain ":". | |
120 | # Colon is used by the info/enable/disable pretty-printer commands | |
121 | # to delimit subprinters. | |
122 | if printer.name.find(":") >= 0: | |
123 | raise ValueError("colon ':' in printer name") | |
124 | # Also make sure the name is unique. | |
125 | # Alas, we can't do the same for functions and __name__, they could | |
126 | # all have a canonical name like "lookup_function". | |
127 | # PERF: gdb records printers in a list, making this inefficient. | |
128 | if (printer.name in | |
129 | [p.name for p in obj.pretty_printers if hasattr(p, "name")]): | |
130 | raise RuntimeError("pretty-printer already registered: %s" % | |
131 | printer.name) | |
132 | ||
133 | obj.pretty_printers.insert(0, printer) | |
134 | ||
135 | ||
136 | class RegexpCollectionPrettyPrinter(PrettyPrinter): | |
137 | """Class for implementing a collection of regular-expression based pretty-printers. | |
138 | ||
139 | Intended usage: | |
140 | ||
141 | pretty_printer = RegexpCollectionPrettyPrinter("my_library") | |
142 | pretty_printer.add_printer("myclass1", "^myclass1$", MyClass1Printer) | |
143 | ... | |
144 | pretty_printer.add_printer("myclassN", "^myclassN$", MyClassNPrinter) | |
145 | register_pretty_printer(obj, pretty_printer) | |
146 | """ | |
147 | ||
148 | class RegexpSubprinter(SubPrettyPrinter): | |
149 | def __init__(self, name, regexp, gen_printer): | |
150 | super(RegexpCollectionPrettyPrinter.RegexpSubprinter, self).__init__(name) | |
151 | self.regexp = regexp | |
152 | self.gen_printer = gen_printer | |
153 | self.compiled_re = re.compile(regexp) | |
154 | ||
155 | def __init__(self, name): | |
156 | super(RegexpCollectionPrettyPrinter, self).__init__(name, []) | |
157 | ||
158 | def add_printer(self, name, regexp, gen_printer): | |
159 | """Add a printer to the list. | |
160 | ||
161 | The printer is added to the end of the list. | |
162 | ||
163 | Arguments: | |
164 | name: The name of the subprinter. | |
165 | regexp: The regular expression, as a string. | |
166 | gen_printer: A function/method that given a value returns an | |
167 | object to pretty-print it. | |
168 | ||
169 | Returns: | |
170 | Nothing. | |
171 | """ | |
172 | ||
173 | # NOTE: A previous version made the name of each printer the regexp. | |
174 | # That makes it awkward to pass to the enable/disable commands (it's | |
175 | # cumbersome to make a regexp of a regexp). So now the name is a | |
176 | # separate parameter. | |
177 | ||
178 | self.subprinters.append(self.RegexpSubprinter(name, regexp, | |
179 | gen_printer)) | |
180 | ||
181 | def __call__(self, val): | |
182 | """Lookup the pretty-printer for the provided value.""" | |
183 | ||
184 | # Get the type name. | |
185 | typename = gdb.types.get_basic_type(val.type).tag | |
186 | if not typename: | |
187 | return None | |
188 | ||
189 | # Iterate over table of type regexps to determine | |
190 | # if a printer is registered for that type. | |
191 | # Return an instantiation of the printer if found. | |
192 | for printer in self.subprinters: | |
193 | if printer.enabled and printer.compiled_re.search(typename): | |
194 | return printer.gen_printer(val) | |
195 | ||
196 | # Cannot find a pretty printer. Return None. | |
197 | return None |