Bug 1449099 - Update various parts of gdb JS::Value integration for the removal of the jsval/jsval_layout types, instead generally referring to Value.h directly. r=sfink (5x, lol Bugzilla)
authorJeff Walden <jwalden@mit.edu>
Tue, 27 Mar 2018 13:59:32 -0700
changeset 773613 9a145bccd8870c361551b11fa20ffda97c82bc31
parent 773612 d9a446d356da021876e540db7884907ef6ffdaad
child 773614 c751156bf6d48fccea115931dcee147aec74ff40
push id104266
push userbmo:hsivonen@hsivonen.fi
push dateWed, 28 Mar 2018 07:33:03 +0000
reviewerssfink
bugs1449099
milestone61.0a1
Bug 1449099 - Update various parts of gdb JS::Value integration for the removal of the jsval/jsval_layout types, instead generally referring to Value.h directly. r=sfink (5x, lol Bugzilla)
js/src/gdb/README
js/src/gdb/mozilla/jsval.py
js/src/gdb/mozilla/prettyprinters.py
js/src/gdb/tests/test-Root.py
js/src/gdb/tests/test-asmjs.py
js/src/gdb/tests/test-jsval.cpp
js/src/gdb/tests/test-jsval.py
--- a/js/src/gdb/README
+++ b/js/src/gdb/README
@@ -1,37 +1,37 @@
 This directory holds Python code to support debugging SpiderMonkey with
-GDB. It includes pretty-printers for common SpiderMonkey types like jsval,
+GDB. It includes pretty-printers for common SpiderMonkey types like JS::Value,
 jsid, and JSObject, and makes GDB "see through" the SpiderMonkey rooting
 types like js::Rooted and JS::Handle. For example:
 
     (gdb) frame
     #0  js::baseops::SetPropertyHelper (cx=0xbf3460,
         obj=(JSObject * const) 0x7ffff150b060 [object global] delegate,
         receiver=(JSObject * const) 0x7ffff150b060 [object global] delegate,
-        id=$jsid("x"), defineHow=4, vp=$jsval(1), strict=0)
+        id=$jsid("x"), defineHow=4, vp=$JS::Int32Value(1), strict=0)
         at /home/jimb/moz/archer/js/src/jsobj.cpp:4495
     4495	    MOZ_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_UNQUALIFIED)) == 0);
     (gdb)
 
 Things to note here:
 
 - obj, a JS::HandleObject, prints as:
       obj=(JSObject * const) 0x7ffff150b060 [object global] delegate,
   This immediately shows the handle's referent, along with a JavaScript-like summary
   of the object.
 
 - id, a JS::HandleId, prints as:
       id=$jsid("x"),
   We show the handle's referent, and print the identifier as a string.
 
 - vp, a JS::MutableHandleValue, prints as:
-      vp=$jsval(1)
-  We show the handle's referent, using the jsval's tag to print it in its
-  JavaScript form.
+      vp=$JS::Int32Value(1)
+  We show the handle's referent, using the JS::Value's tag to print it noting
+  its particular internal type and value.
 
 You can still see the raw form of a value with 'print/r':
 
     (gdb) p/r obj
     $1 = {<js::HandleBase<JSObject*>> = {<No data fields>}, ptr = 0x7fffffffca60}
     (gdb)
 
 You can also use GDB's 'disable pretty-printer' command to turn off
@@ -51,23 +51,23 @@ In general, pretty-printers for pointer 
 pointer's referent:
 
     (gdb) b math_atan2
     Breakpoint 1 at 0x542e0a: file /home/jimb/moz/archer/js/src/jsmath.cpp, line 214.
     (gdb) run
     js> Math.atan2('Spleen', 42)
     Breakpoint 1, math_atan2 (cx=0xbf3440, argc=2, vp=0x7ffff172f0a0)
     (gdb) print vp[0]
-    $1 = $jsval((JSObject *) 0x7ffff151c0c0 [object Function "atan2"])
+    $1 = $JS::Value((JSObject *) 0x7ffff151c0c0 [object Function "atan2"])
     (gdb) print vp[1]
-    $2 = $jsval((JSObject *) 0x7ffff150d0a0 [object Math])
+    $2 = $JS::Value((JSObject *) 0x7ffff150d0a0 [object Math])
     (gdb) print vp[2]
