blob: cc9a9fc46cf1897beebf2d99a61264cd58ee041a [file] [log] [blame]
// 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.
// TODO(srdjan): fix limitations.
// - shift amount must be a Smi.
class _IntegerImplementation {
factory _IntegerImplementation._uninstantiable() {
throw new UnsupportedError(
"_IntegerImplementation can only be allocated by the VM");
}
Type get runtimeType => int;
num operator +(num other) {
return other._addFromInteger(this);
}
num operator -(num other) {
return other._subFromInteger(this);
}
num operator *(num other) {
return other._mulFromInteger(this);
}
num operator ~/(num other) {
if ((other is int) && (other == 0)) {
throw const IntegerDivisionByZeroException();
}
return other._truncDivFromInteger(this);
}
num operator /(num other) {
return this.toDouble() / other.toDouble();
}
num operator %(num other) {
if ((other is int) && (other == 0)) {
throw const IntegerDivisionByZeroException();
}
return other._moduloFromInteger(this);
}
int operator -() {
return 0 - this;
}
int operator &(int other) {
return other._bitAndFromInteger(this);
}
int operator |(int other) {
return other._bitOrFromInteger(this);
}
int operator ^(int other) {
return other._bitXorFromInteger(this);
}
num remainder(num other) {
return other._remainderFromInteger(this);
}
int _bitAndFromInteger(int other) native "Integer_bitAndFromInteger";
int _bitOrFromInteger(int other) native "Integer_bitOrFromInteger";
int _bitXorFromInteger(int other) native "Integer_bitXorFromInteger";
int _addFromInteger(int other) native "Integer_addFromInteger";
int _subFromInteger(int other) native "Integer_subFromInteger";
int _mulFromInteger(int other) native "Integer_mulFromInteger";
int _truncDivFromInteger(int other) native "Integer_truncDivFromInteger";
int _moduloFromInteger(int other) native "Integer_moduloFromInteger";
int _remainderFromInteger(int other) {
return other - (other ~/ this) * this;
}
int operator >>(int other) {
return other._shrFromInt(this);
}
int operator <<(int other) {
return other._shlFromInt(this);
}
bool operator <(num other) {
return other > this;
}
bool operator >(num other) {
return other._greaterThanFromInteger(this);
}
bool operator >=(num other) {
return (this == other) || (this > other);
}
bool operator <=(num other) {
return (this == other) || (this < other);
}
bool _greaterThanFromInteger(int other)
native "Integer_greaterThanFromInteger";
bool operator ==(other) {
if (other is num) {
return other._equalToInteger(this);
}
return false;
}
bool _equalToInteger(int other) native "Integer_equalToInteger";
int abs() {
return this < 0 ? -this : this;
}
bool get isEven => ((this & 1) == 0);
bool get isOdd => !isEven;
bool get isNaN => false;
bool get isNegative => this < 0;
bool get isInfinite => false;
bool get isFinite => true;
int toUnsigned(int width) {
return this & ((1 << width) - 1);
}
int toSigned(int width) {
// The value of binary number weights each bit by a power of two. The
// twos-complement value weights the sign bit negatively. We compute the
// value of the negative weighting by isolating the sign bit with the
// correct power of two weighting and subtracting it from the value of the
// lower bits.
int signMask = 1 << (width - 1);
return (this & (signMask - 1)) - (this & signMask);
}
int compareTo(num other) {
final int EQUAL = 0, LESS = -1, GREATER = 1;
if (other is double) {
// TODO(floitsch): the following locals should be 'const'.
int MAX_EXACT_INT_TO_DOUBLE = 9007199254740992; // 2^53.
int MIN_EXACT_INT_TO_DOUBLE = -MAX_EXACT_INT_TO_DOUBLE;
double d = other;
if (d.isInfinite) {
return d == double.NEGATIVE_INFINITY ? GREATER : LESS;
}
if (d.isNaN) {
return LESS;
}
if (MIN_EXACT_INT_TO_DOUBLE <= this && this <= MAX_EXACT_INT_TO_DOUBLE) {
// Let the double implementation deal with -0.0.
return -(d.compareTo(this.toDouble()));
} else {
// If abs(other) > MAX_EXACT_INT_TO_DOUBLE, then other has an integer
// value (no bits below the decimal point).
other = d.toInt();
}
}
if (this < other) {
return LESS;
} else if (this > other) {
return GREATER;
} else {
return EQUAL;
}
}
int round() { return this; }
int floor() { return this; }
int ceil() { return this; }
int truncate() { return this; }
double roundToDouble() { return this.toDouble(); }
double floorToDouble() { return this.toDouble(); }
double ceilToDouble() { return this.toDouble(); }
double truncateToDouble() { return this.toDouble(); }
num clamp(num lowerLimit, num upperLimit) {
if (lowerLimit is! num) throw new ArgumentError(lowerLimit);
if (upperLimit is! num) throw new ArgumentError(upperLimit);
// Special case for integers.
if (lowerLimit is int && upperLimit is int) {
if (lowerLimit > upperLimit) {
throw new ArgumentError(lowerLimit);
}
if (this < lowerLimit) return lowerLimit;
if (this > upperLimit) return upperLimit;
return this;
}
// Generic case involving doubles.
if (lowerLimit.compareTo(upperLimit) > 0) {
throw new ArgumentError(lowerLimit);
}
if (lowerLimit.isNaN) return lowerLimit;
// Note that we don't need to care for -0.0 for the lower limit.
if (this < lowerLimit) return lowerLimit;
if (this.compareTo(upperLimit) > 0) return upperLimit;
return this;
}
int toInt() { return this; }
double toDouble() { return new _Double.fromInteger(this); }
String toStringAsFixed(int fractionDigits) {
return this.toDouble().toStringAsFixed(fractionDigits);
}
String toStringAsExponential([int fractionDigits]) {
return this.toDouble().toStringAsExponential(fractionDigits);
}
String toStringAsPrecision(int precision) {
return this.toDouble().toStringAsPrecision(precision);
}
static const _digits = "0123456789abcdefghijklmnopqrstuvwxyz";
String toRadixString(int radix) {
if (radix is! int || radix < 2 || radix > 36) {
throw new ArgumentError(radix);
}
if (radix & (radix - 1) == 0) {
return _toPow2String(this, radix);
}
if (radix == 10) return this.toString();
final bool isNegative = this < 0;
int value = isNegative ? -this : this;
List temp = new List();
do {
int digit = value % radix;
value ~/= radix;
temp.add(_digits.codeUnitAt(digit));
} while (value > 0);
if (isNegative) temp.add(0x2d); // '-'.
_OneByteString string = _OneByteString._allocate(temp.length);
for (int i = 0, j = temp.length; j > 0; i++) {
string._setAt(i, temp[--j]);
}
return string;
}
static String _toPow2String(value, radix) {
if (value == 0) return "0";
assert(radix & (radix - 1) == 0);
var negative = value < 0;
var bitsPerDigit = radix.bitLength - 1;
var length = 0;
if (negative) {
value = -value;
length = 1;
}
// Integer division, rounding up, to find number of _digits.
length += (value.bitLength + bitsPerDigit - 1) ~/ bitsPerDigit;
_OneByteString string = _OneByteString._allocate(length);
string._setAt(0, 0x2d); // '-'. Is overwritten if not negative.
var mask = radix - 1;
do {
string._setAt(--length, _digits.codeUnitAt(value & mask));
value >>= bitsPerDigit;
} while (value > 0);
return string;
}
_leftShiftWithMask32(count, mask) native "Integer_leftShiftWithMask32";
}
class _Smi extends _IntegerImplementation implements int {
factory _Smi._uninstantiable() {
throw new UnsupportedError(
"_Smi can only be allocated by the VM");
}
int get _identityHashCode {
return this;
}
int operator ~() native "Smi_bitNegate";
int get bitLength native "Smi_bitLength";
int _shrFromInt(int other) native "Smi_shrFromInt";
int _shlFromInt(int other) native "Smi_shlFromInt";
String toString() {
if (this == 0) return "0";
var reversed = _toStringBuffer;
var negative = false;
var val = this;
int index = 0;
if (this < 0) {
negative = true;
// Handle the first digit as negative to avoid negating the minimum
// smi, for which the negation is not a smi.
int digit = -(val.remainder(10));
reversed[index++] = digit + 0x30;
val = -(val ~/ 10);
}
while (val > 0) {
int digit = val % 10;
reversed[index++] = (digit + 0x30);
val = val ~/ 10;
}
if (negative) reversed[index++] = 0x2D; // '-'.
_OneByteString string = _OneByteString._allocate(index);
for (int i = 0, j = index; i < index; i++) {
string._setAt(i, reversed[--j]);
}
return string;
}
}
// Reusable buffer used by smi.toString.
List _toStringBuffer = new Uint8List(20);
// Represents integers that cannot be represented by Smi but fit into 64bits.
class _Mint extends _IntegerImplementation implements int {
factory _Mint._uninstantiable() {
throw new UnsupportedError(
"_Mint can only be allocated by the VM");
}
int get _identityHashCode {
return this;
}
int operator ~() native "Mint_bitNegate";
int get bitLength native "Mint_bitLength";
// Shift by mint exceeds range that can be handled by the VM.
int _shrFromInt(int other) {
if (other < 0) {
return -1;
} else {
return 0;
}
}
int _shlFromInt(int other) native "Mint_shlFromInt";
}
// A number that can be represented as Smi or Mint will never be represented as
// Bigint.
class _Bigint extends _IntegerImplementation implements int {
factory _Bigint._uninstantiable() {
throw new UnsupportedError(
"_Bigint can only be allocated by the VM");
}
int get _identityHashCode {
return this;
}
int operator ~() native "Bigint_bitNegate";
int get bitLength native "Bigint_bitLength";
// Shift by bigint exceeds range that can be handled by the VM.
int _shrFromInt(int other) {
if (other < 0) {
return -1;
} else {
return 0;
}
}
int _shlFromInt(int other) native "Bigint_shlFromInt";
int pow(int exponent) {
throw "Bigint.pow not implemented";
}
}