Bug 822563: Pretty-print references to JSObject and its subclasses. r=sfink
authorJim Blandy <jimb@mozilla.com>
Fri, 21 Dec 2012 14:49:21 -0800
changeset 125941 723e403c36b15bf3955cedd2ee1b6dc85eda35ea
parent 125940 767debcb04d8d217de59d52f1523eaefa2a24a14
child 125942 e6940cd4d42a725c24e5493f3bf76009245928c4
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs822563
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 822563: Pretty-print references to JSObject and its subclasses. r=sfink
js/src/gdb/mozilla/JSObject.py
js/src/gdb/mozilla/prettyprinters.py
js/src/gdb/tests/test-JSObject.cpp
js/src/gdb/tests/test-JSObject.py
js/src/gdb/tests/test-prettyprinters.py
--- a/js/src/gdb/mozilla/JSObject.py
+++ b/js/src/gdb/mozilla/JSObject.py
@@ -1,42 +1,48 @@
 # Pretty-printers for SpiderMonkey JSObjects.
 
 import gdb
 import mozilla.JSString
 import mozilla.prettyprinters as prettyprinters
-from mozilla.prettyprinters import ptr_pretty_printer
+from mozilla.prettyprinters import ptr_pretty_printer, ref_pretty_printer
 from mozilla.Root import deref
 
 prettyprinters.clear_module_printers(__name__)
 
 class JSObjectTypeCache(object):
     def __init__(self, value, cache):
         baseshape_flags = gdb.lookup_type('js::BaseShape::Flag')
         self.flag_DELEGATE = prettyprinters.enum_value(baseshape_flags, 'js::BaseShape::DELEGATE')
         self.func_ptr_type = gdb.lookup_type('JSFunction').pointer()
 
 # There should be no need to register this for JSFunction as well, since we
 # search for pretty-printers under the names of base classes, and
 # JSFunction has JSObject as a base class.
 
 @ptr_pretty_printer('JSObject')
-class JSObjectPtr(prettyprinters.Pointer):
+class JSObjectPtrOrRef(prettyprinters.Pointer):
     def __init__(self, value, cache):
-        super(JSObjectPtr, self).__init__(value, cache)
+        super(JSObjectPtrOrRef, self).__init__(value, cache)
         if not cache.mod_JSObject:
             cache.mod_JSObject = JSObjectTypeCache(value, cache)
         self.otc = cache.mod_JSObject
 
     def summary(self):
         shape = deref(self.value['shape_'])
         baseshape = deref(shape['base_'])
         class_name = baseshape['clasp']['name'].string()
         flags = baseshape['flags']
         is_delegate = bool(flags & self.otc.flag_DELEGATE)
         name = None
         if class_name == 'Function':
-            function = self.value.cast(self.otc.func_ptr_type)
+            if self.value.type.code == gdb.TYPE_CODE_PTR:
+                function = self.value.cast(self.otc.func_ptr_type)
+            elif self.value.type.code == gdb.TYPE_CODE_REF:
+                function = self.value.address.cast(self.otc.func_ptr_type)
             atom = deref(function['atom_'])
             name = str(atom) if atom else '<unnamed>'
         return '[object %s%s]%s' % (class_name,
                                     ' ' + name if name else '',
                                     ' delegate' if is_delegate else '')
+
+@ref_pretty_printer('JSObject')
+def JSObjectRef(value, cache): return JSObjectPtrOrRef(value, cache)
--- a/js/src/gdb/mozilla/prettyprinters.py
+++ b/js/src/gdb/mozilla/prettyprinters.py
@@ -40,16 +40,30 @@ ptr_printers_by_tag = {}
 def ptr_pretty_printer(type_name):
     def add(fn):
         check_for_reused_pretty_printer(fn)
         add_to_subprinter_list(fn, "ptr-to-" + type_name)
         ptr_printers_by_tag[type_name] = fn
         return fn
     return add
 
+# a dictionary mapping gdb.Type tags to pretty-printer functions for
+# references to that type.
+ref_printers_by_tag = {}
+
+# A decorator: add the decoratee as a pretty-printer lookup function for
+# references to instances of types named |type_name|.
+def ref_pretty_printer(type_name):
+    def add(fn):
+        check_for_reused_pretty_printer(fn)
+        add_to_subprinter_list(fn, "ref-to-" + type_name)
+        ref_printers_by_tag[type_name] = fn
+        return fn
+    return add
+
 # a dictionary mapping the template name portion of gdb.Type tags to
 # pretty-printer functions for instantiations of that template.
 template_printers_by_tag = {}
 
 # A decorator: add the decoratee as a pretty-printer lookup function for
 # instantiations of templates named |template_name|.
 def template_pretty_printer(template_name):
     def add(fn):
