Revision: 8269
Author: r...@google.com
Date: Wed Jun 16 15:55:40 2010
Log: Improve wire format for primitive long values. Keep support for the previous format in server-side code.

Review at http://gwt-code-reviews.appspot.com/626801

Review by: j...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=8269

Modified:
 /trunk/dev/core/super/com/google/gwt/lang/LongLib.java
 /trunk/dev/core/test/com/google/gwt/lang/LongLibTest.java
 /trunk/dev/core/test/com/google/gwt/lang/LongLibTestBase.java
/trunk/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java /trunk/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java /trunk/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java /trunk/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java /trunk/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
 /trunk/user/src/com/google/gwt/user/server/Base64Utils.java
/trunk/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java /trunk/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
 /trunk/user/test/com/google/gwt/user/server/rpc/RPCTest.java

=======================================
--- /trunk/dev/core/super/com/google/gwt/lang/LongLib.java Mon Jun 7 09:38:44 2010 +++ /trunk/dev/core/super/com/google/gwt/lang/LongLib.java Wed Jun 16 15:55:40 2010
@@ -30,7 +30,9 @@
   }

   private static LongEmul[] boxedValues;
-
+
+  private static boolean haveNonZero;
+
   public static LongEmul add(LongEmul a, LongEmul b) {
     int sum0 = getL(a) + getL(b);
     int sum1 = getM(a) + getM(b) + (sum0 >> BITS);
@@ -42,12 +44,67 @@
   public static LongEmul and(LongEmul a, LongEmul b) {
     return create(getL(a) & getL(b), getM(a) & getM(b), getH(a) & getH(b));
   }
+
+  /**
+   * Return an optionally single-quoted string containing a base-64 encoded
+   * version of the given long value.
+   */
+  public static String base64Emit(long value, boolean quote) {
+    // Convert to ints early to avoid need for long ops
+    int low = (int) (value & 0xffffffff);
+    int high = (int) (value >> 32);
+
+    StringBuilder sb = new StringBuilder();
+    if (quote) {
+      sb.append('\'');
+    }
+    haveNonZero = false;
+    base64Append(sb, (high >> 28) & 0xf); // bits 63 - 60
+    base64Append(sb, (high >> 22) & 0x3f); // bits 59 - 54
+    base64Append(sb, (high >> 16) & 0x3f); // bits 53 - 48
+    base64Append(sb, (high >> 10) & 0x3f); // bits 47 - 42
+    base64Append(sb, (high >> 4) & 0x3f); // bits 41 - 36
+    int v = ((high & 0xf) << 2) | ((low >> 30) & 0x3);
+    base64Append(sb, v); // bits 35 - 30
+    base64Append(sb, (low >> 24) & 0x3f); // bits 29 - 24
+    base64Append(sb, (low >> 18) & 0x3f); // bits 23 - 18
+    base64Append(sb, (low >> 12) & 0x3f); // bits 17 - 12
+    base64Append(sb, (low >> 6) & 0x3f); // bits 11 - 6
+    haveNonZero = true; // always emit final digit
+    base64Append(sb, low & 0x3f); // bits 5 - 0
+    if (quote) {
+      sb.append('\'');
+    }
+
+    return sb.toString();
+  }
+
+  /**
+   * Parse a string containing a base-64 encoded version of a long value.
+   */
+  public static long base64Parse(String value) {
+    int pos = 0;
+    char first = value.charAt(pos++);
+    int len = value.length();
+
+    // Skip surrounding single quotes
+    if (first == '\'') {
+      first = value.charAt(pos++);
+      len--;
+    }
+    long longVal = base64Value(first);
+    while (pos < len) {
+      longVal <<= 6;
+      longVal |= base64Value(value.charAt(pos++));
+    }
+    return longVal;
+  }

   /**
    * 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.
+   *         greater, or a negative value if the argument is greater.
    */
   public static int compare(LongEmul a, LongEmul b) {
     int signA = sign(a);
@@ -76,7 +133,7 @@
   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);
   }
@@ -130,7 +187,7 @@

     return create(value);
   }
