~ubuntu-branches/ubuntu/vivid/mozjs24/vivid

« back to all changes in this revision

Viewing changes to js/src/gdb/mozilla/prettyprinters.py

  • Committer: Package Import Robot
  • Author(s): Tim Lunn
  • Date: 2014-02-11 21:55:34 UTC
  • Revision ID: package-import@ubuntu.com-20140211215534-m1zyq5aj59md3y07
Tags: upstream-24.2.0
ImportĀ upstreamĀ versionĀ 24.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# mozilla/prettyprinters.py --- infrastructure for SpiderMonkey's auto-loaded pretty-printers.
 
2
 
 
3
import gdb
 
4
import re
 
5
 
 
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.
 
11
 
 
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)
 
20
 
 
21
# a dictionary mapping gdb.Type tags to pretty-printer functions.
 
22
printers_by_tag = {}
 
23
 
 
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
 
33
 
 
34
# a dictionary mapping gdb.Type tags to pretty-printer functions for pointers to
 
35
# that type.
 
36
ptr_printers_by_tag = {}
 
37
 
 
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
 
47
 
 
48
# a dictionary mapping gdb.Type tags to pretty-printer functions for
 
49
# references to that type.
 
50
ref_printers_by_tag = {}
 
51
 
 
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
 
61
 
 
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 = {}
 
65
 
 
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
 
75
 
 
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 = []
 
81
 
 
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
 
93
 
 
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
 
102
 
 
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]
 
116
 
 
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)
 
121
 
 
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
 
130
 
 
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 = []
 
135
 
 
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)
 
142
 
 
143
# Remove |subprinter| from our list of all SpiderMonkey subprinters.
 
144
def remove_from_subprinter_list(subprinter):
 
145
    subprinters.remove(subprinter)
 
146
 
 
147
# An exception class meaning, "This objfile has no SpiderMonkey in it."
 
148
class NotSpiderMonkeyObjfileError(TypeError):
 
149
    pass
 
150
 
 
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
 
168
 
 
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
 
180
 
 
181
        self.mod_JSString = None
 
182
        self.mod_JSObject = None
 
183
        self.mod_jsval = None
 
184
 
 
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):
 
203
 
 
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
 
217
 
 
218
    yield t
 
219
    for t2 in followers(t): yield t2
 
220
 
 
221
template_regexp = re.compile("([\w_:]+)<")
 
222
 
 
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
 
235
 
 
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
 
247
 
 
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
 
255
 
 
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
 
273
 
 
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
 
284
 
 
285
        # No luck.
 
286
        return None
 
287
 
 
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
 
293
 
 
294
    return lookup
 
295
 
 
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)
 
321
 
 
322
    def __init__(self, value, cache):
 
323
        self.value = value
 
324
        self.cache = cache
 
325
 
 
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
 
342
 
 
343
    def summary(self):
 
344
        raise NotImplementedError
 
345
 
 
346
field_enum_value = None
 
347
 
 
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)