js/src/gdb/mozilla/jsval.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.

     1 # Pretty-printers for SpiderMonkey jsvals.
     3 import gdb
     4 import gdb.types
     5 import mozilla.prettyprinters
     6 from mozilla.prettyprinters import pretty_printer, ptr_pretty_printer
     8 # Forget any printers from previous loads of this module.
     9 mozilla.prettyprinters.clear_module_printers(__name__)
    11 # Summary of the JS::Value (also known as jsval) type:
    12 #
    13 # Viewed abstractly, JS::Value is a 64-bit discriminated union, with
    14 # JSString *, JSObject *, IEEE 64-bit floating-point, and 32-bit integer
    15 # branches (and a few others). (It is not actually a C++ union;
    16 # 'discriminated union' just describes the overall effect.) Note that
    17 # JS::Value is always 64 bits long, even on 32-bit architectures.
    18 #
    19 # The ECMAScript standard specifies that ECMAScript numbers are IEEE 64-bit
    20 # floating-point values. A JS::Value can represent any JavaScript number
    21 # value directly, without referring to additional storage, or represent an
    22 # object, string, or other ECMAScript value, and remember which type it is.
    23 # This may seem surprising: how can a 64-bit type hold all the 64-bit IEEE
    24 # values, and still distinguish them from objects, strings, and so on,
    25 # which have 64-bit addresses?
    26 #
    27 # This is possible for two reasons:
    28 #
    29 # - First, ECMAScript implementations aren't required to distinguish all
    30 #   the values the IEEE 64-bit format can represent. The IEEE format
    31 #   specifies many bitstrings representing NaN values, while ECMAScript
    32 #   requires only a single NaN value. This means we can use one IEEE NaN to
    33 #   represent ECMAScript's NaN, and use all the other IEEE NaNs to
    34 #   represent the other ECMAScript values.
    35 #
    36 #   (IEEE says that any floating-point value whose 11-bit exponent field is
    37 #   0x7ff (all ones) and whose 52-bit fraction field is non-zero is a NaN.
    38 #   So as long as we ensure the fraction field is non-zero, and save a NaN
    39 #   for ECMAScript, we have 2^52 values to play with.)
    40 #
    41 # - Second, on the only 64-bit architecture we support, x86_64, only the
    42 #   lower 48 bits of an address are significant. The upper sixteen bits are
    43 #   required to be the sign-extension of bit 48. Furthermore, user code
    44 #   always runs in "positive addresses": those in which bit 48 is zero. So
    45 #   we only actually need 47 bits to store all possible object or string
    46 #   addresses, even on 64-bit platforms.
    47 #
    48 # With a 52-bit fraction field, and 47 bits needed for the 'payload', we
    49 # have up to five bits left to store a 'tag' value, to indicate which
    50 # branch of our discriminated union is live.
    51 #
    52 # Thus, we define JS::Value representations in terms of the IEEE 64-bit
    53 # floating-point format:
    54 #
    55 # - Any bitstring that IEEE calls a number or an infinity represents that
    56 #   ECMAScript number.
    57 #
    58 # - Any bitstring that IEEE calls a NaN represents either an ECMAScript NaN
    59 #   or a non-number ECMAScript value, as determined by a tag field stored
    60 #   towards the most significant end of the fraction field (exactly where
    61 #   depends on the address size). If the tag field indicates that this
    62 #   JS::Value is an object, the fraction field's least significant end
    63 #   holds the address of a JSObject; if a string, the address of a
    64 #   JSString; and so on.
    65 #
    66 # On the only 64-bit platform we support, x86_64, only the lower 48 bits of
    67 # an address are significant, and only those values whose top bit is zero
    68 # are used for user-space addresses. This means that x86_64 addresses are
    69 # effectively 47 bits long, and thus fit nicely in the available portion of
    70 # the fraction field.
    71 #
    72 #
    73 # In detail:
    74 #
    75 # - jsval (Value.h) is a typedef for JS::Value.
    76 #
    77 # - JS::Value (Value.h) is a class with a lot of methods and a single data
    78 #   member, of type jsval_layout.
    79 #
    80 # - jsval_layout (Value.h) is a helper type for picking apart values. This
    81 #   is always 64 bits long, with a variant for each address size (32 bits
    82 #   or 64 bits) and endianness (little- or big-endian).
    83 #
    84 #   jsval_layout is a union with 'asBits', 'asDouble', and 'asPtr'
    85 #   branches, and an 's' branch, which is a struct that tries to break out
    86 #   the bitfields a little for the non-double types. On 64-bit machines,
    87 #   jsval_layout also has an 'asUIntPtr' branch.
    88 #
    89 #   On 32-bit platforms, the 's' structure has a 'tag' member at the
    90 #   exponent end of the 's' struct, and a 'payload' union at the mantissa
    91 #   end. The 'payload' union's branches are things like JSString *,
    92 #   JSObject *, and so on: the natural representations of the tags.
    93 #
    94 #   On 64-bit platforms, the payload is 47 bits long; since C++ doesn't let
    95 #   us declare bitfields that hold unions, we can't break it down so
    96 #   neatly. In this case, we apply bit-shifting tricks to the 'asBits'
    97 #   branch of the union to extract the tag.
    99 class Box(object):
   100     def __init__(self, asBits, jtc):
   101         self.asBits = asBits
   102         self.jtc = jtc
   103         # jsval_layout::asBits is uint64, but somebody botches the sign bit, even
   104         # though Python integers are arbitrary precision.
   105         if self.asBits < 0:
   106             self.asBits = self.asBits + (1 << 64)
   108     # Return this value's type tag.
   109     def tag(self): raise NotImplementedError
   111     # Return this value as a 32-bit integer, double, or address.
   112     def as_uint32(self): raise NotImplementedError
   113     def as_double(self): raise NotImplementedError
   114     def as_address(self): raise NotImplementedError
   116 # Packed non-number boxing --- the format used on x86_64. It would be nice to simply
   117 # call JSVAL_TO_INT, etc. here, but the debugger is likely to see many jsvals, and
   118 # doing several inferior calls for each one seems like a bad idea.
   119 class Punbox(Box):
   121     FULL_WIDTH     = 64
   122     TAG_SHIFT      = 47
   123     PAYLOAD_MASK   = (1 << TAG_SHIFT) - 1
   124     TAG_MASK       = (1 << (FULL_WIDTH - TAG_SHIFT)) - 1
   125     TAG_MAX_DOUBLE = 0x1fff0
   126     TAG_TYPE_MASK  = 0x0000f
   128     def tag(self):
   129         tag = self.asBits >> Punbox.TAG_SHIFT
   130         if tag <= Punbox.TAG_MAX_DOUBLE:
   131             return self.jtc.DOUBLE
   132         else:
   133             return tag & Punbox.TAG_TYPE_MASK
   135     def as_uint32(self): return int(self.asBits & ((1 << 32) - 1))
   136     def as_address(self): return gdb.Value(self.asBits & Punbox.PAYLOAD_MASK)
   138 class Nunbox(Box):
   139     TAG_SHIFT      = 32
   140     TAG_CLEAR      = 0xffff0000
   141     PAYLOAD_MASK   = 0xffffffff
   142     TAG_TYPE_MASK  = 0x0000000f
   144     def tag(self):
   145         tag = self.asBits >> Nunbox.TAG_SHIFT
   146         if tag < Nunbox.TAG_CLEAR:
   147             return self.jtc.DOUBLE
   148         return tag & Nunbox.TAG_TYPE_MASK
   150     def as_uint32(self): return int(self.asBits & Nunbox.PAYLOAD_MASK)
   151     def as_address(self): return gdb.Value(self.asBits & Nunbox.PAYLOAD_MASK)
   153 # Cache information about the jsval type for this objfile.
   154 class jsvalTypeCache(object):
   155     def __init__(self, cache):
   156         # Capture the tag values.
   157         d = gdb.types.make_enum_dict(gdb.lookup_type('JSValueType'))
   158         self.DOUBLE    = d['JSVAL_TYPE_DOUBLE']
   159         self.INT32     = d['JSVAL_TYPE_INT32']
   160         self.UNDEFINED = d['JSVAL_TYPE_UNDEFINED']
   161         self.BOOLEAN   = d['JSVAL_TYPE_BOOLEAN']
   162         self.MAGIC     = d['JSVAL_TYPE_MAGIC']
   163         self.STRING    = d['JSVAL_TYPE_STRING']
   164         self.NULL      = d['JSVAL_TYPE_NULL']
   165         self.OBJECT    = d['JSVAL_TYPE_OBJECT']
   167         # Let self.magic_names be an array whose i'th element is the name of
   168         # the i'th magic value.
   169         d = gdb.types.make_enum_dict(gdb.lookup_type('JSWhyMagic'))
   170         self.magic_names = range(max(d.itervalues()) + 1)
   171         for (k,v) in d.items(): self.magic_names[v] = k
   173         # Choose an unboxing scheme for this architecture.
   174         self.boxer = Punbox if cache.void_ptr_t.sizeof == 8 else Nunbox
   176 @pretty_printer('jsval_layout')
   177 class jsval_layout(object):
   178     def __init__(self, value, cache):
   179         # Save the generic typecache, and create our own, if we haven't already.
   180         self.cache = cache
   181         if not cache.mod_jsval:
   182             cache.mod_jsval = jsvalTypeCache(cache)
   183         self.jtc = cache.mod_jsval
   185         self.value = value
   186         self.box = self.jtc.boxer(value['asBits'], self.jtc)
   188     def to_string(self):
   189         tag = self.box.tag()
   190         if tag == self.jtc.INT32:
   191             value = self.box.as_uint32()
   192             signbit = 1 << 31
   193             value = (value ^ signbit) - signbit
   194         elif tag == self.jtc.UNDEFINED:
   195             return 'JSVAL_VOID'
   196         elif tag == self.jtc.BOOLEAN:
   197             return 'JSVAL_TRUE' if self.box.as_uint32() else 'JSVAL_FALSE'
   198         elif tag == self.jtc.MAGIC:
   199             value = self.box.as_uint32()
   200             if 0 <= value and value < len(self.jtc.magic_names):
   201                 return '$jsmagic(%s)' % (self.jtc.magic_names[value],)
   202             else:
   203                 return '$jsmagic(%d)' % (value,)
   204         elif tag == self.jtc.STRING:
   205             value = self.box.as_address().cast(self.cache.JSString_ptr_t)
   206         elif tag == self.jtc.NULL:
   207             return 'JSVAL_NULL'
   208         elif tag == self.jtc.OBJECT:
   209             value = self.box.as_address().cast(self.cache.JSObject_ptr_t)
   210         elif tag == self.jtc.DOUBLE:
   211             value = self.value['asDouble']
   212         else:
   213             return '$jsval(unrecognized!)'
   214         return '$jsval(%s)' % (value,)
   216 @pretty_printer('JS::Value')
   217 class JSValue(object):
   218     def __new__(cls, value, cache):
   219         return jsval_layout(value['data'], cache)

mercurial