-    $3 = $jsval("Spleen")
+    $3 = $JS::Value("Spleen")
     (gdb) print vp[3]
-    $4 = $jsval(42)
+    $4 = $JS::Int32Value(42)
     (gdb)
 
 We used to also have pretty-printers for the actual contents of a JSString
 struct, that knew which union branches were live and which were dead. These were
 more fragile than the summary pretty-printers, and harder to test, so I've
 removed them until we can see how to do better.
 
 There are unit tests; see 'Running the unit tests', below.
--- a/js/src/gdb/mozilla/jsval.py
+++ b/js/src/gdb/mozilla/jsval.py
@@ -1,19 +1,19 @@
-# Pretty-printers for SpiderMonkey jsvals.
+# Pretty-printers for SpiderMonkey's JS::Value.
 
 import gdb
 import gdb.types
 import mozilla.prettyprinters
 from mozilla.prettyprinters import pretty_printer, ptr_pretty_printer
 
 # Forget any printers from previous loads of this module.
 mozilla.prettyprinters.clear_module_printers(__name__)
 
-# Summary of the JS::Value (also known as jsval) type:
+# Summary of the JS::Value type:
 #
 # Viewed abstractly, JS::Value is a 64-bit discriminated union, with
 # JSString *, JSObject *, IEEE 64-bit floating-point, and 32-bit integer
 # branches (and a few others). (It is not actually a C++ union;
 # 'discriminated union' just describes the overall effect.) Note that
 # JS::Value is always 64 bits long, even on 32-bit architectures.
 #
 # The ECMAScript standard specifies that ECMAScript numbers are IEEE 64-bit
@@ -64,63 +64,38 @@ mozilla.prettyprinters.clear_module_prin
 #   JSString; and so on.
 #
 # On the only 64-bit platform we support, x86_64, only the lower 48 bits of
 # an address are significant, and only those values whose top bit is zero
 # are used for user-space addresses. This means that x86_64 addresses are
 # effectively 47 bits long, and thus fit nicely in the available portion of
 # the fraction field.
 #
-#
-# In detail:
-#
-# - jsval (Value.h) is a typedef for JS::Value.
-#
-# - JS::Value (Value.h) is a class with a lot of methods and a single data
-#   member, of type jsval_layout.
-#
-# - jsval_layout (Value.h) is a helper type for picking apart values. This
-#   is always 64 bits long, with a variant for each address size (32 bits
-#   or 64 bits) and endianness (little- or big-endian).
-#
-#   jsval_layout is a union with 'asBits', 'asDouble', and 'asPtr'
-#   branches, and an 's' branch, which is a struct that tries to break out
-#   the bitfields a little for the non-double types. On 64-bit machines,
-#   jsval_layout also has an 'asUIntPtr' branch.
-#
-#   On 32-bit platforms, the 's' structure has a 'tag' member at the
-#   exponent end of the 's' struct, and a 'payload' union at the mantissa
-#   end. The 'payload' union's branches are things like JSString *,
-#   JSObject *, and so on: the natural representations of the tags.
-#
-#   On 64-bit platforms, the payload is 47 bits long; since C++ doesn't let
-#   us declare bitfields that hold unions, we can't break it down so
-#   neatly. In this case, we apply bit-shifting tricks to the 'asBits'
-#   branch of the union to extract the tag.
+# See Value.h for full details.
 
 class Box(object):
     def __init__(self, asBits, jtc):
         self.asBits = asBits
         self.jtc = jtc
-        # jsval_layout::asBits is uint64, but somebody botches the sign bit, even
-        # though Python integers are arbitrary precision.
+        # Value::layout::asBits is uint64_t, but somehow the sign bit can be
+        # botched here, even though Python integers are arbitrary precision.
         if self.asBits < 0:
             self.asBits = self.asBits + (1 << 64)
 
     # Return this value's type tag.
     def tag(self): raise NotImplementedError
 
     # Return this value as a 32-bit integer, double, or address.
     def as_uint32(self): raise NotImplementedError
     def as_double(self): raise NotImplementedError
     def as_address(self): raise NotImplementedError
 
