tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

jsval.py (6246B)


      1 # This Source Code Form is subject to the terms of the Mozilla Public
      2 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 # You can obtain one at http://mozilla.org/MPL/2.0/.
      4 
      5 # Pretty-printers for SpiderMonkey's JS::Value.
      6 
      7 import struct
      8 
      9 import gdb
     10 import gdb.types
     11 
     12 import mozilla.prettyprinters
     13 from mozilla.prettyprinters import pretty_printer
     14 
     15 # Forget any printers from previous loads of this module.
     16 mozilla.prettyprinters.clear_module_printers(__name__)
     17 
     18 # See Value.h [SMDOC] JS::Value Boxing Formats for details on the JS::Value boxing
     19 # formats handled below.
     20 
     21 
     22 class Box:
     23    def __init__(self, asBits, jtc):
     24        self.asBits = asBits
     25        self.jtc = jtc
     26        # Value::asBits is uint64_t, but somehow the sign bit can be botched
     27        # here, even though Python integers are arbitrary precision.
     28        if self.asBits < 0:
     29            self.asBits = self.asBits + (1 << 64)
     30 
     31    # Return this value's type tag.
     32    def tag(self):
     33        raise NotImplementedError
     34 
     35    # Return this value as a 32-bit integer, double, or address.
     36    def as_uint32(self):
     37        raise NotImplementedError
     38 
     39    def as_double(self):
     40        packed = struct.pack("q", self.asBits)
     41        (unpacked,) = struct.unpack("d", packed)
     42        return unpacked
     43 
     44    def as_address(self):
     45        raise NotImplementedError
     46 
     47 
     48 class Punbox(Box):
     49    # Packed non-number boxing --- the format used on x86_64. It would be nice to
     50    # simply call Value::toInt32, etc. here, but the debugger is likely to see many
     51    # Values, and doing several inferior calls for each one seems like a bad idea.
     52 
     53    FULL_WIDTH = 64
     54    TAG_SHIFT = 47
     55    PAYLOAD_MASK = (1 << TAG_SHIFT) - 1
     56    TAG_MASK = (1 << (FULL_WIDTH - TAG_SHIFT)) - 1
     57    TAG_MAX_DOUBLE = 0x1FFF0
     58    TAG_TYPE_MASK = 0x0000F
     59 
     60    def tag(self):
     61        tag = self.asBits >> Punbox.TAG_SHIFT
     62        if tag <= Punbox.TAG_MAX_DOUBLE:
     63            return self.jtc.DOUBLE
     64        else:
     65            return tag & Punbox.TAG_TYPE_MASK
     66 
     67    def as_uint32(self):
     68        return int(self.asBits & ((1 << 32) - 1))
     69 
     70    def as_address(self):
     71        return gdb.Value(self.asBits & Punbox.PAYLOAD_MASK)
     72 
     73 
     74 class Nunbox(Box):
     75    TAG_SHIFT = 32
     76    TAG_CLEAR = 0xFFFF0000
     77    PAYLOAD_MASK = 0xFFFFFFFF
     78    TAG_TYPE_MASK = 0x0000000F
     79 
     80    def tag(self):
     81        tag = self.asBits >> Nunbox.TAG_SHIFT
     82        if tag < Nunbox.TAG_CLEAR:
     83            return self.jtc.DOUBLE
     84        return tag & Nunbox.TAG_TYPE_MASK
     85 
     86    def as_uint32(self):
     87        return int(self.asBits & Nunbox.PAYLOAD_MASK)
     88 
     89    def as_address(self):
     90        return gdb.Value(self.asBits & Nunbox.PAYLOAD_MASK)
     91 
     92 
     93 class JSValueTypeCache:
     94    # Cache information about the Value type for this objfile.
     95 
     96    def __init__(self, cache):
     97        # Capture the tag values.
     98        d = gdb.types.make_enum_dict(gdb.lookup_type("JSValueType"))
     99 
    100        # The enum keys are prefixed when building with some compilers (clang at
    101        # a minimum), so use a helper function to handle either key format.
    102        def get(key):
    103            val = d.get(key)
    104            if val is not None:
    105                return val
    106            return d["JSValueType::" + key]
    107 
    108        self.DOUBLE = get("JSVAL_TYPE_DOUBLE")
    109        self.INT32 = get("JSVAL_TYPE_INT32")
    110        self.UNDEFINED = get("JSVAL_TYPE_UNDEFINED")
    111        self.BOOLEAN = get("JSVAL_TYPE_BOOLEAN")
    112        self.MAGIC = get("JSVAL_TYPE_MAGIC")
    113        self.STRING = get("JSVAL_TYPE_STRING")
    114        self.SYMBOL = get("JSVAL_TYPE_SYMBOL")
    115        self.BIGINT = get("JSVAL_TYPE_BIGINT")
    116        self.NULL = get("JSVAL_TYPE_NULL")
    117        self.OBJECT = get("JSVAL_TYPE_OBJECT")
    118 
    119        # Let self.magic_names be an array whose i'th element is the name of
    120        # the i'th magic value.
    121        d = gdb.types.make_enum_dict(gdb.lookup_type("JSWhyMagic"))
    122        self.magic_names = list(range(max(d.values()) + 1))
    123        for k, v in d.items():
    124            self.magic_names[v] = k
    125 
    126        # Choose an unboxing scheme for this architecture.
    127        self.boxer = Punbox if cache.void_ptr_t.sizeof == 8 else Nunbox
    128 
    129 
    130 @pretty_printer("JS::Value")
    131 class JSValue:
    132    def __init__(self, value, cache):
    133        # Save the generic typecache, and create our own, if we haven't already.
    134        self.cache = cache
    135        if not cache.mod_JS_Value:
    136            cache.mod_JS_Value = JSValueTypeCache(cache)
    137        self.jtc = cache.mod_JS_Value
    138 
    139        self.value = value
    140        self.box = self.jtc.boxer(value["asBits_"], self.jtc)
    141 
    142    def to_string(self):
    143        tag = self.box.tag()
    144 
    145        if tag == self.jtc.UNDEFINED:
    146            return "$JS::UndefinedValue()"
    147        if tag == self.jtc.NULL:
    148            return "$JS::NullValue()"
    149        if tag == self.jtc.BOOLEAN:
    150            return "$JS::BooleanValue(%s)" % str(self.box.as_uint32() != 0).lower()
    151        if tag == self.jtc.MAGIC:
    152            value = self.box.as_uint32()
    153            if 0 <= value < len(self.jtc.magic_names):
    154                return "$JS::MagicValue(%s)" % (self.jtc.magic_names[value],)
    155            else:
    156                return "$JS::MagicValue(%d)" % (value,)
    157 
    158        if tag == self.jtc.INT32:
    159            value = self.box.as_uint32()
    160            signbit = 1 << 31
    161            value = (value ^ signbit) - signbit
    162            return "$JS::Int32Value(%s)" % value
    163 
    164        if tag == self.jtc.DOUBLE:
    165            return "$JS::DoubleValue(%s)" % self.box.as_double()
    166 
    167        if tag == self.jtc.STRING:
    168            value = self.box.as_address().cast(self.cache.JSString_ptr_t)
    169        elif tag == self.jtc.OBJECT:
    170            value = self.box.as_address().cast(self.cache.JSObject_ptr_t)
    171        elif tag == self.jtc.SYMBOL:
    172            value = self.box.as_address().cast(self.cache.JSSymbol_ptr_t)
    173        elif tag == self.jtc.BIGINT:
    174            return "$JS::BigIntValue()"
    175        else:
    176            value = "unrecognized!"
    177        return "$JS::Value(%s)" % (value,)
    178 
    179    def is_undefined(self):
    180        return self.box.tag() == self.jtc.UNDEFINED
    181 
    182    def get_string(self):
    183        assert self.box.tag() == self.jtc.STRING
    184        return self.box.as_address().cast(self.cache.JSString_ptr_t)
    185 
    186    def get_private(self):
    187        assert self.box.tag() == self.jtc.DOUBLE
    188        return self.box.asBits