@@ -78,17 +92,18 @@ def pretty_printer_for_regexp(pattern, n
     return add
 
 # Forget all pretty-printer lookup functions defined in the module name
 # |module_name|, if any exist. Use this at the top of each pretty-printer
 # module like this:
 #
 #   clear_module_printers(__name__)
 def clear_module_printers(module_name):
-    global printers_by_tag, ptr_printers_by_tag, template_printers_by_tag, printers_by_regexp
+    global printers_by_tag, ptr_printers_by_tag, ref_printers_by_tag
+    global template_printers_by_tag, printers_by_regexp
 
     # Remove all pretty-printers defined in the module named |module_name|
     # from d.
     def clear_dictionary(d):
         # Walk the dictionary, building a list of keys whose entries we
         # should remove. (It's not safe to delete entries from a dictionary
         # while we're iterating over it.)
         to_delete = []
@@ -96,16 +111,17 @@ def clear_module_printers(module_name):
             if v.__module__ == module_name:
                 to_delete.append(k)
                 remove_from_subprinter_list(v)
         for k in to_delete:
             del d[k]
 
     clear_dictionary(printers_by_tag)
     clear_dictionary(ptr_printers_by_tag)
+    clear_dictionary(ref_printers_by_tag)
     clear_dictionary(template_printers_by_tag)
 
     # Iterate over printers_by_regexp, deleting entries from the given module.
     new_list = []
     for p in printers_by_regexp:
         if p.__module__ == module_name:
             remove_from_subprinter_list(p)
         else:
@@ -221,38 +237,41 @@ def lookup_for_objfile(objfile):
         # If |table| has a pretty-printer for |tag|, apply it to |value|.
         def check_table(table, tag):
             if tag in table:
                 f = table[tag]
                 if f.enabled:
                     return f(value, cache)
             return None
 
+        def check_table_by_type_name(table, t):
+            if t.code == gdb.TYPE_CODE_TYPEDEF:
+                return check_table(table, str(t))
+            elif t.code == gdb.TYPE_CODE_STRUCT and t.tag:
+                return check_table(table, t.tag)
+            else:
+                return None
+
         for t in implemented_types(value.type):
             if t.code == gdb.TYPE_CODE_PTR:
                 for t2 in implemented_types(t.target()):
-                    if t2.code == gdb.TYPE_CODE_TYPEDEF:
-                        p = check_table(ptr_printers_by_tag, str(t2))
-                    elif t2.code == gdb.TYPE_CODE_STRUCT and t2.tag:
-                        p = check_table(ptr_printers_by_tag, t2.tag)
-                    else:
-                        p = None
+                    p = check_table_by_type_name(ptr_printers_by_tag, t2)
+                    if p: return p
+            elif t.code == gdb.TYPE_CODE_REF:
+                for t2 in implemented_types(t.target()):
+                    p = check_table_by_type_name(ref_printers_by_tag, t2)
                     if p: return p
             else:
-                if t.code == gdb.TYPE_CODE_TYPEDEF:
-                    p = check_table(printers_by_tag, str(t))
-                elif t.code == gdb.TYPE_CODE_STRUCT and t.tag:
+                p = check_table_by_type_name(printers_by_tag, t)
+                if p: return p
+                if t.code == gdb.TYPE_CODE_STRUCT and t.tag:
                     m = template_regexp.match(t.tag)
                     if m:
                         p = check_table(template_printers_by_tag, m.group(1))
-                    else:
-                        p = check_table(printers_by_tag, t.tag)
-                else:
-                    p = None
-                if p: return p
+                        if p: return p
 
         # Failing that, look for a printer in printers_by_regexp. We have
         # to scan the whole list, so regexp printers should be used
         # sparingly.
         s = str(value.type)
         for (r, f) in printers_by_regexp:
             if f.enabled:
                 m = r.match(s)
@@ -270,43 +289,49 @@ def lookup_for_objfile(objfile):
     lookup.subprinters = subprinters
 
     return lookup
 
 # A base class for pretty-printers for pointer values that handles null
 # pointers, by declining to construct a pretty-printer for them at all.
 # Derived classes may simply assume that self.value is non-null.
 #
+# To help share code, this class can also be used with reference types.
+#
 # This class provides the following methods, which subclasses are free to
 # override:
 #
 # __init__(self, value, cache): Save value and cache as properties by those names
 #     on the instance.
 #
 # to_string(self): format the type's name and address, as GDB would, and then
 #     call a 'summary' method (which the subclass must define) to produce a
 #     description of the referent.
 #
 #     Note that pretty-printers returning a 'string' display hint must not use
 #     this default 'to_string' method, as GDB will take everything it returns,
 #     including the type name and address, as string contents.
 class Pointer(object):
     def __new__(cls, value, cache):
         # Don't try to provide pretty-printers for NULL pointers.
-        if value == 0: return None
+        if value.type.code == gdb.TYPE_CODE_PTR and value == 0:
+            return None
         return super(Pointer, cls).__new__(cls)
 
     def __init__(self, value, cache):
         self.value = value
         self.cache = cache
 
     def to_string(self):
         # See comment above.
         assert not hasattr(self, 'display_hint') or self.display_hint() != 'string'
-        address = self.value.cast(self.cache.void_ptr_t)
+        if self.value.type.code == gdb.TYPE_CODE_PTR:
+            address = self.value.cast(self.cache.void_ptr_t)
+        elif self.value.type.code == gdb.TYPE_CODE_REF:
+            address = '@' + str(self.value.address.cast(self.cache.void_ptr_t))
         try:
             summary = self.summary()
         except gdb.MemoryError as r:
             summary = str(r)
         v = '(%s) %s %s' % (self.value.type, address, summary)
         return v
 
     def summary(self):
--- a/js/src/gdb/tests/test-JSObject.cpp
+++ b/js/src/gdb/tests/test-JSObject.cpp
@@ -5,25 +5,29 @@ FRAGMENT(JSObject, simple) {
   js::Rooted<JSObject *> plain(cx, JS_NewObject(cx, 0, 0, 0));
   js::Rooted<JSObject *> func(cx, (JSObject *) JS_NewFunction(cx, (JSNative) 1, 0, 0,
                                                               JS_GetGlobalObject(cx), "dys"));
   js::Rooted<JSObject *> anon(cx, (JSObject *) JS_NewFunction(cx, (JSNative) 1, 0, 0,
                                                               JS_GetGlobalObject(cx), 0));
   js::Rooted<JSFunction *> funcPtr(cx, JS_NewFunction(cx, (JSNative) 1, 0, 0,
                                                       JS_GetGlobalObject(cx), "formFollows"));
 
+  JSObject &plainRef = *plain;
+  JSFunction &funcRef = *funcPtr;
+
   breakpoint();
 
   (void) glob;
   (void) plain;
   (void) func;
   (void) anon;
   (void) funcPtr;
+  (void) &plainRef;
+  (void) &funcRef;
 }
 
 FRAGMENT(JSObject, null) {
   js::Rooted<JSObject *> null(cx, NULL);
 
   breakpoint();
 
   (void) null;
 }
-
--- a/js/src/gdb/tests/test-JSObject.py
+++ b/js/src/gdb/tests/test-JSObject.py
@@ -1,15 +1,20 @@
 # Printing JSObjects.
 
 assert_subprinter_registered('SpiderMonkey', 'ptr-to-JSObject')
+assert_subprinter_registered('SpiderMonkey', 'ref-to-JSObject')
+
 run_fragment('JSObject.simple')
 
 # These patterns look a little strange because of prolog.py's 'set print
 # address off', which avoids putting varying addresses in the output. After
 # the '(JSObject *) ', there is a 'void *' value printing as the empty
 # string.
 
 assert_pretty('glob', '(JSObject *)  [object global] delegate')
 assert_pretty('plain', '(JSObject *)  [object Object]')
 assert_pretty('func', '(JSObject *)  [object Function "dys"]')
 assert_pretty('anon', '(JSObject *)  [object Function <unnamed>]')
 assert_pretty('funcPtr', '(JSFunction *)  [object Function "formFollows"]')
+
+assert_pretty('plainRef', '(JSObject &) @ [object Object]')
+assert_pretty('funcRef', '(JSFunction &) @ [object Function "formFollows"]')
--- a/js/src/gdb/tests/test-prettyprinters.py
+++ b/js/src/gdb/tests/test-prettyprinters.py
@@ -11,8 +11,12 @@ assert_eq(implemented_type_names('i'), [
 assert_eq(implemented_type_names('a'), ['A', 'int'])
 assert_eq(implemented_type_names('b'), ['B', 'A', 'int'])
 assert_eq(implemented_type_names('c'), ['C'])
 assert_eq(implemented_type_names('c_'), ['C_', 'C'])
 assert_eq(implemented_type_names('e'), ['E', 'C', 'D'])
 assert_eq(implemented_type_names('e_'), ['E_', 'E', 'C', 'D'])
 assert_eq(implemented_type_names('f'), ['F', 'C', 'D'])
 assert_eq(implemented_type_names('h'), ['H', 'F', 'G', 'C', 'D'])
+
+# Check that our pretty-printers aren't interfering with printing other types.
+assert_pretty('10', '10')
+assert_pretty('(void*) 0', '') # Because of 'set print address off'