-
+
   /**
* Return a triple of ints { low, middle, high } that concatenate bitwise to
    * the given number.
@@ -142,7 +199,7 @@
     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);
@@ -222,7 +279,7 @@
     int p2 = a2 * b0; // << 26
     int p3 = a3 * b0; // << 39
     int p4 = a4 * b0; // << 52
-
+
     if (b1 != 0) {
       p1 += a0 * b1;
       p2 += a1 * b1;
@@ -395,56 +452,96 @@
     }
     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;
         }
       }
-
+
       res = digits + res;
     }
-
+
     return res;
   }
-
+
   public static LongEmul xor(LongEmul a, LongEmul b) {
     return create(getL(a) ^ getL(b), getM(a) ^ getM(b), getH(a) ^ getH(b));
   }
-
+
+  private static void base64Append(StringBuilder sb, int digit) {
+    if (digit > 0) {
+      haveNonZero = true;
+    }
+    if (haveNonZero) {
+      int c;
+      if (digit < 26) {
+        c = 'A' + digit;
+      } else if (digit < 52) {
+        c = 'a' + digit - 26;
+      } else if (digit < 62) {
+        c = '0' + digit - 52;
+      } else if (digit == 62) {
+        c = '$';
+      } else {
+        c = '_';
+      }
+      sb.append((char) c);
+    }
+  }
+
+  // Assume digit is one of [A-Za-z0-9$_]
+  private static int base64Value(char digit) {
+    if (digit >= 'A' && digit <= 'Z') {
+      return digit - 'A';
+    }
+    // No need to check digit <= 'z'
+    if (digit >= 'a') {
+      return digit - 'a' + 26;
+    }
+    if (digit >= '0' && digit <= '9') {
+      return digit - '0' + 52;
+    }
+    if (digit == '$') {
+      return 62;
+    }
+    // digit == '_'
+    return 63;
+  }
+
   /**
    * Not instantiable.
    */
=======================================
--- /trunk/dev/core/test/com/google/gwt/lang/LongLibTest.java Mon Jun 14 08:04:23 2010 +++ /trunk/dev/core/test/com/google/gwt/lang/LongLibTest.java Wed Jun 16 15:55:40 2010
@@ -405,6 +405,41 @@
   public static void testAnd() {
     doTestBinary(OP_AND);
   }