-# Packed non-number boxing --- the format used on x86_64. It would be nice to simply
-# call JSVAL_TO_INT, etc. here, but the debugger is likely to see many jsvals, and
-# doing several inferior calls for each one seems like a bad idea.
+# Packed non-number boxing --- the format used on x86_64. It would be nice to
+# simply call Value::toInt32, etc. here, but the debugger is likely to see many
+# Values, and doing several inferior calls for each one seems like a bad idea.
 class Punbox(Box):
 
     FULL_WIDTH     = 64
     TAG_SHIFT      = 47
     PAYLOAD_MASK   = (1 << TAG_SHIFT) - 1
     TAG_MASK       = (1 << (FULL_WIDTH - TAG_SHIFT)) - 1
     TAG_MAX_DOUBLE = 0x1fff0
     TAG_TYPE_MASK  = 0x0000f
@@ -145,18 +120,18 @@ class Nunbox(Box):
         tag = self.asBits >> Nunbox.TAG_SHIFT
         if tag < Nunbox.TAG_CLEAR:
             return self.jtc.DOUBLE
         return tag & Nunbox.TAG_TYPE_MASK
 
     def as_uint32(self): return int(self.asBits & Nunbox.PAYLOAD_MASK)
     def as_address(self): return gdb.Value(self.asBits & Nunbox.PAYLOAD_MASK)
 
-# Cache information about the jsval type for this objfile.
-class jsvalTypeCache(object):
+# Cache information about the Value type for this objfile.
+class JSValueTypeCache(object):
     def __init__(self, cache):
         # Capture the tag values.
         d = gdb.types.make_enum_dict(gdb.lookup_type('JSValueType'))
 
         # The enum keys are prefixed when building with some compilers (clang at
         # a minimum), so use a helper function to handle either key format.
         def get(key):
             val = d.get(key)
@@ -178,54 +153,55 @@ class jsvalTypeCache(object):
         # the i'th magic value.
         d = gdb.types.make_enum_dict(gdb.lookup_type('JSWhyMagic'))
         self.magic_names = list(range(max(d.values()) + 1))
         for (k,v) in d.items(): self.magic_names[v] = k
 
         # Choose an unboxing scheme for this architecture.
         self.boxer = Punbox if cache.void_ptr_t.sizeof == 8 else Nunbox
 
-@pretty_printer('jsval_layout')
-class jsval_layout(object):
+@pretty_printer('JS::Value')
+class JSValue(object):
     def __init__(self, value, cache):
         # Save the generic typecache, and create our own, if we haven't already.
         self.cache = cache
-        if not cache.mod_jsval:
-            cache.mod_jsval = jsvalTypeCache(cache)
-        self.jtc = cache.mod_jsval
+        if not cache.mod_JS_Value:
+            cache.mod_JS_Value = JSValueTypeCache(cache)
+        self.jtc = cache.mod_JS_Value
 
-        self.value = value
-        self.box = self.jtc.boxer(value['asBits'], self.jtc)
+        data = value['data']
+        self.data = data
+        self.box = self.jtc.boxer(data['asBits'], self.jtc)
 
     def to_string(self):
         tag = self.box.tag()
+
+        if tag == self.jtc.UNDEFINED:
+            return '$JS::UndefinedValue()'
+        if tag == self.jtc.NULL:
+            return '$JS::NullValue()'
+        if tag == self.jtc.BOOLEAN:
+            return '$JS::BooleanValue(%s)' % str(self.box.as_uint32() != 0).lower()
+        if tag == self.jtc.MAGIC:
+            value = self.box.as_uint32()
+            if 0 <= value and value < len(self.jtc.magic_names):
+                return '$JS::MagicValue(%s)' % (self.jtc.magic_names[value],)
+            else:
+                return '$JS::MagicValue(%d)' % (value,)
+
         if tag == self.jtc.INT32:
             value = self.box.as_uint32()
             signbit = 1 << 31
             value = (value ^ signbit) - signbit
