Unify object tracking in client- and server-side SerializationStreamWriters.
This will correct a condition in which unique objects will not be written into the stream if there is a hash collision in System.identityHashCode().
Patch by: bobv
Reported by: shuniu
Review by: scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2335 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
index 366fd05..60c2e0c 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -18,14 +18,36 @@
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
/**
* Base class for the client and server serialization streams. This class
* handles the basic serialization and deserialization formatting for primitive
- * types since these are common between the client and the server.
+ * types since these are common between the client and the server. It also
+ * handles Object- and String-tracking for building graph references.
*/
public abstract class AbstractSerializationStreamWriter extends
AbstractSerializationStream implements SerializationStreamWriter {
+ private int objectCount;
+
+ private Map<Object, Integer> objectMap = new IdentityHashMap<Object, Integer>();
+
+ private Map<String, Integer> stringMap = new HashMap<String, Integer>();
+
+ private List<String> stringTable = new ArrayList<String>();
+
+ public void prepareToWrite() {
+ objectCount = 0;
+ objectMap.clear();
+ stringMap.clear();
+ stringTable.clear();
+ }
+
@Override
public abstract String toString();
@@ -97,7 +119,20 @@
* @param string the string to add
* @return the index to the string
*/
- protected abstract int addString(String string);
+ protected int addString(String string) {
+ if (string == null) {
+ return 0;
+ }
+ Integer o = stringMap.get(string);
+ if (o != null) {
+ return o;
+ }
+ stringTable.add(string);
+ // index is 1-based
+ int index = stringTable.size();
+ stringMap.put(string, index);
+ return index;
+ }
/**
* Append a token to the underlying output buffer.
@@ -114,7 +149,9 @@
* @return the index associated with this object, or -1 if this object hasn't
* been seen before
*/
- protected abstract int getIndexForObject(Object instance);
+ protected int getIndexForObject(Object instance) {
+ return objectMap.containsKey(instance) ? objectMap.get(instance) : -1;
+ }
/**
* Compute and return the type signature for an object.
@@ -125,11 +162,20 @@
protected abstract String getObjectTypeSignature(Object instance);
/**
+ * Gets the string table.
+ */
+ protected List<String> getStringTable() {
+ return stringTable;
+ }
+
+ /**
* Remember this object as having been seen before.
*
* @param instance the object to remember
*/
- protected abstract void saveIndexForObject(Object instance);
+ protected void saveIndexForObject(Object instance) {
+ objectMap.put(instance, objectCount++);
+ }
/**
* Serialize an object into the stream.
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
index d466958..85dfc22 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -15,10 +15,9 @@
*/
package com.google.gwt.user.client.rpc.impl;
-import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.rpc.SerializationException;
-import java.util.ArrayList;
+import java.util.List;
/**
* For internal use only. Used for server call serialization.
@@ -36,29 +35,10 @@
private final String moduleBaseURL;
- private int objectCount;
-
- /*
- * Accessed from JSNI code, so ignore unused warning.
- */
- @SuppressWarnings("unused")
- private JavaScriptObject objectMap;
-
private final String serializationPolicyStrongName;
private final Serializer serializer;
- /*
- * Accesses need to be prefixed with ':' to prevent conflict with built-in
- * JavaScript properties.
- *
- * Accessed from JSNI code, so ignore unused warning.
- */
- @SuppressWarnings("unused")
- private JavaScriptObject stringMap;
-
- private ArrayList<String> stringTable = new ArrayList<String>();
-
/**
* Constructs a <code>ClientSerializationStreamWriter</code> that does not
* use a serialization policy file.
@@ -94,10 +74,7 @@
* implementation <b>must</b> be called by any overridden version.
*/
public void prepareToWrite() {
- objectCount = 0;
- objectMap = JavaScriptObject.createObject();
- stringMap = JavaScriptObject.createObject();
- stringTable.clear();
+ super.prepareToWrite();
encodeBuffer = new StringBuffer();
if (hasSerializationPolicyInfo()) {
@@ -119,23 +96,6 @@
append(Long.toString(fieldValue, 16));
}
- @Override
- protected int addString(String string) {
- if (string == null) {
- return 0;
- }
-
- int index = getIntForString(string);
- if (index > 0) {
- return index;
- }
- stringTable.add(string);
- // index is 1-based (that's why we're taking the size AFTER add)
- index = stringTable.size();
- setIntForString(string, index);
- return index;
- }
-
/**
* Appends a token to the end of the buffer.
*/
@@ -145,11 +105,6 @@
}
@Override
- protected int getIndexForObject(Object instance) {
- return getIntForInt(System.identityHashCode(instance));
- }
-
- @Override
protected String getObjectTypeSignature(Object o) {
Class<?> clazz = o.getClass();
@@ -159,7 +114,7 @@
}
String typeName = clazz.getName();
-
+
String serializationSignature = serializer.getSerializationSignature(typeName);
if (serializationSignature != null) {
typeName += "/" + serializationSignature;
@@ -168,36 +123,11 @@
}
@Override
- protected void saveIndexForObject(Object instance) {
- setIntForInt(System.identityHashCode(instance), objectCount++);
- }
-
- @Override
protected void serialize(Object instance, String typeSignature)
throws SerializationException {
serializer.serialize(this, instance, typeSignature);
}
- private native int getIntForInt(int key) /*-{
- var result = this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter::objectMap[key];
- return (result == null) ? -1 : result;
- }-*/;
-
- // prefix needed to prevent conflict with built-in JavaScript properties.
- private native int getIntForString(String key) /*-{
- var result = this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter::stringMap[':' + key];
- return (result == null) ? 0 : result;
- }-*/;
-
- private native void setIntForInt(int key, int value) /*-{
- this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter::objectMap[key] = value;
- }-*/;
-
- // prefix needed to prevent conflict with built-in JavaScript properties.
- private native void setIntForString(String key, int value) /*-{
- this.@com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter::stringMap[':' + key] = value;
- }-*/;
-
private void writeHeader(StringBuffer buffer) {
append(buffer, String.valueOf(getVersion()));
append(buffer, String.valueOf(getFlags()));
@@ -208,10 +138,10 @@
}
private StringBuffer writeStringTable(StringBuffer buffer) {
- int stringTableSize = stringTable.size();
- append(buffer, String.valueOf(stringTableSize));
- for (int i = 0; i < stringTableSize; ++i) {
- append(buffer, stringTable.get(i));
+ List<String> stringTable = getStringTable();
+ append(buffer, String.valueOf(stringTable.size()));
+ for (String s : stringTable) {
+ append(buffer, s);
}
return buffer;
}
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
index 9dc1e9d..2b87eef 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
@@ -25,8 +25,8 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -330,13 +330,13 @@
}
/**
- * Returns the {@link Class} instance to use for serialization. Enumerations
- * are serialized as their declaring class while all others are serialized
+ * Returns the {@link Class} instance to use for serialization. Enumerations
+ * are serialized as their declaring class while all others are serialized
* using their true class instance.
*/
private static Class<?> getClassForSerialization(Object instance) {
assert (instance != null);
-
+
if (instance instanceof Enum) {
Enum<?> e = (Enum<?>) instance;
return e.getDeclaringClass();
@@ -436,14 +436,6 @@
}
}
- private int objectCount;
-
- private IdentityHashMap<Object, Integer> objectMap = new IdentityHashMap<Object, Integer>();
-
- private HashMap<String, Integer> stringMap = new HashMap<String, Integer>();
-
- private ArrayList<String> stringTable = new ArrayList<String>();
-
private ArrayList<String> tokenList = new ArrayList<String>();
private int tokenListCharCount;
@@ -454,13 +446,11 @@
this.serializationPolicy = serializationPolicy;
}
+ @Override
public void prepareToWrite() {
- objectCount = 0;
- objectMap.clear();
+ super.prepareToWrite();
tokenList.clear();
tokenListCharCount = 0;
- stringMap.clear();
- stringTable.clear();
}
public void serializeValue(Object value, Class<?> type)
@@ -505,22 +495,6 @@
}
@Override
- protected int addString(String string) {
- if (string == null) {
- return 0;
- }
- Integer o = stringMap.get(string);
- if (o != null) {
- return o;
- }
- stringTable.add(string);
- // index is 1-based
- int index = stringTable.size();
- stringMap.put(string, index);
- return index;
- }
-
- @Override
protected void append(String token) {
tokenList.add(token);
if (token != null) {
@@ -529,18 +503,9 @@
}
@Override
- protected int getIndexForObject(Object instance) {
- Integer o = objectMap.get(instance);
- if (o != null) {
- return o;
- }
- return -1;
- }
-
- @Override
protected String getObjectTypeSignature(Object instance) {
assert (instance != null);
-
+
Class<?> clazz = getClassForSerialization(instance);
if (shouldEnforceTypeVersioning()) {
return SerializabilityUtil.encodeSerializedInstanceReference(clazz);
@@ -550,11 +515,6 @@
}
@Override
- protected void saveIndexForObject(Object instance) {
- objectMap.put(instance, objectCount++);
- }
-
- @Override
protected void serialize(Object instance, String typeSignature)
throws SerializationException {
assert (instance != null);
@@ -691,6 +651,7 @@
}
private void writeStringTable(StringBuffer buffer) {
+ List<String> stringTable = getStringTable();
if (tokenList.size() > 0) {
buffer.append(",");
}