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 |