| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of _interceptors; | |
| 6 | |
| 7 /** | |
| 8 * The super interceptor class for [JSInt] and [JSDouble]. The compiler | |
| 9 * recognizes this class as an interceptor, and changes references to | |
| 10 * [:this:] to actually use the receiver of the method, which is | |
| 11 * generated as an extra argument added to each member. | |
| 12 * | |
| 13 * Note that none of the methods here delegate to a method defined on JSInt or | |
| 14 * JSDouble. This is exploited in [tryComputeConstantInterceptor]. | |
| 15 */ | |
| 16 class JSNumber extends Interceptor implements num { | |
| 17 const JSNumber(); | |
| 18 | |
| 19 int compareTo(num b) { | |
| 20 if (b is! num) throw new ArgumentError(b); | |
| 21 if (this < b) { | |
| 22 return -1; | |
| 23 } else if (this > b) { | |
| 24 return 1; | |
| 25 } else if (this == b) { | |
| 26 if (this == 0) { | |
| 27 bool bIsNegative = b.isNegative; | |
| 28 if (isNegative == bIsNegative) return 0; | |
| 29 if (isNegative) return -1; | |
| 30 return 1; | |
| 31 } | |
| 32 return 0; | |
| 33 } else if (isNaN) { | |
| 34 if (b.isNaN) { | |
| 35 return 0; | |
| 36 } | |
| 37 return 1; | |
| 38 } else { | |
| 39 return -1; | |
| 40 } | |
| 41 } | |
| 42 | |
| 43 bool get isNegative => (this == 0) ? (1 / this) < 0 : this < 0; | |
| 44 | |
| 45 bool get isNaN => JS('bool', r'isNaN(#)', this); | |
| 46 | |
| 47 bool get isInfinite { | |
| 48 return JS('bool', r'# == Infinity', this) | |
| 49 || JS('bool', r'# == -Infinity', this); | |
| 50 } | |
| 51 | |
| 52 bool get isFinite => JS('bool', r'isFinite(#)', this); | |
| 53 | |
| 54 num remainder(num b) { | |
| 55 checkNull(b); // TODO(ngeoffray): This is not specified but co19 tests it. | |
| 56 if (b is! num) throw new ArgumentError(b); | |
| 57 return JS('num', r'# % #', this, b); | |
| 58 } | |
| 59 | |
| 60 num abs() => JS('num', r'Math.abs(#)', this); | |
| 61 | |
| 62 num get sign => this > 0 ? 1 : this < 0 ? -1 : this; | |
| 63 | |
| 64 static const int _MIN_INT32 = -0x80000000; | |
| 65 static const int _MAX_INT32 = 0x7FFFFFFF; | |
| 66 | |
| 67 int toInt() { | |
| 68 if (this >= _MIN_INT32 && this <= _MAX_INT32) { | |
| 69 return JS('int', '# | 0', this); | |
| 70 } | |
| 71 if (JS('bool', r'isFinite(#)', this)) { | |
| 72 return JS('int', r'# + 0', truncateToDouble()); // Converts -0.0 to +0.0. | |
| 73 } | |
| 74 // This is either NaN, Infinity or -Infinity. | |
| 75 throw new UnsupportedError(JS("String", "''+#", this)); | |
| 76 } | |
| 77 | |
| 78 int truncate() => toInt(); | |
| 79 int ceil() => ceilToDouble().toInt(); | |
| 80 int floor() => floorToDouble().toInt(); | |
| 81 int round() => roundToDouble().toInt(); | |
| 82 | |
| 83 double ceilToDouble() => JS('num', r'Math.ceil(#)', this); | |
| 84 | |
| 85 double floorToDouble() => JS('num', r'Math.floor(#)', this); | |
| 86 | |
| 87 double roundToDouble() { | |
| 88 if (this < 0) { | |
| 89 return JS('num', r'-Math.round(-#)', this); | |
| 90 } else { | |
| 91 return JS('num', r'Math.round(#)', this); | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 double truncateToDouble() => this < 0 ? ceilToDouble() : floorToDouble(); | |
| 96 | |
| 97 num clamp(num lowerLimit, num upperLimit) { | |
| 98 if (lowerLimit is! num) throw new ArgumentError(lowerLimit); | |
| 99 if (upperLimit is! num) throw new ArgumentError(upperLimit); | |
| 100 if (lowerLimit.compareTo(upperLimit) > 0) { | |
| 101 throw new ArgumentError(lowerLimit); | |
| 102 } | |
| 103 if (this.compareTo(lowerLimit) < 0) return lowerLimit; | |
| 104 if (this.compareTo(upperLimit) > 0) return upperLimit; | |
| 105 return this; | |
| 106 } | |
| 107 | |
| 108 // The return type is intentionally omitted to avoid type checker warnings | |
| 109 // from assigning JSNumber to double. | |
| 110 toDouble() => this; | |
| 111 | |
| 112 String toStringAsFixed(int fractionDigits) { | |
| 113 checkInt(fractionDigits); | |
| 114 if (fractionDigits < 0 || fractionDigits > 20) { | |
| 115 throw new RangeError(fractionDigits); | |
| 116 } | |
| 117 String result = JS('String', r'#.toFixed(#)', this, fractionDigits); | |
| 118 if (this == 0 && isNegative) return "-$result"; | |
| 119 return result; | |
| 120 } | |
| 121 | |
| 122 String toStringAsExponential([int fractionDigits]) { | |
| 123 String result; | |
| 124 if (fractionDigits != null) { | |
| 125 checkInt(fractionDigits); | |
| 126 if (fractionDigits < 0 || fractionDigits > 20) { | |
| 127 throw new RangeError(fractionDigits); | |
| 128 } | |
| 129 result = JS('String', r'#.toExponential(#)', this, fractionDigits); | |
| 130 } else { | |
| 131 result = JS('String', r'#.toExponential()', this); | |
| 132 } | |
| 133 if (this == 0 && isNegative) return "-$result"; | |
| 134 return result; | |
| 135 } | |
| 136 | |
| 137 String toStringAsPrecision(int precision) { | |
| 138 checkInt(precision); | |
| 139 if (precision < 1 || precision > 21) { | |
| 140 throw new RangeError(precision); | |
| 141 } | |
| 142 String result = JS('String', r'#.toPrecision(#)', | |
| 143 this, precision); | |
| 144 if (this == 0 && isNegative) return "-$result"; | |
| 145 return result; | |
| 146 } | |
| 147 | |
| 148 String toRadixString(int radix) { | |
| 149 checkInt(radix); | |
| 150 if (radix < 2 || radix > 36) throw new RangeError(radix); | |
| 151 String result = JS('String', r'#.toString(#)', this, radix); | |
| 152 const int rightParenCode = 0x29; | |
| 153 if (result.codeUnitAt(result.length - 1) != rightParenCode) { | |
| 154 return result; | |
| 155 } | |
| 156 return _handleIEtoString(result); | |
| 157 } | |
| 158 | |
| 159 static String _handleIEtoString(String result) { | |
| 160 // Result is probably IE's untraditional format for large numbers, | |
| 161 // e.g., "8.0000000000008(e+15)" for 0x8000000000000800.toString(16). | |
| 162 var match = JS('List|Null', | |
| 163 r'/^([\da-z]+)(?:\.([\da-z]+))?\(e\+(\d+)\)$/.exec(#)', | |
| 164 result); | |
| 165 if (match == null) { | |
| 166 // Then we don't know how to handle it at all. | |
| 167 throw new UnsupportedError("Unexpected toString result: $result"); | |
| 168 } | |
| 169 result = JS('String', '#', match[1]); | |
| 170 int exponent = JS("int", "+#", match[3]); | |
| 171 if (match[2] != null) { | |
| 172 result = JS('String', '# + #', result, match[2]); | |
| 173 exponent -= JS('int', '#.length', match[2]); | |
| 174 } | |
| 175 return result + "0" * exponent; | |
| 176 } | |
| 177 | |
| 178 // Note: if you change this, also change the function [S]. | |
| 179 String toString() { | |
| 180 if (this == 0 && JS('bool', '(1 / #) < 0', this)) { | |
| 181 return '-0.0'; | |
| 182 } else { | |
| 183 return JS('String', r'"" + (#)', this); | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 int get hashCode => JS('int', '# & 0x1FFFFFFF', this); | |
| 188 | |
| 189 num operator -() => JS('num', r'-#', this); | |
| 190 | |
| 191 num operator +(num other) { | |
| 192 if (other is !num) throw new ArgumentError(other); | |
| 193 return JS('num', '# + #', this, other); | |
| 194 } | |
| 195 | |
| 196 num operator -(num other) { | |
| 197 if (other is !num) throw new ArgumentError(other); | |
| 198 return JS('num', '# - #', this, other); | |
| 199 } | |
| 200 | |
| 201 double operator /(num other) { | |
| 202 if (other is !num) throw new ArgumentError(other); | |
| 203 return JS('double', '# / #', this, other); | |
| 204 } | |
| 205 | |
| 206 num operator *(num other) { | |
| 207 if (other is !num) throw new ArgumentError(other); | |
| 208 return JS('num', '# * #', this, other); | |
| 209 } | |
| 210 | |
| 211 num operator %(num other) { | |
| 212 if (other is !num) throw new ArgumentError(other); | |
| 213 // Euclidean Modulo. | |
| 214 num result = JS('num', r'# % #', this, other); | |
| 215 if (result == 0) return 0; // Make sure we don't return -0.0. | |
| 216 if (result > 0) return result; | |
| 217 if (JS('num', '#', other) < 0) { | |
| 218 return result - JS('num', '#', other); | |
| 219 } else { | |
| 220 return result + JS('num', '#', other); | |
| 221 } | |
| 222 } | |
| 223 | |
| 224 bool _isInt32(value) => JS('bool', '(# | 0) === #', value, value); | |
| 225 | |
| 226 int operator ~/(num other) { | |
| 227 if (false) _tdivFast(other); // Ensure resolution. | |
| 228 if (_isInt32(this) && _isInt32(other) && 0 != other && -1 != other) { | |
| 229 return JS('int', r'(# / #) | 0', this, other); | |
| 230 } else { | |
| 231 return _tdivSlow(other); | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 int _tdivFast(num other) { | |
| 236 return _isInt32(this) | |
| 237 ? JS('int', r'(# / #) | 0', this, other) | |
| 238 : (JS('num', r'# / #', this, other)).toInt(); | |
| 239 } | |
| 240 | |
| 241 int _tdivSlow(num other) { | |
| 242 if (other is !num) throw new ArgumentError(other); | |
| 243 return (JS('num', r'# / #', this, other)).toInt(); | |
| 244 } | |
| 245 | |
| 246 // TODO(ngeoffray): Move the bit operations below to [JSInt] and | |
| 247 // make them take an int. Because this will make operations slower, | |
| 248 // we define these methods on number for now but we need to decide | |
| 249 // the grain at which we do the type checks. | |
| 250 | |
| 251 num operator <<(num other) { | |
| 252 if (other is !num) throw new ArgumentError(other); | |
| 253 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | |
| 254 return _shlPositive(other); | |
| 255 } | |
| 256 | |
| 257 num _shlPositive(num other) { | |
| 258 // JavaScript only looks at the last 5 bits of the shift-amount. Shifting | |
| 259 // by 33 is hence equivalent to a shift by 1. | |
| 260 return JS('bool', r'# > 31', other) | |
| 261 ? 0 | |
| 262 : JS('JSUInt32', r'(# << #) >>> 0', this, other); | |
| 263 } | |
| 264 | |
| 265 num operator >>(num other) { | |
| 266 if (false) _shrReceiverPositive(other); | |
| 267 if (other is !num) throw new ArgumentError(other); | |
| 268 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | |
| 269 return _shrOtherPositive(other); | |
| 270 } | |
| 271 | |
| 272 num _shrOtherPositive(num other) { | |
| 273 return JS('num', '#', this) > 0 | |
| 274 ? _shrBothPositive(other) | |
| 275 // For negative numbers we just clamp the shift-by amount. | |
| 276 // `this` could be negative but not have its 31st bit set. | |
| 277 // The ">>" would then shift in 0s instead of 1s. Therefore | |
| 278 // we cannot simply return 0xFFFFFFFF. | |
| 279 : JS('JSUInt32', r'(# >> #) >>> 0', this, other > 31 ? 31 : other); | |
| 280 } | |
| 281 | |
| 282 num _shrReceiverPositive(num other) { | |
| 283 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | |
| 284 return _shrBothPositive(other); | |
| 285 } | |
| 286 | |
| 287 num _shrBothPositive(num other) { | |
| 288 return JS('bool', r'# > 31', other) | |
| 289 // JavaScript only looks at the last 5 bits of the shift-amount. In JS | |
| 290 // shifting by 33 is hence equivalent to a shift by 1. Shortcut the | |
| 291 // computation when that happens. | |
| 292 ? 0 | |
| 293 // Given that `this` is positive we must not use '>>'. Otherwise a | |
| 294 // number that has the 31st bit set would be treated as negative and | |
| 295 // shift in ones. | |
| 296 : JS('JSUInt32', r'# >>> #', this, other); | |
| 297 } | |
| 298 | |
| 299 num operator &(num other) { | |
| 300 if (other is !num) throw new ArgumentError(other); | |
| 301 return JS('JSUInt32', r'(# & #) >>> 0', this, other); | |
| 302 } | |
| 303 | |
| 304 num operator |(num other) { | |
| 305 if (other is !num) throw new ArgumentError(other); | |
| 306 return JS('JSUInt32', r'(# | #) >>> 0', this, other); | |
| 307 } | |
| 308 | |
| 309 num operator ^(num other) { | |
| 310 if (other is !num) throw new ArgumentError(other); | |
| 311 return JS('JSUInt32', r'(# ^ #) >>> 0', this, other); | |
| 312 } | |
| 313 | |
| 314 bool operator <(num other) { | |
| 315 if (other is !num) throw new ArgumentError(other); | |
| 316 return JS('bool', '# < #', this, other); | |
| 317 } | |
| 318 | |
| 319 bool operator >(num other) { | |
| 320 if (other is !num) throw new ArgumentError(other); | |
| 321 return JS('bool', '# > #', this, other); | |
| 322 } | |
| 323 | |
| 324 bool operator <=(num other) { | |
| 325 if (other is !num) throw new ArgumentError(other); | |
| 326 return JS('bool', '# <= #', this, other); | |
| 327 } | |
| 328 | |
| 329 bool operator >=(num other) { | |
| 330 if (other is !num) throw new ArgumentError(other); | |
| 331 return JS('bool', '# >= #', this, other); | |
| 332 } | |
| 333 | |
| 334 Type get runtimeType => num; | |
| 335 } | |
| 336 | |
| 337 /** | |
| 338 * The interceptor class for [int]s. | |
| 339 * | |
| 340 * This class implements double since in JavaScript all numbers are doubles, so | |
| 341 * while we want to treat `2.0` as an integer for some operations, its | |
| 342 * interceptor should answer `true` to `is double`. | |
| 343 */ | |
| 344 class JSInt extends JSNumber implements int, double { | |
| 345 const JSInt(); | |
| 346 | |
| 347 bool get isEven => (this & 1) == 0; | |
| 348 | |
| 349 bool get isOdd => (this & 1) == 1; | |
| 350 | |
| 351 int toUnsigned(int width) { | |
| 352 return this & ((1 << width) - 1); | |
| 353 } | |
| 354 | |
| 355 int toSigned(int width) { | |
| 356 int signMask = 1 << (width - 1); | |
| 357 return (this & (signMask - 1)) - (this & signMask); | |
| 358 } | |
| 359 | |
| 360 int get bitLength { | |
| 361 int nonneg = this < 0 ? -this - 1 : this; | |
| 362 if (nonneg >= 0x100000000) { | |
| 363 nonneg = nonneg ~/ 0x100000000; | |
| 364 return _bitCount(_spread(nonneg)) + 32; | |
| 365 } | |
| 366 return _bitCount(_spread(nonneg)); | |
| 367 } | |
| 368 | |
| 369 // Assumes i is <= 32-bit and unsigned. | |
| 370 static int _bitCount(int i) { | |
| 371 // See "Hacker's Delight", section 5-1, "Counting 1-Bits". | |
| 372 | |
| 373 // The basic strategy is to use "divide and conquer" to | |
| 374 // add pairs (then quads, etc.) of bits together to obtain | |
| 375 // sub-counts. | |
| 376 // | |
| 377 // A straightforward approach would look like: | |
| 378 // | |
| 379 // i = (i & 0x55555555) + ((i >> 1) & 0x55555555); | |
| 380 // i = (i & 0x33333333) + ((i >> 2) & 0x33333333); | |
| 381 // i = (i & 0x0F0F0F0F) + ((i >> 4) & 0x0F0F0F0F); | |
| 382 // i = (i & 0x00FF00FF) + ((i >> 8) & 0x00FF00FF); | |
| 383 // i = (i & 0x0000FFFF) + ((i >> 16) & 0x0000FFFF); | |
| 384 // | |
| 385 // The code below removes unnecessary &'s and uses a | |
| 386 // trick to remove one instruction in the first line. | |
| 387 | |
| 388 i = _shru(i, 0) - (_shru(i, 1) & 0x55555555); | |
| 389 i = (i & 0x33333333) + (_shru(i, 2) & 0x33333333); | |
| 390 i = 0x0F0F0F0F & (i + _shru(i, 4)); | |
| 391 i += _shru(i, 8); | |
| 392 i += _shru(i, 16); | |
| 393 return (i & 0x0000003F); | |
| 394 } | |
| 395 | |
| 396 static int _shru(int value, int shift) => JS('int', '# >>> #', value, shift); | |
| 397 static int _shrs(int value, int shift) => JS('int', '# >> #', value, shift); | |
| 398 static int _ors(int a, int b) => JS('int', '# | #', a, b); | |
| 399 | |
| 400 // Assumes i is <= 32-bit | |
| 401 static int _spread(int i) { | |
| 402 i = _ors(i, _shrs(i, 1)); | |
| 403 i = _ors(i, _shrs(i, 2)); | |
| 404 i = _ors(i, _shrs(i, 4)); | |
| 405 i = _ors(i, _shrs(i, 8)); | |
| 406 i = _shru(_ors(i, _shrs(i, 16)), 0); | |
| 407 return i; | |
| 408 } | |
| 409 | |
| 410 Type get runtimeType => int; | |
| 411 | |
| 412 int operator ~() => JS('JSUInt32', r'(~#) >>> 0', this); | |
| 413 } | |
| 414 | |
| 415 class JSDouble extends JSNumber implements double { | |
| 416 const JSDouble(); | |
| 417 Type get runtimeType => double; | |
| 418 } | |
| 419 | |
| 420 class JSPositiveInt extends JSInt {} | |
| 421 class JSUInt32 extends JSPositiveInt {} | |
| 422 class JSUInt31 extends JSUInt32 {} | |
| OLD | NEW |