-        elif tag == self.jtc.UNDEFINED:
-            return 'JSVAL_VOID'
-        elif tag == self.jtc.BOOLEAN:
-            return 'JSVAL_TRUE' if self.box.as_uint32() else 'JSVAL_FALSE'
-        elif tag == self.jtc.MAGIC:
-            value = self.box.as_uint32()
-            if 0 <= value and value < len(self.jtc.magic_names):
-                return '$jsmagic(%s)' % (self.jtc.magic_names[value],)
-            else:
-                return '$jsmagic(%d)' % (value,)
-        elif tag == self.jtc.STRING:
+            return '$JS::Int32Value(%s)' % value
+
+        if tag == self.jtc.DOUBLE:
+            return '$JS::DoubleValue(%s)' % self.data['asDouble']
+
+        if tag == self.jtc.STRING:
             value = self.box.as_address().cast(self.cache.JSString_ptr_t)
+        elif tag == self.jtc.OBJECT:
+            value = self.box.as_address().cast(self.cache.JSObject_ptr_t)
         elif tag == self.jtc.SYMBOL:
             value = self.box.as_address().cast(self.cache.JSSymbol_ptr_t)
-        elif tag == self.jtc.NULL:
-            return 'JSVAL_NULL'
-        elif tag == self.jtc.OBJECT:
-            value = self.box.as_address().cast(self.cache.JSObject_ptr_t)
-        elif tag == self.jtc.DOUBLE:
-            value = self.value['asDouble']
         else:
-            return '$jsval(unrecognized!)'
-        return '$jsval(%s)' % (value,)
-
-@pretty_printer('JS::Value')
-class JSValue(object):
-    def __new__(cls, value, cache):
-        return jsval_layout(value['data'], cache)
+            value = 'unrecognized!'
+        return '$JS::Value(%s)' % (value,)
--- a/js/src/gdb/mozilla/prettyprinters.py
+++ b/js/src/gdb/mozilla/prettyprinters.py
@@ -178,17 +178,17 @@ class TypeCache(object):
             self.JSObject_ptr_t = gdb.lookup_type('JSObject').pointer()
         except gdb.error:
             raise NotSpiderMonkeyObjfileError
 
         self.mod_GCCellPtr = None
         self.mod_Interpreter = None
         self.mod_JSObject = None
         self.mod_JSString = None
-        self.mod_jsval = None
+        self.mod_JS_Value = None
         self.mod_ExecutableAllocator = None
         self.mod_IonGraph = None
 
 # Yield a series of all the types that |t| implements, by following typedefs
 # and iterating over base classes. Specifically:
 # - |t| itself is the first value yielded.
 # - If we yield a typedef, we later yield its definition.
 # - If we yield a type with base classes, we later yield those base classes.
--- a/js/src/gdb/tests/test-Root.py
+++ b/js/src/gdb/tests/test-Root.py
@@ -9,19 +9,19 @@ run_fragment('Root.handle')
 
 assert_pretty('obj', '(JSObject * const)  [object global] delegate')
 assert_pretty('mutableObj', '(JSObject *)  [object global] delegate')
 
 run_fragment('Root.HeapSlot')
 
 # This depends on implementation details of arrays, but since HeapSlot is
 # not a public type, I'm not sure how to avoid doing *something* ugly.
-assert_pretty('((js::NativeObject *) array.ptr)->elements_[0]', '$jsval("plinth")')
+assert_pretty('((js::NativeObject *) array.ptr)->elements_[0]', '$JS::Value("plinth")')
 
 run_fragment('Root.barriers');
 
 assert_pretty('prebarriered', '(JSObject *)  [object Object]');
 assert_pretty('heapptr', '(JSObject *)  [object Object]');
 assert_pretty('relocatable', '(JSObject *)  [object Object]');
-assert_pretty('val', '$jsval((JSObject *)  [object Object])');
-assert_pretty('heapValue', '$jsval((JSObject *)  [object Object])');
-assert_pretty('prebarrieredValue', '$jsval((JSObject *)  [object Object])');
-assert_pretty('relocatableValue', '$jsval((JSObject *)  [object Object])');
+assert_pretty('val', '$JS::Value((JSObject *)  [object Object])');
+assert_pretty('heapValue', '$JS::Value((JSObject *)  [object Object])');
+assert_pretty('prebarrieredValue', '$JS::Value((JSObject *)  [object Object])');
+assert_pretty('relocatableValue', '$JS::Value((JSObject *)  [object Object])');
--- a/js/src/gdb/tests/test-asmjs.py
+++ b/js/src/gdb/tests/test-asmjs.py
@@ -7,9 +7,9 @@
 run_fragment('asmjs.segfault')
 
 # If SIGSEGV handling is broken, GDB would have stopped at the SIGSEGV signal.
 # The breakpoint would not have hit, and run_fragment would have thrown.
 #
 # So if we get here, and the asm.js code actually ran, we win.
 
 assert_pretty('ok', 'true')
