diff --git a/user/src/com/google/gwt/user/server/rpc/core/java/util/EnumMap_ServerCustomFieldSerializer.java b/user/src/com/google/gwt/user/server/rpc/core/java/util/EnumMap_ServerCustomFieldSerializer.java index dfe6b2dc7a0..cc4f16fbfa4 100644 --- a/user/src/com/google/gwt/user/server/rpc/core/java/util/EnumMap_ServerCustomFieldSerializer.java +++ b/user/src/com/google/gwt/user/server/rpc/core/java/util/EnumMap_ServerCustomFieldSerializer.java @@ -68,11 +68,26 @@ public EnumMap instantiateInstance(SerializationStreamReader streamReader) return EnumMap_CustomFieldSerializer.instantiate(streamReader); } + /** + * The key type of an {@link EnumMap} is always an enum. Read the exemplar + * object with a type check, as the other server custom field serializers + * do, so that a non-enum type cannot be substituted for it. Without the + * check the substituted type reaches {@code new EnumMap(nonEnumClass)}, + * which throws an uncaught {@link NullPointerException} rather than a clean + * serialization error. + */ + @SuppressWarnings("unused") + public static EnumMap instantiate(ServerSerializationStreamReader streamReader, + Type[] expectedParameterTypes, DequeMap, Type> resolvedTypes) + throws SerializationException { + Object exemplar = streamReader.readObject(Enum.class, resolvedTypes); + return new EnumMap(exemplar.getClass()); + } @Override public EnumMap instantiateInstance(ServerSerializationStreamReader streamReader, Type[] expectedParameterTypes, DequeMap, Type> resolvedTypes) throws SerializationException { - return EnumMap_CustomFieldSerializer.instantiate(streamReader); + return instantiate(streamReader, expectedParameterTypes, resolvedTypes); } @Override diff --git a/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckFactory.java b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckFactory.java index 9801f82c8eb..cec6b2015ec 100644 --- a/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckFactory.java +++ b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckFactory.java @@ -233,6 +233,17 @@ public String toString() { /** * Add data for an int object. */ + /** + * Writes an EnumMap whose exemplar object (the value from which the server + * derives the enum key type) is a plain Integer rather than an enum constant. + */ + public void writeEnumMapWithSpoofedExemplar(int spoofedExemplar) + throws SerializationException { + writeStringFromTable(generateSerializedClassString(java.util.EnumMap.class)); + write(Integer.valueOf(spoofedExemplar)); + bodyString += "0" + RPC_SEPARATOR_CHAR; // empty map body + } + public void write(int integer) throws SerializationException { try { bodyString += Integer.toString(integer) + RPC_SEPARATOR_CHAR; 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 1b9361e4b79..7ae7551aa03 100644 --- a/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckTest.java +++ b/user/test/com/google/gwt/user/server/rpc/RPCTypeCheckTest.java @@ -196,6 +196,10 @@ public static class ClassesParamTestClass & MInterface