js/src/gdb/mozilla/prettyprinters.py

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial