Addressing a problem with GWT RPC type checking failing to correctly
detect the expected type when the object that is passed as an argument
is actually a base class of the expected type. In such cases, the
previous, wrong behavior was to use the instance's type variables to
resolve the expected type. The fix is to resolve the types using the
instance "cast" to the expected type.
This changes the method signatures for custom serializers. Users will
need to modify their code if they have already produced server-side
custom serializers.
Review by: jat@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10582 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/server/rpc/RPC.java b/user/src/com/google/gwt/user/server/rpc/RPC.java
index 5a9ff5f..6a1bd5b 100644
--- a/user/src/com/google/gwt/user/server/rpc/RPC.java
+++ b/user/src/com/google/gwt/user/server/rpc/RPC.java
@@ -300,7 +300,7 @@
// RPC stream. For stronger message verification, get the parameterized
// types from the method declaration.
Type[] methodParameterTypes = method.getGenericParameterTypes();
- DequeMap<Type, Type> resolvedTypes = new DequeMap<Type, Type>();
+ DequeMap<TypeVariable<?>, Type> resolvedTypes = new DequeMap<TypeVariable<?>, Type>();
TypeVariable<Method>[] methodTypes = method.getTypeParameters();
for (TypeVariable<Method> methodType : methodTypes) {
@@ -309,9 +309,8 @@
Object[] parameterValues = new Object[parameterTypes.length];
for (int i = 0; i < parameterValues.length; i++) {
- parameterValues[i] =
- streamReader.deserializeValue(parameterTypes[i], methodParameterTypes[i],
- resolvedTypes);
+ parameterValues[i] = streamReader.deserializeValue(parameterTypes[i],
+ methodParameterTypes[i], resolvedTypes);
}
return new RPCRequest(method, parameterValues, rpcToken, serializationPolicy, streamReader
diff --git a/user/src/com/google/gwt/user/server/rpc/ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/ServerCustomFieldSerializer.java
index 3376e54..2641df4 100644
--- a/user/src/com/google/gwt/user/server/rpc/ServerCustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/server/rpc/ServerCustomFieldSerializer.java
@@ -22,6 +22,7 @@
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
/**
* An interface that may be implemented by server-side class-based custom field
@@ -37,18 +38,35 @@
* Deserializes the content of the object from the
* {@link ServerSerializationStreamReader}, with type checking.
*
+ * The calling code has verified that the instance this method is
+ * deserializing is of the correct type for the RPC call. However, is has not
+ * verified the objects that this deserializer will read. It is this method's
+ * responsibility to verify the types of objects that it reads. Failure to
+ * do so leaves the server vulnerable to an attacker who replaces
+ * deserialized data in the RPC message with data that takes an exponential
+ * time to deserialize or otherwise causes problems.
+ *
+ * In practice, any call to ServerSerilizationStreamReader.readObject() should
+ * use the type checking version, passing in the expected type of the object
+ * to be read. For classes that deserialize objects of generic types, the
+ * expectedParameterTypes array provides the type bound to each type
+ * generic parameter defined by the instance. See the built-in GWT
+ * server custom field serializers for examples.
+ *
* @param streamReader the {@link ServerSerializationStreamReader} to read the
* object's content from
* @param instance the object instance to deserialize
- * @param instanceClass the class of the instance for type checking purposes
+ * @param expectedParameterTypes the types we expect for any generic
+ * parameters used by this class, in the order in which they
+ * appear in the instance.getTypeParameters()
* @param resolvedTypes map from generic types to actual types
*
* @throws SerializationException if the deserialization operation is not
* successful
*/
public abstract void deserializeInstance(ServerSerializationStreamReader streamReader,
- T instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException;
+ T instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException;
/**
* Instantiates an object from the {@link ServerSerializationStreamReader},
@@ -80,9 +98,26 @@
* {@link CustomFieldSerializer#hasCustomInstantiateInstance()} method must
* return <code>true</code> in order for the framework to know to call it.
*
+ * The calling code has verified that the instance this method is
+ * instantiating is of the correct type for the RPC call. However, is has not
+ * verified the objects that this instantiator will read. It is this method's
+ * responsibility to verify the types of objects that it reads. Failure to
+ * do so leaves the server vulnerable to an attacker who replaces
+ * deserialized data in the RPC message with data that takes an exponential
+ * time to instantiate or otherwise causes problems.
+ *
+ * In practice, any call to ServerSerilizationStreamReader.readObject() should
+ * use the type checking version, passing in the expected type of the object
+ * to be read. For classes that instantiate objects of generic types, the
+ * expectedParameterTypes array provides the type bound to each type
+ * generic parameter defined by the instance. See the built-in GWT
+ * server custom field serializers for examples.
+ *
* @param streamReader the {@link ServerSerializationStreamReader} to read the
* object's content from
- * @param instanceClass the class of the instance for type checking purposes
+ * @param expectedParameterTypes the types we expect for any generic
+ * parameters used by this class, in the order returned by
+ * instance.getTypeParameters()
* @param resolvedTypes map from generic types to actual types
*
* @return an object that has been loaded from the
@@ -91,11 +126,9 @@
* @throws SerializationException if the instantiation operation is not
* successful
*/
- @SuppressWarnings("unused")
public T instantiateInstance(ServerSerializationStreamReader streamReader,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
+ Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
return super.instantiateInstance(streamReader);
}
-
}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/lang/Object_Array_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/lang/Object_Array_ServerCustomFieldSerializer.java
index 74ec8e3..45845f7 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/lang/Object_Array_ServerCustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/lang/Object_Array_ServerCustomFieldSerializer.java
@@ -21,10 +21,10 @@
import com.google.gwt.user.client.rpc.core.java.lang.Object_Array_CustomFieldSerializer;
import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
import com.google.gwt.user.server.rpc.impl.DequeMap;
-import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
/**
* Server-side Custom Serializer for arrays of {@link java.lang.Object}.
@@ -33,12 +33,10 @@
extends ServerCustomFieldSerializer<Object[]> {
public static void deserialize(ServerSerializationStreamReader streamReader,
- Object[] instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
- Type componentType =
- SerializabilityUtil.findInstanceParameters(instanceClass, resolvedTypes)[0];
+ Object[] instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
for (int itemIndex = 0; itemIndex < instance.length; ++itemIndex) {
- instance[itemIndex] = streamReader.readObject(componentType, resolvedTypes);
+ instance[itemIndex] = streamReader.readObject(expectedParameterTypes[0], resolvedTypes);
}
}
@@ -50,9 +48,9 @@
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader,
- Object[] instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
- deserialize(streamReader, instance, instanceClass, resolvedTypes);
+ Object[] instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
+ deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
}
@Override
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/ArrayList_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/ArrayList_ServerCustomFieldSerializer.java
index 32b968f..c150513 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/ArrayList_ServerCustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/ArrayList_ServerCustomFieldSerializer.java
@@ -24,6 +24,7 @@
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
/**
@@ -34,9 +35,10 @@
ServerCustomFieldSerializer<ArrayList> {
public static void deserialize(ServerSerializationStreamReader streamReader, ArrayList instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
- resolvedTypes);
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance,
+ expectedParameterTypes, resolvedTypes);
}
@Override
@@ -47,8 +49,9 @@
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader, ArrayList instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- deserialize(streamReader, instance, instanceClass, resolvedTypes);
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
}
@Override
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/Arrays.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/Arrays.java
index ba6149b..ff647f7 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/Arrays.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/Arrays.java
@@ -25,6 +25,7 @@
import java.lang.reflect.Array;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.List;
/**
@@ -35,6 +36,7 @@
/**
* Server-side Custom field serializer for {@link java.util.Arrays.ArrayList}.
*/
+ @SuppressWarnings("rawtypes")
public static final class ArrayList_ServerCustomFieldSerializer extends
ServerCustomFieldSerializer<List> {
@@ -56,17 +58,10 @@
* type.
*/
public static List<?> instantiate(ServerSerializationStreamReader streamReader,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes)
throws SerializationException {
-
- Type[] actualTypes =
- SerializabilityUtil.findInstanceParameters(instanceClass, resolvedTypes);
- if (actualTypes == null || actualTypes.length < 1) {
- return com.google.gwt.user.client.rpc.core.java.util.Arrays.ArrayList_CustomFieldSerializer
- .instantiate(streamReader);
- }
-
- Class<?> componentClass = SerializabilityUtil.getClassFromType(actualTypes[0], resolvedTypes);
+ Class<?> componentClass = SerializabilityUtil.getClassFromType(expectedParameterTypes[0],
+ resolvedTypes);
if (componentClass == null) {
return com.google.gwt.user.client.rpc.core.java.util.Arrays.ArrayList_CustomFieldSerializer
.instantiate(streamReader);
@@ -84,9 +79,10 @@
.deserialize(streamReader, instance);
}
+ @SuppressWarnings("unused")
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader, List instance,
- Class<?> instanceClass, DequeMap<Type, Type> actualParameterTypes)
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> actualParameterTypes)
throws SerializationException {
// Handled in instantiateInstance.
}
@@ -105,8 +101,9 @@
@Override
public List instantiateInstance(ServerSerializationStreamReader streamReader,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- return instantiate(streamReader, instanceClass, resolvedTypes);
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ return instantiate(streamReader, expectedParameterTypes, resolvedTypes);
}
@Override
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/Collection_ServerCustomFieldSerializerBase.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/Collection_ServerCustomFieldSerializerBase.java
index 962cf43..442da92 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/Collection_ServerCustomFieldSerializerBase.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/Collection_ServerCustomFieldSerializerBase.java
@@ -16,12 +16,11 @@
package com.google.gwt.user.server.rpc.core.java.util;
import com.google.gwt.user.client.rpc.SerializationException;
-import com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase;
import com.google.gwt.user.server.rpc.impl.DequeMap;
-import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.Collection;
/**
@@ -32,16 +31,11 @@
@SuppressWarnings({"rawtypes", "unchecked"})
public static void deserialize(ServerSerializationStreamReader streamReader, Collection instance,
- Class<?> actualClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- Type[] actualTypes = SerializabilityUtil.findInstanceParameters(actualClass, resolvedTypes);
- if (actualTypes == null || actualTypes.length < 1) {
- Collection_CustomFieldSerializerBase.deserialize(streamReader, instance);
- return;
- }
-
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
int size = streamReader.readInt();
for (int i = 0; i < size; ++i) {
- Object obj = streamReader.readObject(actualTypes[0], resolvedTypes);
+ Object obj = streamReader.readObject(expectedParameterTypes[0], resolvedTypes);
instance.add(obj);
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/Collections.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/Collections.java
index aa54331..bb6bc09 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/Collections.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/Collections.java
@@ -20,10 +20,10 @@
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.google.gwt.user.server.rpc.ServerCustomFieldSerializer;
import com.google.gwt.user.server.rpc.impl.DequeMap;
-import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.List;
/**
@@ -43,15 +43,9 @@
}
public static List instantiate(ServerSerializationStreamReader streamReader,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes)
throws SerializationException {
- Type[] actualTypes =
- SerializabilityUtil.findInstanceParameters(instanceClass, resolvedTypes);
- if (actualTypes == null || actualTypes.length < 1) {
- return com.google.gwt.user.client.rpc.core.java.util.Collections.SingletonList_CustomFieldSerializer.instantiate(streamReader);
- }
-
- return java.util.Collections.singletonList(streamReader.readObject(actualTypes[0],
+ return java.util.Collections.singletonList(streamReader.readObject(expectedParameterTypes[0],
resolvedTypes));
}
@@ -65,7 +59,7 @@
@SuppressWarnings("unused")
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader, List instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes)
throws SerializationException {
// Handled in instantiate.
}
@@ -83,9 +77,9 @@
@Override
public List instantiateInstance(ServerSerializationStreamReader streamReader,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes)
throws SerializationException {
- return instantiate(streamReader, instanceClass, resolvedTypes);
+ return instantiate(streamReader, expectedParameterTypes, resolvedTypes);
}
@Override
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/HashMap_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/HashMap_ServerCustomFieldSerializer.java
index b4d6bc6..98cd774 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/HashMap_ServerCustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/HashMap_ServerCustomFieldSerializer.java
@@ -24,6 +24,7 @@
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.HashMap;
/**
@@ -34,10 +35,10 @@
ServerCustomFieldSerializer<HashMap> {
public static void deserialize(ServerSerializationStreamReader streamReader,
- HashMap instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
- Map_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
- resolvedTypes);
+ HashMap instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
+ Map_ServerCustomFieldSerializerBase.deserialize(streamReader, instance,
+ expectedParameterTypes, resolvedTypes);
}
@Override
@@ -48,9 +49,9 @@
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader,
- HashMap instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
- deserialize(streamReader, instance, instanceClass, resolvedTypes);
+ HashMap instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
+ deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
}
@Override
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/HashSet_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/HashSet_ServerCustomFieldSerializer.java
index d20210f..abc7f91 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/HashSet_ServerCustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/HashSet_ServerCustomFieldSerializer.java
@@ -24,6 +24,7 @@
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.HashSet;
/**
@@ -34,10 +35,10 @@
ServerCustomFieldSerializer<HashSet> {
public static void deserialize(ServerSerializationStreamReader streamReader,
- HashSet instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
- Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
- resolvedTypes);
+ HashSet instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
+ Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance,
+ expectedParameterTypes, resolvedTypes);
}
@Override
@@ -48,9 +49,9 @@
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader,
- HashSet instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
- deserialize(streamReader, instance, instanceClass, resolvedTypes);
+ HashSet instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
+ deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
}
@Override
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/IdentityHashMap_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/IdentityHashMap_ServerCustomFieldSerializer.java
index c2818ac..324d3fd 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/IdentityHashMap_ServerCustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/IdentityHashMap_ServerCustomFieldSerializer.java
@@ -24,6 +24,7 @@
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.IdentityHashMap;
/**
@@ -34,9 +35,9 @@
ServerCustomFieldSerializer<IdentityHashMap> {
public static void deserialize(ServerSerializationStreamReader streamReader,
- IdentityHashMap instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
- Map_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
+ IdentityHashMap instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
+ Map_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, expectedParameterTypes,
resolvedTypes);
}
@@ -48,9 +49,9 @@
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader,
- IdentityHashMap instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
- deserialize(streamReader, instance, instanceClass, resolvedTypes);
+ IdentityHashMap instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
+ deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
}
@Override
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedHashMap_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedHashMap_ServerCustomFieldSerializer.java
index 9ab2f8b..70a7be5 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedHashMap_ServerCustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedHashMap_ServerCustomFieldSerializer.java
@@ -24,6 +24,7 @@
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.LinkedHashMap;
/**
@@ -35,9 +36,9 @@
ServerCustomFieldSerializer<LinkedHashMap> {
public static void deserialize(ServerSerializationStreamReader streamReader,
- LinkedHashMap instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
- Map_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
+ LinkedHashMap instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
+ Map_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, expectedParameterTypes,
resolvedTypes);
}
@@ -49,9 +50,9 @@
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader,
- LinkedHashMap instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
- deserialize(streamReader, instance, instanceClass, resolvedTypes);
+ LinkedHashMap instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
+ deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
}
@Override
@@ -67,8 +68,8 @@
@Override
public LinkedHashMap instantiateInstance(ServerSerializationStreamReader streamReader,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
return LinkedHashMap_CustomFieldSerializer.instantiate(streamReader);
}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedList_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedList_ServerCustomFieldSerializer.java
index 7a318b9..709aea1 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedList_ServerCustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/LinkedList_ServerCustomFieldSerializer.java
@@ -24,6 +24,7 @@
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.LinkedList;
/**
@@ -34,9 +35,10 @@
ServerCustomFieldSerializer<LinkedList> {
public static void deserialize(ServerSerializationStreamReader streamReader, LinkedList instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
- resolvedTypes);
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance,
+ expectedParameterTypes, resolvedTypes);
}
@Override
@@ -47,9 +49,9 @@
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader,
- LinkedList instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
- deserialize(streamReader, instance, instanceClass, resolvedTypes);
+ LinkedList instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
+ deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
}
@Override
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/Map_ServerCustomFieldSerializerBase.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/Map_ServerCustomFieldSerializerBase.java
index 2fe7368..dcb32dc 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/Map_ServerCustomFieldSerializerBase.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/Map_ServerCustomFieldSerializerBase.java
@@ -16,12 +16,11 @@
package com.google.gwt.user.server.rpc.core.java.util;
import com.google.gwt.user.client.rpc.SerializationException;
-import com.google.gwt.user.client.rpc.core.java.util.Map_CustomFieldSerializerBase;
import com.google.gwt.user.server.rpc.impl.DequeMap;
-import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.Map;
/**
@@ -31,19 +30,12 @@
@SuppressWarnings({"rawtypes", "unchecked"})
public static void deserialize(ServerSerializationStreamReader streamReader, Map instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes)
throws SerializationException {
- Type[] actualTypes =
- SerializabilityUtil.findInstanceParameters(instanceClass, resolvedTypes);
- if (actualTypes == null || actualTypes.length < 2) {
- Map_CustomFieldSerializerBase.deserialize(streamReader, instance);
- return;
- }
-
int size = streamReader.readInt();
for (int i = 0; i < size; ++i) {
- Object key = streamReader.readObject(actualTypes[0], resolvedTypes);
- Object value = streamReader.readObject(actualTypes[1], resolvedTypes);
+ Object key = streamReader.readObject(expectedParameterTypes[0], resolvedTypes);
+ Object value = streamReader.readObject(expectedParameterTypes[1], resolvedTypes);
instance.put(key, value);
}
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeMap_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeMap_ServerCustomFieldSerializer.java
index 3bd896e..1b657b8 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeMap_ServerCustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeMap_ServerCustomFieldSerializer.java
@@ -24,6 +24,7 @@
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.Comparator;
import java.util.TreeMap;
@@ -34,14 +35,16 @@
public class TreeMap_ServerCustomFieldSerializer extends ServerCustomFieldSerializer<TreeMap> {
public static void deserialize(ServerSerializationStreamReader streamReader, TreeMap instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- Map_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ Map_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, expectedParameterTypes,
resolvedTypes);
}
@SuppressWarnings("unused")
public static TreeMap instantiate(ServerSerializationStreamReader streamReader,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
return new TreeMap((Comparator) streamReader.readObject(Comparator.class, resolvedTypes));
}
@@ -53,8 +56,9 @@
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader, TreeMap instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- deserialize(streamReader, instance, instanceClass, resolvedTypes);
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
}
@Override
@@ -70,9 +74,9 @@
@Override
public TreeMap instantiateInstance(ServerSerializationStreamReader streamReader,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
- return instantiate(streamReader, instanceClass, resolvedTypes);
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ return instantiate(streamReader, expectedParameterTypes, resolvedTypes);
}
@Override
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeSet_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeSet_ServerCustomFieldSerializer.java
index 311ec91..88f398c 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeSet_ServerCustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/TreeSet_ServerCustomFieldSerializer.java
@@ -25,6 +25,7 @@
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.Comparator;
import java.util.TreeSet;
@@ -35,14 +36,16 @@
public class TreeSet_ServerCustomFieldSerializer extends ServerCustomFieldSerializer<TreeSet> {
public static void deserialize(ServerSerializationStreamReader streamReader, TreeSet instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
- resolvedTypes);
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance,
+ expectedParameterTypes, resolvedTypes);
}
@SuppressWarnings({"cast", "unused"})
public static TreeSet instantiate(ServerSerializationStreamReader streamReader,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
return new TreeSet((Comparator) streamReader.readObject((Type) Comparator.class,
resolvedTypes));
}
@@ -55,8 +58,9 @@
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader, TreeSet instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- deserialize(streamReader, instance, instanceClass, resolvedTypes);
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
}
@Override
@@ -72,8 +76,9 @@
@Override
public TreeSet instantiateInstance(ServerSerializationStreamReader streamReader,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- return instantiate(streamReader, instanceClass, resolvedTypes);
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ return instantiate(streamReader, expectedParameterTypes, resolvedTypes);
}
@Override
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/Vector_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/Vector_ServerCustomFieldSerializer.java
index e1e460b..a299b63 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/Vector_ServerCustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/Vector_ServerCustomFieldSerializer.java
@@ -24,6 +24,7 @@
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.Vector;
/**
@@ -33,9 +34,10 @@
public final class Vector_ServerCustomFieldSerializer extends ServerCustomFieldSerializer<Vector> {
public static void deserialize(ServerSerializationStreamReader streamReader, Vector instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance, instanceClass,
- resolvedTypes);
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ Collection_ServerCustomFieldSerializerBase.deserialize(streamReader, instance,
+ expectedParameterTypes, resolvedTypes);
}
@Override
@@ -46,8 +48,9 @@
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader, Vector instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- deserialize(streamReader, instance, instanceClass, resolvedTypes);
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
}
@Override
diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/logging/LogRecord_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/logging/LogRecord_ServerCustomFieldSerializer.java
index e93815f..26d6113 100644
--- a/user/src/com/google/gwt/user/server/rpc/core/java/util/logging/LogRecord_ServerCustomFieldSerializer.java
+++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/logging/LogRecord_ServerCustomFieldSerializer.java
@@ -26,6 +26,7 @@
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.logging.LogRecord;
/**
@@ -34,7 +35,8 @@
public class LogRecord_ServerCustomFieldSerializer extends ServerCustomFieldSerializer<LogRecord> {
@SuppressWarnings("unused")
public static void deserialize(ServerSerializationStreamReader streamReader, LogRecord instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
String loggerName = streamReader.readString();
Long millis = streamReader.readLong();
Object throwable = streamReader.readObject(SerializableThrowable.class, resolvedTypes);
@@ -54,8 +56,9 @@
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader, LogRecord instance,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
- deserialize(streamReader, instance, instanceClass, resolvedTypes);
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
+ deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
}
@Override
@@ -64,8 +67,9 @@
}
@Override
- public LogRecord instantiateInstance(ServerSerializationStreamReader reader, Class<?> instanceClass,
- DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ public LogRecord instantiateInstance(ServerSerializationStreamReader reader,
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
return LogRecord_CustomFieldSerializer.instantiate(reader);
}
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java b/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java
index a66c332..34255f0 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/SerializabilityUtil.java
@@ -53,6 +53,7 @@
* Comparator used to sort fields.
*/
public static final Comparator<Field> FIELD_COMPARATOR = new Comparator<Field>() {
+ @Override
public int compare(Field f1, Field f2) {
return f1.getName().compareTo(f2.getName());
}
@@ -131,8 +132,8 @@
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader,
- Object instance, Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
- throws SerializationException {
+ Object instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
throw new SerializationException("This should never be called.");
}
@@ -219,10 +220,12 @@
encodedSerializedInstanceReference
.split(SerializedInstanceReference.SERIALIZED_REFERENCE_SEPARATOR);
return new SerializedInstanceReference() {
+ @Override
public String getName() {
return components.length > 0 ? components[0] : "";
}
+ @Override
public String getSignature() {
return components.length > 1 ? components[1] : "";
}
@@ -242,70 +245,84 @@
* @param resolvedTypes A map of generic types to actual types.
* @return The actual type, which may be of any subclass of Type.
*/
- public static Type findActualType(Type genericType, DequeMap<Type, Type> resolvedTypes) {
+ public static Type findActualType(Type genericType,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) {
Type result = genericType;
// Look for things that TypeVariables are mapped to, but stop if mapped
// to itself. We map a TypeVariable to itself when we wish to explicitly
// mark it as unmapped.
while (result instanceof TypeVariable<?> &&
- resolvedTypes.get(result) != result &&
- resolvedTypes.get(result) != null) {
- result = resolvedTypes.get(result);
+ resolvedTypes.get((TypeVariable<?>) result) != result &&
+ resolvedTypes.get((TypeVariable<?>) result) != null) {
+ result = resolvedTypes.get((TypeVariable<?>) result);
}
return result;
}
/**
- * Attempt to find the actual types for the generic parameters of an instance,
- * given the types we have resolved from the method signature.
+ * Determine the expected types for any instance type parameters.
*
- * @param instanceClass The instance for which we want actual generic
- * parameter types.
+ * This method also determines whether or not the instance can be assigned to
+ * the expected type. We combine the tasks because they require traversing the
+ * same data structures.
+ *
+ * @param instanceClass The instance for which we want generic parameter types
+ * @param expectedType The type we are expecting this instance to be
* @param resolvedTypes The types that have been resolved to actual values
- * @return An array of types representing the actual declared types for the
- * parameters of the instanceClass. Some may be of type TypeVariable,
- * in which case they could not be resolved.
+ * @return The expected types of the instance class' parameters. If null, the
+ * instance class is not assignable to the expected type.
*/
- public static Type[] findInstanceParameters(Class<?> instanceClass,
- DequeMap<Type, Type> resolvedTypes) {
+ public static Type[] findExpectedParameterTypes(Class<?> instanceClass,
+ Type expectedType, DequeMap<TypeVariable<?>, Type> resolvedTypes) {
+ // Make a copy of the instance parameters from its class
TypeVariable<?>[] instanceTypes = instanceClass.getTypeParameters();
- Type[] foundParameters = Arrays.copyOf(instanceTypes, instanceTypes.length, Type[].class);
+ Type[] expectedParameterTypes = Arrays.copyOf(instanceTypes, instanceTypes.length,
+ Type[].class);
- if (resolvedTypes == null) {
- return foundParameters;
- }
-
- for (int i = 0; i < foundParameters.length; ++i) {
- // Check if we already know about this type.
- Type capturedType = findActualType(foundParameters[i], resolvedTypes);
- if (capturedType != foundParameters[i]) {
- foundParameters[i] = capturedType;
- continue;
+ // Determine the type we are really expecting, to the best of our knowledge
+ if (expectedType == null) {
+ // Match up parameters assuming that the instance class is the best match
+ // for the expected class, which we can only do if we have resolved types.
+ if (resolvedTypes != null) {
+ findInstanceParameters(instanceClass, resolvedTypes, expectedParameterTypes);
}
-
- if (instanceClass.getGenericSuperclass() != null) {
- Type superParameter =
- findInstanceParameter(instanceTypes[i], instanceClass.getGenericSuperclass(),
- resolvedTypes);
- if (!(superParameter instanceof TypeVariable)) {
- foundParameters[i] = superParameter;
- continue;
- }
+
+ // With no expected type, the instance is assignable and we fall through
+ } else {
+ // First determine what type we are really expecting. The type may still
+ // be a TypeVariable<?> at this time when we are deserializing class
+ // fields or components of another data structure.
+ Type actualType = findActualType(expectedType, resolvedTypes);
+
+ // Try to match the instanceClass to the expected type, updating the
+ // expectedParameterTypes so that we can track them back to the resolved
+ // types once we determine what class to treat the instance as. In
+ // this method, only type information from the instance is used to resolve
+ // types because the information in the resolved types map is only
+ // relevant to the expected type(s). We still pass in the resolvedTypes
+ // because we may need to resolve expected types.
+ // Note that certain expected types may require the instance to extend
+ // or implement multiple classes or interfaces, so we may capture multiple
+ // types here.
+ Set<Class<?>> expectedInstanceClasses = new HashSet<Class<?>>();
+ if (!findExpectedInstanceClass(instanceClass, actualType,
+ resolvedTypes, expectedInstanceClasses, expectedParameterTypes)) {
+ // If we could not match the instance to the expected, it is not assignable
+ // and we are done.
+ return null;
}
-
- Type[] interfaceTypes = instanceClass.getGenericInterfaces();
- for (Type interfaceType : interfaceTypes) {
- Type interfaceParameter =
- findInstanceParameter(instanceTypes[i], interfaceType, resolvedTypes);
- if (!(interfaceParameter instanceof TypeVariable)) {
- foundParameters[i] = interfaceParameter;
- break;
+
+ // Now that we know what class the instance should be,
+ // get any remaining parameters using resolved types.
+ if (resolvedTypes != null) {
+ for (Class<?> expectedClass : expectedInstanceClasses) {
+ findInstanceParameters(expectedClass, resolvedTypes, expectedParameterTypes);
}
}
}
- return foundParameters;
+ return expectedParameterTypes;
}
/**
@@ -314,7 +331,8 @@
* @param type The type of interest
* @return The Class that type represents
*/
- public static Class<?> getClassFromType(Type type, DequeMap<Type, Type> resolvedTypes) {
+ public static Class<?> getClassFromType(Type type,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) {
Type actualType = findActualType(type, resolvedTypes);
if (actualType instanceof Class) {
return (Class<?>) actualType;
@@ -426,7 +444,7 @@
* @param methodType The type we wish to assign this instance to
* @param resolvedTypes The types that have been resolved to actual values
*/
- public static void releaseTypes(Type methodType, DequeMap<Type, Type> resolvedTypes) {
+ public static void releaseTypes(Type methodType, DequeMap<TypeVariable<?>, Type> resolvedTypes) {
SerializabilityUtil.resolveTypesWorker(methodType, resolvedTypes, false);
}
@@ -441,61 +459,10 @@
* @param methodType The type we wish to assign this instance to
* @param resolvedTypes The types that have been resolved to actual values
*/
- public static void resolveTypes(Type methodType, DequeMap<Type, Type> resolvedTypes) {
+ public static void resolveTypes(Type methodType, DequeMap<TypeVariable<?>, Type> resolvedTypes) {
SerializabilityUtil.resolveTypesWorker(methodType, resolvedTypes, true);
}
- /**
- * Determine whether or not an instance can be assigned to the type expected
- * by a method.
- *
- * @param instanceClass The instance to check
- * @param expectedType The type we wish to assign this instance to
- * @param resolvedTypes The types that have been resolved to actual values
- *
- * @return True if the instance may be assigned to the type; otherwise false.
- */
- static boolean isInstanceAssignableToType(Class<?> instanceClass, Type expectedType,
- DequeMap<Type, Type> resolvedTypes) {
- if (expectedType == null) {
- return true;
- }
- Type actualType = findActualType(expectedType, resolvedTypes);
-
- if (actualType instanceof TypeVariable) {
- Type[] typeVariableBounds = ((TypeVariable<?>) actualType).getBounds();
- for (Type boundType : typeVariableBounds) {
- if (!isInstanceAssignableToType(instanceClass, boundType, resolvedTypes)) {
- return false;
- }
- }
- return true;
- }
- if (actualType instanceof ParameterizedType) {
- ParameterizedType paramType = (ParameterizedType) actualType;
- if (!((Class<?>) paramType.getRawType()).isAssignableFrom(instanceClass)) {
- return false;
- }
- } else if (actualType instanceof GenericArrayType) {
- if (!(instanceClass.isArray())) {
- return false;
- }
- Type expectedComponentType = ((GenericArrayType) actualType).getGenericComponentType();
- Class<?> instanceComponentClass = instanceClass.getComponentType();
- return isInstanceAssignableToType(instanceComponentClass, expectedComponentType,
- resolvedTypes);
- } else if (actualType instanceof WildcardType) {
- if (!isInstanceAssignableToWildcard(instanceClass, (WildcardType) actualType,
- resolvedTypes)) {
- return false;
- }
- } else if (!((Class<?>) actualType).isAssignableFrom(instanceClass)) {
- return false;
- }
-
- return true;
- }
-
static boolean isNotStaticTransientOrFinal(Field field) {
/*
* Only serialize fields that are not static, transient (including
@@ -634,7 +601,157 @@
}
}
- /**
+ private static boolean findExpectedInstanceClass(Class<?> instanceClass,
+ Type expectedType, DequeMap<TypeVariable<?>, Type> resolvedTypes,
+ Set<Class<?>> expectedInstanceClasses, Type[] expectedParameterTypes) {
+ // Check for an exact match for the instance class and the expected type.
+ // If found, we can return. For expected types that allow one of several
+ // possible class to match (wildcards, type variables) see if this
+ // instance matches any of the allowed types.
+ if (expectedType instanceof TypeVariable) {
+ // Every bound must have a match
+ Type[] typeVariableBounds = ((TypeVariable<?>) expectedType).getBounds();
+ for (Type boundType : typeVariableBounds) {
+ if (!findExpectedInstanceClass(instanceClass, boundType, resolvedTypes,
+ expectedInstanceClasses, expectedParameterTypes)) {
+ return false;
+ }
+ }
+ return true;
+ } else if (expectedType instanceof ParameterizedType) {
+ ParameterizedType paramType = (ParameterizedType) expectedType;
+ if (paramType.getRawType() == instanceClass) {
+ expectedInstanceClasses.add(instanceClass);
+ return true;
+ }
+ } else if (expectedType instanceof GenericArrayType) {
+ if (instanceClass.isArray()) {
+ expectedInstanceClasses.add(instanceClass);
+ return true;
+ }
+ } else if (expectedType instanceof WildcardType) {
+ WildcardType wildcardType = (WildcardType) expectedType;
+
+ Type[] lowerBounds = wildcardType.getLowerBounds();
+ for (Type type : lowerBounds) {
+ /* Require instance to be a superclass of type, or type itself. */
+ Class<?> boundClass = getClassFromType(type, resolvedTypes);
+
+ while (boundClass != null) {
+ if (instanceClass == boundClass) {
+ expectedInstanceClasses.add(boundClass);
+ break;
+ }
+ boundClass = boundClass.getSuperclass();
+ }
+
+ // We fail if the class does not meet any bound, as we should.
+ if (boundClass == null) {
+ return false;
+ }
+ }
+
+ Type[] upperBounds = wildcardType.getUpperBounds();
+ for (Type type : upperBounds) {
+ /* Require instanceClass to be a subclass of type. */
+ if (!findExpectedInstanceClass(instanceClass, type, resolvedTypes,
+ expectedInstanceClasses, expectedParameterTypes)) {
+ return false;
+ }
+ }
+ return true;
+ } else if (((Class<?>) expectedType).getComponentType() != null) {
+ // Array types just pass through here, and we catch any problems when
+ // we try to deserialize the entries.
+ if (((Class<?>) expectedType).isAssignableFrom(instanceClass)) {
+ expectedInstanceClasses.add(instanceClass);
+ return true;
+ } else {
+ return false;
+ }
+ } else if (((Class<?>) expectedType) == instanceClass) {
+ expectedInstanceClasses.add(instanceClass);
+ return true;
+ }
+
+ // We know that the instance class does not exactly match the expected type,
+ // so try its superclass and its interfaces. At the same time, update any
+ // expected types to use the superclass or interface type. We know at this
+ // point that the expected type does not involve wildcards or TypeVariables,
+ // so we know that the first thing to return true indicates we are done,
+ // and failure of anything to return true means the instance does not meet
+ // the expected type.
+ if (instanceClass.getGenericSuperclass() != null) {
+ Type[] localTypes = expectedParameterTypes.clone();
+ if (findExpectedInstanceClassFromSuper(instanceClass.getGenericSuperclass(), expectedType,
+ resolvedTypes, expectedInstanceClasses, localTypes)) {
+ for (int i = 0; i < expectedParameterTypes.length; ++i) {
+ expectedParameterTypes[i] = localTypes[i];
+ }
+ return true;
+ }
+ }
+
+ Type[] interfaces = instanceClass.getGenericInterfaces();
+ for (Type interfaceType : interfaces) {
+ Type[] localTypes = expectedParameterTypes.clone();
+ if (findExpectedInstanceClassFromSuper(interfaceType, expectedType, resolvedTypes,
+ expectedInstanceClasses, localTypes)) {
+ for (int i = 0; i < expectedParameterTypes.length; ++i) {
+ expectedParameterTypes[i] = localTypes[i];
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean findExpectedInstanceClassFromSuper(
+ Type superType, Type expectedType, DequeMap<TypeVariable<?>, Type> resolvedTypes,
+ Set<Class<?>> expectedInstanceClasses, Type[] expectedParameterTypes) {
+
+ if (superType instanceof GenericArrayType) {
+ // Can't use array types as supertypes or interfaces
+ return false;
+ } else if (superType instanceof Class) {
+ // No additional type info from the superclass
+ return findExpectedInstanceClass((Class<?>) superType, expectedType, resolvedTypes,
+ expectedInstanceClasses, expectedParameterTypes);
+ } else if (superType instanceof ParameterizedType) {
+ ParameterizedType paramType = (ParameterizedType) superType;
+ Type rawType = paramType.getRawType();
+
+ if (rawType instanceof Class) {
+ Class<?> rawClass = (Class<?>) rawType;
+ TypeVariable<?>[] classGenericTypes = rawClass.getTypeParameters();
+ Type[] actualTypes = paramType.getActualTypeArguments();
+
+ for (int i = 0; i < actualTypes.length; ++i) {
+ for (int j = 0; j < expectedParameterTypes.length; ++j) {
+ if (actualTypes[i] == expectedParameterTypes[j]) {
+ expectedParameterTypes[j] = classGenericTypes[i];
+ }
+ }
+ }
+ return findExpectedInstanceClass(rawClass, expectedType, resolvedTypes,
+ expectedInstanceClasses, expectedParameterTypes);
+ }
+ } else if (superType instanceof WildcardType) {
+ WildcardType wildcardType = (WildcardType) superType;
+ Type[] upperBounds = wildcardType.getUpperBounds();
+ for (Type boundType : upperBounds) {
+ if (findExpectedInstanceClassFromSuper(boundType, expectedType,
+ resolvedTypes, expectedInstanceClasses, expectedParameterTypes)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Attempt to find known type for TypeVariable type from an instance.
*
* @param foundParameter The currently known parameter, which must be of type
@@ -645,9 +762,8 @@
* @return A new value for the foundParameter, if we find one
*/
private static Type findInstanceParameter(Type foundParameter, Type instanceType,
- DequeMap<Type, Type> resolvedTypes) {
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) {
// See what we know about the types that are matched to this type.
-
if (instanceType instanceof GenericArrayType) {
return findInstanceParameter(foundParameter, ((GenericArrayType) instanceType)
.getGenericComponentType(), resolvedTypes);
@@ -701,6 +817,67 @@
return foundParameter;
}
+ /**
+ * Attempt to find the actual types for the generic parameters of an instance,
+ * given the types we have resolved from the method signature or class field
+ * declaration.
+ *
+ * @param instanceClass The instance for which we want actual generic
+ * parameter types.
+ * @param resolvedTypes The types that have been resolved to actual values
+ * @param expectedParameterTypes An array of types representing the actual
+ * declared types for the parameters of the instanceClass. Some may
+ * be of type TypeVariable, in which case they need to be resolved, if
+ * possible, by this method.
+ */
+ private static void findInstanceParameters(Class<?> instanceClass,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes, Type[] expectedParameterTypes) {
+ TypeVariable<?>[] instanceTypes = instanceClass.getTypeParameters();
+
+ for (int i = 0; i < expectedParameterTypes.length; ++i) {
+ if (!(expectedParameterTypes[i] instanceof TypeVariable)) {
+ // We already have an actual type
+ continue;
+ }
+
+ // Check if we already know about this type.
+ boolean haveMatch = false;
+ for (int j = 0; !haveMatch && j < instanceTypes.length; ++j) {
+ if (expectedParameterTypes[i] == instanceTypes[j]) {
+ Type capturedType = findActualType(instanceTypes[j], resolvedTypes);
+ if (!(capturedType instanceof TypeVariable)) {
+ expectedParameterTypes[i] = capturedType;
+ haveMatch = true;
+ }
+ }
+ }
+ if (haveMatch) {
+ continue;
+ }
+
+ // Check if it is defined by superclasses
+ if (instanceClass.getGenericSuperclass() != null) {
+ Type superParameter = findInstanceParameter(expectedParameterTypes[i],
+ instanceClass.getGenericSuperclass(), resolvedTypes);
+ if (!(superParameter instanceof TypeVariable)) {
+ expectedParameterTypes[i] = superParameter;
+ continue;
+ }
+ }
+
+ // Check if it is defined by interfaces
+ Type[] interfaceTypes = instanceClass.getGenericInterfaces();
+ for (Type interfaceType : interfaceTypes) {
+ Type interfaceParameter =
+ findInstanceParameter(expectedParameterTypes[i], interfaceType, resolvedTypes);
+ if (!(interfaceParameter instanceof TypeVariable)) {
+ expectedParameterTypes[i] = interfaceParameter;
+ break;
+ }
+ }
+ }
+ }
+
private static void generateSerializationSignature(Class<?> instanceType, CRC32 crc,
SerializationPolicy policy) throws UnsupportedEncodingException {
crc.update(getSerializedTypeName(instanceType).getBytes(DEFAULT_ENCODING));
@@ -746,66 +923,8 @@
}
}
- /**
- * Determine if an instance class may be assigned to a wildcard type.
- *
- * @param instanceClass The instance class that we wish to assign
- * @param wildcardType The wildcard type we wish to assign to
- * @param resolvedTypes The type variables that we have actual types for
- * @return True when the instance may be assigned to the wildcard; false
- * otherwise.
- */
- private static boolean isInstanceAssignableToWildcard(Class<?> instanceClass,
- WildcardType wildcardType, DequeMap<Type, Type> resolvedTypes) {
- Type[] lowerBounds = wildcardType.getLowerBounds();
- for (Type type : lowerBounds) {
- /* Require instance to be a superclass of type, or type itself. */
- if (!isInstanceSuperOfType(instanceClass, type, resolvedTypes)) {
- return false;
- }
- }
-
- Type[] upperBounds = wildcardType.getUpperBounds();
- for (Type type : upperBounds) {
- /* Require instanceClass to be a subclass of type. */
- if (!isInstanceAssignableToType(instanceClass, type, resolvedTypes)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Determine if an instance class is a superclass of a given type, in the
- * generic wildcard sense of super.
- *
- * @param instanceClass The instance class that we wish to assign
- * @param boundType The type that must represent a subclass of instance class,
- * or instance class itself.
- * @param resolvedTypes The type variables that we have actual types for
- * @return True when the instance may be used in ? super boundType; false
- * otherwise.
- */
- private static boolean isInstanceSuperOfType(Class<?> instanceClass, Type boundType,
- DequeMap<Type, Type> resolvedTypes) {
- Class<?> boundClass = getClassFromType(boundType, resolvedTypes);
- if (boundClass == null) {
- // Can't verify against an unknown class, so just assume it's acceptable.
- return true;
- }
-
- while (boundClass != null) {
- if (instanceClass == boundClass) {
- return true;
- }
- boundClass = boundClass.getSuperclass();
- }
-
- return false;
- }
-
- private static void resolveTypesWorker(Type methodType, DequeMap<Type, Type> resolvedTypes,
- boolean addTypes) {
+ private static void resolveTypesWorker(Type methodType,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes, boolean addTypes) {
if (methodType instanceof GenericArrayType) {
SerializabilityUtil.resolveTypesWorker(((GenericArrayType) methodType)
.getGenericComponentType(), resolvedTypes, addTypes);
@@ -857,8 +976,8 @@
// A type that is of instance Class, with TypeParameters, must be a raw
// class, so strip off any parameters in the map.
- Type[] classParams = classType.getTypeParameters();
- for (Type classParamType : classParams) {
+ TypeVariable<?>[] classParams = classType.getTypeParameters();
+ for (TypeVariable<?> classParamType : classParams) {
if (addTypes) {
resolvedTypes.add(classParamType, classParamType);
} else {
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 080ebf3..f237740 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
@@ -37,6 +37,7 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -135,7 +136,7 @@
@Override
Object readValue(ServerSerializationStreamReader stream, Type expectedType,
- DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
return stream.readObject(expectedType, resolvedTypes);
}
},
@@ -156,7 +157,7 @@
@SuppressWarnings("unused")
Object readValue(ServerSerializationStreamReader stream, Type expectedType,
- DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
return readValue(stream);
}
}
@@ -258,7 +259,7 @@
@Override
protected Object readSingleValue(ServerSerializationStreamReader stream, Type expectedType,
- DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
return stream.readObject(expectedType, resolvedTypes);
}
@@ -297,7 +298,7 @@
@SuppressWarnings("unused")
protected Object readSingleValue(ServerSerializationStreamReader stream, Type expectedType,
- DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
return readSingleValue(stream);
}
@@ -333,7 +334,8 @@
}
Object read(ServerSerializationStreamReader stream, BoundedList<Object> instance,
- Type expectedType, DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ Type expectedType, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException {
for (int i = 0, n = instance.getExpectedSize(); i < n; ++i) {
instance.add(readSingleValue(stream, expectedType, resolvedTypes));
}
@@ -414,7 +416,7 @@
}
public Object deserializeValue(Class<?> rpcType, Type methodType,
- DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
ValueReader valueReader = CLASS_TO_VALUE_READER.get(rpcType);
if (valueReader != null) {
return valueReader.readValue(this, methodType, resolvedTypes);
@@ -495,10 +497,12 @@
}
}
+ @Override
public boolean readBoolean() throws SerializationException {
return !extract().equals("0");
}
+ @Override
public byte readByte() throws SerializationException {
String value = extract();
try {
@@ -508,19 +512,23 @@
}
}
+ @Override
public char readChar() throws SerializationException {
// just use an int, it's more foolproof
return (char) readInt();
}
+ @Override
public double readDouble() throws SerializationException {
return Double.parseDouble(extract());
}
+ @Override
public float readFloat() throws SerializationException {
return (float) Double.parseDouble(extract());
}
+ @Override
public int readInt() throws SerializationException {
String value = extract();
try {
@@ -530,6 +538,7 @@
}
}
+ @Override
public long readLong() throws SerializationException {
if (getVersion() == SERIALIZATION_STREAM_MIN_VERSION) {
return (long) readDouble() + (long) readDouble();
@@ -538,7 +547,7 @@
}
}
- public Object readObject(Type expectedType, DequeMap<Type, Type> resolvedTypes)
+ public Object readObject(Type expectedType, DequeMap<TypeVariable<?>, Type> resolvedTypes)
throws SerializationException {
int token = readInt();
@@ -558,6 +567,7 @@
return deserialize(typeSignature, expectedType, resolvedTypes);
}
+ @Override
public short readShort() throws SerializationException {
String value = extract();
try {
@@ -567,6 +577,7 @@
}
}
+ @Override
public String readString() throws SerializationException {
return getString(readInt());
}
@@ -577,7 +588,7 @@
}
protected Object deserialize(String typeSignature, Type expectedType,
- DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
Object instance = null;
try {
Class<?> instanceClass;
@@ -606,18 +617,33 @@
// resolvedTypes because we did not pass it through the non-type
// checking serializer. Create a map, so that from this point forward
// we have some way of type checking.
- resolvedTypes = new DequeMap<Type, Type>();
+ resolvedTypes = new DequeMap<TypeVariable<?>, Type>();
}
+ // Try to determine the type for the generic type parameters of the
+ // instance class, based upon the expected type and any class hierarchy
+ // type information.
+ // In looking for the expected parameter types, we also find out the
+ // way in which the instance meets the expected type, and hence can find
+ // out when it does not meet expectations.
+ TypeVariable<?>[] instanceParameterTypes = instanceClass.getTypeParameters();
+ Type[] expectedParameterTypes = null;
if (expectedType != null) {
SerializabilityUtil.resolveTypes(expectedType, resolvedTypes);
- if (!SerializabilityUtil.isInstanceAssignableToType(instanceClass, expectedType,
- resolvedTypes)) {
+ expectedParameterTypes = SerializabilityUtil.findExpectedParameterTypes(
+ instanceClass, expectedType, resolvedTypes);
+ if (expectedParameterTypes == null) {
throw new SerializedTypeViolationException("Attempt to deserialize an object of type "
+ instanceClass.toString() + " when an object of type "
+ SerializabilityUtil.findActualType(expectedType, resolvedTypes).toString()
+ " is expected");
}
+
+ // Add mappings from the instance type variables to the resolved types
+ // map.
+ for (int i = 0; i < instanceParameterTypes.length; ++i) {
+ resolvedTypes.add(instanceParameterTypes[i], expectedParameterTypes[i]);
+ }
}
assert (serializationPolicy != null);
@@ -628,13 +654,20 @@
int index = reserveDecodedObjectIndex();
- instance = instantiate(customSerializer, instanceClass, expectedType, resolvedTypes);
+ instance = instantiate(customSerializer, instanceClass, expectedParameterTypes,
+ resolvedTypes);
rememberDecodedObject(index, instance);
- Object replacement =
- deserializeImpl(customSerializer, instanceClass, instance, expectedType, resolvedTypes);
+ Object replacement = deserializeImpl(customSerializer, instanceClass, instance, expectedType,
+ expectedParameterTypes, resolvedTypes);
+ // Remove resolved types that were added for this instance.
+ if (expectedParameterTypes != null) {
+ for (int i = 0; i < instanceParameterTypes.length; ++i) {
+ resolvedTypes.remove(instanceParameterTypes[i]);
+ }
+ }
SerializabilityUtil.releaseTypes(expectedType, resolvedTypes);
// It's possible that deserializing an object requires the original proxy
@@ -707,7 +740,7 @@
*/
@SuppressWarnings("unchecked")
private Object deserializeArray(Class<?> instanceClass, Object instance, Type expectedType,
- DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
assert (instanceClass.isArray());
BoundedList<Object> buffer = (BoundedList<Object>) instance;
@@ -720,8 +753,9 @@
}
private void deserializeClass(Class<?> instanceClass, Object instance, Type expectedType,
- DequeMap<Type, Type> resolvedTypes) throws SerializationException, IllegalAccessException,
- NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ SerializationException, IllegalAccessException, NoSuchMethodException,
+ InvocationTargetException, ClassNotFoundException {
/**
* A map from field names to corresponding setter methods. The reference
* will be null for classes that do not require special handling for
@@ -797,13 +831,16 @@
Class<?> superClass = instanceClass.getSuperclass();
if (serializationPolicy.shouldDeserializeFields(superClass)) {
+ Type[] superParameterTypes = SerializabilityUtil.findExpectedParameterTypes(
+ superClass, superClass, resolvedTypes);
deserializeImpl(SerializabilityUtil.hasServerCustomFieldSerializer(superClass), superClass,
- instance, expectedType, resolvedTypes);
+ instance, expectedType, superParameterTypes, resolvedTypes);
}
}
private Object deserializeImpl(Class<?> customSerializer, Class<?> instanceClass,
- Object instance, Type expectedType, DequeMap<Type, Type> resolvedTypes)
+ Object instance, Type expectedType, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes)
throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException,
InvocationTargetException, SerializationException, ClassNotFoundException {
@@ -814,11 +851,12 @@
.loadCustomFieldSerializer(customSerializer);
if (customFieldSerializer == null) {
deserializeWithCustomFieldDeserializer(customSerializer, instanceClass, instance,
- resolvedTypes);
- } else if (customFieldSerializer instanceof ServerCustomFieldSerializer) {
+ expectedParameterTypes, resolvedTypes);
+ } else if (customFieldSerializer instanceof ServerCustomFieldSerializer &&
+ expectedParameterTypes != null) {
ServerCustomFieldSerializer<Object> serverCFS =
(ServerCustomFieldSerializer<Object>) customFieldSerializer;
- serverCFS.deserializeInstance(this, instance, instanceClass, resolvedTypes);
+ serverCFS.deserializeInstance(this, instance, expectedParameterTypes, resolvedTypes);
} else {
customFieldSerializer.deserializeInstance(this, instance);
}
@@ -837,7 +875,8 @@
} else if (instanceClass.isEnum()) {
// Enums are deserialized when they are instantiated
} else {
- deserializeClass(instanceClass, instance, expectedType, resolvedTypes);
+ deserializeClass(instanceClass, instance, expectedType, expectedParameterTypes,
+ resolvedTypes);
}
return instance;
@@ -901,14 +940,17 @@
}
private void deserializeWithCustomFieldDeserializer(Class<?> customSerializer,
- Class<?> instanceClass, Object instance, DequeMap<Type, Type> resolvedTypes)
+ Class<?> instanceClass, Object instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
assert (!instanceClass.isArray());
- for (Method method : customSerializer.getMethods()) {
- if ("deserializeChecked".equals(method.getName())) {
- method.invoke(null, this, instance, instanceClass, resolvedTypes);
- return;
+ if (expectedParameterTypes != null) {
+ for (Method method : customSerializer.getMethods()) {
+ if ("deserializeChecked".equals(method.getName())) {
+ method.invoke(null, this, instance, expectedParameterTypes, resolvedTypes);
+ return;
+ }
}
}
for (Method method : customSerializer.getMethods()) {
@@ -999,25 +1041,26 @@
}
}
- private Object instantiate(Class<?> customSerializer, Class<?> instanceClass, Type expectedType,
- DequeMap<Type, Type> resolvedTypes) throws InstantiationException, IllegalAccessException,
- IllegalArgumentException, InvocationTargetException, NoSuchMethodException,
- SerializationException {
+ private Object instantiate(Class<?> customSerializer, Class<?> instanceClass,
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws
+ InstantiationException, IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, NoSuchMethodException, SerializationException {
if (customSerializer != null) {
CustomFieldSerializer<?> customFieldSerializer =
SerializabilityUtil.loadCustomFieldSerializer(customSerializer);
if (customFieldSerializer == null) {
- Object result = instantiateWithCustomFieldInstantiator(customSerializer, instanceClass, resolvedTypes);
+ Object result = instantiateWithCustomFieldInstantiator(customSerializer,
+ expectedParameterTypes, resolvedTypes);
if (result != null) {
return result;
}
// Ok to not have a custom instantiate.
} else if (customFieldSerializer.hasCustomInstantiateInstance()) {
- if (expectedType != null &&
+ if (expectedParameterTypes != null &&
(customFieldSerializer instanceof ServerCustomFieldSerializer)) {
ServerCustomFieldSerializer<?> serverCFS =
(ServerCustomFieldSerializer<?>) customFieldSerializer;
- return serverCFS.instantiateInstance(this, instanceClass, resolvedTypes);
+ return serverCFS.instantiateInstance(this, expectedParameterTypes, resolvedTypes);
} else {
return customFieldSerializer.instantiateInstance(this);
}
@@ -1041,11 +1084,13 @@
}
private Object instantiateWithCustomFieldInstantiator(Class<?> customSerializer,
- Class<?> instanceClass, DequeMap<Type, Type> resolvedTypes)
+ Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes)
throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
- for (Method method : customSerializer.getMethods()) {
- if ("instantiateChecked".equals(method.getName())) {
- return method.invoke(null, this, instanceClass, resolvedTypes);
+ if (expectedParameterTypes != null) {
+ for (Method method : customSerializer.getMethods()) {
+ if ("instantiateChecked".equals(method.getName())) {
+ return method.invoke(null, this, expectedParameterTypes, resolvedTypes);
+ }
}
}
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedBaseClass.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedBaseClass.java
new file mode 100644
index 0000000..a68f54b
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedBaseClass.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2011 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.user.client.rpc;
+
+import java.io.Serializable;
+
+/**
+ * Test class for the
+ * {@link com.google.gwt.user.client.rpc.TypeCheckedObjectsTest} unit tests.
+ */
+public class TypeCheckedBaseClass implements Serializable, IsSerializable {
+ public String name;
+
+ public TypeCheckedBaseClass() {
+ }
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedInnerClass.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedInnerClass.java
new file mode 100644
index 0000000..4a616ce
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedInnerClass.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2011 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.user.client.rpc;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Test class for the
+ * {@link com.google.gwt.user.client.rpc.TypeCheckedObjectsTest} unit tests.
+ *
+ * Intended to test the type checking of containers that use static nested classes.
+ * This class is in its own file so that it can have a static nested class.
+ */
+public class TypeCheckedInnerClass extends TypeCheckedBaseClass {
+ /**
+ * An InnerClass for use in the List.
+ */
+ public static class InnerClass implements Serializable, IsSerializable {
+ public int value;
+ }
+
+ public List<InnerClass> values;
+
+ public TypeCheckedInnerClass() {
+ }
+
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTest.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTest.java
index f07b436..cc21a04 100644
--- a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTest.java
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTest.java
@@ -17,6 +17,7 @@
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedNestedLists;
import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedSuperClass;
import junit.framework.AssertionFailedError;
@@ -139,6 +140,28 @@
});
}
+ public void testTypeCheckedNestedLists() {
+ TypeCheckedObjectsTestServiceAsync service = getServiceAsync();
+ delayTestFinishForRpc();
+ service.echo(TypeCheckedObjectsTestSetFactory.createTypeCheckedNestedLists(),
+ new AsyncCallback<TypeCheckedNestedLists>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ AssertionFailedError er =
+ new AssertionFailedError("Could not serialize/deserialize TypeCheckedNestedLists");
+ er.initCause(caught);
+ throw er;
+ }
+
+ @Override
+ public void onSuccess(TypeCheckedNestedLists result) {
+ assertNotNull(result);
+ assertTrue(TypeCheckedObjectsTestSetValidator.isValid(result));
+ finishTest();
+ }
+ });
+ }
+
public void testTypeCheckedSerializer() {
TypeCheckedObjectsTestServiceAsync service = getServiceAsync();
delayTestFinishForRpc();
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestService.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestService.java
index 4a642e7..362ca44 100644
--- a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestService.java
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestService.java
@@ -16,6 +16,7 @@
package com.google.gwt.user.client.rpc;
import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedNestedLists;
import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedSuperClass;
/**
@@ -24,11 +25,13 @@
* TypeCheckedObjectsTest} unit test.
*/
public interface TypeCheckedObjectsTestService extends RemoteService {
+ TypeCheckedFieldClass<Integer, String> echo(TypeCheckedFieldClass<Integer, String> arg1);
+
TypeCheckedGenericClass<Integer, String> echo(TypeCheckedGenericClass<Integer, String> arg1);
- TypeCheckedSuperClass<Integer, String> echo(TypeCheckedSuperClass<Integer, String> arg1);
+ TypeCheckedNestedLists echo(TypeCheckedNestedLists arg1);
- TypeCheckedFieldClass<Integer, String> echo(TypeCheckedFieldClass<Integer, String> arg1);
+ TypeCheckedSuperClass<Integer, String> echo(TypeCheckedSuperClass<Integer, String> arg1);
TypeUncheckedGenericClass<Integer, String> echo(TypeUncheckedGenericClass<Integer, String> arg1);
}
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestServiceAsync.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestServiceAsync.java
index 5c63807..e9c0bb2 100644
--- a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestServiceAsync.java
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestServiceAsync.java
@@ -17,6 +17,7 @@
package com.google.gwt.user.client.rpc;
import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedNestedLists;
import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedSuperClass;
/**
@@ -27,6 +28,17 @@
*
*/
public interface TypeCheckedObjectsTestServiceAsync {
+ /**
+ * @see com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass)
+ */
+ @SuppressWarnings("rawtypes")
+ void echo(TypeCheckedFieldClass arg1,
+ AsyncCallback<TypeCheckedFieldClass<Integer, String>> callback);
+
+ /**
+ * @see com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedNestedLists)
+ */
+ void echo(TypeCheckedNestedLists arg1, AsyncCallback<TypeCheckedNestedLists> callback);
/**
* @see com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google.gwt.user.client.rpc.TypeCheckedGenericClass)
@@ -43,13 +55,6 @@
AsyncCallback<TypeCheckedSuperClass<Integer, String>> callback);
/**
- * @see com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass)
- */
- @SuppressWarnings("rawtypes")
- void echo(TypeCheckedFieldClass arg1,
- AsyncCallback<TypeCheckedFieldClass<Integer, String>> callback);
-
- /**
* @see com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google.gwt.user.client.rpc.TypeUncheckedGenericClass)
*/
@SuppressWarnings("rawtypes")
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetFactory.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetFactory.java
index faafc86..93788b9 100644
--- a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetFactory.java
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetFactory.java
@@ -16,14 +16,15 @@
package com.google.gwt.user.client.rpc;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
/**
* Generated test data for the
* {@link com.google.gwt.user.client.rpc.TypeCheckedObjectsTest} unit tests.
*/
public class TypeCheckedObjectsTestSetFactory {
-
/**
* Used to test that the type checked field for an unchecked class is actually
* type checked.
@@ -44,12 +45,24 @@
}
/**
+ * Used to test classes that use other classes as fields, where some of those
+ * fields are of the same type, and use nested static classes as parameters to
+ * generic types.
+ */
+ public static class TypeCheckedNestedLists implements Serializable {
+ public ArrayList<TypeCheckedBaseClass> values;
+
+ TypeCheckedNestedLists() {
+ }
+ }
+
+ /**
* Used to test that the type checked base class for an unchecked class is
* actually type checked.
*/
public static class TypeCheckedSuperClass<X, Y> extends TypeCheckedGenericClass<X, Y> {
}
-
+
public static TypeCheckedFieldClass<HashSet<Integer>, String> createInvalidCheckedFieldClass() {
TypeCheckedFieldClass<HashSet<Integer>, String> result =
new TypeCheckedFieldClass<HashSet<Integer>, String>();
@@ -78,6 +91,17 @@
return result;
}
+ public static
+ TypeUncheckedGenericClass<Integer, HashSet<String>> createInvalidUncheckedGenericClass() {
+ TypeUncheckedGenericClass<Integer, HashSet<String>> result =
+ new TypeUncheckedGenericClass<Integer, HashSet<String>>();
+ result.setMarker(TypeCheckedObjectsTestSetValidator.markerKey,
+ TypeCheckedObjectsTestSetValidator.invalidMarkerValue);
+ result.checkedField.hashField.put(TypeCheckedObjectsTestSetValidator.markerKey,
+ TypeCheckedObjectsTestSetValidator.invalidMarkerValue);
+ return result;
+ }
+
public static TypeCheckedFieldClass<Integer, String> createTypeCheckedFieldClass() {
TypeCheckedFieldClass<Integer, String> result =
new TypeCheckedFieldClass<Integer, String>();
@@ -97,6 +121,26 @@
return result;
}
+ public static TypeCheckedNestedLists createTypeCheckedNestedLists() {
+ TypeCheckedInnerClass nestedContent = new TypeCheckedInnerClass();
+ TypeCheckedInnerClass.InnerClass value1 = new TypeCheckedInnerClass.InnerClass();
+ value1.value = 12345;
+ TypeCheckedInnerClass.InnerClass value2 = new TypeCheckedInnerClass.InnerClass();
+ value2.value = 67890;
+ List<TypeCheckedInnerClass.InnerClass> values =
+ new ArrayList<TypeCheckedInnerClass.InnerClass>(2);
+ values.add(value1);
+ values.add(value2);
+ nestedContent.values = values;
+ nestedContent.name = "foo";
+
+ TypeCheckedNestedLists result = new TypeCheckedNestedLists();
+ result.values = new ArrayList<TypeCheckedBaseClass>();
+ result.values.add(nestedContent);
+
+ return result;
+ }
+
public static TypeCheckedSuperClass<Integer, String> createTypeCheckedSuperClass() {
TypeCheckedSuperClass<Integer, String> result = new TypeCheckedSuperClass<Integer, String>();
result.hashField.put(TypeCheckedObjectsTestSetValidator.markerKey,
@@ -104,17 +148,6 @@
return result;
}
- public static
- TypeUncheckedGenericClass<Integer, HashSet<String>> createInvalidUncheckedGenericClass() {
- TypeUncheckedGenericClass<Integer, HashSet<String>> result =
- new TypeUncheckedGenericClass<Integer, HashSet<String>>();
- result.setMarker(TypeCheckedObjectsTestSetValidator.markerKey,
- TypeCheckedObjectsTestSetValidator.invalidMarkerValue);
- result.checkedField.hashField.put(TypeCheckedObjectsTestSetValidator.markerKey,
- TypeCheckedObjectsTestSetValidator.invalidMarkerValue);
- return result;
- }
-
public static TypeUncheckedGenericClass<Integer, String> createTypeUncheckedGenericClass() {
TypeUncheckedGenericClass<Integer, String> result =
new TypeUncheckedGenericClass<Integer, String>();
diff --git a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetValidator.java b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetValidator.java
index 4adfbf8..64cde64 100644
--- a/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetValidator.java
+++ b/user/test/com/google/gwt/user/client/rpc/TypeCheckedObjectsTestSetValidator.java
@@ -16,6 +16,7 @@
package com.google.gwt.user.client.rpc;
import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedNestedLists;
import java.util.HashSet;
@@ -90,6 +91,36 @@
return true;
}
+ public static boolean isValid(TypeCheckedNestedLists arg1) {
+ if (arg1 == null) {
+ return false;
+ }
+
+ if (arg1.values.size() != 1) {
+ return false;
+ }
+
+ TypeCheckedBaseClass baseClass = arg1.values.get(0);
+ if (!(baseClass instanceof TypeCheckedInnerClass)) {
+ return false;
+ }
+ TypeCheckedInnerClass innerClass = (TypeCheckedInnerClass) baseClass;
+
+ if (innerClass.values.get(0).value != 12345) {
+ return false;
+ }
+
+ if (innerClass.values.get(1).value != 67890) {
+ return false;
+ }
+
+ if (!innerClass.name.equals("foo")) {
+ return false;
+ }
+
+ return true;
+ }
+
@SuppressWarnings({"rawtypes", "unchecked"})
public static boolean isValid(TypeUncheckedGenericClass arg1) {
if (arg1 == null) {
diff --git a/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckTest.java b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckTest.java
index f45b1f8..ee1f40d3 100644
--- a/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckTest.java
@@ -76,6 +76,7 @@
* Test collections in fields of classes:
* - BClass contains a List<Integer>: testClassField
* - CClass contains a BClass: testClassField
+ * - KClass<X> contains a DClass<X, Integer> and List<X>: testGenericFields
*
* Test generic classes that use their generic types in various ways
* - DClass<X, Y> extends LinkedList<X>: testGenericClasses
@@ -83,12 +84,12 @@
* - FClass<X, Y> { List<X> ... List<Y> ... }: testGenericClasses
* - GClass<X, Y> { DClass<X, Y>, ... }: testNestedGenericClasses
* - JClass<X, Y> { fields of HashMap }: testGenericFields
- * - DClass<X, Y> where <X, Y> come from declaring class:
- * testMethodClassGenerics
+ *
*
* Wildcard types
* - (IClass<? extends T> arg1) with
- * <T extends Set<? super GEClass<Integer, String>>>: testComplexGenerics
+ * <T extends Set<? super GEClass<Integer, String>>>: testComplexGenerics
+ * - LClass<T implements List<X> & MClass<Y>>: testMethodClassGenerics
*/
/**
@@ -185,7 +186,8 @@
* A class containing a method used to check type spoofing attacks on RPC
* messages containing novel classes.
*/
- public static class ClassesParamTestClass<X, Y> implements RemoteService {
+ public static class ClassesParamTestClass<T extends List<Integer> & MInterface<String>>
+ implements RemoteService {
@SuppressWarnings("unused")
public static void testAClass(AClass arg1) {
}
@@ -276,8 +278,16 @@
public static void testJClassRaw2(JClass arg1) {
}
+ @SuppressWarnings({"unused"})
+ public static void testKClass(KClass<String> arg1) {
+ }
+
+ @SuppressWarnings({"unused", "rawtypes"})
+ public static void testKClassRaw(KClass arg1) {
+ }
+
@SuppressWarnings("unused")
- public void testDClassGeneric(DClass<X, Y> arg1) {
+ public void testWildcardBounds(T arg1) {
}
}
@@ -461,6 +471,66 @@
}
/**
+ * Test case for a generic type that extends a generic type with fewer
+ * parameters, used as a field and as an array.
+ *
+ * During testing, instances of DClass are used for all of the fields.
+ */
+ public static class KClass<X> implements IsSerializable {
+ public DClass<X, Integer> field1 = null;
+ public List<X> field2 = null;
+ public DClass<X, Integer>[] field3 = null;
+ public List<X>[] field4 = null;
+ }
+
+ /**
+ * Test case for multiple wildcard bounds.
+ */
+ public static class LClass<X, Y> extends LinkedList<X>
+ implements MInterface<Y>, IsSerializable {
+ public Y field1 = null;
+ public List<Y> field2 = null;
+
+ @Override
+ public Y echo(Y arg) {
+ return arg;
+ }
+ }
+
+ /**
+ * An invalid test case for multiple wildcard bounds.
+ *
+ * It does not implement List.
+ */
+ public static class LClassInvalid1<X, Y> extends IClass<X>
+ implements MInterface<Y> {
+ public Y field1 = null;
+ public List<Y> field2 = null;
+
+ @Override
+ public Y echo(Y arg) {
+ return arg;
+ }
+ }
+
+ /**
+ * An invalid test case for multiple wildcard bounds.
+ *
+ * It does not implement MInterface.
+ */
+ public static class LClassInvalid2<X, Y> extends LinkedList<X> implements IsSerializable {
+ public Y field1 = null;
+ public List<Y> field2 = null;
+ }
+
+ /**
+ * Test interface for multiple wildcard bounds.
+ */
+ public interface MInterface<X> {
+ public X echo(X arg);
+ }
+
+ /**
* A class containing a method used to check type spoofing attacks on RPC
* messages containing primitive types.
*/
@@ -485,7 +555,6 @@
strFactory.write(arrayList);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -502,7 +571,6 @@
strFactory.write(arrayAsList);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -523,7 +591,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -543,7 +610,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -566,7 +632,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -588,7 +653,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -608,45 +672,6 @@
strFactory.write((Object) arg1);
return strFactory.toString();
-
- } catch (Exception e) {
- fail(e.getMessage());
-
- return null;
- }
- }
-
- private static String generateDClassValid1() {
- try {
- RPCTypeCheckFactory strFactory =
- new RPCTypeCheckFactory(ClassesParamTestClass.class, "testDClassGeneric");
-
- DClass<Integer, String> arg1 = new DClass<Integer, String>();
- arg1.setY("foo");
- arg1.add(12345);
- strFactory.write((Object) arg1);
-
- return strFactory.toString();
-
- } catch (Exception e) {
- fail(e.getMessage());
-
- return null;
- }
- }
-
- private static String generateDClassValid2() {
- try {
- RPCTypeCheckFactory strFactory =
- new RPCTypeCheckFactory(ClassesParamTestClass.class, "testDClassGeneric");
-
- DClass<String, Integer> arg1 = new DClass<String, Integer>();
- arg1.setY(12345);
- arg1.add("foo");
- strFactory.write((Object) arg1);
-
- return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -666,7 +691,6 @@
strFactory.write((Object) arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -686,7 +710,6 @@
strFactory.write((Object) arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -705,7 +728,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -726,7 +748,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -742,7 +763,6 @@
strFactory.writeEmptyList();
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -758,7 +778,6 @@
strFactory.writeEmptyMap();
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -774,7 +793,6 @@
strFactory.writeEmptySet();
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -797,7 +815,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -821,7 +838,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -845,7 +861,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -865,7 +880,6 @@
strFactory.write((Object) gClass);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -885,7 +899,6 @@
strFactory.write((Object) gClass);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -905,7 +918,6 @@
strFactory.write((Object) gClass);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -925,7 +937,6 @@
strFactory.write((Object) gClass);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -946,7 +957,6 @@
strFactory.write((Object) arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -965,7 +975,6 @@
strFactory.write((Object) arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -983,7 +992,6 @@
strFactory.write(hashMap);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1002,7 +1010,6 @@
strFactory.write(hashSet);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1023,7 +1030,6 @@
strFactory.write(hClass);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1044,7 +1050,6 @@
strFactory.write(hClass);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1066,7 +1071,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1086,7 +1090,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1118,7 +1121,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1158,7 +1160,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1189,7 +1190,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1224,7 +1224,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1265,7 +1264,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1283,7 +1281,6 @@
strFactory.write(hashMap);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1303,7 +1300,6 @@
strFactory.write(j);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1323,7 +1319,6 @@
strFactory.write(j);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1354,7 +1349,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1385,7 +1379,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1415,7 +1408,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1444,7 +1436,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1474,7 +1465,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1504,7 +1494,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1533,7 +1522,6 @@
strFactory.write(arg1);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1563,7 +1551,405 @@
strFactory.write(arg1);
return strFactory.toString();
+ } catch (Exception e) {
+ fail(e.getMessage());
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static String generateKClassValid(String methodName) {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, methodName);
+
+ DClass<String, Integer> field1 = new DClass<String, Integer>();
+ field1.setY(12);
+ field1.add("foo");
+
+ DClass<String, Integer> field2 = new DClass<String, Integer>();
+ field2.setY(34);
+ field2.add("bar");
+
+ DClass<String, Integer>[] field3 = new DClass[1];
+ field3[0] = new DClass<String, Integer>();
+ field3[0].setY(56);
+ field3[0].add("oof");
+
+ DClass<String, Integer>[] field4 = new DClass[1];
+ field4[0] = new DClass<String, Integer>();
+ field4[0].setY(78);
+ field4[0].add("rab");
+
+ KClass<String> arg1 = new KClass<String>();
+ arg1.field1 = field1;
+ arg1.field2 = field2;
+ arg1.field3 = field3;
+ arg1.field4 = field4;
+ strFactory.write(arg1);
+
+ return strFactory.toString();
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static String generateKClassInvalid1(String methodName) {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, methodName);
+
+ DClass<Integer, Integer> field1 = new DClass<Integer, Integer>();
+ field1.setY(12);
+ field1.add(90);
+
+ DClass<String, Integer> field2 = new DClass<String, Integer>();
+ field2.setY(34);
+ field2.add("bar");
+
+ DClass<String, Integer>[] field3 = new DClass[1];
+ field3[0] = new DClass<String, Integer>();
+ field3[0].setY(56);
+ field3[0].add("oof");
+
+ DClass<String, Integer>[] field4 = new DClass[1];
+ field4[0] = new DClass<String, Integer>();
+ field4[0].setY(78);
+ field4[0].add("rab");
+
+ KClass arg1 = new KClass();
+ arg1.field1 = field1;
+ arg1.field2 = field2;
+ arg1.field3 = field3;
+ arg1.field4 = field4;
+ strFactory.write(arg1);
+
+ return strFactory.toString();
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static String generateKClassInvalid2() {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, "testKClass");
+
+ DClass<String, Integer> field1 = new DClass<String, Integer>();
+ field1.setY(12);
+ field1.add("foo");
+
+ DClass<Integer, Integer> field2 = new DClass<Integer, Integer>();
+ field2.setY(34);
+ field2.add(90);
+
+ DClass<String, Integer>[] field3 = new DClass[1];
+ field3[0] = new DClass<String, Integer>();
+ field3[0].setY(56);
+ field3[0].add("oof");
+
+ DClass<String, Integer>[] field4 = new DClass[1];
+ field4[0] = new DClass<String, Integer>();
+ field4[0].setY(78);
+ field4[0].add("rab");
+
+ KClass arg1 = new KClass();
+ arg1.field1 = field1;
+ arg1.field2 = field2;
+ arg1.field3 = field3;
+ arg1.field4 = field4;
+ strFactory.write(arg1);
+
+ return strFactory.toString();
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static String generateKClassInvalid3() {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, "testKClass");
+
+ DClass<String, Integer> field1 = new DClass<String, Integer>();
+ field1.setY(12);
+ field1.add("foo");
+
+ DClass<String, Integer> field2 = new DClass<String, Integer>();
+ field2.setY(34);
+ field2.add("bar");
+
+ DClass<Integer, Integer>[] field3 = new DClass[1];
+ field3[0] = new DClass<Integer, Integer>();
+ field3[0].setY(56);
+ field3[0].add(90);
+
+ DClass<String, Integer>[] field4 = new DClass[1];
+ field4[0] = new DClass<String, Integer>();
+ field4[0].setY(78);
+ field4[0].add("rab");
+
+ KClass arg1 = new KClass();
+ arg1.field1 = field1;
+ arg1.field2 = field2;
+ arg1.field3 = field3;
+ arg1.field4 = field4;
+ strFactory.write(arg1);
+
+ return strFactory.toString();
+
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static String generateKClassInvalid4() {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, "testKClass");
+
+ DClass<String, Integer> field1 = new DClass<String, Integer>();
+ field1.setY(12);
+ field1.add("foo");
+
+ DClass<String, Integer> field2 = new DClass<String, Integer>();
+ field2.setY(34);
+ field2.add("bar");
+
+ DClass<String, Integer>[] field3 = new DClass[1];
+ field3[0] = new DClass<String, Integer>();
+ field3[0].setY(56);
+ field3[0].add("oof");
+
+ DClass<Integer, Integer>[] field4 = new DClass[1];
+ field4[0] = new DClass<Integer, Integer>();
+ field4[0].setY(78);
+ field4[0].add(90);
+
+ KClass arg1 = new KClass();
+ arg1.field1 = field1;
+ arg1.field2 = field2;
+ arg1.field3 = field3;
+ arg1.field4 = field4;
+ strFactory.write(arg1);
+
+ return strFactory.toString();
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static String generateKClassInvalid5() {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, "testKClass");
+
+ DClass<String, String> field1 = new DClass<String, String>();
+ field1.setY("bar");
+ field1.add("foo");
+
+ DClass<String, Integer> field2 = new DClass<String, Integer>();
+ field2.setY(34);
+ field2.add("bar");
+
+ DClass<String, Integer>[] field3 = new DClass[1];
+ field3[0] = new DClass<String, Integer>();
+ field3[0].setY(56);
+ field3[0].add("oof");
+
+ DClass<String, Integer>[] field4 = new DClass[1];
+ field4[0] = new DClass<String, Integer>();
+ field4[0].setY(78);
+ field4[0].add("rab");
+
+ KClass arg1 = new KClass();
+ arg1.field1 = field1;
+ arg1.field2 = field2;
+ arg1.field3 = field3;
+ arg1.field4 = field4;
+ strFactory.write(arg1);
+
+ return strFactory.toString();
+
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ private static String generateLClassInvalid1() {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, "testWildcardBounds");
+
+ LClass<String, String> arg = new LClass<String, String>();
+ arg.add("foo");
+ arg.field1 = "foo";
+ arg.field2 = new LinkedList<String>();
+ arg.field2.add("bar");
+ strFactory.write((Object) arg);
+
+ return strFactory.toString();
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ private static String generateLClassInvalid2() {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, "testWildcardBounds");
+
+ LClass<Integer, Integer> arg = new LClass<Integer, Integer>();
+ arg.add(12345);
+ arg.field1 = 67890;
+ arg.field2 = new LinkedList<Integer>();
+ arg.field2.add(45678);
+ strFactory.write((Object) arg);
+
+ return strFactory.toString();
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ private static String generateLClassInvalid3() {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, "testWildcardBounds");
+
+ LClassInvalid1<Integer, String> arg = new LClassInvalid1<Integer, String>();
+ arg.setA(12345);
+ arg.field1 = "foo";
+ arg.field2 = new LinkedList<String>();
+ arg.field2.add("bar");
+ strFactory.write(arg);
+
+ return strFactory.toString();
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ private static String generateLClassInvalid4() {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, "testWildcardBounds");
+
+ LClassInvalid2<Integer, String> arg = new LClassInvalid2<Integer, String>();
+ arg.add(12345);
+ arg.field1 = "foo";
+ arg.field2 = new LinkedList<String>();
+ arg.field2.add("bar");
+ strFactory.write((Object) arg);
+
+ return strFactory.toString();
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private static String generateLClassInvalid5() {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, "testWildcardBounds");
+
+ LClass arg = new LClass();
+ arg.add("oof");
+ arg.field1 = "foo";
+ arg.field2 = new LinkedList<String>();
+ arg.field2.add("bar");
+ strFactory.write((Object) arg);
+
+ return strFactory.toString();
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private static String generateLClassInvalid6() {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, "testWildcardBounds");
+
+ LClass arg = new LClass();
+ arg.add(12345);
+ arg.field1 = 67890;
+ arg.field2 = new LinkedList<String>();
+ arg.field2.add("bar");
+ strFactory.write((Object) arg);
+
+ return strFactory.toString();
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private static String generateLClassInvalid7() {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, "testWildcardBounds");
+
+ LClass arg = new LClass();
+ arg.add(12345);
+ arg.field1 = "foo";
+ arg.field2 = new LinkedList<Integer>();
+ arg.field2.add(67890);
+ strFactory.write((Object) arg);
+
+ return strFactory.toString();
+ } catch (Exception e) {
+ fail(e.getMessage());
+
+ return null;
+ }
+ }
+
+ private static String generateLClassValid() {
+ try {
+ RPCTypeCheckFactory strFactory =
+ new RPCTypeCheckFactory(ClassesParamTestClass.class, "testWildcardBounds");
+
+ LClass<Integer, String> arg = new LClass<Integer, String>();
+ arg.add(12345);
+ arg.field1 = "foo";
+ arg.field2 = new LinkedList<String>();
+ arg.field2.add("bar");
+ strFactory.write((Object) arg);
+
+ return strFactory.toString();
} catch (Exception e) {
fail(e.getMessage());
@@ -1581,7 +1967,6 @@
strFactory.write(hashMap);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1600,7 +1985,6 @@
strFactory.write(hashSet);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1619,7 +2003,6 @@
strFactory.write(list);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1637,7 +2020,6 @@
strFactory.write(singletonList);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1657,7 +2039,6 @@
strFactory.write(arg2);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1677,7 +2058,6 @@
strFactory.write(arg2);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1695,7 +2075,6 @@
strFactory.write(treeMap);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1715,7 +2094,6 @@
strFactory.write(treeSet);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1734,7 +2112,6 @@
strFactory.write(vector);
return strFactory.toString();
-
} catch (Exception e) {
fail(e.getMessage());
@@ -1962,6 +2339,10 @@
/**
* This tests that generic class with fields that have the same types but
* different actual parameters are correctly handled.
+ *
+ * It also checks classes with fields that have more generic arguments
+ * than the class provides, or that use instances with more parameters than
+ * the field itself requires (i.e List<X> when used as Object).
*/
public void testGenericFields() {
try {
@@ -2019,6 +2400,61 @@
assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*"));
}
+ try {
+ RPC.decodeRequest(generateKClassValid("testKClass"));
+ } catch (Exception e) {
+ fail("Unexpected assertion from testGenericFields (7a): " + e.getMessage());
+ }
+ try {
+ RPC.decodeRequest(generateKClassValid("testKClassRaw"));
+ } catch (Exception e) {
+ fail("Unexpected assertion from testGenericFields (7b): " + e.getMessage());
+ }
+ try {
+ RPC.decodeRequest(generateKClassInvalid1("testKClass"));
+ fail("Expected IncompatibleRemoteServiceException from testGenericFields (7c)");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected to get here
+ assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+ assertTrue(e.getCause().getMessage().matches(".*Integer.*String.*"));
+ }
+ try {
+ RPC.decodeRequest(generateKClassInvalid1("testKClassRaw"));
+ } catch (Exception e) {
+ fail("Unexpected assertion from testGenericFields (7d): " + e.getMessage());
+ }
+ try {
+ RPC.decodeRequest(generateKClassInvalid2());
+ fail("Expected IncompatibleRemoteServiceException from testGenericFields (7e)");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected to get here
+ assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+ assertTrue(e.getCause().getMessage().matches(".*Integer.*String.*"));
+ }
+ try {
+ RPC.decodeRequest(generateKClassInvalid3());
+ fail("Expected IncompatibleRemoteServiceException from testGenericFields (7f)");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected to get here
+ assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+ assertTrue(e.getCause().getMessage().matches(".*Integer.*String.*"));
+ }
+ try {
+ RPC.decodeRequest(generateKClassInvalid4());
+ fail("Expected IncompatibleRemoteServiceException from testGenericFields (7g)");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected to get here
+ assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+ assertTrue(e.getCause().getMessage().matches(".*Integer.*String.*"));
+ }
+ try {
+ RPC.decodeRequest(generateKClassInvalid5());
+ fail("Expected IncompatibleRemoteServiceException from testGenericFields (7h)");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected to get here
+ assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+ assertTrue(e.getCause().getMessage().matches(".*String.*Integer.*"));
+ }
}
/**
@@ -2102,20 +2538,76 @@
}
/**
- * This checks that we do not report an error when we cannot know the actual
- * value of a generic because it comes from the method class (which has no
- * known parameters when the method is invoked on the server).
+ * This checks that we correctly report, or don't report, errors when we
+ * cannot know the actual value of a generic but may have bounds from the
+ * method class.
+ *
+ * In this case,
+ * ClassesParamTestClass<T extends List<Integer> & MInterface<String>>
+ * is the class containing the method, and the method expects an argument of
+ * type T, so the thing in the RPC method should meet the bounds on T.
*/
public void testMethodClassGenerics() {
try {
- RPC.decodeRequest(generateDClassValid1());
+ RPC.decodeRequest(generateLClassValid());
} catch (Exception e) {
- fail("Unexpected Exception from testMethodClassGenerics (1): " + e.getMessage());
+ fail("Unexpected assertion from testMethodClassGenerics (1): " + e.getMessage());
}
try {
- RPC.decodeRequest(generateDClassValid2());
- } catch (Exception e) {
- fail("Unexpected Exception from testMethodClassGenerics (2): " + e.getMessage());
+ RPC.decodeRequest(generateLClassInvalid1());
+ fail("Expected IncompatibleRemoteServiceException from testMethodClassGenerics (2)");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected to get here
+ assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+ assertTrue(e.getCause().getMessage().matches(".*String.*Integer.*"));
+ }
+ try {
+ RPC.decodeRequest(generateLClassInvalid2());
+ fail("Expected IncompatibleRemoteServiceException from testMethodClassGenerics (3)");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected to get here
+ assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+ assertTrue(e.getCause().getMessage().matches(".*Integer.*String.*"));
+ }
+ try {
+ RPC.decodeRequest(generateLClassInvalid3());
+ fail("Expected IncompatibleRemoteServiceException from testMethodClassGenerics (4)");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected to get here
+ assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+ assertTrue(e.getCause().getMessage().matches(".*LClassInvalid1.*T.*"));
+ }
+ try {
+ RPC.decodeRequest(generateLClassInvalid4());
+ fail("Expected IncompatibleRemoteServiceException from testMethodClassGenerics (5)");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected to get here
+ assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+ assertTrue(e.getCause().getMessage().matches(".*LClassInvalid2.*T.*"));
+ }
+ try {
+ RPC.decodeRequest(generateLClassInvalid5());
+ fail("Expected IncompatibleRemoteServiceException from testMethodClassGenerics (6)");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected to get here
+ assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+ assertTrue(e.getCause().getMessage().matches(".*String.*Integer.*"));
+ }
+ try {
+ RPC.decodeRequest(generateLClassInvalid6());
+ fail("Expected IncompatibleRemoteServiceException from testMethodClassGenerics (7)");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected to get here
+ assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+ assertTrue(e.getCause().getMessage().matches(".*Integer.*String.*"));
+ }
+ try {
+ RPC.decodeRequest(generateLClassInvalid7());
+ fail("Expected IncompatibleRemoteServiceException from testMethodClassGenerics (8)");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected to get here
+ assertEquals(SerializedTypeViolationException.class, e.getCause().getClass());
+ assertTrue(e.getCause().getMessage().matches(".*Integer.*String.*"));
}
}
diff --git a/user/test/com/google/gwt/user/server/rpc/TypeCheckedGenericClass_ServerCustomFieldSerializer.java b/user/test/com/google/gwt/user/server/rpc/TypeCheckedGenericClass_ServerCustomFieldSerializer.java
index 7fd6fba..ff9a774 100644
--- a/user/test/com/google/gwt/user/server/rpc/TypeCheckedGenericClass_ServerCustomFieldSerializer.java
+++ b/user/test/com/google/gwt/user/server/rpc/TypeCheckedGenericClass_ServerCustomFieldSerializer.java
@@ -24,6 +24,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.HashMap;
/**
@@ -35,8 +36,8 @@
public class TypeCheckedGenericClass_ServerCustomFieldSerializer {
@SuppressWarnings("unchecked")
public static void deserializeChecked(ServerSerializationStreamReader streamReader,
- TypeCheckedGenericClass instance, Class<?> instanceClass,
- DequeMap<Type, Type> resolvedTypes) throws SerializationException {
+ TypeCheckedGenericClass instance, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException {
Object junkKey = streamReader.readObject();
Object junkValue = streamReader.readObject();
@@ -68,9 +69,43 @@
}
}
+ @SuppressWarnings("unchecked")
+ public static void deserialize(ServerSerializationStreamReader streamReader,
+ TypeCheckedGenericClass instance) throws SerializationException {
+ Object junkKey = streamReader.readObject();
+ Object junkValue = streamReader.readObject();
+
+ /*
+ * If deserializing a superclass we will not have been instantiated using
+ * the custom instantiator, so skip the checks for correct markers.
+ */
+ if (instance.getClass() != TypeCheckedGenericClass.class
+ || ((instance.getMarkerKey() instanceof Integer)
+ && ((Integer) instance.getMarkerKey()).intValue() == 54321
+ && (instance.getMarkerValue() instanceof String) && ((String) instance.getMarkerValue())
+ .equals("LocalMarker"))) {
+ instance.setMarker(TypeCheckedObjectsTestSetValidator.markerKey,
+ TypeCheckedObjectsTestSetValidator.markerValue);
+ } else {
+ throw new SerializationException(
+ "Incorrect markers in TypeCheckedGenericClass server deserialization. "
+ + "Custom instantiate probably not called.");
+ }
+
+ instance.hashField = (HashMap) streamReader.readObject();
+ }
+
public static TypeCheckedGenericClass instantiateChecked(
- ServerSerializationStreamReader streamReader, Class<?> instanceClass,
- DequeMap<Type, Type> resolvedTypes) {
+ ServerSerializationStreamReader streamReader, Type[] expectedParameterTypes,
+ DequeMap<TypeVariable<?>, Type> resolvedTypes) {
+ TypeCheckedGenericClass<Integer, String> result =
+ new TypeCheckedGenericClass<Integer, String>();
+ result.setMarker(54321, "LocalMarker");
+ return result;
+ }
+
+ public static TypeCheckedGenericClass instantiate(
+ ServerSerializationStreamReader streamReader) {
TypeCheckedGenericClass<Integer, String> result =
new TypeCheckedGenericClass<Integer, String>();
result.setMarker(54321, "LocalMarker");
diff --git a/user/test/com/google/gwt/user/server/rpc/TypeCheckedObjectsTestServiceImpl.java b/user/test/com/google/gwt/user/server/rpc/TypeCheckedObjectsTestServiceImpl.java
index 4d2a6b0..5b9ceb0 100644
--- a/user/test/com/google/gwt/user/server/rpc/TypeCheckedObjectsTestServiceImpl.java
+++ b/user/test/com/google/gwt/user/server/rpc/TypeCheckedObjectsTestServiceImpl.java
@@ -18,6 +18,7 @@
import com.google.gwt.user.client.rpc.TypeCheckedGenericClass;
import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService;
import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass;
+import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedNestedLists;
import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedSuperClass;
import com.google.gwt.user.client.rpc.TypeCheckedObjectsTestSetValidator;
import com.google.gwt.user.client.rpc.TypeUncheckedGenericClass;
@@ -32,6 +33,36 @@
/*
* @see
* com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google
+ * .
+ * gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass)
+ */
+ @Override
+ public TypeCheckedFieldClass<Integer, String> echo(TypeCheckedFieldClass<Integer, String> arg1) {
+ if (!TypeCheckedObjectsTestSetValidator.isValid(arg1)) {
+ throw new RuntimeException();
+ }
+
+ return arg1;
+ }
+
+ /*
+ * @see
+ * com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google
+ * .
+ * gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedNestedLists)
+ */
+ @Override
+ public TypeCheckedNestedLists echo(TypeCheckedNestedLists arg1) {
+ if (!TypeCheckedObjectsTestSetValidator.isValid(arg1)) {
+ throw new RuntimeException();
+ }
+
+ return arg1;
+ }
+
+ /*
+ * @see
+ * com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google
* .gwt.user.client.rpc.TypeCheckedGenericClass)
*/
@Override
@@ -62,21 +93,6 @@
/*
* @see
* com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google
- * .
- * gwt.user.client.rpc.TypeCheckedObjectsTestSetFactory.TypeCheckedFieldClass)
- */
- @Override
- public TypeCheckedFieldClass<Integer, String> echo(TypeCheckedFieldClass<Integer, String> arg1) {
- if (!TypeCheckedObjectsTestSetValidator.isValid(arg1)) {
- throw new RuntimeException();
- }
-
- return arg1;
- }
-
- /*
- * @see
- * com.google.gwt.user.client.rpc.TypeCheckedObjectsTestService#echo(com.google
* .gwt.user.client.rpc.TypeUncheckedGenericClass)
*/
@Override