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: jat@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8269 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/super/com/google/gwt/lang/LongLib.java b/dev/core/super/com/google/gwt/lang/LongLib.java
index 1afcfa8..edffae2 100644
--- a/dev/core/super/com/google/gwt/lang/LongLib.java
+++ b/dev/core/super/com/google/gwt/lang/LongLib.java
@@ -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);
@@ -44,10 +46,65 @@
}
/**
+ * 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.
*/
diff --git a/dev/core/test/com/google/gwt/lang/LongLibTest.java b/dev/core/test/com/google/gwt/lang/LongLibTest.java
index dbbe9bf..7591238 100644
--- a/dev/core/test/com/google/gwt/lang/LongLibTest.java
+++ b/dev/core/test/com/google/gwt/lang/LongLibTest.java
@@ -406,6 +406,41 @@
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);
diff --git a/dev/core/test/com/google/gwt/lang/LongLibTestBase.java b/dev/core/test/com/google/gwt/lang/LongLibTestBase.java
index 9f2052a..1e85690 100644
--- a/dev/core/test/com/google/gwt/lang/LongLibTestBase.java
+++ b/dev/core/test/com/google/gwt/lang/LongLibTestBase.java
@@ -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="
diff --git a/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java b/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java
index 39e61eb..83586a0 100644
--- a/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java
+++ b/user/src/com/google/gwt/rpc/client/impl/CommandClientSerializationStreamReader.java
@@ -65,7 +65,6 @@
@UnsafeNativeLong
private static native long readLong0(JavaScriptObject obj, int idx) /*-{
- // TODO (rice): use backwards-compatible wire format?
return obj[idx];
}-*/;
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java
index fec3660..cc9d86b 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java
@@ -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.
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
index 769ea02..c77e46b 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
@@ -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) {
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
index f1bb0e1..eeb5f4c 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
@@ -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 this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::results[--this.@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 = this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::results[--this.@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 this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::results[--this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader::index];
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
index 584d695..19ac418 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
@@ -147,15 +147,6 @@
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();
}
diff --git a/user/src/com/google/gwt/user/server/Base64Utils.java b/user/src/com/google/gwt/user/server/Base64Utils.java
index daf5065..1909fef 100644
--- a/user/src/com/google/gwt/user/server/Base64Utils.java
+++ b/user/src/com/google/gwt/user/server/Base64Utils.java
@@ -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];
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
index 55274a6..0be3812 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
@@ -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 {
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
index 3c21899..9710012 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
@@ -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) {
diff --git a/user/test/com/google/gwt/user/server/rpc/RPCTest.java b/user/test/com/google/gwt/user/server/rpc/RPCTest.java
index 47a7b2b..bc63ab6 100644
--- a/user/test/com/google/gwt/user/server/rpc/RPCTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/RPCTest.java
@@ -69,6 +69,11 @@
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
@@ -391,6 +441,26 @@
}
}
+ 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 {
class TestPolicy extends SerializationPolicy implements TypeNameObfuscator {