-assert_pretty('rval', '$jsval("ok")')
+assert_pretty('rval', '$JS::Value("ok")')
--- a/js/src/gdb/tests/test-jsval.cpp
+++ b/js/src/gdb/tests/test-jsval.cpp
@@ -1,15 +1,16 @@
 #include "gdb-tests.h"
 #include "jsapi.h"
 
 FRAGMENT(jsval, simple) {
   using namespace JS;
 
   RootedValue fortytwo(cx, Int32Value(42));
+  RootedValue fortytwoD(cx, DoubleValue(42));
   RootedValue negone(cx, Int32Value(-1));
   RootedValue undefined(cx, UndefinedValue());
   RootedValue null(cx, NullValue());
   RootedValue js_true(cx, BooleanValue(true));
   RootedValue js_false(cx, BooleanValue(false));
   RootedValue elements_hole(cx, js::MagicValue(JS_ELEMENTS_HOLE));
 
   RootedValue empty_string(cx);
@@ -22,16 +23,17 @@ FRAGMENT(jsval, simple) {
   global.setObject(*CurrentGlobalOrNull(cx));
 
   // Some interesting value that floating-point won't munge.
   RootedValue onehundredthirtysevenonehundredtwentyeighths(cx, DoubleValue(137.0 / 128.0));
 
   breakpoint();
 
   use(fortytwo);
+  use(fortytwoD);
   use(negone);
   use(undefined);
   use(js_true);
   use(js_false);
   use(null);
   use(elements_hole);
   use(empty_string);
   use(friendly_string);
--- a/js/src/gdb/tests/test-jsval.py
+++ b/js/src/gdb/tests/test-jsval.py
@@ -1,18 +1,19 @@
 # Basic unit tests for jsval pretty-printer.
 
 assert_subprinter_registered('SpiderMonkey', 'JS::Value')
 
 run_fragment('jsval.simple')
 
-assert_pretty('fortytwo', '$jsval(42)')
-assert_pretty('negone', '$jsval(-1)')
-assert_pretty('undefined', 'JSVAL_VOID')
-assert_pretty('null', 'JSVAL_NULL')
-assert_pretty('js_true', 'JSVAL_TRUE')
-assert_pretty('js_false', 'JSVAL_FALSE')
-assert_pretty('elements_hole', '$jsmagic(JS_ELEMENTS_HOLE)')
-assert_pretty('empty_string', '$jsval("")')
-assert_pretty('friendly_string', '$jsval("Hello!")')
-assert_pretty('symbol', '$jsval(Symbol.for("Hello!"))')
-assert_pretty('global', '$jsval((JSObject *)  [object global] delegate)')
-assert_pretty('onehundredthirtysevenonehundredtwentyeighths', '$jsval(1.0703125)')
+assert_pretty('fortytwo', '$JS::Int32Value(42)')
+assert_pretty('fortytwoD', '$JS::DoubleValue(42)')
+assert_pretty('negone', '$JS::Int32Value(-1)')
+assert_pretty('undefined', '$JS::UndefinedValue()')
+assert_pretty('null', '$JS::NullValue()')
+assert_pretty('js_true', '$JS::BooleanValue(true)')
+assert_pretty('js_false', '$JS::BooleanValue(false)')
+assert_pretty('elements_hole', '$JS::MagicValue(JS_ELEMENTS_HOLE)')
+assert_pretty('empty_string', '$JS::Value("")')
+assert_pretty('friendly_string', '$JS::Value("Hello!")')
+assert_pretty('symbol', '$JS::Value(Symbol.for("Hello!"))')
+assert_pretty('global', '$JS::Value((JSObject *)  [object global] delegate)')
+assert_pretty('onehundredthirtysevenonehundredtwentyeighths', '$JS::DoubleValue(1.0703125)')