1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/gdb/mozilla/jsval.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,219 @@ 1.4 +# Pretty-printers for SpiderMonkey jsvals. 1.5 + 1.6 +import gdb 1.7 +import gdb.types 1.8 +import mozilla.prettyprinters 1.9 +from mozilla.prettyprinters import pretty_printer, ptr_pretty_printer 1.10 + 1.11 +# Forget any printers from previous loads of this module. 1.12 +mozilla.prettyprinters.clear_module_printers(__name__) 1.13 + 1.14 +# Summary of the JS::Value (also known as jsval) type: 1.15 +# 1.16 +# Viewed abstractly, JS::Value is a 64-bit discriminated union, with 1.17 +# JSString *, JSObject *, IEEE 64-bit floating-point, and 32-bit integer 1.18 +# branches (and a few others). (It is not actually a C++ union; 1.19 +# 'discriminated union' just describes the overall effect.) Note that 1.20 +# JS::Value is always 64 bits long, even on 32-bit architectures. 1.21 +# 1.22 +# The ECMAScript standard specifies that ECMAScript numbers are IEEE 64-bit 1.23 +# floating-point values. A JS::Value can represent any JavaScript number 1.24 +# value directly, without referring to additional storage, or represent an 1.25 +# object, string, or other ECMAScript value, and remember which type it is. 1.26 +# This may seem surprising: how can a 64-bit type hold all the 64-bit IEEE 1.27 +# values, and still distinguish them from objects, strings, and so on, 1.28 +# which have 64-bit addresses? 1.29 +# 1.30 +# This is possible for two reasons: 1.31 +# 1.32 +# - First, ECMAScript implementations aren't required to distinguish all 1.33 +# the values the IEEE 64-bit format can represent. The IEEE format 1.34 +# specifies many bitstrings representing NaN values, while ECMAScript 1.35 +# requires only a single NaN value. This means we can use one IEEE NaN to 1.36 +# represent ECMAScript's NaN, and use all the other IEEE NaNs to 1.37 +# represent the other ECMAScript values. 1.38 +# 1.39 +# (IEEE says that any floating-point value whose 11-bit exponent field is 1.40 +# 0x7ff (all ones) and whose 52-bit fraction field is non-zero is a NaN. 1.41 +# So as long as we ensure the fraction field is non-zero, and save a NaN 1.42 +# for ECMAScript, we have 2^52 values to play with.) 1.43 +# 1.44 +# - Second, on the only 64-bit architecture we support, x86_64, only the 1.45 +# lower 48 bits of an address are significant. The upper sixteen bits are 1.46 +# required to be the sign-extension of bit 48. Furthermore, user code 1.47 +# always runs in "positive addresses": those in which bit 48 is zero. So 1.48 +# we only actually need 47 bits to store all possible object or string 1.49 +# addresses, even on 64-bit platforms. 1.50 +# 1.51 +# With a 52-bit fraction field, and 47 bits needed for the 'payload', we 1.52 +# have up to five bits left to store a 'tag' value, to indicate which 1.53 +# branch of our discriminated union is live. 1.54 +# 1.55 +# Thus, we define JS::Value representations in terms of the IEEE 64-bit 1.56 +# floating-point format: 1.57 +# 1.58 +# - Any bitstring that IEEE calls a number or an infinity represents that 1.59 +# ECMAScript number. 1.60 +# 1.61 +# - Any bitstring that IEEE calls a NaN represents either an ECMAScript NaN 1.62 +# or a non-number ECMAScript value, as determined by a tag field stored 1.63 +# towards the most significant end of the fraction field (exactly where 1.64 +# depends on the address size). If the tag field indicates that this 1.65 +# JS::Value is an object, the fraction field's least significant end 1.66 +# holds the address of a JSObject; if a string, the address of a 1.67 +# JSString; and so on. 1.68 +# 1.69 +# On the only 64-bit platform we support, x86_64, only the lower 48 bits of 1.70 +# an address are significant, and only those values whose top bit is zero 1.71 +# are used for user-space addresses. This means that x86_64 addresses are 1.72 +# effectively 47 bits long, and thus fit nicely in the available portion of 1.73 +# the fraction field. 1.74 +# 1.75 +# 1.76 +# In detail: 1.77 +# 1.78 +# - jsval (Value.h) is a typedef for JS::Value. 1.79 +# 1.80 +# - JS::Value (Value.h) is a class with a lot of methods and a single data 1.81 +# member, of type jsval_layout. 1.82 +# 1.83 +# - jsval_layout (Value.h) is a helper type for picking apart values. This 1.84 +# is always 64 bits long, with a variant for each address size (32 bits 1.85 +# or 64 bits) and endianness (little- or big-endian). 1.86 +# 1.87 +# jsval_layout is a union with 'asBits', 'asDouble', and 'asPtr' 1.88 +# branches, and an 's' branch, which is a struct that tries to break out 1.89 +# the bitfields a little for the non-double types. On 64-bit machines, 1.90 +# jsval_layout also has an 'asUIntPtr' branch. 1.91 +# 1.92 +# On 32-bit platforms, the 's' structure has a 'tag' member at the 1.93 +# exponent end of the 's' struct, and a 'payload' union at the mantissa 1.94 +# end. The 'payload' union's branches are things like JSString *, 1.95 +# JSObject *, and so on: the natural representations of the tags. 1.96 +# 1.97 +# On 64-bit platforms, the payload is 47 bits long; since C++ doesn't let 1.98 +# us declare bitfields that hold unions, we can't break it down so 1.99 +# neatly. In this case, we apply bit-shifting tricks to the 'asBits' 1.100 +# branch of the union to extract the tag. 1.101 + 1.102 +class Box(object): 1.103 + def __init__(self, asBits, jtc): 1.104 + self.asBits = asBits 1.105 + self.jtc = jtc 1.106 + # jsval_layout::asBits is uint64, but somebody botches the sign bit, even 1.107 + # though Python integers are arbitrary precision. 1.108 + if self.asBits < 0: 1.109 + self.asBits = self.asBits + (1 << 64) 1.110 + 1.111 + # Return this value's type tag. 1.112 + def tag(self): raise NotImplementedError 1.113 + 1.114 + # Return this value as a 32-bit integer, double, or address. 1.115 + def as_uint32(self): raise NotImplementedError 1.116 + def as_double(self): raise NotImplementedError 1.117 + def as_address(self): raise NotImplementedError 1.118 + 1.119 +# Packed non-number boxing --- the format used on x86_64. It would be nice to simply 1.120 +# call JSVAL_TO_INT, etc. here, but the debugger is likely to see many jsvals, and 1.121 +# doing several inferior calls for each one seems like a bad idea. 1.122 +class Punbox(Box): 1.123 + 1.124 + FULL_WIDTH = 64 1.125 + TAG_SHIFT = 47 1.126 + PAYLOAD_MASK = (1 << TAG_SHIFT) - 1 1.127 + TAG_MASK = (1 << (FULL_WIDTH - TAG_SHIFT)) - 1 1.128 + TAG_MAX_DOUBLE = 0x1fff0 1.129 + TAG_TYPE_MASK = 0x0000f 1.130 + 1.131 + def tag(self): 1.132 + tag = self.asBits >> Punbox.TAG_SHIFT 1.133 + if tag <= Punbox.TAG_MAX_DOUBLE: 1.134 + return self.jtc.DOUBLE 1.135 + else: 1.136 + return tag & Punbox.TAG_TYPE_MASK 1.137 + 1.138 + def as_uint32(self): return int(self.asBits & ((1 << 32) - 1)) 1.139 + def as_address(self): return gdb.Value(self.asBits & Punbox.PAYLOAD_MASK) 1.140 + 1.141 +class Nunbox(Box): 1.142 + TAG_SHIFT = 32 1.143 + TAG_CLEAR = 0xffff0000 1.144 + PAYLOAD_MASK = 0xffffffff 1.145 + TAG_TYPE_MASK = 0x0000000f 1.146 + 1.147 + def tag(self): 1.148 + tag = self.asBits >> Nunbox.TAG_SHIFT 1.149 + if tag < Nunbox.TAG_CLEAR: 1.150 + return self.jtc.DOUBLE 1.151 + return tag & Nunbox.TAG_TYPE_MASK 1.152 + 1.153 + def as_uint32(self): return int(self.asBits & Nunbox.PAYLOAD_MASK) 1.154 + def as_address(self): return gdb.Value(self.asBits & Nunbox.PAYLOAD_MASK) 1.155 + 1.156 +# Cache information about the jsval type for this objfile. 1.157 +class jsvalTypeCache(object): 1.158 + def __init__(self, cache): 1.159 + # Capture the tag values. 1.160 + d = gdb.types.make_enum_dict(gdb.lookup_type('JSValueType')) 1.161 + self.DOUBLE = d['JSVAL_TYPE_DOUBLE'] 1.162 + self.INT32 = d['JSVAL_TYPE_INT32'] 1.163 + self.UNDEFINED = d['JSVAL_TYPE_UNDEFINED'] 1.164 + self.BOOLEAN = d['JSVAL_TYPE_BOOLEAN'] 1.165 + self.MAGIC = d['JSVAL_TYPE_MAGIC'] 1.166 + self.STRING = d['JSVAL_TYPE_STRING'] 1.167 + self.NULL = d['JSVAL_TYPE_NULL'] 1.168 + self.OBJECT = d['JSVAL_TYPE_OBJECT'] 1.169 + 1.170 + # Let self.magic_names be an array whose i'th element is the name of 1.171 + # the i'th magic value. 1.172 + d = gdb.types.make_enum_dict(gdb.lookup_type('JSWhyMagic')) 1.173 + self.magic_names = range(max(d.itervalues()) + 1) 1.174 + for (k,v) in d.items(): self.magic_names[v] = k 1.175 + 1.176 + # Choose an unboxing scheme for this architecture. 1.177 + self.boxer = Punbox if cache.void_ptr_t.sizeof == 8 else Nunbox 1.178 + 1.179 +@pretty_printer('jsval_layout') 1.180 +class jsval_layout(object): 1.181 + def __init__(self, value, cache): 1.182 + # Save the generic typecache, and create our own, if we haven't already. 1.183 + self.cache = cache 1.184 + if not cache.mod_jsval: 1.185 + cache.mod_jsval = jsvalTypeCache(cache) 1.186 + self.jtc = cache.mod_jsval 1.187 + 1.188 + self.value = value 1.189 + self.box = self.jtc.boxer(value['asBits'], self.jtc) 1.190 + 1.191 + def to_string(self): 1.192 + tag = self.box.tag() 1.193 + if tag == self.jtc.INT32: 1.194 + value = self.box.as_uint32() 1.195 + signbit = 1 << 31 1.196 + value = (value ^ signbit) - signbit 1.197 + elif tag == self.jtc.UNDEFINED: 1.198 + return 'JSVAL_VOID' 1.199 + elif tag == self.jtc.BOOLEAN: 1.200 + return 'JSVAL_TRUE' if self.box.as_uint32() else 'JSVAL_FALSE' 1.201 + elif tag == self.jtc.MAGIC: 1.202 + value = self.box.as_uint32() 1.203 + if 0 <= value and value < len(self.jtc.magic_names): 1.204 + return '$jsmagic(%s)' % (self.jtc.magic_names[value],) 1.205 + else: 1.206 + return '$jsmagic(%d)' % (value,) 1.207 + elif tag == self.jtc.STRING: 1.208 + value = self.box.as_address().cast(self.cache.JSString_ptr_t) 1.209 + elif tag == self.jtc.NULL: 1.210 + return 'JSVAL_NULL' 1.211 + elif tag == self.jtc.OBJECT: 1.212 + value = self.box.as_address().cast(self.cache.JSObject_ptr_t) 1.213 + elif tag == self.jtc.DOUBLE: 1.214 + value = self.value['asDouble'] 1.215 + else: 1.216 + return '$jsval(unrecognized!)' 1.217 + return '$jsval(%s)' % (value,) 1.218 + 1.219 +@pretty_printer('JS::Value') 1.220 +class JSValue(object): 1.221 + def __new__(cls, value, cache): 1.222 + return jsval_layout(value['data'], cache)