Introduces a faster long emulation.
Doubles are capable of holding integral numbers up to 53
bits without losing any precision. The new implementation
exploits this fact and uses double to represent long numbers
up to 45 bits and then switches to existing number triplet
representation for any number beyond that.
45 bits is chosen to simplify the conversion between two
representations as the number triplet representation uses two 22bit
slots for lower bits. Later the approach can be extended to
represent all numbers that fits into 53 bits.
The added overhead for big longs is very small. However it
uses much less memory for small longs and provides extremely
fast arithmetic operations.
Benchmarks results shows division is now 5170 times faster
in Firefox compared to earlier implementation[1].
The new implementation is also JsInterop friendly as small
numbers are real non-opaque numbers usable in javascript side
without any conversion. For example, even though time is
represented by long in java and double in javascript, both
uses less than 45 bits hence now interoperable.
I also experimented with doing optimistic arithmetic, meaning that,
without any type check I can assume SmallLong and do the arithmethic
operation. If it is not a SmallLong the result will never be in the
safe-range so we will fallback to BigLong arithmethic.
That appraoch provided around another 20% performance improvement
for small longs. However surprisingly the overhead for large numbers
significantly increased (slows down by half). It doesn't seem like
a good trade off so this patches uses explicit type check.
[1] Benchmark results:
Chrome Firefox IE11
Add(Small): +84.96% +227.51% +7.59%
Add(Big): -23.56% -35.74% -59.48%
Div(Small): +694.25% +5170.80% +717.81%
Div(Big): -8.60% -8.31% -14.12%
Mul(Small): +242.78% +785.87% +33.80%
Mul(Big): -9.12% -8.55% -38.86%
Shr(Small): -34.72% -12.21% -77.41%
Shr(Big): -12.23% -16.02% -35.75%
Change-Id: I6fcf5650c9357a7255fc126d65f463138fa47e68
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
index 802a82b..6860e88 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
@@ -406,7 +406,10 @@
@Override
JsLiteral translate(JExpression literal) {
SourceInfo sourceInfo = literal.getSourceInfo();
- int[] values = LongLib.getAsIntArray(((JLongLiteral) literal).getValue());
+ long[] values = LongLib.getAsLongArray(((JLongLiteral) literal).getValue());
+ if (values.length == 1) {
+ return new JsNumberLiteral(literal.getSourceInfo(), ((JLongLiteral) literal).getValue());
+ }
JsObjectLiteral objectLiteral = new JsObjectLiteral(sourceInfo);
objectLiteral.setInternable();
@@ -449,7 +452,7 @@
}
private static void addPropertyToObject(SourceInfo sourceInfo, JsName propertyName,
- int propertyValue, JsObjectLiteral objectLiteral) {
+ long propertyValue, JsObjectLiteral objectLiteral) {
JsExpression label = propertyName.makeRef(sourceInfo);
JsExpression value = new JsNumberLiteral(sourceInfo, propertyValue);
objectLiteral.addProperty(sourceInfo, label, value);
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/JavaClassHierarchySetupUtil.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/JavaClassHierarchySetupUtil.java
index 6ab202b..0937e44 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/JavaClassHierarchySetupUtil.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/JavaClassHierarchySetupUtil.java
@@ -230,6 +230,6 @@
* Convert a double to a long.
*/
public static native Object coerceFromLong(Object o) /*-{
- return @com.google.gwt.lang.LongLib::toDouble(Lcom/google/gwt/lang/LongLibBase$LongEmul;)(o);
+ return @com.google.gwt.lang.LongLib::toDouble(*)(o);
}-*/;
}
diff --git a/dev/core/super/com/google/gwt/lang/BigLongLib.java b/dev/core/super/com/google/gwt/lang/BigLongLib.java
new file mode 100644
index 0000000..a580145
--- /dev/null
+++ b/dev/core/super/com/google/gwt/lang/BigLongLib.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.lang;
+
+/**
+ * Implements a Java <code>long</code> in a way that can be translated to
+ * JavaScript and could handle numbers needs more than 44 bits.
+ */
+class BigLongLib extends BigLongLibBase {
+
+ static class Const {
+ static final BigLong MAX_VALUE = create(MASK, MASK, MASK_2 >> 1);
+ static final BigLong MIN_VALUE = create(0, 0, SIGN_BIT_VALUE);
+ static final BigLong ONE = fromInt(1);
+ static final BigLong TWO = fromInt(2);
+ static final BigLong ZERO = fromInt(0);
+ }
+
+ public static BigLong add(BigLong a, BigLong b) {
+ int sum0 = getL(a) + getL(b);
+ int sum1 = getM(a) + getM(b) + (sum0 >> BITS);
+ int sum2 = getH(a) + getH(b) + (sum1 >> BITS);
+
+ return create(sum0 & MASK, sum1 & MASK, sum2 & MASK_2);
+ }
+
+ public static BigLong and(BigLong a, BigLong b) {
+ return create(getL(a) & getL(b), getM(a) & getM(b), getH(a) & getH(b));
+ }
+
+ public static double compare(BigLong a, BigLong b) {
+ int signA = sign(a);
+ int signB = sign(b);
+ if (signA != signB) {
+ return signB - signA;
+ }
+
+ int a2 = getH(a);
+ int b2 = getH(b);
+ if (a2 != b2) {
+ return a2 - b2;
+ }
+
+ int a1 = getM(a);
+ int b1 = getM(b);
+ if (a1 != b1) {
+ return a1 - b1;
+ }
+
+ int a0 = getL(a);
+ int b0 = getL(b);
+ return a0 - b0;
+ }
+
+ public static BigLong div(BigLong a, BigLong b) {
+ return divMod(a, b, false);
+ }
+
+ public static BigLong fromDouble(double value) {
+ if (Double.isNaN(value)) {
+ return Const.ZERO;
+ }
+ if (value < -TWO_PWR_63_DBL) {
+ return Const.MIN_VALUE;
+ }
+ if (value >= TWO_PWR_63_DBL) {
+ return Const.MAX_VALUE;
+ }
+
+ boolean negative = false;
+ if (value < 0) {
+ negative = true;
+ value = -value;
+ }
+ int a2 = 0;
+ if (value >= TWO_PWR_44_DBL) {
+ a2 = (int) (value / TWO_PWR_44_DBL);
+ value -= a2 * TWO_PWR_44_DBL;
+ }
+ int a1 = 0;
+ if (value >= TWO_PWR_22_DBL) {
+ a1 = (int) (value / TWO_PWR_22_DBL);
+ value -= a1 * TWO_PWR_22_DBL;
+ }
+ int a0 = (int) value;
+ BigLong result = create(a0, a1, a2);
+ if (negative) {
+ negate(result);
+ }
+ return result;
+ }
+
+ public static BigLong fromInt(int value) {
+ return create(value);
+ }
+
+ public static long[] getAsLongArray(long l) {
+ long[] a = new long[3];
+ a[0] = (int) (l & MASK);
+ a[1] = (int) ((l >> BITS) & MASK);
+ a[2] = (int) ((l >> BITS01) & MASK_2);
+ return a;
+ }
+
+ public static BigLong mod(BigLong a, BigLong b) {
+ divMod(a, b, true);
+ return remainder;
+ }
+
+ // Assumes BITS == 22
+ public static BigLong mul(BigLong a, BigLong b) {
+ // Grab 13-bit chunks
+ int a0 = getL(a) & 0x1fff;
+ int a1 = (getL(a) >> 13) | ((getM(a) & 0xf) << 9);
+ int a2 = (getM(a) >> 4) & 0x1fff;
+ int a3 = (getM(a) >> 17) | ((getH(a) & 0xff) << 5);
+ int a4 = (getH(a) & 0xfff00) >> 8;
+
+ int b0 = getL(b) & 0x1fff;
+ int b1 = (getL(b) >> 13) | ((getM(b) & 0xf) << 9);
+ int b2 = (getM(b) >> 4) & 0x1fff;
+ int b3 = (getM(b) >> 17) | ((getH(b) & 0xff) << 5);
+ int b4 = (getH(b) & 0xfff00) >> 8;
+
+ // Compute partial products
+ // Optimization: if b is small, avoid multiplying by parts that are 0
+ int p0 = a0 * b0; // << 0
+ int p1 = a1 * b0; // << 13
+ int p2 = a2 * b0; // << 26
+ int p3 = a3 * b0; // << 39
+ int p4 = a4 * b0; // << 52
+
+ if (b1 != 0) {
+ p1 += a0 * b1;
+ p2 += a1 * b1;
+ p3 += a2 * b1;
+ p4 += a3 * b1;
+ }
+ if (b2 != 0) {
+ p2 += a0 * b2;
+ p3 += a1 * b2;
+ p4 += a2 * b2;
+ }
+ if (b3 != 0) {
+ p3 += a0 * b3;
+ p4 += a1 * b3;
+ }
+ if (b4 != 0) {
+ p4 += a0 * b4;
+ }
+
+ // Accumulate into 22-bit chunks:
+ // .........................................c10|...................c00|
+ // |....................|..................xxxx|xxxxxxxxxxxxxxxxxxxxxx| p0
+ // |....................|......................|......................|
+ // |....................|...................c11|......c01.............|
+ // |....................|....xxxxxxxxxxxxxxxxxx|xxxxxxxxx.............| p1
+ // |....................|......................|......................|
+ // |.................c22|...............c12....|......................|
+ // |..........xxxxxxxxxx|xxxxxxxxxxxxxxxxxx....|......................| p2
+ // |....................|......................|......................|
+ // |.................c23|..c13.................|......................|
+ // |xxxxxxxxxxxxxxxxxxxx|xxxxx.................|......................| p3
+ // |....................|......................|......................|
+ // |.........c24........|......................|......................|
+ // |xxxxxxxxxxxx........|......................|......................| p4
+
+ int c00 = p0 & 0x3fffff;
+ int c01 = (p1 & 0x1ff) << 13;
+ int c0 = c00 + c01;
+
+ int c10 = p0 >> 22;
+ int c11 = p1 >> 9;
+ int c12 = (p2 & 0x3ffff) << 4;
+ int c13 = (p3 & 0x1f) << 17;
+ int c1 = c10 + c11 + c12 + c13;
+
+ int c22 = p2 >> 18;
+ int c23 = p3 >> 5;
+ int c24 = (p4 & 0xfff) << 8;
+ int c2 = c22 + c23 + c24;
+
+ // Propagate high bits from c0 -> c1, c1 -> c2
+ c1 += c0 >> BITS;
+ c0 &= MASK;
+ c2 += c1 >> BITS;
+ c1 &= MASK;
+ c2 &= MASK_2;
+
+ return create(c0, c1, c2);
+ }
+
+ public static BigLong neg(BigLong a) {
+ int neg0 = (~getL(a) + 1) & MASK;
+ int neg1 = (~getM(a) + (neg0 == 0 ? 1 : 0)) & MASK;
+ int neg2 = (~getH(a) + ((neg0 == 0 && neg1 == 0) ? 1 : 0)) & MASK_2;
+
+ return create(neg0, neg1, neg2);
+ }
+
+ public static BigLong not(BigLong a) {
+ return create((~getL(a)) & MASK, (~getM(a)) & MASK, (~getH(a)) & MASK_2);
+ }
+
+ public static BigLong or(BigLong a, BigLong b) {
+ return create(getL(a) | getL(b), getM(a) | getM(b), getH(a) | getH(b));
+ }
+
+ public static BigLong shl(BigLong a, int n) {
+ n &= 63;
+
+ int res0, res1, res2;
+ if (n < BITS) {
+ res0 = getL(a) << n;
+ res1 = (getM(a) << n) | (getL(a) >> (BITS - n));
+ res2 = (getH(a) << n) | (getM(a) >> (BITS - n));
+ } else if (n < BITS01) {
+ res0 = 0;
+ res1 = getL(a) << (n - BITS);
+ res2 = (getM(a) << (n - BITS)) | (getL(a) >> (BITS01 - n));
+ } else {
+ res0 = 0;
+ res1 = 0;
+ res2 = getL(a) << (n - BITS01);
+ }
+
+ return create(res0 & MASK, res1 & MASK, res2 & MASK_2);
+ }
+
+ public static BigLong shr(BigLong a, int n) {
+ n &= 63;
+
+ int res0, res1, res2;
+
+ // Sign extend h(a)
+ int a2 = getH(a);
+ boolean negative = (a2 & SIGN_BIT_VALUE) != 0;
+ if (negative) {
+ a2 |= ~MASK_2;
+ }
+
+ if (n < BITS) {
+ res2 = a2 >> n;
+ res1 = (getM(a) >> n) | (a2 << (BITS - n));
+ res0 = (getL(a) >> n) | (getM(a) << (BITS - n));
+ } else if (n < BITS01) {
+ res2 = negative ? MASK_2 : 0;
+ res1 = a2 >> (n - BITS);
+ res0 = (getM(a) >> (n - BITS)) | (a2 << (BITS01 - n));
+ } else {
+ res2 = negative ? MASK_2 : 0;
+ res1 = negative ? MASK : 0;
+ res0 = a2 >> (n - BITS01);
+ }
+
+ return create(res0 & MASK, res1 & MASK, res2 & MASK_2);
+ }
+
+ /**
+ * Logical right shift. It does not preserve the sign of the input.
+ */
+ public static BigLong shru(BigLong a, int n) {
+ n &= 63;
+
+ int res0, res1, res2;
+ int a2 = getH(a) & MASK_2;
+ if (n < BITS) {
+ res2 = a2 >>> n;
+ res1 = (getM(a) >> n) | (a2 << (BITS - n));
+ res0 = (getL(a) >> n) | (getM(a) << (BITS - n));
+ } else if (n < BITS01) {
+ res2 = 0;
+ res1 = a2 >>> (n - BITS);
+ res0 = (getM(a) >> (n - BITS)) | (getH(a) << (BITS01 - n));
+ } else {
+ res2 = 0;
+ res1 = 0;
+ res0 = a2 >>> (n - BITS01);
+ }
+
+ return create(res0 & MASK, res1 & MASK, res2 & MASK_2);
+ }
+
+ public static BigLong sub(BigLong a, BigLong b) {
+ int sum0 = getL(a) - getL(b);
+ int sum1 = getM(a) - getM(b) + (sum0 >> BITS);
+ int sum2 = getH(a) - getH(b) + (sum1 >> BITS);
+
+ return create(sum0 & MASK, sum1 & MASK, sum2 & MASK_2);
+ }
+
+ public static double toDouble(BigLong a) {
+ if (BigLongLib.compare(a, Const.ZERO) < 0) {
+ return -toDoubleHelper(BigLongLib.neg(a));
+ }
+ return toDoubleHelper(a);
+ }
+
+ // Assumes Integer.MIN_VALUE <= a <= Integer.MAX_VALUE
+ public static int toInt(BigLong a) {
+ return getL(a) | (getM(a) << BITS);
+ }
+
+ public static String toString(BigLong a) {
+ if (BigLongLibBase.isZero(a)) {
+ return "0";
+ }
+
+ if (BigLongLibBase.isMinValue(a)) {
+ // Special-case MIN_VALUE because neg(MIN_VALUE) == MIN_VALUE
+ return "-9223372036854775808";
+ }
+
+ if (BigLongLibBase.isNegative(a)) {
+ return "-" + toString(neg(a));
+ }
+
+ BigLong rem = a;
+ String res = "";
+
+ while (!BigLongLibBase.isZero(rem)) {
+ // Do several digits each time through the loop, so as to
+ // minimize the calls to the very expensive emulated div.
+ final int tenPowerZeroes = 9;
+ final int tenPower = 1000000000;
+ BigLong tenPowerLong = fromInt(tenPower);
+
+ rem = divMod(rem, tenPowerLong, true);
+ String digits = "" + toInt(BigLongLibBase.remainder);
+
+ if (!BigLongLibBase.isZero(rem)) {
+ int zeroesNeeded = tenPowerZeroes - digits.length();
+ for (; zeroesNeeded > 0; zeroesNeeded--) {
+ digits = "0" + digits;
+ }
+ }
+
+ res = digits + res;
+ }
+
+ return res;
+ }
+
+ public static BigLong xor(BigLong a, BigLong b) {
+ return create(getL(a) ^ getL(b), getM(a) ^ getM(b), getH(a) ^ getH(b));
+ }
+
+ /**
+ * Not instantiable.
+ */
+ private BigLongLib() {
+ }
+}
diff --git a/dev/core/super/com/google/gwt/lang/LongLibBase.java b/dev/core/super/com/google/gwt/lang/BigLongLibBase.java
similarity index 77%
rename from dev/core/super/com/google/gwt/lang/LongLibBase.java
rename to dev/core/super/com/google/gwt/lang/BigLongLibBase.java
index ac25f0b..ebc31f8 100644
--- a/dev/core/super/com/google/gwt/lang/LongLibBase.java
+++ b/dev/core/super/com/google/gwt/lang/BigLongLibBase.java
@@ -19,20 +19,9 @@
/**
* Implements a Java <code>long</code> in a way that can be translated to
- * JavaScript. Methods that are meant to be called from outside this package are
- * located in {@link LongLib}.
+ * JavaScript.
*/
-class LongLibBase {
- static final class LongEmul {
- public static LongEmul getInstance() {
- return new LongEmul();
- }
-
- int l, m, h; // Used only when RUN_IN_JVM is true
- }
-
- // Force the class to exist
- public static LongEmul instance = new LongEmul();
+class BigLongLibBase {
/*
* Implementation: A LongEmul containing three values {l, m, h} (low, middle,
@@ -46,8 +35,12 @@
* Note that this class must be careful using type "long". Being the
* implementation of the long type for Production Mode, any place it uses a
* long is not usable in Production Mode. There is currently one such method:
- * {@link LongLib#getAsIntArray}.
+ * {@link LongLib#getAsLongArray}.
*/
+ static class BigLong {
+ int l, m, h;
+ }
+
// Note that the {@link LonghLib#mul} method implicitly depends on the
// specific value BITS == 22
@@ -56,13 +49,7 @@
protected static final int BITS2 = 64 - BITS01;
protected static final int MASK = (1 << BITS) - 1;
protected static final int MASK_2 = (1 << BITS2) - 1;
- protected static LongEmul remainder;
-
- /**
- * Allow standalone Java tests such as LongLibTest/LongLibJreTest to run this
- * code.
- */
- protected static boolean RUN_IN_JVM = false;
+ protected static BigLong remainder;
protected static final int SIGN_BIT = BITS2 - 1;
protected static final int SIGN_BIT_VALUE = 1 << SIGN_BIT;
@@ -82,17 +69,17 @@
* Production Mode implementation; the int array is already the right object.
*/
@UnsafeNativeLong
- protected static native long asLong(LongEmul value) /*-{
+ protected static native long asLong(BigLong value) /*-{
return value;
}-*/;
- protected static LongEmul create(int value) {
+ protected static BigLong create(int value) {
int a0 = value & MASK;
int a1 = (value >> BITS) & MASK;
int a2 = (value < 0) ? MASK_2 : 0;
- if (RUN_IN_JVM) {
- LongEmul a = new LongEmul();
+ if (LongLib.RUN_IN_JVM) {
+ BigLong a = new BigLong();
a.l = a0;
a.m = a1;
a.h = a2;
@@ -101,9 +88,9 @@
return create0(a0, a1, a2);
}
- protected static LongEmul create(int a0, int a1, int a2) {
- if (RUN_IN_JVM) {
- LongEmul a = new LongEmul();
+ protected static BigLong create(int a0, int a1, int a2) {
+ if (LongLib.RUN_IN_JVM) {
+ BigLong a = new BigLong();
a.l = a0;
a.m = a1;
a.h = a2;
@@ -112,7 +99,7 @@
return create0(a0, a1, a2);
}
- protected static LongEmul divMod(LongEmul a, LongEmul b,
+ protected static BigLong divMod(BigLong a, BigLong b,
boolean computeRemainder) {
if (isZero(b)) {
throw new ArithmeticException("divide by zero");
@@ -133,7 +120,7 @@
// We can do this because we have already ensured that b != MIN_VALUE.
boolean negative = false;
if (isNegative(b)) {
- b = LongLib.neg(b);
+ b = BigLongLib.neg(b);
negative = !negative;
}
@@ -169,12 +156,12 @@
// If b is not a power of two, treat -a as MAX_VALUE (instead of the
// actual value (MAX_VALUE + 1)).
if (bpower == -1) {
- a = create(LongLib.Const.MAX_VALUE);
+ a = create(BigLongLib.Const.MAX_VALUE);
aIsCopy = true;
negative = !negative;
} else {
// Signed shift of MIN_VALUE produces the right answer
- LongEmul c = LongLib.shr(a, bpower);
+ BigLong c = BigLongLib.shr(a, bpower);
if (negative) {
negate(c);
}
@@ -185,7 +172,7 @@
}
} else if (isNegative(a)) {
aIsNegative = true;
- a = LongLib.neg(a);
+ a = BigLongLib.neg(a);
aIsCopy = true;
negative = !negative;
}
@@ -198,10 +185,10 @@
}
// if a < b, the quotient is 0 and the remainder is a
- if (LongLib.lt(a, b)) {
+ if (BigLongLib.compare(a, b) < 0) {
if (computeRemainder) {
if (aIsNegative) {
- remainder = LongLib.neg(a);
+ remainder = BigLongLib.neg(a);
} else {
remainder = create(a);
}
@@ -214,48 +201,48 @@
aIsMinValue, computeRemainder);
}
- protected static int getH(LongEmul a) {
- if (RUN_IN_JVM) {
+ protected static int getH(BigLong a) {
+ if (LongLib.RUN_IN_JVM) {
return a.h;
}
return getHNative(a);
}
- protected static int getL(LongEmul a) {
- if (RUN_IN_JVM) {
+ protected static int getL(BigLong a) {
+ if (LongLib.RUN_IN_JVM) {
return a.l;
}
return getLNative(a);
}
- protected static int getM(LongEmul a) {
- if (RUN_IN_JVM) {
+ protected static int getM(BigLong a) {
+ if (LongLib.RUN_IN_JVM) {
return a.m;
}
return getMNative(a);
}
- protected static boolean isMinValue(LongEmul a) {
+ protected static boolean isMinValue(BigLong a) {
return getH(a) == SIGN_BIT_VALUE && getM(a) == 0 && getL(a) == 0;
}
- protected static boolean isNegative(LongEmul a) {
+ protected static boolean isNegative(BigLong a) {
return sign(a) != 0;
}
- protected static boolean isZero(LongEmul a) {
+ protected static boolean isZero(BigLong a) {
return getL(a) == 0 && getM(a) == 0 && getH(a) == 0;
}
/**
* a = -a
*/
- protected static void negate(LongEmul a) {
+ protected static void negate(BigLong a) {
int neg0 = (~getL(a) + 1) & MASK;
int neg1 = (~getM(a) + (neg0 == 0 ? 1 : 0)) & MASK;
int neg2 = (~getH(a) + ((neg0 == 0 && neg1 == 0) ? 1 : 0)) & MASK_2;
- if (RUN_IN_JVM) {
+ if (LongLib.RUN_IN_JVM) {
a.l = neg0;
a.m = neg1;
a.h = neg2;
@@ -269,12 +256,12 @@
/**
* @return 0 if a is >= 0, 1 if a < 0.
*/
- protected static int sign(LongEmul a) {
+ protected static int sign(BigLong a) {
return getH(a) >> (BITS2 - 1);
}
// Assumes a is non-negative
- protected static double toDoubleHelper(LongEmul a) {
+ protected static double toDoubleHelper(BigLong a) {
return getL(a) + (getM(a) * TWO_PWR_22_DBL) + (getH(a) * TWO_PWR_44_DBL);
}
@@ -282,7 +269,7 @@
* Return the number of leading zeros of a long value.
*/
// package-private for testing
- static int numberOfLeadingZeros(LongEmul a) {
+ static int numberOfLeadingZeros(BigLong a) {
int b2 = Integer.numberOfLeadingZeros(getH(a));
if (b2 == 32) {
int b1 = Integer.numberOfLeadingZeros(getM(a));
@@ -299,9 +286,9 @@
/**
* Creates a long instance equal to 0.
*/
- private static LongEmul create() {
- if (RUN_IN_JVM) {
- return new LongEmul();
+ private static BigLong create() {
+ if (LongLib.RUN_IN_JVM) {
+ return new BigLong();
}
return create0(0, 0, 0);
}
@@ -309,9 +296,9 @@
/**
* Creates a long instance equal to a given long.
*/
- private static LongEmul create(LongEmul a) {
- if (RUN_IN_JVM) {
- LongEmul b = new LongEmul();
+ static BigLong create(BigLong a) {
+ if (LongLib.RUN_IN_JVM) {
+ BigLong b = new BigLong();
b.l = getL(a);
b.m = getM(a);
b.h = getH(a);
@@ -320,18 +307,18 @@
return create0(getL(a), getM(a), getH(a));
}
- private static native LongEmul create0(int l, int m, int h) /*-{
+ private static native BigLong create0(int l, int m, int h) /*-{
return {l:l,m:m,h:h};
}-*/;
- private static LongEmul divModByMinValue(LongEmul a, boolean computeRemainder) {
+ private static BigLong divModByMinValue(BigLong a, boolean computeRemainder) {
// MIN_VALUE / MIN_VALUE == 1, remainder = 0
// (a != MIN_VALUE) / MIN_VALUE == 0, remainder == a
if (isMinValue(a)) {
if (computeRemainder) {
remainder = create(); // zero
}
- return create(LongLib.Const.ONE);
+ return create(BigLongLib.Const.ONE);
}
if (computeRemainder) {
remainder = create(a);
@@ -339,9 +326,9 @@
return create(); // zero
}
- private static LongEmul divModByShift(LongEmul a, int bpower,
+ private static BigLong divModByShift(BigLong a, int bpower,
boolean negative, boolean aIsNegative, boolean computeRemainder) {
- LongEmul c = LongLib.shr(a, bpower);
+ BigLong c = BigLongLib.shr(a, bpower);
if (negative) {
negate(c);
}
@@ -349,7 +336,7 @@
if (computeRemainder) {
a = maskRight(a, bpower);
if (aIsNegative) {
- remainder = LongLib.neg(a);
+ remainder = BigLongLib.neg(a);
} else {
remainder = create(a);
}
@@ -357,14 +344,14 @@
return c;
}
- private static LongEmul divModHelper(LongEmul a, LongEmul b,
+ private static BigLong divModHelper(BigLong a, BigLong b,
boolean negative, boolean aIsNegative, boolean aIsMinValue,
boolean computeRemainder) {
// Align the leading one bits of a and b by shifting b left
int shift = numberOfLeadingZeros(b) - numberOfLeadingZeros(a);
- LongEmul bshift = LongLib.shl(b, shift);
+ BigLong bshift = BigLongLib.shl(b, shift);
- LongEmul quotient = create();
+ BigLong quotient = create();
while (shift >= 0) {
boolean gte = trialSubtract(a, bshift);
if (gte) {
@@ -384,9 +371,9 @@
if (computeRemainder) {
if (aIsNegative) {
- remainder = LongLib.neg(a);
+ remainder = BigLongLib.neg(a);
if (aIsMinValue) {
- remainder = LongLib.sub(remainder, LongLib.Const.ONE);
+ remainder = BigLongLib.sub(remainder, BigLongLib.Const.ONE);
}
} else {
remainder = create(a);
@@ -396,22 +383,22 @@
return quotient;
}
- private static native int getHNative(LongEmul a) /*-{
+ private static native int getHNative(BigLong a) /*-{
return a.h;
}-*/;
- private static native int getLNative(LongEmul a) /*-{
+ private static native int getLNative(BigLong a) /*-{
return a.l;
}-*/;
- private static native int getMNative(LongEmul a) /*-{
+ private static native int getMNative(BigLong a) /*-{
return a.m;
}-*/;
/**
* a &= ((1L << bits) - 1)
*/
- private static LongEmul maskRight(LongEmul a, int bits) {
+ private static BigLong maskRight(BigLong a, int bits) {
int b0, b1, b2;
if (bits <= BITS) {
b0 = getL(a) & ((1 << bits) - 1);
@@ -440,7 +427,7 @@
* }
* </pre>
*/
- private static int powerOfTwo(LongEmul a) {
+ private static int powerOfTwo(BigLong a) {
// Power of two or 0
int l = getL(a);
if ((l & (l - 1)) != 0) {
@@ -470,8 +457,8 @@
return -1;
}
- private static void setBit(LongEmul a, int bit) {
- if (RUN_IN_JVM) {
+ private static void setBit(BigLong a, int bit) {
+ if (LongLib.RUN_IN_JVM) {
if (bit < BITS) {
a.l |= 0x1 << bit;
} else if (bit < BITS01) {
@@ -490,39 +477,39 @@
}
}
- private static native void setBitH(LongEmul a, int bit) /*-{
+ private static native void setBitH(BigLong a, int bit) /*-{
a.h |= 1 << bit;
}-*/;
- private static native void setBitL(LongEmul a, int bit) /*-{
+ private static native void setBitL(BigLong a, int bit) /*-{
a.l |= 1 << bit;
}-*/;
- private static native void setBitM(LongEmul a, int bit) /*-{
+ private static native void setBitM(BigLong a, int bit) /*-{
a.m |= 1 << bit;
}-*/;
- private static native void setH(LongEmul a, int x) /*-{
+ private static native void setH(BigLong a, int x) /*-{
a.h = x;
}-*/;
- private static native void setL(LongEmul a, int x) /*-{
+ private static native void setL(BigLong a, int x) /*-{
a.l = x;
}-*/;
- private static native void setM(LongEmul a, int x) /*-{
+ private static native void setM(BigLong a, int x) /*-{
a.m = x;
}-*/;
/**
* a >>= 1. Assumes a >= 0.
*/
- private static void toShru1(LongEmul a) {
+ private static void toShru1(BigLong a) {
int a1 = getM(a);
int a2 = getH(a);
int a0 = getL(a);
- if (RUN_IN_JVM) {
+ if (LongLib.RUN_IN_JVM) {
a.h = a2 >>> 1;
a.m = (a1 >>> 1) | ((a2 & 0x1) << (BITS - 1));
a.l = (a0 >>> 1) | ((a1 & 0x1) << (BITS - 1));
@@ -545,7 +532,7 @@
* }
* </pre>
*/
- private static boolean trialSubtract(LongEmul a, LongEmul b) {
+ private static boolean trialSubtract(BigLong a, BigLong b) {
// Early exit
int sum2 = getH(a) - getH(b);
if (sum2 < 0) {
@@ -560,7 +547,7 @@
return false;
}
- if (RUN_IN_JVM) {
+ if (LongLib.RUN_IN_JVM) {
a.l = sum0 & MASK;
a.m = sum1 & MASK;
a.h = sum2 & MASK_2;
@@ -576,6 +563,6 @@
/**
* Not instantiable outside this package.
*/
- LongLibBase() {
+ BigLongLibBase() {
}
}
diff --git a/dev/core/super/com/google/gwt/lang/LongLib.java b/dev/core/super/com/google/gwt/lang/LongLib.java
index 08a9699..dca4118 100644
--- a/dev/core/super/com/google/gwt/lang/LongLib.java
+++ b/dev/core/super/com/google/gwt/lang/LongLib.java
@@ -15,431 +15,359 @@
*/
package com.google.gwt.lang;
-/**
- * Implements a Java <code>long</code> in a way that can be translated to
- * JavaScript.
- */
-public class LongLib extends LongLibBase {
+import com.google.gwt.lang.BigLongLibBase.BigLong;
- static class Const {
- static final LongEmul MAX_VALUE = create(MASK, MASK, MASK_2 >> 1);
- static final LongEmul MIN_VALUE = create(0, 0, SIGN_BIT_VALUE);
- static final LongEmul ONE = fromInt(1);
- static final LongEmul TWO = fromInt(2);
- static final LongEmul ZERO = fromInt(0);
+/**
+ * Implements a Java <code>long</code> in a way that can be translated to JavaScript.
+ */
+public class LongLib {
+
+/**
+ * Abstraction for long emulation. The emulation could be done using FastLong if the number is
+ * small enough or BigLong if the number is big. Note that, the class here is just a place holder
+ * and would normally extend JavaScriptObject if there wasn't a JVM mode.
+ */
+ static class LongEmul {
+ SmallLong small;
+ BigLong big;
}
- private static LongEmul[] boxedValues;
+ /**
+ * A LongEmul represented as double number.
+ */
+ static class SmallLong {
+ double d;
+ }
+
+ /**
+ * Allow standalone Java tests such as LongLibTest/LongLibJreTest to run this
+ * code.
+ */
+ protected static boolean RUN_IN_JVM = false;
public static LongEmul add(LongEmul a, LongEmul b) {
- int sum0 = getL(a) + getL(b);
- int sum1 = getM(a) + getM(b) + (sum0 >> BITS);
- int sum2 = getH(a) + getH(b) + (sum1 >> BITS);
-
- return create(sum0 & MASK, sum1 & MASK, sum2 & MASK_2);
- }
-
- public static LongEmul and(LongEmul a, LongEmul b) {
- return create(getL(a) & getL(b), getM(a) & getM(b), getH(a) & getH(b));
- }
-
- /**
- * Compare the receiver a to the argument b.
- *
- * @return 0 if they are the same, a positive value if the receiver is
- * greater, or a negative value if the argument is greater.
- */
- public static int compare(LongEmul a, LongEmul b) {
- int signA = sign(a);
- int signB = sign(b);
- if (signA != signB) {
- return signB - signA;
- }
-
- int a2 = getH(a);
- int b2 = getH(b);
- if (a2 != b2) {
- return a2 - b2;
- }
-
- int a1 = getM(a);
- int b1 = getM(b);
- if (a1 != b1) {
- return a1 - b1;
- }
-
- int a0 = getL(a);
- int b0 = getL(b);
- return a0 - b0;
- }
-
- public static LongEmul div(LongEmul a, LongEmul b) {
- return divMod(a, b, false);
- }
-
- public static boolean eq(LongEmul a, LongEmul b) {
- return getL(a) == getL(b) && getM(a) == getM(b) && getH(a) == getH(b);
- }
-
- public static LongEmul fromDouble(double value) {
- if (Double.isNaN(value)) {
- return Const.ZERO;
- }
- if (value < -TWO_PWR_63_DBL) {
- return Const.MIN_VALUE;
- }
- if (value >= TWO_PWR_63_DBL) {
- return Const.MAX_VALUE;
- }
-
- boolean negative = false;
- if (value < 0) {
- negative = true;
- value = -value;
- }
- int a2 = 0;
- if (value >= TWO_PWR_44_DBL) {
- a2 = (int) (value / TWO_PWR_44_DBL);
- value -= a2 * TWO_PWR_44_DBL;
- }
- int a1 = 0;
- if (value >= TWO_PWR_22_DBL) {
- a1 = (int) (value / TWO_PWR_22_DBL);
- value -= a1 * TWO_PWR_22_DBL;
- }
- int a0 = (int) value;
- LongEmul result = create(a0, a1, a2);
- if (negative) {
- negate(result);
- }
- return result;
- }
-
- public static LongEmul fromInt(int value) {
- if (value > -129 && value < 128) {
- int rebase = value + 128;
- if (boxedValues == null) {
- boxedValues = new LongEmul[256];
- }
- LongEmul result = boxedValues[rebase];
- if (result == null) {
- result = boxedValues[rebase] = create(value);
- }
- return result;
- }
-
- return create(value);
- }
-
- /**
- * Return a triple of ints { low, middle, high } that concatenate bitwise to
- * the given number.
- */
- public static int[] getAsIntArray(long l) {
- int[] a = new int[3];
- a[0] = (int) (l & MASK);
- a[1] = (int) ((l >> BITS) & MASK);
- a[2] = (int) ((l >> BITS01) & MASK_2);
- return a;
- }
-
- public static boolean gt(LongEmul a, LongEmul b) {
- int signa = getH(a) >> (BITS2 - 1);
- int signb = getH(b) >> (BITS2 - 1);
- if (signa == 0) {
- if (signb != 0 || getH(a) > getH(b)
- || (getH(a) == getH(b) && getM(a) > getM(b))
- || (getH(a) == getH(b) && getM(a) == getM(b) && getL(a) > getL(b))) {
- return true;
- } else {
- return false;
- }
- } else {
- if (signb == 0 || getH(a) < getH(b)
- || (getH(a) == getH(b) && getM(a) < getM(b))
- || (getH(a) == getH(b) && getM(a) == getM(b) && getL(a) <= getL(b))) {
- return false;
- } else {
- return true;
+ if (isSmallLong(a) && isSmallLong(b)) {
+ double result = asDouble(a) + asDouble(b);
+ if (isSafeIntegerRange(result)) {
+ return createSmallLongEmul(result);
}
}
- }
- public static boolean gte(LongEmul a, LongEmul b) {
- int signa = getH(a) >> (BITS2 - 1);
- int signb = getH(b) >> (BITS2 - 1);
- if (signa == 0) {
- if (signb != 0 || getH(a) > getH(b)
- || (getH(a) == getH(b) && getM(a) > getM(b))
- || (getH(a) == getH(b) && getM(a) == getM(b) && getL(a) >= getL(b))) {
- return true;
- } else {
- return false;
- }
- } else {
- if (signb == 0 || getH(a) < getH(b)
- || (getH(a) == getH(b) && getM(a) < getM(b))
- || (getH(a) == getH(b) && getM(a) == getM(b) && getL(a) < getL(b))) {
- return false;
- } else {
- return true;
- }
- }
- }
-
- public static boolean lt(LongEmul a, LongEmul b) {
- return !gte(a, b);
- }
-
- public static boolean lte(LongEmul a, LongEmul b) {
- return !gt(a, b);
- }
-
- public static LongEmul mod(LongEmul a, LongEmul b) {
- divMod(a, b, true);
- return remainder;
- }
-
- // Assumes BITS == 22
- public static LongEmul mul(LongEmul a, LongEmul b) {
- // Grab 13-bit chunks
- int a0 = getL(a) & 0x1fff;
- int a1 = (getL(a) >> 13) | ((getM(a) & 0xf) << 9);
- int a2 = (getM(a) >> 4) & 0x1fff;
- int a3 = (getM(a) >> 17) | ((getH(a) & 0xff) << 5);
- int a4 = (getH(a) & 0xfff00) >> 8;
-
- int b0 = getL(b) & 0x1fff;
- int b1 = (getL(b) >> 13) | ((getM(b) & 0xf) << 9);
- int b2 = (getM(b) >> 4) & 0x1fff;
- int b3 = (getM(b) >> 17) | ((getH(b) & 0xff) << 5);
- int b4 = (getH(b) & 0xfff00) >> 8;
-
- // Compute partial products
- // Optimization: if b is small, avoid multiplying by parts that are 0
- int p0 = a0 * b0; // << 0
- int p1 = a1 * b0; // << 13
- int p2 = a2 * b0; // << 26
- int p3 = a3 * b0; // << 39
- int p4 = a4 * b0; // << 52
-
- if (b1 != 0) {
- p1 += a0 * b1;
- p2 += a1 * b1;
- p3 += a2 * b1;
- p4 += a3 * b1;
- }
- if (b2 != 0) {
- p2 += a0 * b2;
- p3 += a1 * b2;
- p4 += a2 * b2;
- }
- if (b3 != 0) {
- p3 += a0 * b3;
- p4 += a1 * b3;
- }
- if (b4 != 0) {
- p4 += a0 * b4;
- }
-
- // Accumulate into 22-bit chunks:
- // .........................................c10|...................c00|
- // |....................|..................xxxx|xxxxxxxxxxxxxxxxxxxxxx| p0
- // |....................|......................|......................|
- // |....................|...................c11|......c01.............|
- // |....................|....xxxxxxxxxxxxxxxxxx|xxxxxxxxx.............| p1
- // |....................|......................|......................|
- // |.................c22|...............c12....|......................|
- // |..........xxxxxxxxxx|xxxxxxxxxxxxxxxxxx....|......................| p2
- // |....................|......................|......................|
- // |.................c23|..c13.................|......................|
- // |xxxxxxxxxxxxxxxxxxxx|xxxxx.................|......................| p3
- // |....................|......................|......................|
- // |.........c24........|......................|......................|
- // |xxxxxxxxxxxx........|......................|......................| p4
-
- int c00 = p0 & 0x3fffff;
- int c01 = (p1 & 0x1ff) << 13;
- int c0 = c00 + c01;
-
- int c10 = p0 >> 22;
- int c11 = p1 >> 9;
- int c12 = (p2 & 0x3ffff) << 4;
- int c13 = (p3 & 0x1f) << 17;
- int c1 = c10 + c11 + c12 + c13;
-
- int c22 = p2 >> 18;
- int c23 = p3 >> 5;
- int c24 = (p4 & 0xfff) << 8;
- int c2 = c22 + c23 + c24;
-
- // Propagate high bits from c0 -> c1, c1 -> c2
- c1 += c0 >> BITS;
- c0 &= MASK;
- c2 += c1 >> BITS;
- c1 &= MASK;
- c2 &= MASK_2;
-
- return create(c0, c1, c2);
- }
-
- public static LongEmul neg(LongEmul a) {
- int neg0 = (~getL(a) + 1) & MASK;
- int neg1 = (~getM(a) + (neg0 == 0 ? 1 : 0)) & MASK;
- int neg2 = (~getH(a) + ((neg0 == 0 && neg1 == 0) ? 1 : 0)) & MASK_2;
-
- return create(neg0, neg1, neg2);
- }
-
- public static boolean neq(LongEmul a, LongEmul b) {
- return getL(a) != getL(b) || getM(a) != getM(b) || getH(a) != getH(b);
- }
-
- public static LongEmul not(LongEmul a) {
- return create((~getL(a)) & MASK, (~getM(a)) & MASK, (~getH(a)) & MASK_2);
- }
-
- public static LongEmul or(LongEmul a, LongEmul b) {
- return create(getL(a) | getL(b), getM(a) | getM(b), getH(a) | getH(b));
- }
-
- public static LongEmul shl(LongEmul a, int n) {
- n &= 63;
-
- int res0, res1, res2;
- if (n < BITS) {
- res0 = getL(a) << n;
- res1 = (getM(a) << n) | (getL(a) >> (BITS - n));
- res2 = (getH(a) << n) | (getM(a) >> (BITS - n));
- } else if (n < BITS01) {
- res0 = 0;
- res1 = getL(a) << (n - BITS);
- res2 = (getM(a) << (n - BITS)) | (getL(a) >> (BITS01 - n));
- } else {
- res0 = 0;
- res1 = 0;
- res2 = getL(a) << (n - BITS01);
- }
-
- return create(res0 & MASK, res1 & MASK, res2 & MASK_2);
- }
-
- public static LongEmul shr(LongEmul a, int n) {
- n &= 63;
-
- int res0, res1, res2;
-
- // Sign extend h(a)
- int a2 = getH(a);
- boolean negative = (a2 & SIGN_BIT_VALUE) != 0;
- if (negative) {
- a2 |= ~MASK_2;
- }
-
- if (n < BITS) {
- res2 = a2 >> n;
- res1 = (getM(a) >> n) | (a2 << (BITS - n));
- res0 = (getL(a) >> n) | (getM(a) << (BITS - n));
- } else if (n < BITS01) {
- res2 = negative ? MASK_2 : 0;
- res1 = a2 >> (n - BITS);
- res0 = (getM(a) >> (n - BITS)) | (a2 << (BITS01 - n));
- } else {
- res2 = negative ? MASK_2 : 0;
- res1 = negative ? MASK : 0;
- res0 = a2 >> (n - BITS01);
- }
-
- return create(res0 & MASK, res1 & MASK, res2 & MASK_2);
- }
-
- /**
- * Logical right shift. It does not preserve the sign of the input.
- */
- public static LongEmul shru(LongEmul a, int n) {
- n &= 63;
-
- int res0, res1, res2;
- int a2 = getH(a) & MASK_2;
- if (n < BITS) {
- res2 = a2 >>> n;
- res1 = (getM(a) >> n) | (a2 << (BITS - n));
- res0 = (getL(a) >> n) | (getM(a) << (BITS - n));
- } else if (n < BITS01) {
- res2 = 0;
- res1 = a2 >>> (n - BITS);
- res0 = (getM(a) >> (n - BITS)) | (getH(a) << (BITS01 - n));
- } else {
- res2 = 0;
- res1 = 0;
- res0 = a2 >>> (n - BITS01);
- }
-
- return create(res0 & MASK, res1 & MASK, res2 & MASK_2);
+ return createLongEmul(BigLongLib.add(toBigLong(a), toBigLong(b)));
}
public static LongEmul sub(LongEmul a, LongEmul b) {
- int sum0 = getL(a) - getL(b);
- int sum1 = getM(a) - getM(b) + (sum0 >> BITS);
- int sum2 = getH(a) - getH(b) + (sum1 >> BITS);
-
- return create(sum0 & MASK, sum1 & MASK, sum2 & MASK_2);
- }
-
- public static double toDouble(LongEmul a) {
- if (LongLib.lt(a, Const.ZERO)) {
- return -toDoubleHelper(LongLib.neg(a));
- }
- return toDoubleHelper(a);
- }
-
- // Assumes Integer.MIN_VALUE <= a <= Integer.MAX_VALUE
- public static int toInt(LongEmul a) {
- return getL(a) | (getM(a) << BITS);
- }
-
- public static String toString(LongEmul a) {
- if (LongLibBase.isZero(a)) {
- return "0";
- }
-
- if (LongLibBase.isMinValue(a)) {
- // Special-case MIN_VALUE because neg(MIN_VALUE) == MIN_VALUE
- return "-9223372036854775808";
- }
-
- if (LongLibBase.isNegative(a)) {
- return "-" + toString(neg(a));
- }
-
- LongEmul rem = a;
- String res = "";
-
- while (!LongLibBase.isZero(rem)) {
- // Do several digits each time through the loop, so as to
- // minimize the calls to the very expensive emulated div.
- final int tenPowerZeroes = 9;
- final int tenPower = 1000000000;
- LongEmul tenPowerLong = fromInt(tenPower);
-
- rem = LongLibBase.divMod(rem, tenPowerLong, true);
- String digits = "" + toInt(LongLibBase.remainder);
-
- if (!LongLibBase.isZero(rem)) {
- int zeroesNeeded = tenPowerZeroes - digits.length();
- for (; zeroesNeeded > 0; zeroesNeeded--) {
- digits = "0" + digits;
- }
+ if (isSmallLong(a) && isSmallLong(b)) {
+ double result = asDouble(a) - asDouble(b);
+ if (isSafeIntegerRange(result)) {
+ return createSmallLongEmul(result);
}
-
- res = digits + res;
}
- return res;
+ return createLongEmul(BigLongLib.sub(toBigLong(a), toBigLong(b)));
+ }
+
+ public static LongEmul neg(LongEmul a) {
+ // TODO: add test for max neg number
+ if (isSmallLong(a)) {
+ double result = 0 - asDouble(a);
+ if (!Double.isNaN(result)) {
+ return createSmallLongEmul(result);
+ }
+ }
+
+ return createLongEmul(BigLongLib.neg(asBigLong(a)));
+ }
+
+ public static boolean gt(LongEmul a, LongEmul b) {
+ return compare(a, b) > 0;
+ }
+
+ public static boolean gte(LongEmul a, LongEmul b) {
+ return compare(a, b) >= 0;
+ }
+
+ public static boolean lt(LongEmul a, LongEmul b) {
+ return compare(a, b) < 0;
+ }
+
+ public static boolean lte(LongEmul a, LongEmul b) {
+ return compare(a, b) <= 0;
+ }
+
+ public static boolean eq(LongEmul a, LongEmul b) {
+ return compare(a, b) == 0;
+ }
+
+ public static boolean neq(LongEmul a, LongEmul b) {
+ return compare(a, b) != 0;
+ }
+
+ // VisibleForTesting
+ static double compare(LongEmul a, LongEmul b) {
+ if (isSmallLong(a) && isSmallLong(b)) {
+ double result = asDouble(a) - asDouble(b);
+ if (!Double.isNaN(result)) {
+ return result;
+ }
+ }
+
+ return BigLongLib.compare(toBigLong(a), toBigLong(b));
+ }
+
+ public static LongEmul div(LongEmul a, LongEmul b) {
+ if (isSmallLong(a) && isSmallLong(b)) {
+ double result = asDouble(a) / asDouble(b);
+ if (isSafeIntegerRange(result)) {
+ return createSmallLongEmul(truncate(result));
+ }
+ }
+
+ return createLongEmul(BigLongLib.div(toBigLong(a), toBigLong(b)));
+ }
+
+ public static LongEmul mod(LongEmul a, LongEmul b) {
+ if (isSmallLong(a) && isSmallLong(b)) {
+ double result = asDouble(a) % asDouble(b);
+ if (isSafeIntegerRange(result)) {
+ return createSmallLongEmul(result);
+ }
+ }
+
+ return createLongEmul(BigLongLib.mod(toBigLong(a), toBigLong(b)));
+ }
+
+ public static LongEmul mul(LongEmul a, LongEmul b) {
+ if (isSmallLong(a) && isSmallLong(b)) {
+ double result = asDouble(a) * asDouble(b);
+ if (isSafeIntegerRange(result)) {
+ return createSmallLongEmul(result);
+ }
+ }
+
+ return createLongEmul(BigLongLib.mul(toBigLong(a), toBigLong(b)));
+ }
+
+ public static LongEmul not(LongEmul a) {
+ return createLongEmul(BigLongLib.not(toBigLong(a)));
+ }
+
+ public static LongEmul and(LongEmul a, LongEmul b) {
+ return createLongEmul(BigLongLib.and(toBigLong(a), toBigLong(b)));
+ }
+
+ public static LongEmul or(LongEmul a, LongEmul b) {
+ return createLongEmul(BigLongLib.or(toBigLong(a), toBigLong(b)));
}
public static LongEmul xor(LongEmul a, LongEmul b) {
- return create(getL(a) ^ getL(b), getM(a) ^ getM(b), getH(a) ^ getH(b));
+ return createLongEmul(BigLongLib.xor(toBigLong(a), toBigLong(b)));
+ }
+
+ public static LongEmul shl(LongEmul a, int n) {
+ return createLongEmul(BigLongLib.shl(toBigLong(a), n));
+ }
+
+ public static LongEmul shr(LongEmul a, int n) {
+ return createLongEmul(BigLongLib.shr(toBigLong(a), n));
+ }
+
+ public static LongEmul shru(LongEmul a, int n) {
+ return createLongEmul(BigLongLib.shru(toBigLong(a), n));
+ }
+
+ public static LongEmul fromDouble(double value) {
+ if (isSafeIntegerRange(value)) {
+ return createSmallLongEmul(truncate(value));
+ }
+
+ return createLongEmul(BigLongLib.fromDouble(value));
+ }
+
+ public static double toDouble(LongEmul a) {
+ if (isSmallLong(a)) {
+ double d = asDouble(a);
+ // We need to kill negative zero because that could never happen in long but our double based
+ // representation may result with that.
+ return d == -0.0 ? 0 : d;
+ }
+
+ return BigLongLib.toDouble(asBigLong(a));
+ }
+
+ public static LongEmul fromInt(int value) {
+ return createSmallLongEmul(value);
+ }
+
+ public static int toInt(LongEmul a) {
+ if (isSmallLong(a)) {
+ return coerceToInt(asDouble(a));
+ }
+
+ return BigLongLib.toInt(asBigLong(a));
+ }
+
+ public static String toString(LongEmul a) {
+ if (isSmallLong(a)) {
+ return toString(asDouble(a));
+ }
+
+ return BigLongLib.toString(asBigLong(a));
+ }
+
+ // Called by compiler to generate constants.
+ public static long[] getAsLongArray(long l) {
+ if (isSafeIntegerRange(l)) {
+ return new long[] {l};
+ }
+
+ return BigLongLib.getAsLongArray(l);
+ }
+
+ // TODO(goktug): Safe integer range could potentially increased update up to 53 bits.
+ private static boolean isSafeIntegerRange(double value) {
+ return -BigLongLibBase.TWO_PWR_44_DBL < value && value < BigLongLibBase.TWO_PWR_44_DBL;
+ }
+
+ private static double truncate(double value) {
+ // Same as Math.trunc() but not available everywhere.
+ return value < 0 ? Math.ceil(value) : Math.floor(value);
+ }
+
+ private static int coerceToInt(double value) {
+ if (LongLib.RUN_IN_JVM) {
+ return (int) (long) value;
+ }
+ return coerceToInt0(value);
+ }
+
+ private static native int coerceToInt0(double value)/*-{
+ return value | 0;
+ }-*/;
+
+ private static String toString(double value) {
+ if (LongLib.RUN_IN_JVM) {
+ return String.valueOf((long) value);
+ }
+ return String.valueOf(value);
+ }
+
+ private static double asDouble(LongEmul value) {
+ return asDouble(asSmallLong(value));
+ }
+
+ private static SmallLong asSmallLong(LongEmul value) {
+ if (LongLib.RUN_IN_JVM) {
+ return value.small;
+ }
+ return asSmallLong0(value);
+ }
+
+ private static native SmallLong asSmallLong0(LongEmul value)/*-{
+ return value;
+ }-*/;
+
+ private static double asDouble(SmallLong value) {
+ if (LongLib.RUN_IN_JVM) {
+ return value == null ? Double.NaN : value.d;
+ }
+ return asDouble0(value);
+ }
+
+ private static native double asDouble0(SmallLong value)/*-{
+ return value;
+ }-*/;
+
+ private static boolean isSmallLong(LongEmul value) {
+ if (LongLib.RUN_IN_JVM) {
+ return value.small != null;
+ }
+ return isSmallLong0(value);
+ }
+
+ private static native boolean isSmallLong0(LongEmul value)/*-{
+ return typeof(value) === 'number';
+ }-*/;
+
+ // Visible for testing
+ static BigLong asBigLong(LongEmul value) {
+ if (LongLib.RUN_IN_JVM) {
+ return value.big;
+ }
+ return asBigLong0(value);
+ }
+
+ private static native BigLong asBigLong0(LongEmul value)/*-{
+ return value;
+ }-*/;
+
+ private static BigLong toBigLong(LongEmul value) {
+ return isSmallLong(value) ? toBigLong(asSmallLong(value)) : asBigLong(value);
+ }
+
+ private static BigLong toBigLong(SmallLong longValue) {
+ double value = asDouble(longValue);
+ int a3 = 0;
+ if (value < 0) {
+ // Convert to a positive number that will have the exact same first 44 bits
+ value += BigLongLibBase.TWO_PWR_44_DBL;
+ a3 = BigLongLib.MASK_2;
+ }
+ int a1 = (int) (value / BigLongLibBase.TWO_PWR_22_DBL);
+ int a0 = (int) (value - a1 * BigLongLibBase.TWO_PWR_22_DBL);
+ return BigLongLibBase.create(a0, a1, a3);
+ }
+
+ private static LongEmul createSmallLongEmul(double value) {
+ if (LongLib.RUN_IN_JVM) {
+ SmallLong small = new SmallLong();
+ small.d = value;
+ LongEmul emul = new LongEmul();
+ emul.small = small;
+ return emul;
+ }
+ return createSmallLongEmul0(value);
+ }
+
+ private static native LongEmul createSmallLongEmul0(double value)/*-{
+ return value;
+ }-*/;
+
+ private static LongEmul createLongEmul(BigLong big) {
+ int a2 = BigLongLibBase.getH(big);
+ if (a2 == 0) {
+ return createSmallLongEmul(
+ BigLongLibBase.getL(big) + BigLongLibBase.getM(big) * BigLongLibBase.TWO_PWR_22_DBL);
+ }
+ if (a2 == BigLongLibBase.MASK_2) {
+ return createSmallLongEmul(BigLongLibBase.getL(big)
+ + BigLongLibBase.getM(big) * BigLongLibBase.TWO_PWR_22_DBL
+ - BigLongLib.TWO_PWR_44_DBL);
+ }
+
+ return createBigLongEmul(big);
+ }
+
+ private static LongEmul createBigLongEmul(BigLong big) {
+ if (LongLib.RUN_IN_JVM) {
+ LongEmul emul = new LongEmul();
+ emul.big = big;
+ return emul;
+ }
+ return createBigLongEmul0(big);
+ }
+
+ private static native LongEmul createBigLongEmul0(BigLong value)/*-{
+ return value;
+ }-*/;
+
+ // VisibleForTesting
+ static LongEmul copy(LongEmul value) {
+ if (isSmallLong(value)) {
+ return createSmallLongEmul(asDouble(value));
+ } else {
+ return createBigLongEmul(BigLongLibBase.create(asBigLong(value)));
+ }
}
/**
diff --git a/dev/core/test/com/google/gwt/lang/LongLibJreTest.java b/dev/core/test/com/google/gwt/lang/LongLibJreTest.java
index 0ed5dfd..a2f3489 100644
--- a/dev/core/test/com/google/gwt/lang/LongLibJreTest.java
+++ b/dev/core/test/com/google/gwt/lang/LongLibJreTest.java
@@ -23,7 +23,7 @@
public class LongLibJreTest extends TestCase {
static {
- LongLibBase.RUN_IN_JVM = true;
+ LongLib.RUN_IN_JVM = true;
}
private LongLibTestBase impl = new LongLibTestBase();
@@ -72,6 +72,10 @@
impl.testNegate();
}
+ public void testNegativeZeroSeal() {
+ impl.testNegativeZeroSeal();
+ }
+
public void testShift() {
impl.testShift();
}
diff --git a/dev/core/test/com/google/gwt/lang/LongLibTest.java b/dev/core/test/com/google/gwt/lang/LongLibTest.java
index ed982b4..b5b42ac 100644
--- a/dev/core/test/com/google/gwt/lang/LongLibTest.java
+++ b/dev/core/test/com/google/gwt/lang/LongLibTest.java
@@ -15,7 +15,8 @@
*/
package com.google.gwt.lang;
-import com.google.gwt.lang.LongLibBase.LongEmul;
+import com.google.gwt.lang.BigLongLibBase.BigLong;
+import com.google.gwt.lang.LongLib.LongEmul;
import junit.framework.TestCase;
@@ -130,7 +131,7 @@
@Override
public int test(LongEmul longVal0, LongEmul longVal1) {
- return LongLib.compare(longVal0, longVal1);
+ return (int) Math.signum(LongLib.compare(longVal0, longVal1));
}
};
@@ -345,7 +346,7 @@
private static long[] TEST_VALUES;
static {
- LongLibBase.RUN_IN_JVM = true;
+ LongLib.RUN_IN_JVM = true;
}
static {
@@ -357,11 +358,11 @@
testSet.add(Long.MIN_VALUE + i);
testSet.add(Long.MAX_VALUE - i);
- testSet.add(i << LongLibBase.BITS / 2);
- testSet.add(i << LongLibBase.BITS);
- testSet.add(i << (3 * LongLibBase.BITS) / 2);
- testSet.add(i << 2 * LongLibBase.BITS);
- testSet.add(i << (5 * LongLibBase.BITS) / 2);
+ testSet.add(i << BigLongLib.BITS / 2);
+ testSet.add(i << BigLongLib.BITS);
+ testSet.add(i << (3 * BigLongLib.BITS) / 2);
+ testSet.add(i << 2 * BigLongLib.BITS);
+ testSet.add(i << (5 * BigLongLib.BITS) / 2);
}
for (int i = 0; i < 16; i++) {
@@ -436,15 +437,30 @@
doTestBoolean(OP_EQ);
}
- public static void testGetAsIntArray() {
- long longVal = 0x123456789abcdef0L;
- int[] array = LongLib.getAsIntArray(longVal);
+ public static void testGetAsLongArray() {
+ long longVal = 0L;
+ long[] array = LongLib.getAsLongArray(longVal);
+ assertEquals(1, array.length);
+ assertEquals(0, array[0]);
+
+ longVal = 10L;
+ array = LongLib.getAsLongArray(longVal);
+ assertEquals(1, array.length);
+ assertEquals(10, array[0]);
+
+ longVal = -10L;
+ array = LongLib.getAsLongArray(longVal);
+ assertEquals(1, array.length);
+ assertEquals(-10, array[0]);
+
+ longVal = 0x123456789abcdef0L;
+ array = LongLib.getAsLongArray(longVal);
assertEquals(0x12345, array[2]);
assertEquals(0x19e26a, array[1]);
assertEquals(0x3cdef0, array[0]);
- longVal = -longVal;
- array = LongLib.getAsIntArray(longVal);
+ longVal = -0x123456789abcdef0L;
+ array = LongLib.getAsLongArray(longVal);
assertEquals(0xedcba, array[2]);
assertEquals(0x261d95, array[1]);
assertEquals(0x32110, array[0]);
@@ -487,10 +503,10 @@
}
public static void testNumberOfLeadingZeros() {
- LongEmul longVal0 = fromLong(0xfedcba9876543210L);
+ BigLong longVal0 = LongLib.asBigLong(fromLong(0xfedcba9876543210L));
for (int i = 0; i <= 64; i++) {
- assertEquals(i, LongLibBase.numberOfLeadingZeros(longVal0));
- longVal0 = LongLib.shru(longVal0, 1);
+ assertEquals(i, BigLongLibBase.numberOfLeadingZeros(longVal0));
+ longVal0 = BigLongLib.shru(longVal0, 1);
}
}
@@ -598,11 +614,7 @@
}
private static LongEmul copy(LongEmul longVal) {
- LongEmul result = new LongEmul();
- result.l = longVal.l;
- result.m = longVal.m;
- result.h = longVal.h;
- return result;
+ return LongLib.copy(longVal);
}
private static void doTestBinary(BinaryOp op) {
@@ -791,21 +803,11 @@
}
private static LongEmul fromLong(long longVal) {
- LongEmul result = new LongEmul();
- result.l = (int) (longVal & LongLibBase.MASK);
- result.m = (int) ((longVal >> LongLibBase.BITS) & LongLibBase.MASK);
- result.h = (int) ((longVal >> (2 * LongLibBase.BITS)) & LongLibBase.MASK_2);
- return result;
+ return LongLibTestBase.longFromBits((int) (longVal >> 32), (int) longVal);
}
private static int getBit(LongEmul longVal, int bit) {
- if (bit < LongLibBase.BITS) {
- return (longVal.l >> bit) & 0x1;
- }
- if (bit < 2 * LongLibBase.BITS) {
- return (longVal.m >> (bit - LongLibBase.BITS)) & 0x1;
- }
- return (longVal.h >> (bit - (2 * LongLibBase.BITS))) & 0x1;
+ return LongLib.toInt(LongLib.shr(longVal, bit)) & 0x1;
}
private static String toHex(long longVal) {
@@ -834,9 +836,7 @@
}
private static long toLong(LongEmul longVal) {
- long b2 = ((long) longVal.h) << (2 * LongLibBase.BITS);
- long b1 = ((long) longVal.m) << LongLibBase.BITS;
- return b2 | b1 | longVal.l;
+ return Long.parseLong(LongLib.toString(longVal));
}
public LongLibTest() {
diff --git a/dev/core/test/com/google/gwt/lang/LongLibTestBase.java b/dev/core/test/com/google/gwt/lang/LongLibTestBase.java
index b5ead10..bee9570 100644
--- a/dev/core/test/com/google/gwt/lang/LongLibTestBase.java
+++ b/dev/core/test/com/google/gwt/lang/LongLibTestBase.java
@@ -15,8 +15,7 @@
*/
package com.google.gwt.lang;
-import com.google.gwt.lang.LongLib.Const;
-import com.google.gwt.lang.LongLibBase.LongEmul;
+import com.google.gwt.lang.LongLib.LongEmul;
import junit.framework.TestCase;
@@ -25,9 +24,12 @@
* by using a Java println on normal Java longs.
*/
public class LongLibTestBase extends TestCase {
-
- static {
- LongLibBase.RUN_IN_JVM = true;
+ private static class Const {
+ static final LongEmul MAX_VALUE = LongLib.fromDouble(Double.POSITIVE_INFINITY);
+ static final LongEmul MIN_VALUE = LongLib.fromDouble(Double.NEGATIVE_INFINITY);
+ static final LongEmul ONE = LongLib.fromInt(1);
+ static final LongEmul TWO = LongLib.fromInt(2);
+ static final LongEmul ZERO = LongLib.fromInt(0);
}
static void assertEquals(LongEmul expected, LongEmul actual) {
@@ -196,6 +198,12 @@
assertEquals(LongLib.fromInt(-122055), LongLib.div(LongLib.fromInt(-1000000000), LongLib.fromInt(8193)));
assertEquals(LongLib.fromInt(122070), LongLib.div(LongLib.fromInt(1000000000), LongLib.fromInt(8192)));
assertEquals(LongLib.fromInt(122055), LongLib.div(LongLib.fromInt(1000000000), LongLib.fromInt(8193)));
+ assertEquals(LongLib.fromInt(1), LongLib.div(LongLib.fromInt(5), LongLib.fromInt(5)));
+ assertEquals(LongLib.fromInt(333), LongLib.div(LongLib.fromInt(1000), LongLib.fromInt(3)));
+ assertEquals(LongLib.fromInt(-333), LongLib.div(LongLib.fromInt(1000), LongLib.fromInt(-3)));
+ assertEquals(LongLib.fromInt(-333), LongLib.div(LongLib.fromInt(-1000), LongLib.fromInt(3)));
+ assertEquals(LongLib.fromInt(333), LongLib.div(LongLib.fromInt(-1000), LongLib.fromInt(-3)));
+ assertEquals(LongLib.fromInt(0), LongLib.div(LongLib.fromInt(3), LongLib.fromInt(1000)));
assertEquals(longFromBits(0x1fffff, 0xffffffff), LongLib.div(Const.MAX_VALUE, longFromBits(0x00000000, 0x00000400)));
assertEquals(longFromBits(0x1fff, 0xffffffff), LongLib.div(Const.MAX_VALUE, longFromBits(0x00000000, 0x00040000)));
@@ -205,6 +213,15 @@
assertEquals(LongLib.fromInt(8191), LongLib.div(Const.MAX_VALUE, longFromBits(0x00040000, 0x00000000)));
assertEquals(LongLib.fromInt(31), LongLib.div(Const.MAX_VALUE, longFromBits(0x04000000, 0x00000000)));
+ assertEquals(longFromBits(0x1003d0, 0xe84f5ae8), LongLib.div(longFromBits(0x12345678, 0x12345678), longFromBits(0x0, 0x123)));
+ assertEquals(longFromBits(0x0, 0x10003), LongLib.div(longFromBits(0x12345678, 0x12345678), longFromBits(0x1234, 0x12345678)));
+ assertEquals(longFromBits(0xffffffff, 0xffff3dfe), LongLib.div(longFromBits(0xf2345678, 0x12345678), longFromBits(0x1234, 0x12345678)));
+ assertEquals(longFromBits(0x0, 0xeda),LongLib.div(longFromBits(0xf2345678, 0x12345678), longFromBits(0xffff1234, 0x12345678)));
+
+ assertEquals(longFromBits(0xc0000000, 0x00000000), LongLib.div(Const.MIN_VALUE, LongLib.fromInt(2)));
+ assertEquals(Const.MIN_VALUE, LongLib.div(Const.MIN_VALUE, LongLib.fromInt(1)));
+ assertEquals(Const.MIN_VALUE, LongLib.div(Const.MIN_VALUE, LongLib.fromInt(-1)));
+
LongLib.div(Const.MAX_VALUE, longFromBits(0x00000000, 0x00000300));
LongLib.div(Const.MAX_VALUE, longFromBits(0x00000000, 0x30000000));
LongLib.div(Const.MAX_VALUE, longFromBits(0x00300000, 0x00000000));
@@ -212,6 +229,18 @@
LongLib.div(Const.MAX_VALUE, longFromBits(0x00300000, 0x30000000));
LongLib.div(Const.MAX_VALUE, longFromBits(0x00000000, 0x30000300));
LongLib.div(Const.MAX_VALUE, longFromBits(0x00300000, 0x30000300));
+
+ try {
+ LongLib.div(LongLib.fromInt(1), LongLib.fromInt(0));
+ fail("Expected an ArithmeticException");
+ } catch (ArithmeticException e) {
+ }
+
+ try {
+ LongLib.div(Const.MAX_VALUE, LongLib.fromInt(0));
+ fail("Expected an ArithmeticException");
+ } catch (ArithmeticException e) {
+ }
}
public void testFactorial() {
@@ -278,6 +307,18 @@
assertEquals(longFromBits(0x3ff, 0xffffffff), LongLib.mod(Const.MAX_VALUE, longFromBits(0x00000400, 0x00000000)));
assertEquals(longFromBits(0x3ffff, 0xffffffff), LongLib.mod(Const.MAX_VALUE, longFromBits(0x00040000, 0x00000000)));
assertEquals(longFromBits(0x3ffffff, 0xffffffff), LongLib.mod(Const.MAX_VALUE, longFromBits(0x04000000, 0x00000000)));
+
+ try {
+ LongLib.mod(LongLib.fromInt(1), LongLib.fromInt(0));
+ fail("Expected an ArithmeticException");
+ } catch (ArithmeticException e) {
+ }
+
+ try {
+ LongLib.mod(Const.MAX_VALUE, LongLib.fromInt(0));
+ fail("Expected an ArithmeticException");
+ } catch (ArithmeticException e) {
+ }
}
public void testMultiplicative() {
@@ -306,40 +347,6 @@
LongLib.fromInt(1)));
assertEquals(Const.MIN_VALUE, LongLib.mul(Const.MIN_VALUE,
LongLib.fromInt(-1)));
-
- assertEquals(LongLib.fromInt(1), LongLib.div(LongLib.fromInt(5),
- LongLib.fromInt(5)));
- assertEquals(LongLib.fromInt(333), LongLib.div(LongLib.fromInt(1000),
- LongLib.fromInt(3)));
- assertEquals(LongLib.fromInt(-333), LongLib.div(LongLib.fromInt(1000),
- LongLib.fromInt(-3)));
- assertEquals(LongLib.fromInt(-333), LongLib.div(LongLib.fromInt(-1000),
- LongLib.fromInt(3)));
- assertEquals(LongLib.fromInt(333), LongLib.div(LongLib.fromInt(-1000),
- LongLib.fromInt(-3)));
- assertEquals(LongLib.fromInt(0), LongLib.div(LongLib.fromInt(3),
- LongLib.fromInt(1000)));
- assertEquals(longFromBits(0x1003d0, 0xe84f5ae8), LongLib.div(longFromBits(
- 0x12345678, 0x12345678), longFromBits(0x0, 0x123)));
- assertEquals(longFromBits(0x0, 0x10003), LongLib.div(longFromBits(
- 0x12345678, 0x12345678), longFromBits(0x1234, 0x12345678)));
- assertEquals(longFromBits(0xffffffff, 0xffff3dfe), LongLib.div(
- longFromBits(0xf2345678, 0x12345678), longFromBits(0x1234, 0x12345678)));
- assertEquals(longFromBits(0x0, 0xeda), LongLib.div(longFromBits(0xf2345678,
- 0x12345678), longFromBits(0xffff1234, 0x12345678)));
-
- try {
- LongLib.div(LongLib.fromInt(1), LongLib.fromInt(0));
- fail("Expected an ArithmeticException");
- } catch (ArithmeticException e) {
- }
-
- assertEquals(longFromBits(0xc0000000, 0x00000000), LongLib.div(
- Const.MIN_VALUE, LongLib.fromInt(2)));
- assertEquals(Const.MIN_VALUE, LongLib.div(Const.MIN_VALUE,
- LongLib.fromInt(1)));
- assertEquals(Const.MIN_VALUE, LongLib.div(Const.MIN_VALUE,
- LongLib.fromInt(-1))); // JLS3 section 15.17.2
}
public void testNegate() {
@@ -350,6 +357,12 @@
assertEquals(Const.MIN_VALUE, LongLib.neg(Const.MIN_VALUE));
}
+ public void testNegativeZeroSeal() {
+ LongEmul shouldBePositiveZero = LongLib.mul(LongLib.fromDouble(0), LongLib.fromDouble(-1));
+ assertEquals(Double.POSITIVE_INFINITY, 1d / LongLib.toDouble(shouldBePositiveZero));
+ assertEquals(Double.POSITIVE_INFINITY, 1d / LongLib.toInt(shouldBePositiveZero));
+ }
+
public void testShift() {
assertEquals(longFromBits(0xd048d115, 0x9d159c00), LongLib.shl(
longFromBits(0x12341234, 0x45674567), 10));
@@ -473,6 +486,7 @@
assertEquals("-1", LongLib.toString(LongLib.fromInt(-1)));
assertEquals("-10", LongLib.toString(LongLib.fromInt(-10)));
assertEquals("-9223372036854775808", LongLib.toString(Const.MIN_VALUE));
+ assertEquals("0", LongLib.toString(LongLib.fromDouble(-0d)));
int top = 922337201;
int bottom = 967490662;
@@ -491,7 +505,7 @@
}
}
- private LongEmul longFromBits(int top, int bottom) {
+ static LongEmul longFromBits(int top, int bottom) {
LongEmul topHalf = LongLib.shl(LongLib.fromInt(top), 32);
LongEmul bottomHalf = LongLib.fromInt(bottom);
if (LongLib.lt(bottomHalf, Const.ZERO)) {
diff --git a/user/test/com/google/gwt/langtest/client/LongLibGwtTest.java b/user/test/com/google/gwt/langtest/client/LongLibGwtTest.java
index 0508b80..90e62d7 100644
--- a/user/test/com/google/gwt/langtest/client/LongLibGwtTest.java
+++ b/user/test/com/google/gwt/langtest/client/LongLibGwtTest.java
@@ -15,12 +15,15 @@
*/
package com.google.gwt.langtest.client;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.lang.LongLibTestBase;
/**
* Test the LongLib class as a GWTTestCase.
*/
+@DoNotRunWith(Platform.Devel)
public class LongLibGwtTest extends GWTTestCase {
private LongLibTestBase impl = new LongLibTestBase();
@@ -74,6 +77,10 @@
impl.testNegate();
}
+ public void testNegativeZeroSeal() {
+ impl.testNegativeZeroSeal();
+ }
+
public void testShift() {
impl.testShift();
}