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