+
+  public static void testBase64() {
+    assertEquals("A", LongLib.base64Emit(0x0L, false));
+    assertEquals(0x0L, LongLib.base64Parse("A"));
+
+    assertEquals("'B'", LongLib.base64Emit(0x1L, true));
+    assertEquals(0x1L, LongLib.base64Parse("B"));
+
+    assertEquals("'BA'", LongLib.base64Emit(0x40L, true));
+    assertEquals(0x40L, LongLib.base64Parse("BA"));
+
+    assertEquals("'P_________A'", LongLib.base64Emit(-0x40L, true));
+    assertEquals(-0x40L, LongLib.base64Parse("P_________A"));
+
+    assertEquals("'P__________'", LongLib.base64Emit(-1L, true));
+    assertEquals(-1L, LongLib.base64Parse("P__________"));
+
+    // Use all types of base 64 chars
+    long value = 0L;
+    value |= 15L << 60; // 'P'
+    value |= 35L << 54; // 'j'
+    value |= 44L << 48; // 's'
+    value |= 62L << 42; // '$'
+    value |= 26L << 36; // 'a'
+    value |=  9L << 30; // 'J'
+    value |= 18L << 24; // 'S'
+    value |= 25L << 18; // 'Z'
+    value |= 52L << 12; // '0'
+    value |= 57L << 6;  // '5'
+    value |= 63L;       // '_'
+
+    String s = "Pjs$aJSZ05_";
+    assertEquals(s, LongLib.base64Emit(value, false));
+    assertEquals(value, LongLib.base64Parse(s));
+  }

   public static void testCompare() {
     doTestCompare(OP_COMPARE);
@@ -417,7 +452,7 @@
   public static void testEq() {
     doTestBoolean(OP_EQ);
   }
-
+
   public static void testGetAsIntArray() {
     long longVal = 0x123456789abcdef0L;
     int[] array = LongLib.getAsIntArray(longVal);
=======================================
--- /trunk/dev/core/test/com/google/gwt/lang/LongLibTestBase.java Mon Jun 7 09:38:44 2010 +++ /trunk/dev/core/test/com/google/gwt/lang/LongLibTestBase.java Wed Jun 16 15:55:40 2010
@@ -24,6 +24,10 @@
  * Java println on normal Java longs.
  */
 public class LongLibTestBase extends TestCase {
+
+  static {
+    LongLibBase.RUN_IN_JVM = true;
+  }

   static void assertEquals(LongEmul expected, LongEmul actual) {
     assertTrue("expected=" + LongLib.toString(expected) + "  actual="
=======================================
--- /trunk/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java Mon Jun 7 09:38:44 2010 +++ /trunk/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java Wed Jun 16 15:55:40 2010
@@ -65,7 +65,6 @@

   @UnsafeNativeLong
   private static native long readLong0(JavaScriptObject obj, int idx) /*-{
-    // TODO (rice): use backwards-compatible wire format?
     return obj[idx];
   }-*/;

=======================================
--- /trunk/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java Wed Feb 18 17:08:17 2009 +++ /trunk/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java Wed Jun 16 15:55:40 2010
@@ -40,9 +40,16 @@
   public static final char RPC_SEPARATOR_CHAR = '|';

   /**
-   * This is the only supported RPC protocol version.
+ * The current RPC protocol version. This version differs from the previous + * one in that primitive long values are represented as single-quoted base-64 + * strings with an alphabet of [A-Za-z0-9$_], rather than as pairs of doubles.
    */
-  public static final int SERIALIZATION_STREAM_VERSION = 5;
+  public static final int SERIALIZATION_STREAM_VERSION = 6;
+
+  /**
+   * The oldest supported RPC protocol version.
+   */
+  public static final int SERIALIZATION_STREAM_MIN_VERSION = 5;

   /**
* Indicates that obfuscated type names should be used in the RPC payload.
=======================================
--- /trunk/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java Mon Jun 14 10:34:59 2010 +++ /trunk/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java Wed Jun 16 15:55:40 2010
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client.rpc.impl;

+import com.google.gwt.lang.LongLib;
 import com.google.gwt.user.client.rpc.SerializationException;
 import com.google.gwt.user.client.rpc.SerializationStreamWriter;

@@ -99,11 +100,18 @@
   public void writeInt(int fieldValue) {
     append(String.valueOf(fieldValue));
   }
-
-  /**
-   * Asymmetric implementation; see subclasses.
-   */
-  public abstract void writeLong(long value) throws SerializationException;
+
+  public void writeLong(long value) {
+    if (getVersion() == SERIALIZATION_STREAM_MIN_VERSION) {
+      // Write longs as a pair of doubles for backwards compatibility
+      double[] parts = getAsDoubleArray(value);
+      assert parts != null && parts.length == 2;
+      writeDouble(parts[0]);
+      writeDouble(parts[1]);
+    } else {
+      append(LongLib.base64Emit(value, true));
+    }
+  }

   public void writeObject(Object instance) throws SerializationException {
     if (instance == null) {
=======================================
--- /trunk/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java Mon Jun 7 09:38:44 2010 +++ /trunk/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java Wed Jun 16 15:55:40 2010
@@ -15,8 +15,8 @@
  */
 package com.google.gwt.user.client.rpc.impl;

-import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.UnsafeNativeLong;
 import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
 import com.google.gwt.user.client.rpc.SerializationException;

@@ -84,16 +84,12 @@
   public native int readInt() /*-{
return th...@com.google.gwt.user.client.rpc.impl.clientserializationstreamreader::results[--th...@com.google.gwt.user.client.rpc.impl.clientserializationstreamreader::index];
   }-*/;
-
-  public long readLong() {
-    double low = readDouble();
-    double high = readDouble();
-    if (GWT.isScript()) {
-      return fromDoubles(low, high);
-    } else {
-      return (long) low + (long) high;
-    }
-  }
+
+  @UnsafeNativeLong
+  public native long readLong() /*-{
+ var s = th...@com.google.gwt.user.client.rpc.impl.clientserializationstreamreader::results[--th...@com.google.gwt.user.client.rpc.impl.clientserializationstreamreader::index]; + return @com.google.gwt.lang.LongLib::base64Parse(Ljava/lang/String;)(s);
+  }-*/;

   public native short readShort() /*-{
return th...@com.google.gwt.user.client.rpc.impl.clientserializationstreamreader::results[--th...@com.google.gwt.user.client.rpc.impl.clientserializationstreamreader::index];
=======================================
--- /trunk/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java Mon Jun 7 09:38:44 2010 +++ /trunk/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java Wed Jun 16 15:55:40 2010
@@ -146,15 +146,6 @@
     writePayload(buffer);
     return buffer.toString();
   }
-
-  @Override
-  public void writeLong(long fieldValue) {
-    // Write longs as a pair of doubles for backwards compatibility
-    double[] parts = getAsDoubleArray(fieldValue);
-    assert parts != null && parts.length == 2;
-    writeDouble(parts[0]);
-    writeDouble(parts[1]);
-  }

   /**
    * Appends a token to the end of the buffer.
@@ -168,7 +159,7 @@
   protected String getObjectTypeSignature(Object o) {
     Class<?> clazz = o.getClass();

-    if (o instanceof Enum) {
+    if (o instanceof Enum<?>) {
       Enum<?> e = (Enum<?>) o;
       clazz = e.getDeclaringClass();
     }
=======================================
--- /trunk/user/src/com/google/gwt/user/server/Base64Utils.java Thu Aug 13 14:05:30 2009 +++ /trunk/user/src/com/google/gwt/user/server/Base64Utils.java Wed Jun 16 15:55:40 2010
@@ -34,7 +34,7 @@

   /**
* An array mapping legal base 64 characters [a-zA-Z0-9$_] to their associated 6-bit values. - * The source indicies will be given by 7-bit ASCII characters, thus the array size needs to + * The source indices will be given by 7-bit ASCII characters, thus the array size needs to * be 128 (actually 123 would suffice for the given set of characters in use).
    */
   private static final byte[] base64Values = new byte[128];
=======================================
--- /trunk/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java Wed Dec 2 13:26:39 2009 +++ /trunk/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java Wed Jun 16 15:55:40 2010
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.server.rpc.impl;

+import com.google.gwt.lang.LongLib;
 import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
 import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader;
@@ -415,11 +416,13 @@
       }
       if (idx == 0) {
         throw new IncompatibleRemoteServiceException(
-            "Malformed or old RPC message received - expecting version "
+ "Malformed or old RPC message received - expecting version between "
+                + SERIALIZATION_STREAM_MIN_VERSION + " and "
                 + SERIALIZATION_STREAM_VERSION);
       } else {
         int version = Integer.valueOf(encodedTokens.substring(0, idx));
-        throw new IncompatibleRemoteServiceException("Expecting version "
+ throw new IncompatibleRemoteServiceException("Expecting version between "
+            + SERIALIZATION_STREAM_MIN_VERSION + " and "
             + SERIALIZATION_STREAM_VERSION + " from client, got " + version
             + ".");
       }
@@ -428,8 +431,10 @@
     super.prepareToRead(encodedTokens);

     // Check the RPC version number sent by the client
-    if (getVersion() != SERIALIZATION_STREAM_VERSION) {
-      throw new IncompatibleRemoteServiceException("Expecting version "
+    if (getVersion() < SERIALIZATION_STREAM_MIN_VERSION
+        || getVersion() > SERIALIZATION_STREAM_VERSION) {
+ throw new IncompatibleRemoteServiceException("Expecting version between "
+          + SERIALIZATION_STREAM_MIN_VERSION + " and "
+ SERIALIZATION_STREAM_VERSION + " from client, got " + getVersion()
           + ".");
     }
@@ -490,9 +495,11 @@
   }

   public long readLong() throws SerializationException {
- // Keep synchronized with LongLib. The wire format are the two component
-    // parts of the double in the client code.
-    return (long) readDouble() + (long) readDouble();
+    if (getVersion() == SERIALIZATION_STREAM_MIN_VERSION) {
+      return (long) readDouble() + (long) readDouble();
+    } else {
+      return LongLib.base64Parse(extract());
+    }
   }

   public short readShort() throws SerializationException {
=======================================
--- /trunk/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java Mon Jun 7 09:38:44 2010 +++ /trunk/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java Wed Jun 16 15:55:40 2010
@@ -396,7 +396,7 @@
   private static Class<?> getClassForSerialization(Object instance) {
     assert (instance != null);

-    if (instance instanceof Enum) {
+    if (instance instanceof Enum<?>) {
       Enum<?> e = (Enum<?>) instance;
       return e.getDeclaringClass();
     } else {
@@ -555,14 +555,6 @@

     return stream.toString();
   }
-
-  public void writeLong(long fieldValue) {
-    // Write longs as a pair of doubles for backwards compatibility
-    double[] parts = getAsDoubleArray(fieldValue);
-    assert parts != null && parts.length == 2;
-    writeDouble(parts[0]);
-    writeDouble(parts[1]);
-  }

   @Override
   protected void append(String token) {
=======================================
--- /trunk/user/test/com/google/gwt/user/server/rpc/RPCTest.java Wed Dec 2 13:26:39 2009 +++ /trunk/user/test/com/google/gwt/user/server/rpc/RPCTest.java Wed Jun 16 15:55:40 2010
@@ -68,6 +68,11 @@
   private static interface B {
     void method1();
   }
+
+  @SuppressWarnings("rpc-validation")
+  private static interface D extends RemoteService {
+    long echo(long val);
+  }

   /**
    * Test error message for an out=of-range int value.
@@ -183,7 +188,52 @@
       "1\uffff" + // interface name
       "2\uffff" + // method name
       "0\uffff"; // param count
-
+
+  /**
+   * Call 'D.echo(0xFEDCBA9876543210L);' using V5 long format
+   * (pair of doubles).
+   */
+  private static final String VALID_V5_ENCODED_REQUEST = "" +
+      AbstractSerializationStream.SERIALIZATION_STREAM_MIN_VERSION +
+      RPC_SEPARATOR_CHAR + // version
+      "0" + RPC_SEPARATOR_CHAR + // flags
+      "5" + RPC_SEPARATOR_CHAR + // string table count
+      "moduleBaseUrl" + RPC_SEPARATOR_CHAR + // string table entry #1
+      "whitelistHashCode" + RPC_SEPARATOR_CHAR + // string table entry #2
+      D.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #3
+      "echo" + RPC_SEPARATOR_CHAR + // string table entry #4
+      "J" + RPC_SEPARATOR_CHAR + // string table entry #5
+      "1" + RPC_SEPARATOR_CHAR + // moduleBaseUrl
+      "2" + RPC_SEPARATOR_CHAR + // whitelist hashcode
+      "3" + RPC_SEPARATOR_CHAR + // interface name
+      "4" + RPC_SEPARATOR_CHAR + // method name
+      "1" + RPC_SEPARATOR_CHAR + // param count
+      "5" + RPC_SEPARATOR_CHAR + // 'J' == long param type
+      "1.985229328E9" + RPC_SEPARATOR_CHAR + // low bits of long
+      "-8.1985531201716224E16" + RPC_SEPARATOR_CHAR; // high bits of long
+
+  /**
+   * Call 'D.echo(0xFEDCBA9876543210L);' using V6 long format
+   * (base-64 encoding).
+   */
+  private static final String VALID_V6_ENCODED_REQUEST = "" +
+      AbstractSerializationStream.SERIALIZATION_STREAM_VERSION +
+      RPC_SEPARATOR_CHAR + // version
+      "0" + RPC_SEPARATOR_CHAR + // flags
+      "5" + RPC_SEPARATOR_CHAR + // string table count
+      "moduleBaseUrl" + RPC_SEPARATOR_CHAR + // string table entry #1
+      "whitelistHashCode" + RPC_SEPARATOR_CHAR + // string table entry #2
+      D.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #3
+      "echo" + RPC_SEPARATOR_CHAR + // string table entry #4
+      "J" + RPC_SEPARATOR_CHAR + // string table entry #5
+      "1" + RPC_SEPARATOR_CHAR + // moduleBaseUrl
+      "2" + RPC_SEPARATOR_CHAR + // whitelist hashcode
+      "3" + RPC_SEPARATOR_CHAR + // interface name
+      "4" + RPC_SEPARATOR_CHAR + // method name
+      "1" + RPC_SEPARATOR_CHAR + // param count
+      "5" + RPC_SEPARATOR_CHAR + // 'J' == long param type
+      "P7cuph2VDIQ" + RPC_SEPARATOR_CHAR; // long in base-64 encoding
+
   /**
    * Tests that out-of-range or other illegal integer values generated
    * by client-side serialization get a nested exception with a reasonable
@@ -390,6 +440,26 @@
       // should get here
     }
   }
+
+  public void testDecodeV5Long() {
+    try {
+      RPCRequest request = RPC.decodeRequest(VALID_V5_ENCODED_REQUEST,
+          D.class, null);
+      assertEquals(0xFEDCBA9876543210L, request.getParameters()[0]);
+    } catch (IncompatibleRemoteServiceException e) {
+      fail();
+    }
+  }
+
+  public void testDecodeV6Long() {
+    try {
+      RPCRequest request = RPC.decodeRequest(VALID_V6_ENCODED_REQUEST,
+          D.class, null);
+      assertEquals(0xFEDCBA9876543210L, request.getParameters()[0]);
+    } catch (IncompatibleRemoteServiceException e) {
+      fail();
+    }
+  }

public void testElision() throws SecurityException, SerializationException,
       NoSuchMethodException {

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to