Fix Long.parseLong to correctly handle MIN_VALUE
Previously Long.parseLong raised an exception for MIN_VALUE because we
accumulated positive values and -MIN_VALUE == MAX_VALUE + 1. So when
parsing MIN_VALUE, the accumulated value overflows MAX_VALUE.
The fix is to accumulate negative values and negate the sign at the
end.
Fixes issue 7308
Review-Link: http://gwt-code-reviews.appspot.com/1825803/
Review by: goktug@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@11481 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/super/com/google/gwt/emul/java/lang/Number.java b/user/super/com/google/gwt/emul/java/lang/Number.java
index 84b7608..ab89197 100644
--- a/user/super/com/google/gwt/emul/java/lang/Number.java
+++ b/user/super/com/google/gwt/emul/java/lang/Number.java
@@ -238,6 +238,8 @@
throw new NumberFormatException("radix " + radix + " out of range");
}
+ final String orig = s;
+
int length = s.length();
boolean negative = (length > 0) && (s.charAt(0) == '-');
if (negative) {
@@ -245,7 +247,7 @@
length--;
}
if (length == 0) {
- throw NumberFormatException.forInputString(s);
+ throw NumberFormatException.forInputString(orig);
}
// Strip leading zeros
@@ -257,7 +259,7 @@
// Immediately eject numbers that are too long -- this avoids more complex
// overflow handling below
if (length > __ParseLong.maxLengthForRadix[radix]) {
- throw NumberFormatException.forInputString(s);
+ throw NumberFormatException.forInputString(orig);
}
// Validate the digits
@@ -275,18 +277,20 @@
if (c >= 'A' && c < maxUpperCaseDigit) {
continue;
}
- throw NumberFormatException.forInputString(s);
+ throw NumberFormatException.forInputString(orig);
}
long toReturn = 0;
int maxDigits = __ParseLong.maxDigitsForRadix[radix];
long radixPower = __ParseLong.maxDigitsRadixPower[radix];
- long maxValue = __ParseLong.maxValueForRadix[radix];
+ long minValue = -__ParseLong.maxValueForRadix[radix];
boolean firstTime = true;
int head = length % maxDigits;
if (head > 0) {
- toReturn = __parseInt(s.substring(0, head), radix);
+ // accumulate negative numbers, as -Long.MAX_VALUE == Long.MIN_VALUE + 1
+ // (in other words, -Long.MIN_VALUE overflows, see issue 7308)
+ toReturn = - __parseInt(s.substring(0, head), radix);
s = s.substring(head);
length -= head;
firstTime = false;
@@ -298,23 +302,27 @@
length -= maxDigits;
if (!firstTime) {
// Check whether multiplying by radixPower will overflow
- if (toReturn > maxValue) {
+ if (toReturn < minValue) {
throw new NumberFormatException(s);
}
toReturn *= radixPower;
} else {
firstTime = false;
}
- toReturn += head;
+ toReturn -= head;
}
-
- // A negative value means we overflowed Long.MAX_VALUE
- if (toReturn < 0) {
- throw NumberFormatException.forInputString(s);
+
+ // A positive value means we overflowed Long.MIN_VALUE
+ if (toReturn > 0) {
+ throw NumberFormatException.forInputString(orig);
}
-
- if (negative) {
+
+ if (!negative) {
toReturn = -toReturn;
+ // A negative value means we overflowed Long.MAX_VALUE
+ if (toReturn < 0) {
+ throw NumberFormatException.forInputString(orig);
+ }
}
return toReturn;
}
diff --git a/user/test/com/google/gwt/emultest/java/lang/LongTest.java b/user/test/com/google/gwt/emultest/java/lang/LongTest.java
index c307636..f1124f6 100644
--- a/user/test/com/google/gwt/emultest/java/lang/LongTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/LongTest.java
@@ -93,6 +93,9 @@
assertEquals(100000000000L, Long.parseLong("100000000000"));
assertEquals(-100000000000L, Long.parseLong("-100000000000"));
assertEquals(10L, Long.parseLong("010"));
+ assertEquals(Long.MAX_VALUE, Long.parseLong("" + Long.MAX_VALUE));
+ // Issue 7308
+ assertEquals(Long.MIN_VALUE, Long.parseLong("" + Long.MIN_VALUE));
try {
Long.parseLong("10L");
fail("expected NumberFormatException");