|
| 1 | +// |
| 2 | +// Tiny module that provides big (64bit) integers. |
| 3 | +// |
| 4 | +// Copyright (c) 2016 Samuel Groß |
| 5 | +// |
| 6 | +// Requires utils.js |
| 7 | +// |
| 8 | + |
| 9 | +// Datatype to represent 64-bit integers. |
| 10 | +// |
| 11 | +// Internally, the integer is stored as a Uint8Array in little endian byte order. |
| 12 | +function Int64(v) { |
| 13 | + // The underlying byte array. |
| 14 | + var bytes = new Uint8Array(8); |
| 15 | + this.bytes = bytes; |
| 16 | + |
| 17 | + switch (typeof v) { |
| 18 | + case 'number': |
| 19 | + v = '0x' + Math.floor(v).toString(16); |
| 20 | + case 'string': |
| 21 | + if (v.startsWith('0x')) |
| 22 | + v = v.substr(2); |
| 23 | + if (v.length % 2 == 1) |
| 24 | + v = '0' + v; |
| 25 | + |
| 26 | + var bigEndian = unhexlify(v, 8); |
| 27 | + bytes.set(Array.from(bigEndian).reverse()); |
| 28 | + break; |
| 29 | + case 'object': |
| 30 | + if (v instanceof Int64) { |
| 31 | + bytes.set(v.getBytes()); |
| 32 | + } else { |
| 33 | + if (v.length != 8) |
| 34 | + throw TypeError("Array must have excactly 8 elements."); |
| 35 | + bytes.set(v); |
| 36 | + } |
| 37 | + break; |
| 38 | + case 'undefined': |
| 39 | + break; |
| 40 | + default: |
| 41 | + throw TypeError("Int64 constructor requires an argument."); |
| 42 | + } |
| 43 | + |
| 44 | + // Return a double whith the same underlying bit representation. |
| 45 | + this.asDouble = function() { |
| 46 | + // Check for NaN |
| 47 | + if (bytes[7] == 0xff && (bytes[6] == 0xff || bytes[6] == 0xfe)) |
| 48 | + throw new RangeError("Integer can not be represented by a double"); |
| 49 | + |
| 50 | + return Struct.unpack(Struct.float64, bytes); |
| 51 | + }; |
| 52 | + |
| 53 | + // Return a javascript value with the same underlying bit representation. |
| 54 | + // This is only possible for integers in the range [0x0001000000000000, 0xffff000000000000) |
| 55 | + // due to double conversion constraints. |
| 56 | + this.asJSValue = function() { |
| 57 | + if ((bytes[7] == 0 && bytes[6] == 0) || (bytes[7] == 0xff && bytes[6] == 0xff)) |
| 58 | + throw new RangeError("Integer can not be represented by a JSValue"); |
| 59 | + |
| 60 | + // For NaN-boxing, JSC adds 2^48 to a double value's bit pattern. |
| 61 | + this.assignSub(this, 0x1000000000000); |
| 62 | + var res = Struct.unpack(Struct.float64, bytes); |
| 63 | + this.assignAdd(this, 0x1000000000000); |
| 64 | + |
| 65 | + return res; |
| 66 | + }; |
| 67 | + |
| 68 | + // Return the underlying bytes of this number as array. |
| 69 | + this.getBytes = function() { |
| 70 | + return Array.from(bytes); |
| 71 | + }; |
| 72 | + |
| 73 | + // Return the byte at the given index. |
| 74 | + this.byteAt = function(i) { |
| 75 | + return bytes[i]; |
| 76 | + }; |
| 77 | + |
| 78 | + // Return the value of this number as unsigned hex string. |
| 79 | + this.toString = function() { |
| 80 | + return '0x' + hexlify(Array.from(bytes).reverse()); |
| 81 | + }; |
| 82 | + |
| 83 | + this.asInt32 = function() { |
| 84 | + var value = new Int64(0); |
| 85 | + for (var i = 0; i < 8; i++) { |
| 86 | + if (i < 4) { |
| 87 | + value.bytes[i] = this.bytes[i]; |
| 88 | + } else { |
| 89 | + value.bytes[i] = 0; |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + return parseInt('0x' + hexlify(Array.from(value.bytes).reverse()).slice(-8)); |
| 94 | + }; |
| 95 | + |
| 96 | + this.asInt16 = function() { |
| 97 | + var value = new Int64(0); |
| 98 | + for (var i = 0; i < 8; i++) { |
| 99 | + if (i < 2) { |
| 100 | + value.bytes[i] = this.bytes[i]; |
| 101 | + } else { |
| 102 | + value.bytes[i] = 0; |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + return parseInt('0x' + hexlify(Array.from(value.bytes).reverse()).slice(-8)); |
| 107 | + }; |
| 108 | + |
| 109 | + // Basic arithmetic. |
| 110 | + // These functions assign the result of the computation to their 'this' object. |
| 111 | + |
| 112 | + // Decorator for Int64 instance operations. Takes care |
| 113 | + // of converting arguments to Int64 instances if required. |
| 114 | + function operation(f, nargs) { |
| 115 | + return function() { |
| 116 | + if (arguments.length != nargs) |
| 117 | + throw Error("Not enough arguments for function " + f.name); |
| 118 | + for (var i = 0; i < arguments.length; i++) |
| 119 | + if (!(arguments[i] instanceof Int64)) |
| 120 | + arguments[i] = new Int64(arguments[i]); |
| 121 | + return f.apply(this, arguments); |
| 122 | + }; |
| 123 | + } |
| 124 | + |
| 125 | + // this = -n (two's complement) |
| 126 | + this.assignNeg = operation(function neg(n) { |
| 127 | + for (var i = 0; i < 8; i++) |
| 128 | + bytes[i] = ~n.byteAt(i); |
| 129 | + |
| 130 | + return this.assignAdd(this, Int64.One); |
| 131 | + }, 1); |
| 132 | + |
| 133 | + // this = a + b |
| 134 | + this.assignAdd = operation(function add(a, b) { |
| 135 | + var carry = 0; |
| 136 | + for (var i = 0; i < 8; i++) { |
| 137 | + var cur = a.byteAt(i) + b.byteAt(i) + carry; |
| 138 | + carry = cur > 0xff | 0; |
| 139 | + bytes[i] = cur; |
| 140 | + } |
| 141 | + return this; |
| 142 | + }, 2); |
| 143 | + |
| 144 | + // this = a - b |
| 145 | + this.assignSub = operation(function sub(a, b) { |
| 146 | + var carry = 0; |
| 147 | + for (var i = 0; i < 8; i++) { |
| 148 | + var cur = a.byteAt(i) - b.byteAt(i) - carry; |
| 149 | + carry = cur < 0 | 0; |
| 150 | + bytes[i] = cur; |
| 151 | + } |
| 152 | + return this; |
| 153 | + }, 2); |
| 154 | + |
| 155 | + // this = a ^ b |
| 156 | + this.assignXor = operation(function xor(a, b) { |
| 157 | + for (var i = 0; i < 8; i++) { |
| 158 | + bytes[i] = a.byteAt(i) ^ b.byteAt(i); |
| 159 | + } |
| 160 | + return this; |
| 161 | + }, 2); |
| 162 | + |
| 163 | + // this = a & b |
| 164 | + this.assignAnd = operation(function and(a, b) { |
| 165 | + for (var i = 0; i < 8; i++) { |
| 166 | + bytes[i] = a.byteAt(i) & b.byteAt(i); |
| 167 | + } |
| 168 | + return this; |
| 169 | + }, 2); |
| 170 | + |
| 171 | + // this = a << b |
| 172 | + this.assignShiftLeft = operation(function shiftLeft(a, b) { |
| 173 | + for (var i = 0; i < 8; i++) { |
| 174 | + if (i < b) { |
| 175 | + bytes[i] = 0; |
| 176 | + } else { |
| 177 | + bytes[i] = a.byteAt(Sub(i, b).asInt32()); |
| 178 | + } |
| 179 | + } |
| 180 | + return this; |
| 181 | + }, 2); |
| 182 | + |
| 183 | + // this = a >> b |
| 184 | + this.assignShiftRight = operation(function shiftRight(a, b) { |
| 185 | + for (var i = 0; i < 8; i++) { |
| 186 | + if (i < (8 - b)) { |
| 187 | + bytes[i] = a.byteAt(Add(i, b).asInt32()); |
| 188 | + } else { |
| 189 | + bytes[i] = 0; |
| 190 | + } |
| 191 | + } |
| 192 | + return this; |
| 193 | + }, 2); |
| 194 | +} |
| 195 | + |
| 196 | +// Constructs a new Int64 instance with the same bit representation as the provided double. |
| 197 | +Int64.fromDouble = function(d) { |
| 198 | + var bytes = Struct.pack(Struct.float64, d); |
| 199 | + return new Int64(bytes); |
| 200 | +}; |
| 201 | + |
| 202 | +// Convenience functions. These allocate a new Int64 to hold the result. |
| 203 | + |
| 204 | +// Return -n (two's complement) |
| 205 | +function Neg(n) { |
| 206 | + return (new Int64()).assignNeg(n); |
| 207 | +} |
| 208 | + |
| 209 | +// Return a + b |
| 210 | +function Add(a, b) { |
| 211 | + return (new Int64()).assignAdd(a, b); |
| 212 | +} |
| 213 | + |
| 214 | +// Return a - b |
| 215 | +function Sub(a, b) { |
| 216 | + return (new Int64()).assignSub(a, b); |
| 217 | +} |
| 218 | + |
| 219 | +// Return a ^ b |
| 220 | +function Xor(a, b) { |
| 221 | + return (new Int64()).assignXor(a, b); |
| 222 | +} |
| 223 | + |
| 224 | +// Return a & b |
| 225 | +function And(a, b) { |
| 226 | + return (new Int64()).assignAnd(a, b); |
| 227 | +} |
| 228 | + |
| 229 | +// Return a << b |
| 230 | +function ShiftLeft(a, b) { |
| 231 | + return (new Int64()).assignShiftLeft(a, b); |
| 232 | +} |
| 233 | + |
| 234 | +// Return a >> b |
| 235 | +function ShiftRight(a, b) { |
| 236 | + return (new Int64()).assignShiftRight(a, b); |
| 237 | +} |
| 238 | + |
| 239 | +// Some commonly used numbers. |
| 240 | +Int64.Zero = new Int64(0); |
| 241 | +Int64.One = new Int64(1); |
| 242 | + |
| 243 | +// That's all the arithmetic we need for exploiting WebKit.. :) |
0 commit comments