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");