| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of _interceptors; |
| |
| /** |
| * The super interceptor class for [JSInt] and [JSDouble]. The compiler |
| * recognizes this class as an interceptor, and changes references to |
| * [:this:] to actually use the receiver of the method, which is |
| * generated as an extra argument added to each member. |
| * |
| * Note that none of the methods here delegate to a method defined on JSInt or |
| * JSDouble. This is exploited in [tryComputeConstantInterceptor]. |
| */ |
| class JSNumber extends Interceptor implements num { |
| const JSNumber(); |
| |
| int compareTo(num b) { |
| if (b is! num) throw new ArgumentError(b); |
| if (this < b) { |
| return -1; |
| } else if (this > b) { |
| return 1; |
| } else if (this == b) { |
| if (this == 0) { |
| bool bIsNegative = b.isNegative; |
| if (isNegative == bIsNegative) return 0; |
| if (isNegative) return -1; |
| return 1; |
| } |
| return 0; |
| } else if (isNaN) { |
| if (b.isNaN) { |
| return 0; |
| } |
| return 1; |
| } else { |
| return -1; |
| } |
| } |
| |
| bool get isNegative => (this == 0) ? (1 / this) < 0 : this < 0; |
| |
| bool get isNaN => JS('bool', r'isNaN(#)', this); |
| |
| bool get isInfinite { |
| return JS('bool', r'# == Infinity', this) |
| || JS('bool', r'# == -Infinity', this); |
| } |
| |
| num remainder(num b) { |
| checkNull(b); // TODO(ngeoffray): This is not specified but co19 tests it. |
| if (b is! num) throw new ArgumentError(b); |
| return JS('num', r'# % #', this, b); |
| } |
| |
| num abs() => JS('num', r'Math.abs(#)', this); |
| |
| int toInt() { |
| if (isNaN) throw new UnsupportedError('NaN'); |
| if (isInfinite) throw new UnsupportedError('Infinity'); |
| num truncated = truncateToDouble(); |
| return JS('bool', r'# == -0.0', truncated) ? 0 : truncated; |
| } |
| |
| int truncate() => toInt(); |
| int ceil() => ceilToDouble().toInt(); |
| int floor() => floorToDouble().toInt(); |
| int round() => roundToDouble().toInt(); |
| |
| double ceilToDouble() => JS('num', r'Math.ceil(#)', this); |
| |
| double floorToDouble() => JS('num', r'Math.floor(#)', this); |
| |
| double roundToDouble() { |
| if (this < 0) { |
| return JS('num', r'-Math.round(-#)', this); |
| } else { |
| return JS('num', r'Math.round(#)', this); |
| } |
| } |
| |
| double truncateToDouble() => this < 0 ? ceilToDouble() : floorToDouble(); |
| |
| num clamp(lowerLimit, upperLimit) { |
| if (lowerLimit is! num) throw new ArgumentError(lowerLimit); |
| if (upperLimit is! num) throw new ArgumentError(upperLimit); |
| if (lowerLimit.compareTo(upperLimit) > 0) { |
| throw new ArgumentError(lowerLimit); |
| } |
| if (this.compareTo(lowerLimit) < 0) return lowerLimit; |
| if (this.compareTo(upperLimit) > 0) return upperLimit; |
| return this; |
| } |
| |
| // The return type is intentionally omitted to avoid type checker warnings |
| // from assigning JSNumber to double. |
| toDouble() => this; |
| |
| String toStringAsFixed(int fractionDigits) { |
| checkNum(fractionDigits); |
| // TODO(floitsch): fractionDigits must be an integer. |
| if (fractionDigits < 0 || fractionDigits > 20) { |
| throw new RangeError(fractionDigits); |
| } |
| String result = JS('String', r'#.toFixed(#)', this, fractionDigits); |
| if (this == 0 && isNegative) return "-$result"; |
| return result; |
| } |
| |
| String toStringAsExponential([int fractionDigits]) { |
| String result; |
| if (fractionDigits != null) { |
| // TODO(floitsch): fractionDigits must be an integer. |
| checkNum(fractionDigits); |
| if (fractionDigits < 0 || fractionDigits > 20) { |
| throw new RangeError(fractionDigits); |
| } |
| result = JS('String', r'#.toExponential(#)', this, fractionDigits); |
| } else { |
| result = JS('String', r'#.toExponential()', this); |
| } |
| if (this == 0 && isNegative) return "-$result"; |
| return result; |
| } |
| |
| String toStringAsPrecision(int precision) { |
| // TODO(floitsch): precision must be an integer. |
| checkNum(precision); |
| if (precision < 1 || precision > 21) { |
| throw new RangeError(precision); |
| } |
| String result = JS('String', r'#.toPrecision(#)', |
| this, precision); |
| if (this == 0 && isNegative) return "-$result"; |
| return result; |
| } |
| |
| String toRadixString(int radix) { |
| checkNum(radix); |
| if (radix < 2 || radix > 36) throw new RangeError(radix); |
| return JS('String', r'#.toString(#)', this, radix); |
| } |
| |
| // Note: if you change this, also change the function [S]. |
| String toString() { |
| if (this == 0 && JS('bool', '(1 / #) < 0', this)) { |
| return '-0.0'; |
| } else { |
| return JS('String', r'"" + (#)', this); |
| } |
| } |
| |
| int get hashCode => JS('int', '# & 0x1FFFFFFF', this); |
| |
| num operator -() => JS('num', r'-#', this); |
| |
| num operator +(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| return JS('num', '# + #', this, other); |
| } |
| |
| num operator -(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| return JS('num', '# - #', this, other); |
| } |
| |
| num operator /(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| return JS('num', '# / #', this, other); |
| } |
| |
| num operator *(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| return JS('num', '# * #', this, other); |
| } |
| |
| num operator %(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| // Euclidean Modulo. |
| num result = JS('num', r'# % #', this, other); |
| if (result == 0) return 0; // Make sure we don't return -0.0. |
| if (result > 0) return result; |
| if (JS('num', '#', other) < 0) { |
| return result - JS('num', '#', other); |
| } else { |
| return result + JS('num', '#', other); |
| } |
| } |
| |
| num operator ~/(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| return (JS('num', r'# / #', this, other)).truncate(); |
| } |
| |
| // TODO(ngeoffray): Move the bit operations below to [JSInt] and |
| // make them take an int. Because this will make operations slower, |
| // we define these methods on number for now but we need to decide |
| // the grain at which we do the type checks. |
| |
| num operator <<(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| if (JS('num', '#', other) < 0) throw new ArgumentError(other); |
| // JavaScript only looks at the last 5 bits of the shift-amount. Shifting |
| // by 33 is hence equivalent to a shift by 1. |
| if (JS('bool', r'# > 31', other)) return 0; |
| return JS('int', r'(# << #) >>> 0', this, other); |
| } |
| |
| num operator >>(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| if (JS('num', '#', other) < 0) throw new ArgumentError(other); |
| if (JS('num', '#', this) > 0) { |
| // JavaScript only looks at the last 5 bits of the shift-amount. In JS |
| // shifting by 33 is hence equivalent to a shift by 1. Shortcut the |
| // computation when that happens. |
| if (JS('bool', r'# > 31', other)) return 0; |
| // Given that 'a' is positive we must not use '>>'. Otherwise a number |
| // that has the 31st bit set would be treated as negative and shift in |
| // ones. |
| return JS('int', r'# >>> #', this, other); |
| } |
| // For negative numbers we just clamp the shift-by amount. 'a' could be |
| // negative but not have its 31st bit set. The ">>" would then shift in |
| // 0s instead of 1s. Therefore we cannot simply return 0xFFFFFFFF. |
| if (JS('num', '#', other) > 31) other = 31; |
| return JS('int', r'(# >> #) >>> 0', this, other); |
| } |
| |
| num operator &(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| return JS('int', r'(# & #) >>> 0', this, other); |
| } |
| |
| num operator |(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| return JS('int', r'(# | #) >>> 0', this, other); |
| } |
| |
| num operator ^(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| return JS('int', r'(# ^ #) >>> 0', this, other); |
| } |
| |
| bool operator <(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| return JS('bool', '# < #', this, other); |
| } |
| |
| bool operator >(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| return JS('bool', '# > #', this, other); |
| } |
| |
| bool operator <=(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| return JS('bool', '# <= #', this, other); |
| } |
| |
| bool operator >=(num other) { |
| if (other is !num) throw new ArgumentError(other); |
| return JS('bool', '# >= #', this, other); |
| } |
| } |
| |
| /** |
| * The interceptor class for [int]s. |
| * |
| * This class implements double since in JavaScript all numbers are doubles, so |
| * while we want to treat `2.0` as an integer for some operations, its |
| * interceptor should answer `true` to `is double`. |
| */ |
| class JSInt extends JSNumber implements int, double { |
| const JSInt(); |
| |
| bool get isEven => (this & 1) == 0; |
| |
| bool get isOdd => (this & 1) == 1; |
| |
| int toUnsigned(int width) { |
| return this & ((1 << width) - 1); |
| } |
| |
| int toSigned(int width) { |
| int signMask = 1 << (width - 1); |
| return (this & (signMask - 1)) - (this & signMask); |
| } |
| |
| int get bitLength { |
| int nonneg = this < 0 ? -this-1 : this; |
| if (nonneg >= 0x100000000) { |
| nonneg = nonneg ~/ 0x100000000; |
| return _bitCount(_spread(nonneg)) + 32; |
| } |
| return _bitCount(_spread(nonneg)); |
| } |
| |
| // Assumes i is <= 32-bit and unsigned. |
| static int _bitCount(int i) { |
| // See "Hacker's Delight", section 5-1, "Counting 1-Bits". |
| |
| // The basic strategy is to use "divide and conquer" to |
| // add pairs (then quads, etc.) of bits together to obtain |
| // sub-counts. |
| // |
| // A straightforward approach would look like: |
| // |
| // i = (i & 0x55555555) + ((i >> 1) & 0x55555555); |
| // i = (i & 0x33333333) + ((i >> 2) & 0x33333333); |
| // i = (i & 0x0F0F0F0F) + ((i >> 4) & 0x0F0F0F0F); |
| // i = (i & 0x00FF00FF) + ((i >> 8) & 0x00FF00FF); |
| // i = (i & 0x0000FFFF) + ((i >> 16) & 0x0000FFFF); |
| // |
| // The code below removes unnecessary &'s and uses a |
| // trick to remove one instruction in the first line. |
| |
| i = _shru(i, 0) - (_shru(i, 1) & 0x55555555); |
| i = (i & 0x33333333) + (_shru(i, 2) & 0x33333333); |
| i = 0x0F0F0F0F & (i + _shru(i, 4)); |
| i += _shru(i, 8); |
| i += _shru(i, 16); |
| return (i & 0x0000003F); |
| } |
| |
| static _shru(int value, int shift) => JS('int', '# >>> #', value, shift); |
| static _shrs(int value, int shift) => JS('int', '# >> #', value, shift); |
| static _ors(int a, int b) => JS('int', '# | #', a, b); |
| |
| // Assumes i is <= 32-bit |
| static int _spread(int i) { |
| i = _ors(i, _shrs(i, 1)); |
| i = _ors(i, _shrs(i, 2)); |
| i = _ors(i, _shrs(i, 4)); |
| i = _ors(i, _shrs(i, 8)); |
| i = _shru(_ors(i, _shrs(i, 16)), 0); |
| return i; |
| } |
| |
| Type get runtimeType => int; |
| |
| int operator ~() => JS('int', r'(~#) >>> 0', this); |
| } |
| |
| class JSDouble extends JSNumber implements double { |
| const JSDouble(); |
| Type get runtimeType => double; |
| } |