| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of _interceptors; | 5 part of _interceptors; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * The super interceptor class for [JSInt] and [JSDouble]. The compiler | 8 * The super interceptor class for [JSInt] and [JSDouble]. The compiler |
| 9 * recognizes this class as an interceptor, and changes references to | 9 * recognizes this class as an interceptor, and changes references to |
| 10 * [:this:] to actually use the receiver of the method, which is | 10 * [:this:] to actually use the receiver of the method, which is |
| 11 * generated as an extra argument added to each member. | 11 * generated as an extra argument added to each member. |
| 12 * | 12 * |
| 13 * Note that none of the methods here delegate to a method defined on JSInt or | 13 * Note that none of the methods here delegate to a method defined on JSInt or |
| 14 * JSDouble. This is exploited in [tryComputeConstantInterceptor]. | 14 * JSDouble. This is exploited in [tryComputeConstantInterceptor]. |
| 15 */ | 15 */ |
| 16 class JSNumber extends Interceptor implements num { | 16 class JSNumber extends Interceptor implements num { |
| 17 const JSNumber(); | 17 const JSNumber(); |
| 18 | 18 |
| 19 int compareTo(num b) { | 19 int compareTo(num b) { |
| 20 if (b is! num) throw new ArgumentError(b); | 20 if (b is! num) throw argumentErrorValue(b); |
| 21 if (this < b) { | 21 if (this < b) { |
| 22 return -1; | 22 return -1; |
| 23 } else if (this > b) { | 23 } else if (this > b) { |
| 24 return 1; | 24 return 1; |
| 25 } else if (this == b) { | 25 } else if (this == b) { |
| 26 if (this == 0) { | 26 if (this == 0) { |
| 27 bool bIsNegative = b.isNegative; | 27 bool bIsNegative = b.isNegative; |
| 28 if (isNegative == bIsNegative) return 0; | 28 if (isNegative == bIsNegative) return 0; |
| 29 if (isNegative) return -1; | 29 if (isNegative) return -1; |
| 30 return 1; | 30 return 1; |
| 31 } | 31 } |
| 32 return 0; | 32 return 0; |
| 33 } else if (isNaN) { | 33 } else if (isNaN) { |
| 34 if (b.isNaN) { | 34 if (b.isNaN) { |
| 35 return 0; | 35 return 0; |
| 36 } | 36 } |
| 37 return 1; | 37 return 1; |
| 38 } else { | 38 } else { |
| 39 return -1; | 39 return -1; |
| 40 } | 40 } |
| 41 } | 41 } |
| 42 | 42 |
| 43 bool get isNegative => (this == 0) ? (1 / this) < 0 : this < 0; | 43 bool get isNegative => (this == 0) ? (1 / this) < 0 : this < 0; |
| 44 | 44 |
| 45 bool get isNaN => JS('bool', r'isNaN(#)', this); | 45 bool get isNaN => JS('bool', r'isNaN(#)', this); |
| 46 | 46 |
| 47 bool get isInfinite { | 47 bool get isInfinite { |
| 48 return JS('bool', r'# == Infinity', this) | 48 return JS('bool', r'# == (1/0)', this) |
| 49 || JS('bool', r'# == -Infinity', this); | 49 || JS('bool', r'# == (-1/0)', this); |
| 50 } | 50 } |
| 51 | 51 |
| 52 bool get isFinite => JS('bool', r'isFinite(#)', this); | 52 bool get isFinite => JS('bool', r'isFinite(#)', this); |
| 53 | 53 |
| 54 num remainder(num b) { | 54 num remainder(num b) { |
| 55 checkNull(b); // TODO(ngeoffray): This is not specified but co19 tests it. | 55 if (b is! num) throw argumentErrorValue(b); |
| 56 if (b is! num) throw new ArgumentError(b); | |
| 57 return JS('num', r'# % #', this, b); | 56 return JS('num', r'# % #', this, b); |
| 58 } | 57 } |
| 59 | 58 |
| 60 num abs() => JS('returns:num;effects:none;depends:none;throws:never', | 59 num abs() => JS('returns:num;effects:none;depends:none;throws:never', |
| 61 r'Math.abs(#)', this); | 60 r'Math.abs(#)', this); |
| 62 | 61 |
| 63 num get sign => this > 0 ? 1 : this < 0 ? -1 : this; | 62 num get sign => this > 0 ? 1 : this < 0 ? -1 : this; |
| 64 | 63 |
| 65 static const int _MIN_INT32 = -0x80000000; | 64 static const int _MIN_INT32 = -0x80000000; |
| 66 static const int _MAX_INT32 = 0x7FFFFFFF; | 65 static const int _MAX_INT32 = 0x7FFFFFFF; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 109 if (this < 0) { | 108 if (this < 0) { |
| 110 return JS('num', r'-Math.round(-#)', this); | 109 return JS('num', r'-Math.round(-#)', this); |
| 111 } else { | 110 } else { |
| 112 return JS('num', r'Math.round(#)', this); | 111 return JS('num', r'Math.round(#)', this); |
| 113 } | 112 } |
| 114 } | 113 } |
| 115 | 114 |
| 116 double truncateToDouble() => this < 0 ? ceilToDouble() : floorToDouble(); | 115 double truncateToDouble() => this < 0 ? ceilToDouble() : floorToDouble(); |
| 117 | 116 |
| 118 num clamp(lowerLimit, upperLimit) { | 117 num clamp(lowerLimit, upperLimit) { |
| 119 if (lowerLimit is! num) throw new ArgumentError(lowerLimit); | 118 if (lowerLimit is! num) throw argumentErrorValue(lowerLimit); |
| 120 if (upperLimit is! num) throw new ArgumentError(upperLimit); | 119 if (upperLimit is! num) throw argumentErrorValue(upperLimit); |
| 121 if (lowerLimit.compareTo(upperLimit) > 0) { | 120 if (lowerLimit.compareTo(upperLimit) > 0) { |
| 122 throw new ArgumentError(lowerLimit); | 121 throw argumentErrorValue(lowerLimit); |
| 123 } | 122 } |
| 124 if (this.compareTo(lowerLimit) < 0) return lowerLimit; | 123 if (this.compareTo(lowerLimit) < 0) return lowerLimit; |
| 125 if (this.compareTo(upperLimit) > 0) return upperLimit; | 124 if (this.compareTo(upperLimit) > 0) return upperLimit; |
| 126 return this; | 125 return this; |
| 127 } | 126 } |
| 128 | 127 |
| 129 // The return type is intentionally omitted to avoid type checker warnings | 128 // The return type is intentionally omitted to avoid type checker warnings |
| 130 // from assigning JSNumber to double. | 129 // from assigning JSNumber to double. |
| 131 toDouble() => this; | 130 toDouble() => this; |
| 132 | 131 |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 205 } else { | 204 } else { |
| 206 return JS('String', r'"" + (#)', this); | 205 return JS('String', r'"" + (#)', this); |
| 207 } | 206 } |
| 208 } | 207 } |
| 209 | 208 |
| 210 int get hashCode => JS('int', '# & 0x1FFFFFFF', this); | 209 int get hashCode => JS('int', '# & 0x1FFFFFFF', this); |
| 211 | 210 |
| 212 num operator -() => JS('num', r'-#', this); | 211 num operator -() => JS('num', r'-#', this); |
| 213 | 212 |
| 214 num operator +(num other) { | 213 num operator +(num other) { |
| 215 if (other is !num) throw new ArgumentError(other); | 214 if (other is !num) throw argumentErrorValue(other); |
| 216 return JS('num', '# + #', this, other); | 215 return JS('num', '# + #', this, other); |
| 217 } | 216 } |
| 218 | 217 |
| 219 num operator -(num other) { | 218 num operator -(num other) { |
| 220 if (other is !num) throw new ArgumentError(other); | 219 if (other is !num) throw argumentErrorValue(other); |
| 221 return JS('num', '# - #', this, other); | 220 return JS('num', '# - #', this, other); |
| 222 } | 221 } |
| 223 | 222 |
| 224 num operator /(num other) { | 223 num operator /(num other) { |
| 225 if (other is !num) throw new ArgumentError(other); | 224 if (other is !num) throw argumentErrorValue(other); |
| 226 return JS('num', '# / #', this, other); | 225 return JS('num', '# / #', this, other); |
| 227 } | 226 } |
| 228 | 227 |
| 229 num operator *(num other) { | 228 num operator *(num other) { |
| 230 if (other is !num) throw new ArgumentError(other); | 229 if (other is !num) throw argumentErrorValue(other); |
| 231 return JS('num', '# * #', this, other); | 230 return JS('num', '# * #', this, other); |
| 232 } | 231 } |
| 233 | 232 |
| 234 num operator %(num other) { | 233 num operator %(num other) { |
| 235 if (other is !num) throw new ArgumentError(other); | 234 if (other is !num) throw argumentErrorValue(other); |
| 236 // Euclidean Modulo. | 235 // Euclidean Modulo. |
| 237 num result = JS('num', r'# % #', this, other); | 236 num result = JS('num', r'# % #', this, other); |
| 238 if (result == 0) return 0; // Make sure we don't return -0.0. | 237 if (result == 0) return 0; // Make sure we don't return -0.0. |
| 239 if (result > 0) return result; | 238 if (result > 0) return result; |
| 240 if (JS('num', '#', other) < 0) { | 239 if (JS('num', '#', other) < 0) { |
| 241 return result - JS('num', '#', other); | 240 return result - JS('num', '#', other); |
| 242 } else { | 241 } else { |
| 243 return result + JS('num', '#', other); | 242 return result + JS('num', '#', other); |
| 244 } | 243 } |
| 245 } | 244 } |
| 246 | 245 |
| 247 bool _isInt32(value) => JS('bool', '(# | 0) === #', value, value); | 246 bool _isInt32(value) => JS('bool', '(# | 0) === #', value, value); |
| 248 | 247 |
| 249 int operator ~/(num other) { | 248 int operator ~/(num other) { |
| 250 if (false) _tdivFast(other); // Ensure resolution. | 249 if (false) _tdivFast(other); // Ensure resolution. |
| 251 if (_isInt32(this) && _isInt32(other) && 0 != other && -1 != other) { | 250 if (_isInt32(this) && _isInt32(other) && 0 != other && -1 != other) { |
| 252 return JS('int', r'(# / #) | 0', this, other); | 251 return JS('int', r'(# / #) | 0', this, other); |
| 253 } else { | 252 } else { |
| 254 return _tdivSlow(other); | 253 return _tdivSlow(other); |
| 255 } | 254 } |
| 256 } | 255 } |
| 257 | 256 |
| 258 int _tdivFast(num other) { | 257 int _tdivFast(num other) { |
| 259 return _isInt32(this) | 258 return _isInt32(this) |
| 260 ? JS('int', r'(# / #) | 0', this, other) | 259 ? JS('int', r'(# / #) | 0', this, other) |
| 261 : (JS('num', r'# / #', this, other)).toInt(); | 260 : (JS('num', r'# / #', this, other)).toInt(); |
| 262 } | 261 } |
| 263 | 262 |
| 264 int _tdivSlow(num other) { | 263 int _tdivSlow(num other) { |
| 265 if (other is !num) throw new ArgumentError(other); | 264 if (other is !num) throw argumentErrorValue(other); |
| 266 return (JS('num', r'# / #', this, other)).toInt(); | 265 return (JS('num', r'# / #', this, other)).toInt(); |
| 267 } | 266 } |
| 268 | 267 |
| 269 // TODO(ngeoffray): Move the bit operations below to [JSInt] and | 268 // TODO(ngeoffray): Move the bit operations below to [JSInt] and |
| 270 // make them take an int. Because this will make operations slower, | 269 // make them take an int. Because this will make operations slower, |
| 271 // we define these methods on number for now but we need to decide | 270 // we define these methods on number for now but we need to decide |
| 272 // the grain at which we do the type checks. | 271 // the grain at which we do the type checks. |
| 273 | 272 |
| 274 num operator <<(num other) { | 273 num operator <<(num other) { |
| 275 if (other is !num) throw new ArgumentError(other); | 274 if (other is !num) throw argumentErrorValue(other); |
| 276 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | 275 if (JS('num', '#', other) < 0) throw argumentErrorValue(other); |
| 277 return _shlPositive(other); | 276 return _shlPositive(other); |
| 278 } | 277 } |
| 279 | 278 |
| 280 num _shlPositive(num other) { | 279 num _shlPositive(num other) { |
| 281 // JavaScript only looks at the last 5 bits of the shift-amount. Shifting | 280 // JavaScript only looks at the last 5 bits of the shift-amount. Shifting |
| 282 // by 33 is hence equivalent to a shift by 1. | 281 // by 33 is hence equivalent to a shift by 1. |
| 283 return JS('bool', r'# > 31', other) | 282 return JS('bool', r'# > 31', other) |
| 284 ? 0 | 283 ? 0 |
| 285 : JS('JSUInt32', r'(# << #) >>> 0', this, other); | 284 : JS('JSUInt32', r'(# << #) >>> 0', this, other); |
| 286 } | 285 } |
| 287 | 286 |
| 288 num operator >>(num other) { | 287 num operator >>(num other) { |
| 289 if (false) _shrReceiverPositive(other); | 288 if (false) _shrReceiverPositive(other); |
| 290 if (other is !num) throw new ArgumentError(other); | 289 if (other is !num) throw argumentErrorValue(other); |
| 291 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | 290 if (JS('num', '#', other) < 0) throw argumentErrorValue(other); |
| 292 return _shrOtherPositive(other); | 291 return _shrOtherPositive(other); |
| 293 } | 292 } |
| 294 | 293 |
| 295 num _shrOtherPositive(num other) { | 294 num _shrOtherPositive(num other) { |
| 296 return JS('num', '#', this) > 0 | 295 return JS('num', '#', this) > 0 |
| 297 ? _shrBothPositive(other) | 296 ? _shrBothPositive(other) |
| 298 // For negative numbers we just clamp the shift-by amount. | 297 // For negative numbers we just clamp the shift-by amount. |
| 299 // `this` could be negative but not have its 31st bit set. | 298 // `this` could be negative but not have its 31st bit set. |
| 300 // The ">>" would then shift in 0s instead of 1s. Therefore | 299 // The ">>" would then shift in 0s instead of 1s. Therefore |
| 301 // we cannot simply return 0xFFFFFFFF. | 300 // we cannot simply return 0xFFFFFFFF. |
| 302 : JS('JSUInt32', r'(# >> #) >>> 0', this, other > 31 ? 31 : other); | 301 : JS('JSUInt32', r'(# >> #) >>> 0', this, other > 31 ? 31 : other); |
| 303 } | 302 } |
| 304 | 303 |
| 305 num _shrReceiverPositive(num other) { | 304 num _shrReceiverPositive(num other) { |
| 306 if (JS('num', '#', other) < 0) throw new ArgumentError(other); | 305 if (JS('num', '#', other) < 0) throw argumentErrorValue(other); |
| 307 return _shrBothPositive(other); | 306 return _shrBothPositive(other); |
| 308 } | 307 } |
| 309 | 308 |
| 310 num _shrBothPositive(num other) { | 309 num _shrBothPositive(num other) { |
| 311 return JS('bool', r'# > 31', other) | 310 return JS('bool', r'# > 31', other) |
| 312 // JavaScript only looks at the last 5 bits of the shift-amount. In JS | 311 // JavaScript only looks at the last 5 bits of the shift-amount. In JS |
| 313 // shifting by 33 is hence equivalent to a shift by 1. Shortcut the | 312 // shifting by 33 is hence equivalent to a shift by 1. Shortcut the |
| 314 // computation when that happens. | 313 // computation when that happens. |
| 315 ? 0 | 314 ? 0 |
| 316 // Given that `this` is positive we must not use '>>'. Otherwise a | 315 // Given that `this` is positive we must not use '>>'. Otherwise a |
| 317 // number that has the 31st bit set would be treated as negative and | 316 // number that has the 31st bit set would be treated as negative and |
| 318 // shift in ones. | 317 // shift in ones. |
| 319 : JS('JSUInt32', r'# >>> #', this, other); | 318 : JS('JSUInt32', r'# >>> #', this, other); |
| 320 } | 319 } |
| 321 | 320 |
| 322 num operator &(num other) { | 321 num operator &(num other) { |
| 323 if (other is !num) throw new ArgumentError(other); | 322 if (other is !num) throw argumentErrorValue(other); |
| 324 return JS('JSUInt32', r'(# & #) >>> 0', this, other); | 323 return JS('JSUInt32', r'(# & #) >>> 0', this, other); |
| 325 } | 324 } |
| 326 | 325 |
| 327 num operator |(num other) { | 326 num operator |(num other) { |
| 328 if (other is !num) throw new ArgumentError(other); | 327 if (other is !num) throw argumentErrorValue(other); |
| 329 return JS('JSUInt32', r'(# | #) >>> 0', this, other); | 328 return JS('JSUInt32', r'(# | #) >>> 0', this, other); |
| 330 } | 329 } |
| 331 | 330 |
| 332 num operator ^(num other) { | 331 num operator ^(num other) { |
| 333 if (other is !num) throw new ArgumentError(other); | 332 if (other is !num) throw argumentErrorValue(other); |
| 334 return JS('JSUInt32', r'(# ^ #) >>> 0', this, other); | 333 return JS('JSUInt32', r'(# ^ #) >>> 0', this, other); |
| 335 } | 334 } |
| 336 | 335 |
| 337 bool operator <(num other) { | 336 bool operator <(num other) { |
| 338 if (other is !num) throw new ArgumentError(other); | 337 if (other is !num) throw argumentErrorValue(other); |
| 339 return JS('bool', '# < #', this, other); | 338 return JS('bool', '# < #', this, other); |
| 340 } | 339 } |
| 341 | 340 |
| 342 bool operator >(num other) { | 341 bool operator >(num other) { |
| 343 if (other is !num) throw new ArgumentError(other); | 342 if (other is !num) throw argumentErrorValue(other); |
| 344 return JS('bool', '# > #', this, other); | 343 return JS('bool', '# > #', this, other); |
| 345 } | 344 } |
| 346 | 345 |
| 347 bool operator <=(num other) { | 346 bool operator <=(num other) { |
| 348 if (other is !num) throw new ArgumentError(other); | 347 if (other is !num) throw argumentErrorValue(other); |
| 349 return JS('bool', '# <= #', this, other); | 348 return JS('bool', '# <= #', this, other); |
| 350 } | 349 } |
| 351 | 350 |
| 352 bool operator >=(num other) { | 351 bool operator >=(num other) { |
| 353 if (other is !num) throw new ArgumentError(other); | 352 if (other is !num) throw argumentErrorValue(other); |
| 354 return JS('bool', '# >= #', this, other); | 353 return JS('bool', '# >= #', this, other); |
| 355 } | 354 } |
| 356 | 355 |
| 357 Type get runtimeType => num; | 356 Type get runtimeType => num; |
| 358 } | 357 } |
| 359 | 358 |
| 360 /** | 359 /** |
| 361 * The interceptor class for [int]s. | 360 * The interceptor class for [int]s. |
| 362 * | 361 * |
| 363 * This class implements double since in JavaScript all numbers are doubles, so | 362 * This class implements double since in JavaScript all numbers are doubles, so |
| (...skipping 20 matching lines...) Expand all Loading... |
| 384 int nonneg = this < 0 ? -this - 1 : this; | 383 int nonneg = this < 0 ? -this - 1 : this; |
| 385 if (nonneg >= 0x100000000) { | 384 if (nonneg >= 0x100000000) { |
| 386 nonneg = nonneg ~/ 0x100000000; | 385 nonneg = nonneg ~/ 0x100000000; |
| 387 return _bitCount(_spread(nonneg)) + 32; | 386 return _bitCount(_spread(nonneg)) + 32; |
| 388 } | 387 } |
| 389 return _bitCount(_spread(nonneg)); | 388 return _bitCount(_spread(nonneg)); |
| 390 } | 389 } |
| 391 | 390 |
| 392 // Returns pow(this, e) % m. | 391 // Returns pow(this, e) % m. |
| 393 int modPow(int e, int m) { | 392 int modPow(int e, int m) { |
| 394 if (e is! int) throw new ArgumentError(e); | 393 if (e is! int) throw argumentErrorValue(e); |
| 395 if (m is! int) throw new ArgumentError(m); | 394 if (m is! int) throw argumentErrorValue(m); |
| 396 if (e < 0) throw new RangeError(e); | 395 if (e < 0) throw new RangeError(e); |
| 397 if (m <= 0) throw new RangeError(m); | 396 if (m <= 0) throw new RangeError(m); |
| 398 if (e == 0) return 1; | 397 if (e == 0) return 1; |
| 399 int b = this; | 398 int b = this; |
| 400 if (b < 0 || b > m) { | 399 if (b < 0 || b > m) { |
| 401 b %= m; | 400 b %= m; |
| 402 } | 401 } |
| 403 int r = 1; | 402 int r = 1; |
| 404 while (e > 0) { | 403 while (e > 0) { |
| 405 if (e.isOdd) { | 404 if (e.isOdd) { |
| 406 r = (r * b) % m; | 405 r = (r * b) % m; |
| 407 } | 406 } |
| 408 e ~/= 2; | 407 e ~/= 2; |
| 409 b = (b * b) % m; | 408 b = (b * b) % m; |
| 410 } | 409 } |
| 411 return r; | 410 return r; |
| 412 } | 411 } |
| 413 | 412 |
| 414 // Returns 1/this % m, with m > 0. | 413 // Returns 1/this % m, with m > 0. |
| 415 int modInverse(int m) { | 414 int modInverse(int m) { |
| 416 if (m is! int) throw new ArgumentError(m); | 415 if (m is! int) throw argumentErrorValue(m); |
| 417 if (m <= 0) throw new RangeError(m); | 416 if (m <= 0) throw new RangeError(m); |
| 418 if (m == 1) return 0; | 417 if (m == 1) return 0; |
| 419 int t = this; | 418 int t = this; |
| 420 if ((t < 0) || (t >= m)) t %= m; | 419 if ((t < 0) || (t >= m)) t %= m; |
| 421 if (t == 1) return 1; | 420 if (t == 1) return 1; |
| 422 final bool ac = m.isEven; | 421 final bool ac = m.isEven; |
| 423 if ((t == 0) || (ac && t.isEven)) throw new RangeError("Not coprime"); | 422 if ((t == 0) || (ac && t.isEven)) throw new RangeError("Not coprime"); |
| 424 int u = m; | 423 int u = m; |
| 425 int v = t; | 424 int v = t; |
| 426 int a = 1, | 425 int a = 1, |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 522 } | 521 } |
| 523 | 522 |
| 524 class JSDouble extends JSNumber implements double { | 523 class JSDouble extends JSNumber implements double { |
| 525 const JSDouble(); | 524 const JSDouble(); |
| 526 Type get runtimeType => double; | 525 Type get runtimeType => double; |
| 527 } | 526 } |
| 528 | 527 |
| 529 class JSPositiveInt extends JSInt {} | 528 class JSPositiveInt extends JSInt {} |
| 530 class JSUInt32 extends JSPositiveInt {} | 529 class JSUInt32 extends JSPositiveInt {} |
| 531 class JSUInt31 extends JSUInt32 {} | 530 class JSUInt31 extends JSUInt32 {} |
| OLD | NEW |