Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
1 # mozilla/prettyprinters.py --- infrastructure for SpiderMonkey's auto-loaded pretty-printers.
3 import gdb
4 import re
6 # Decorators for declaring pretty-printers.
7 #
8 # In each case, the decoratee should be a SpiderMonkey-style pretty-printer
9 # factory, taking both a gdb.Value instance and a TypeCache instance as
10 # arguments; see TypeCache, below.
12 # Check that |fn| hasn't been registered as a pretty-printer under some
13 # other name already. (The 'enabled' flags used by GDB's
14 # 'enable/disable/info pretty-printer' commands are simply stored as
15 # properties of the function objects themselves, so a single function
16 # object can't carry the 'enabled' flags for two different printers.)
17 def check_for_reused_pretty_printer(fn):
18 if hasattr(fn, 'enabled'):
19 raise RuntimeError, ("pretty-printer function %r registered more than once" % fn)
21 # a dictionary mapping gdb.Type tags to pretty-printer functions.
22 printers_by_tag = {}
24 # A decorator: add the decoratee as a pretty-printer lookup function for types
25 # named |type_name|.
26 def pretty_printer(type_name):
27 def add(fn):
28 check_for_reused_pretty_printer(fn)
29 add_to_subprinter_list(fn, type_name)
30 printers_by_tag[type_name] = fn
31 return fn
32 return add
34 # a dictionary mapping gdb.Type tags to pretty-printer functions for pointers to
35 # that type.
36 ptr_printers_by_tag = {}
38 # A decorator: add the decoratee as a pretty-printer lookup function for
39 # pointers to types named |type_name|.
40 def ptr_pretty_printer(type_name):
41 def add(fn):
42 check_for_reused_pretty_printer(fn)
43 add_to_subprinter_list(fn, "ptr-to-" + type_name)
44 ptr_printers_by_tag[type_name] = fn
45 return fn
46 return add
48 # a dictionary mapping gdb.Type tags to pretty-printer functions for
49 # references to that type.
50 ref_printers_by_tag = {}
52 # A decorator: add the decoratee as a pretty-printer lookup function for
53 # references to instances of types named |type_name|.
54 def ref_pretty_printer(type_name):
55 def add(fn):
56 check_for_reused_pretty_printer(fn)
57 add_to_subprinter_list(fn, "ref-to-" + type_name)
58 ref_printers_by_tag[type_name] = fn
59 return fn
60 return add
62 # a dictionary mapping the template name portion of gdb.Type tags to
63 # pretty-printer functions for instantiations of that template.
64 template_printers_by_tag = {}
66 # A decorator: add the decoratee as a pretty-printer lookup function for
67 # instantiations of templates named |template_name|.
68 def template_pretty_printer(template_name):
69 def add(fn):
70 check_for_reused_pretty_printer(fn)
71 add_to_subprinter_list(fn, 'instantiations-of-' + template_name)
72 template_printers_by_tag[template_name] = fn
73 return fn
74 return add
76 # A list of (REGEXP, PRINTER) pairs, such that if REGEXP (a RegexObject)
77 # matches the result of converting a gdb.Value's type to a string, then
78 # PRINTER is a pretty-printer lookup function that will probably like that
79 # value.
80 printers_by_regexp = []
82 # A decorator: add the decoratee as a pretty-printer factory for types
83 # that, when converted to a string, match |pattern|. Use |name| as the
84 # pretty-printer's name, when listing, enabling and disabling.
85 def pretty_printer_for_regexp(pattern, name):
86 compiled = re.compile(pattern)
87 def add(fn):
88 check_for_reused_pretty_printer(fn)
89 add_to_subprinter_list(fn, name)
90 printers_by_regexp.append((compiled, fn))
91 return fn
92 return add
94 # Forget all pretty-printer lookup functions defined in the module name
95 # |module_name|, if any exist. Use this at the top of each pretty-printer
96 # module like this:
97 #
98 # clear_module_printers(__name__)
99 def clear_module_printers(module_name):
100 global printers_by_tag, ptr_printers_by_tag, ref_printers_by_tag
101 global template_printers_by_tag, printers_by_regexp
103 # Remove all pretty-printers defined in the module named |module_name|
104 # from d.
105 def clear_dictionary(d):
106 # Walk the dictionary, building a list of keys whose entries we
107 # should remove. (It's not safe to delete entries from a dictionary
108 # while we're iterating over it.)
109 to_delete = []
110 for (k, v) in d.iteritems():
111 if v.__module__ == module_name:
112 to_delete.append(k)
113 remove_from_subprinter_list(v)
114 for k in to_delete:
115 del d[k]
117 clear_dictionary(printers_by_tag)
118 clear_dictionary(ptr_printers_by_tag)
119 clear_dictionary(ref_printers_by_tag)
120 clear_dictionary(template_printers_by_tag)
122 # Iterate over printers_by_regexp, deleting entries from the given module.
123 new_list = []
124 for p in printers_by_regexp:
125 if p.__module__ == module_name:
126 remove_from_subprinter_list(p)
127 else:
128 new_list.append(p)
129 printers_by_regexp = new_list
131 # Our subprinters array. The 'subprinters' attributes of all lookup
132 # functions returned by lookup_for_objfile point to this array instance,
133 # which we mutate as subprinters are added and removed.
134 subprinters = []
136 # Set up the 'name' and 'enabled' attributes on |subprinter|, and add it to our
137 # list of all SpiderMonkey subprinters.
138 def add_to_subprinter_list(subprinter, name):
139 subprinter.name = name
140 subprinter.enabled = True
141 subprinters.append(subprinter)
143 # Remove |subprinter| from our list of all SpiderMonkey subprinters.
144 def remove_from_subprinter_list(subprinter):
145 subprinters.remove(subprinter)
147 # An exception class meaning, "This objfile has no SpiderMonkey in it."
148 class NotSpiderMonkeyObjfileError(TypeError):
149 pass
151 # TypeCache: a cache for frequently used information about an objfile.
152 #
153 # When a new SpiderMonkey objfile is loaded, we construct an instance of
154 # this class for it. Then, whenever we construct a pretty-printer for some
155 # gdb.Value, we also pass, as a second argument, the TypeCache for the
156 # objfile to which that value's type belongs.
157 #
158 # if objfile doesn't seem to have SpiderMonkey code in it, the constructor
159 # raises NotSpiderMonkeyObjfileError.
160 #
161 # Pretty-printer modules may add attributes to this to hold their own
162 # cached values. Such attributes should be named mod_NAME, where the module
163 # is named mozilla.NAME; for example, mozilla.JSString should store its
164 # metadata in the TypeCache's mod_JSString attribute.
165 class TypeCache(object):
166 def __init__(self, objfile):
167 self.objfile = objfile
169 # Unfortunately, the Python interface doesn't allow us to specify
170 # the objfile in whose scope lookups should occur. But simply
171 # knowing that we need to lookup the types afresh is probably
172 # enough.
173 self.void_t = gdb.lookup_type('void')
174 self.void_ptr_t = self.void_t.pointer()
175 try:
176 self.JSString_ptr_t = gdb.lookup_type('JSString').pointer()
177 self.JSObject_ptr_t = gdb.lookup_type('JSObject').pointer()
178 except gdb.error:
179 raise NotSpiderMonkeyObjfileError
181 self.mod_JSString = None
182 self.mod_JSObject = None
183 self.mod_jsval = None
185 # Yield a series of all the types that |t| implements, by following typedefs
186 # and iterating over base classes. Specifically:
187 # - |t| itself is the first value yielded.
188 # - If we yield a typedef, we later yield its definition.
189 # - If we yield a type with base classes, we later yield those base classes.
190 # - If we yield a type with some base classes that are typedefs,
191 # we yield all the type's base classes before following the typedefs.
192 # (Actually, this never happens, because G++ doesn't preserve the typedefs in
193 # the DWARF.)
194 #
195 # This is a hokey attempt to order the implemented types by meaningfulness when
196 # pretty-printed. Perhaps it is entirely misguided, and we should actually
197 # collect all applicable pretty-printers, and then use some ordering on the
198 # pretty-printers themselves.
199 #
200 # We may yield a type more than once (say, if it appears more than once in the
201 # class hierarchy).
202 def implemented_types(t):
204 # Yield all types that follow |t|.
205 def followers(t):
206 if t.code == gdb.TYPE_CODE_TYPEDEF:
207 yield t.target()
208 for t2 in followers(t.target()): yield t2
209 elif t.code == gdb.TYPE_CODE_STRUCT:
210 base_classes = []
211 for f in t.fields():
212 if f.is_base_class:
213 yield f.type
214 base_classes.append(f.type)
215 for b in base_classes:
216 for t2 in followers(b): yield t2
218 yield t
219 for t2 in followers(t): yield t2
221 template_regexp = re.compile("([\w_:]+)<")
223 # Construct and return a pretty-printer lookup function for objfile, or
224 # return None if the objfile doesn't contain SpiderMonkey code
225 # (specifically, definitions for SpiderMonkey types).
226 def lookup_for_objfile(objfile):
227 # Create a type cache for this objfile.
228 try:
229 cache = TypeCache(objfile)
230 except NotSpiderMonkeyObjfileError:
231 if gdb.parameter("verbose"):
232 gdb.write("objfile '%s' has no SpiderMonkey code; not registering pretty-printers\n"
233 % (objfile.filename,))
234 return None
236 # Return a pretty-printer for |value|, if we have one. This is the lookup
237 # function object we place in each gdb.Objfile's pretty-printers list, so it
238 # carries |name|, |enabled|, and |subprinters| attributes.
239 def lookup(value):
240 # If |table| has a pretty-printer for |tag|, apply it to |value|.
241 def check_table(table, tag):
242 if tag in table:
243 f = table[tag]
244 if f.enabled:
245 return f(value, cache)
246 return None
248 def check_table_by_type_name(table, t):
249 if t.code == gdb.TYPE_CODE_TYPEDEF:
250 return check_table(table, str(t))
251 elif t.code == gdb.TYPE_CODE_STRUCT and t.tag:
252 return check_table(table, t.tag)
253 else:
254 return None
256 for t in implemented_types(value.type):
257 if t.code == gdb.TYPE_CODE_PTR:
258 for t2 in implemented_types(t.target()):
259 p = check_table_by_type_name(ptr_printers_by_tag, t2)
260 if p: return p
261 elif t.code == gdb.TYPE_CODE_REF:
262 for t2 in implemented_types(t.target()):
263 p = check_table_by_type_name(ref_printers_by_tag, t2)
264 if p: return p
265 else:
266 p = check_table_by_type_name(printers_by_tag, t)
267 if p: return p
268 if t.code == gdb.TYPE_CODE_STRUCT and t.tag:
269 m = template_regexp.match(t.tag)
270 if m:
271 p = check_table(template_printers_by_tag, m.group(1))
272 if p: return p
274 # Failing that, look for a printer in printers_by_regexp. We have
275 # to scan the whole list, so regexp printers should be used
276 # sparingly.
277 s = str(value.type)
278 for (r, f) in printers_by_regexp:
279 if f.enabled:
280 m = r.match(s)
281 if m:
282 p = f(value, cache)
283 if p: return p
285 # No luck.
286 return None
288 # Give |lookup| the attributes expected of a pretty-printer with
289 # subprinters, for enabling and disabling.
290 lookup.name = "SpiderMonkey"
291 lookup.enabled = True
292 lookup.subprinters = subprinters
294 return lookup
296 # A base class for pretty-printers for pointer values that handles null
297 # pointers, by declining to construct a pretty-printer for them at all.
298 # Derived classes may simply assume that self.value is non-null.
299 #
300 # To help share code, this class can also be used with reference types.
301 #
302 # This class provides the following methods, which subclasses are free to
303 # override:
304 #
305 # __init__(self, value, cache): Save value and cache as properties by those names
306 # on the instance.
307 #
308 # to_string(self): format the type's name and address, as GDB would, and then
309 # call a 'summary' method (which the subclass must define) to produce a
310 # description of the referent.
311 #
312 # Note that pretty-printers returning a 'string' display hint must not use
313 # this default 'to_string' method, as GDB will take everything it returns,
314 # including the type name and address, as string contents.
315 class Pointer(object):
316 def __new__(cls, value, cache):
317 # Don't try to provide pretty-printers for NULL pointers.
318 if value.type.strip_typedefs().code == gdb.TYPE_CODE_PTR and value == 0:
319 return None
320 return super(Pointer, cls).__new__(cls)
322 def __init__(self, value, cache):
323 self.value = value
324 self.cache = cache
326 def to_string(self):
327 # See comment above.
328 assert not hasattr(self, 'display_hint') or self.display_hint() != 'string'
329 concrete_type = self.value.type.strip_typedefs()
330 if concrete_type.code == gdb.TYPE_CODE_PTR:
331 address = self.value.cast(self.cache.void_ptr_t)
332 elif concrete_type.code == gdb.TYPE_CODE_REF:
333 address = '@' + str(self.value.address.cast(self.cache.void_ptr_t))
334 else:
335 assert not "mozilla.prettyprinters.Pointer applied to bad value type"
336 try:
337 summary = self.summary()
338 except gdb.MemoryError as r:
339 summary = str(r)
340 v = '(%s) %s %s' % (self.value.type, address, summary)
341 return v
343 def summary(self):
344 raise NotImplementedError
346 field_enum_value = None
348 # Given |t|, a gdb.Type instance representing an enum type, return the
349 # numeric value of the enum value named |name|.
350 #
351 # Pre-2012-4-18 versions of GDB store the value of an enum member on the
352 # gdb.Field's 'bitpos' attribute; later versions store it on the 'enumval'
353 # attribute. This function retrieves the value from either.
354 def enum_value(t, name):
355 global field_enum_value
356 f = t[name]
357 # Monkey-patching is a-okay in polyfills! Just because.
358 if not field_enum_value:
359 if hasattr(f, 'enumval'):
360 field_enum_value = lambda f: f.enumval
361 else:
362 field_enum_value = lambda f: f.bitpos
363 return field_enum_value(f)