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 argumentErrorValue(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'# == (1/0)', this) | |
49 || JS('bool', r'# == (-1/0)', this); | |
50 } | |
51 | |
52 bool get isFinite => JS('bool', r'isFinite(#)', this); | |
53 | |
54 num remainder(num b) { | |
55 if (b is! num) throw argumentErrorValue(b); | |
56 return JS('num', r'# % #', this, b); | |
57 } | |
58 | |
59 num abs() => JS('returns:num;effects:none;depends:none;throws:never', | |
60 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 | |
80 int ceil() => ceilToDouble().toInt(); | |
81 | |
82 int floor() => floorToDouble().toInt(); | |
83 | |
84 int round() { | |
85 if (this > 0) { | |
86 // This path excludes the special cases -0.0, NaN and -Infinity, leaving | |
87 // only +Infinity, for which a direct test is faster than [isFinite]. | |
88 if (JS('bool', r'# !== (1/0)', this)) { | |
89 return JS('int', r'Math.round(#)', this); | |
90 } | |
91 } else if (JS('bool', '# > (-1/0)', this)) { | |
92 // This test excludes NaN and -Infinity, leaving only -0.0. | |
93 // | |
94 // Subtraction from zero rather than negation forces -0.0 to 0.0 so code | |
95 // inside Math.round and code to handle result never sees -0.0, which on | |
96 // some JavaScript VMs can be a slow path. | |
97 return JS('int', r'0 - Math.round(0 - #)', this); | |
98 } | |
99 // This is either NaN, Infinity or -Infinity. | |
100 throw new UnsupportedError(JS("String", '"" + #', this)); | |
101 } | |
102 | |
103 double ceilToDouble() => JS('num', r'Math.ceil(#)', this); | |
104 | |
105 double floorToDouble() => JS('num', r'Math.floor(#)', this); | |
106 | |
107 double roundToDouble() { | |
108 if (this < 0) { | |
109 return JS('num', r'-Math.round(-#)', this); | |
110 } else { | |
111 return JS('num', r'Math.round(#)', this); | |
112 } | |
113 } | |
114 | |
115 double truncateToDouble() => this < 0 ? ceilToDouble() : floorToDouble(); | |
116 | |
117 num clamp(lowerLimit, upperLimit) { | |
118 if (lowerLimit is! num) throw argumentErrorValue(lowerLimit); | |
119 if (upperLimit is! num) throw argumentErrorValue(upperLimit); | |
120 if (lowerLimit.compareTo(upperLimit) > 0) { | |
121 throw argumentErrorValue(lowerLimit); | |
122 } | |
123 if (this.compareTo(lowerLimit) < 0) return lowerLimit; | |
124 if (this.compareTo(upperLimit) > 0) return upperLimit; | |
125 return this; | |
126 } | |
127 | |
128 // The return type is intentionally omitted to avoid type checker warnings | |
129 // from assigning JSNumber to double. | |
130 toDouble() => this; | |
131 | |
132 String toStringAsFixed(int fractionDigits) { | |
133 checkInt(fractionDigits); | |
134 if (fractionDigits < 0 || fractionDigits > 20) { | |
135 throw new RangeError(fractionDigits); | |
136 } | |
137 String result = JS('String', r'#.toFixed(#)', this, fractionDigits); | |
138 if (this == 0 && isNegative) return "-$result"; | |
139 return result; | |
140 } | |
141 | |
142 String toStringAsExponential([int fractionDigits]) { | |
143 String result; | |
144 if (fractionDigits != null) { | |
145 checkInt(fractionDigits); | |
146 if (fractionDigits < 0 || fractionDigits > 20) { | |
147 throw new RangeError(fractionDigits); | |
148 } | |
149 result = JS('String', r'#.toExponential(#)', this, fractionDigits); | |
150 } else { | |
151 result = JS('String', r'#.toExponential()', this); | |
152 } | |
153 if (this == 0 && isNegative) return "-$result"; | |
154 return result; | |
155 } | |
156 | |
157 String toStringAsPrecision(int precision) { | |
158 checkInt(precision); | |
159 if (precision < 1 || precision > 21) { | |
160 throw new RangeError(precision); | |
161 } | |
162 String result = JS('String', r'#.toPrecision(#)', | |
163 this, precision); | |
164 if (this == 0 && isNegative) return "-$result"; | |
165 return result; | |
166 } | |
167 | |
168 String toRadixString(int radix) { | |
169 checkInt(radix); | |
170 if (radix < 2 || radix > 36) { | |
171 throw new RangeError.range(radix, 2, 36, "radix"); | |
172 } | |
173 String result = JS('String', r'#.toString(#)', this, radix); | |
174 const int rightParenCode = 0x29; | |
175 if (result.codeUnitAt(result.length - 1) != rightParenCode) { | |
176 return result; | |
177 } | |
178 return _handleIEtoString(result); | |
179 } | |
180 | |
181 static String _handleIEtoString(String result) { | |
182 // Result is probably IE's untraditional format for large numbers, | |
183 // e.g., "8.0000000000008(e+15)" for 0x8000000000000800.toString(16). | |
184 var match = JS('List|Null', | |
185 r'/^([\da-z]+)(?:\.([\da-z]+))?\(e\+(\d+)\)$/.exec(#)', | |
186 result); | |
187 if (match == null) { | |
188 // Then we don't know how to handle it at all. | |
189 throw new UnsupportedError("Unexpected toString result: $result"); | |
190 } | |
191 String result = JS('String', '#', match[1]); | |
192 int exponent = JS("int", "+#", match[3]); | |
193 if (match[2] != null) { | |
194 result = JS('String', '# + #', result, match[2]); | |
195 exponent -= JS('int', '#.length', match[2]); | |
196 } | |
197 return result + "0" * exponent; | |
198 } | |
199 | |
200 // Note: if you change this, also change the function [S]. | |
201 String toString() { | |
202 if (this == 0 && JS('bool', '(1 / #) < 0', this)) { | |
203 return '-0.0'; | |
204 } else { | |
205 return JS('String', r'"" + (#)', this); | |
206 } | |
207 } | |
208 | |
209 int get hashCode => JS('int', '# & 0x1FFFFFFF', this); | |
210 | |
211 num operator -() => JS('num', r'-#', this); | |
212 | |
213 num operator +(num other) { | |
214 if (other is !num) throw argumentErrorValue(other); | |
215 return JS('num', '# + #', this, other); | |
216 } | |
217 | |
218 num operator -(num other) { | |
219 if (other is !num) throw argumentErrorValue(other); | |
220 return JS('num', '# - #', this, other); | |
221 } | |
222 | |
223 num operator /(num other) { | |
224 if (other is !num) throw argumentErrorValue(other); | |
225 return JS('num', '# / #', this, other); | |
226 } | |
227 | |
228 num operator *(num other) { | |
229 if (other is !num) throw argumentErrorValue(other); | |
230 return JS('num', '# * #', this, other); | |
231 } | |
232 | |
233 num operator %(num other) { | |
234 if (other is !num) throw argumentErrorValue(other); | |
235 // Euclidean Modulo. | |
236 num result = JS('num', r'# % #', this, other); | |
237 if (result == 0) return 0; // Make sure we don't return -0.0. | |
238 if (result > 0) return result; | |
239 if (JS('num', '#', other) < 0) { | |
240 return result - JS('num', '#', other); | |
241 } else { | |
242 return result + JS('num', '#', other); | |
243 } | |
244 } | |
245 | |
246 bool _isInt32(value) => JS('bool', '(# | 0) === #', value, value); | |
247 | |
248 int operator ~/(num other) { | |
249 if (false) _tdivFast(other); // Ensure resolution. | |
250 if (_isInt32(this) && _isInt32(other) && 0 != other && -1 != other) { | |
251 return JS('int', r'(# / #) | 0', this, other); | |
252 } else { | |
253 return _tdivSlow(other); | |
254 } | |
255 } | |
256 | |
257 int _tdivFast(num other) { | |
258 return _isInt32(this) | |
259 ? JS('int', r'(# / #) | 0', this, other) | |
260 : (JS('num', r'# / #', this, other)).toInt(); | |
261 } | |
262 | |
263 int _tdivSlow(num other) { | |
264 if (other is !num) throw argumentErrorValue(other); | |
265 return (JS('num', r'# / #', this, other)).toInt(); | |
266 } | |
267 | |
268 // TODO(ngeoffray): Move the bit operations below to [JSInt] and | |
269 // make them take an int. Because this will make operations slower, | |
270 // we define these methods on number for now but we need to decide | |
271 // the grain at which we do the type checks. | |
272 | |
273 num operator <<(num other) { | |
274 if (other is !num) throw argumentErrorValue(other); | |
275 if (JS('num', '#', other) < 0) throw argumentErrorValue(other); | |
276 return _shlPositive(other); | |
277 } | |
278 | |
279 num _shlPositive(num other) { | |
280 // JavaScript only looks at the last 5 bits of the shift-amount. Shifting | |
281 // by 33 is hence equivalent to a shift by 1. | |
282 return JS('bool', r'# > 31', other) | |
283 ? 0 | |
284 : JS('JSUInt32', r'(# << #) >>> 0', this, other); | |
285 } | |
286 | |
287 num operator >>(num other) { | |
288 if (false) _shrReceiverPositive(other); | |
289 if (other is !num) throw argumentErrorValue(other); | |
290 if (JS('num', '#', other) < 0) throw argumentErrorValue(other); | |
291 return _shrOtherPositive(other); | |
292 } | |
293 | |
294 num _shrOtherPositive(num other) { | |
295 return JS('num', '#', this) > 0 | |
296 ? _shrBothPositive(other) | |
297 // For negative numbers we just clamp the shift-by amount. | |
298 // `this` could be negative but not have its 31st bit set. | |
299 // The ">>" would then shift in 0s instead of 1s. Therefore | |
300 // we cannot simply return 0xFFFFFFFF. | |
301 : JS('JSUInt32', r'(# >> #) >>> 0', this, other > 31 ? 31 : other); | |
302 } | |
303 | |
304 num _shrReceiverPositive(num other) { | |
305 if (JS('num', '#', other) < 0) throw argumentErrorValue(other); | |
306 return _shrBothPositive(other); | |
307 } | |
308 | |
309 num _shrBothPositive(num other) { | |
310 return JS('bool', r'# > 31', other) | |
311 // JavaScript only looks at the last 5 bits of the shift-amount. In JS | |
312 // shifting by 33 is hence equivalent to a shift by 1. Shortcut the | |
313 // computation when that happens. | |
314 ? 0 | |
315 // Given that `this` is positive we must not use '>>'. Otherwise a | |
316 // number that has the 31st bit set would be treated as negative and | |
317 // shift in ones. | |
318 : JS('JSUInt32', r'# >>> #', this, other); | |
319 } | |
320 | |
321 num operator &(num other) { | |
322 if (other is !num) throw argumentErrorValue(other); | |
323 return JS('JSUInt32', r'(# & #) >>> 0', this, other); | |
324 } | |
325 | |
326 num operator |(num other) { | |
327 if (other is !num) throw argumentErrorValue(other); | |
328 return JS('JSUInt32', r'(# | #) >>> 0', this, other); | |
329 } | |
330 | |
331 num operator ^(num other) { | |
332 if (other is !num) throw argumentErrorValue(other); | |
333 return JS('JSUInt32', r'(# ^ #) >>> 0', this, other); | |
334 } | |
335 | |
336 bool operator <(num other) { | |
337 if (other is !num) throw argumentErrorValue(other); | |
338 return JS('bool', '# < #', this, other); | |
339 } | |
340 | |
341 bool operator >(num other) { | |
342 if (other is !num) throw argumentErrorValue(other); | |
343 return JS('bool', '# > #', this, other); | |
344 } | |
345 | |
346 bool operator <=(num other) { | |
347 if (other is !num) throw argumentErrorValue(other); | |
348 return JS('bool', '# <= #', this, other); | |
349 } | |
350 | |
351 bool operator >=(num other) { | |
352 if (other is !num) throw argumentErrorValue(other); | |
353 return JS('bool', '# >= #', this, other); | |
354 } | |
355 | |
356 Type get runtimeType => num; | |
357 } | |
358 | |
359 /** | |
360 * The interceptor class for [int]s. | |
361 * | |
362 * This class implements double since in JavaScript all numbers are doubles, so | |
363 * while we want to treat `2.0` as an integer for some operations, its | |
364 * interceptor should answer `true` to `is double`. | |
365 */ | |
366 class JSInt extends JSNumber implements int, double { | |
367 const JSInt(); | |
368 | |
369 bool get isEven => (this & 1) == 0; | |
370 | |
371 bool get isOdd => (this & 1) == 1; | |
372 | |
373 int toUnsigned(int width) { | |
374 return this & ((1 << width) - 1); | |
375 } | |
376 | |
377 int toSigned(int width) { | |
378 int signMask = 1 << (width - 1); | |
379 return (this & (signMask - 1)) - (this & signMask); | |
380 } | |
381 | |
382 int get bitLength { | |
383 int nonneg = this < 0 ? -this - 1 : this; | |
384 if (nonneg >= 0x100000000) { | |
385 nonneg = nonneg ~/ 0x100000000; | |
386 return _bitCount(_spread(nonneg)) + 32; | |
387 } | |
388 return _bitCount(_spread(nonneg)); | |
389 } | |
390 | |
391 // Returns pow(this, e) % m. | |
392 int modPow(int e, int m) { | |
393 if (e is! int) throw argumentErrorValue(e); | |
394 if (m is! int) throw argumentErrorValue(m); | |
395 if (e < 0) throw new RangeError(e); | |
396 if (m <= 0) throw new RangeError(m); | |
397 if (e == 0) return 1; | |
398 int b = this; | |
399 if (b < 0 || b > m) { | |
400 b %= m; | |
401 } | |
402 int r = 1; | |
403 while (e > 0) { | |
404 if (e.isOdd) { | |
405 r = (r * b) % m; | |
406 } | |
407 e ~/= 2; | |
408 b = (b * b) % m; | |
409 } | |
410 return r; | |
411 } | |
412 | |
413 // If inv is false, returns gcd(x, y). | |
414 // If inv is true and gcd(x, y) = 1, returns d, so that c*x + d*y = 1. | |
415 // If inv is true and gcd(x, y) != 1, throws RangeError("Not coprime"). | |
416 static int _binaryGcd(int x, int y, bool inv) { | |
417 int s = 1; | |
418 if (!inv) { | |
419 while (x.isEven && y.isEven) { | |
420 x ~/= 2; | |
421 y ~/= 2; | |
422 s *= 2; | |
423 } | |
424 if (y.isOdd) { | |
425 var t = x; | |
426 x = y; | |
427 y = t; | |
428 } | |
429 } | |
430 final bool ac = x.isEven; | |
431 int u = x; | |
432 int v = y; | |
433 int a = 1, | |
434 b = 0, | |
435 c = 0, | |
436 d = 1; | |
437 do { | |
438 while (u.isEven) { | |
439 u ~/= 2; | |
440 if (ac) { | |
441 if (!a.isEven || !b.isEven) { | |
442 a += y; | |
443 b -= x; | |
444 } | |
445 a ~/= 2; | |
446 } else if (!b.isEven) { | |
447 b -= x; | |
448 } | |
449 b ~/= 2; | |
450 } | |
451 while (v.isEven) { | |
452 v ~/= 2; | |
453 if (ac) { | |
454 if (!c.isEven || !d.isEven) { | |
455 c += y; | |
456 d -= x; | |
457 } | |
458 c ~/= 2; | |
459 } else if (!d.isEven) { | |
460 d -= x; | |
461 } | |
462 d ~/= 2; | |
463 } | |
464 if (u >= v) { | |
465 u -= v; | |
466 if (ac) a -= c; | |
467 b -= d; | |
468 } else { | |
469 v -= u; | |
470 if (ac) c -= a; | |
471 d -= b; | |
472 } | |
473 } while (u != 0); | |
474 if (!inv) return s*v; | |
475 if (v != 1) throw new RangeError("Not coprime"); | |
476 if (d < 0) { | |
477 d += x; | |
478 if (d < 0) d += x; | |
479 } else if (d > x) { | |
480 d -= x; | |
481 if (d > x) d -= x; | |
482 } | |
483 return d; | |
484 } | |
485 | |
486 // Returns 1/this % m, with m > 0. | |
487 int modInverse(int m) { | |
488 if (m is! int) throw new ArgumentError(m); | |
489 if (m <= 0) throw new RangeError(m); | |
490 if (m == 1) return 0; | |
491 int t = this; | |
492 if ((t < 0) || (t >= m)) t %= m; | |
493 if (t == 1) return 1; | |
494 if ((t == 0) || (t.isEven && m.isEven)) throw new RangeError("Not coprime"); | |
495 return _binaryGcd(m, t, true); | |
496 } | |
497 | |
498 // Returns gcd of abs(this) and abs(other), with this != 0 and other !=0. | |
499 int gcd(int other) { | |
500 if (other is! int) throw new ArgumentError(other); | |
501 if ((this == 0) || (other == 0)) throw new RangeError(0); | |
502 int x = this.abs(); | |
503 int y = other.abs(); | |
504 if ((x == 1) || (y == 1)) return 1; | |
505 return _binaryGcd(x, y, false); | |
506 } | |
507 | |
508 // Assumes i is <= 32-bit and unsigned. | |
509 static int _bitCount(int i) { | |
510 // See "Hacker's Delight", section 5-1, "Counting 1-Bits". | |
511 | |
512 // The basic strategy is to use "divide and conquer" to | |
513 // add pairs (then quads, etc.) of bits together to obtain | |
514 // sub-counts. | |
515 // | |
516 // A straightforward approach would look like: | |
517 // | |
518 // i = (i & 0x55555555) + ((i >> 1) & 0x55555555); | |
519 // i = (i & 0x33333333) + ((i >> 2) & 0x33333333); | |
520 // i = (i & 0x0F0F0F0F) + ((i >> 4) & 0x0F0F0F0F); | |
521 // i = (i & 0x00FF00FF) + ((i >> 8) & 0x00FF00FF); | |
522 // i = (i & 0x0000FFFF) + ((i >> 16) & 0x0000FFFF); | |
523 // | |
524 // The code below removes unnecessary &'s and uses a | |
525 // trick to remove one instruction in the first line. | |
526 | |
527 i = _shru(i, 0) - (_shru(i, 1) & 0x55555555); | |
528 i = (i & 0x33333333) + (_shru(i, 2) & 0x33333333); | |
529 i = 0x0F0F0F0F & (i + _shru(i, 4)); | |
530 i += _shru(i, 8); | |
531 i += _shru(i, 16); | |
532 return (i & 0x0000003F); | |
533 } | |
534 | |
535 static _shru(int value, int shift) => JS('int', '# >>> #', value, shift); | |
536 static _shrs(int value, int shift) => JS('int', '# >> #', value, shift); | |
537 static _ors(int a, int b) => JS('int', '# | #', a, b); | |
538 | |
539 // Assumes i is <= 32-bit | |
540 static int _spread(int i) { | |
541 i = _ors(i, _shrs(i, 1)); | |
542 i = _ors(i, _shrs(i, 2)); | |
543 i = _ors(i, _shrs(i, 4)); | |
544 i = _ors(i, _shrs(i, 8)); | |
545 i = _shru(_ors(i, _shrs(i, 16)), 0); | |
546 return i; | |
547 } | |
548 | |
549 Type get runtimeType => int; | |
550 | |
551 int operator ~() => JS('JSUInt32', r'(~#) >>> 0', this); | |
552 } | |
553 | |
554 class JSDouble extends JSNumber implements double { | |
555 const JSDouble(); | |
556 Type get runtimeType => double; | |
557 } | |
558 | |
559 class JSPositiveInt extends JSInt {} | |
560 class JSUInt32 extends JSPositiveInt {} | |
561 class JSUInt31 extends JSUInt32 {} | |
OLD | NEW |