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.

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

mercurial