Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(122)

Side by Side Diff: sdk/lib/_internal/compiler/js_lib/js_number.dart

Issue 1212513002: sdk files reorganization to make dart2js a proper package (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: renamed Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 {}
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698