Fixes issue #1973; a custom field serializer that internally called readObject() would cause the stream backrefs to get out of sync. This change fixes the off behavior and adds unit tests.
Note that if the instantiate method of a custom field serializer were to deserialize a cycle back to the original object, those refs would be seen as null in the final object graph.
Patch by: fizbin
Review by: me
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2479 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamReader.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamReader.java
index cc7118a..76ed13b 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStreamReader.java
@@ -79,12 +79,17 @@
*/
protected abstract String getString(int index);
- protected final void rememberDecodedObject(Object o) {
- seenArray.add(o);
+ protected final void rememberDecodedObject(int index, Object o) {
+
+ // index is 1-based
+ seenArray.set(index - 1, o);
}
- protected final void replaceRememberedObject(Object old, Object replacement) {
- seenArray.set(seenArray.indexOf(old), replacement);
+ protected final int reserveDecodedObjectIndex() {
+ seenArray.add(null);
+
+ // index is 1-based
+ return seenArray.size();
}
}
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
index b568186..4146c5d 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
@@ -95,8 +95,9 @@
@Override
protected Object deserialize(String typeSignature)
throws SerializationException {
+ int id = reserveDecodedObjectIndex();
Object instance = serializer.instantiate(this, typeSignature);
- rememberDecodedObject(instance);
+ rememberDecodedObject(id, instance);
serializer.deserialize(this, instance, typeSignature);
return instance;
}
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 eca479f..7b6b05e 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
@@ -457,9 +457,11 @@
Class<?> customSerializer = SerializabilityUtil.hasCustomFieldSerializer(instanceClass);
+ int index = reserveDecodedObjectIndex();
+
instance = instantiate(customSerializer, instanceClass);
- rememberDecodedObject(instance);
+ rememberDecodedObject(index, instance);
Object replacement = deserializeImpl(customSerializer, instanceClass,
instance);
@@ -467,7 +469,7 @@
// It's possible that deserializing an object requires the original proxy
// object to be replaced.
if (instance != replacement) {
- replaceRememberedObject(instance, replacement);
+ rememberDecodedObject(index, replacement);
instance = replacement;
}
diff --git a/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTest.java b/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTest.java
index 26c1abe..524a335 100644
--- a/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTest.java
+++ b/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTest.java
@@ -104,6 +104,31 @@
});
}
+ /**
+ * Test that custom serializers that call readObject() inside instantiate
+ * (as is required for most immutable classes) work.
+ */
+ public void testSerializableImmutables() {
+ delayTestFinish(TEST_DELAY);
+
+ CustomFieldSerializerTestServiceAsync service = getServiceAsync();
+ service.echo(
+ CustomFieldSerializerTestSetFactory.createSerializableImmutablesArray(),
+ new AsyncCallback() {
+ public void onFailure(Throwable caught) {
+ fail("Could not serialize/deserialize immutable classes: " +
+ caught);
+ }
+
+ public void onSuccess(Object result) {
+ assertNotNull(result);
+ assertTrue(CustomFieldSerializerTestSetValidator.isValid(
+ (ManuallySerializedImmutableClass[]) result));
+ finishTest();
+ }
+ });
+ }
+
private CustomFieldSerializerTestServiceAsync getServiceAsync() {
if (customFieldSerializerTestService == null) {
customFieldSerializerTestService = (CustomFieldSerializerTestServiceAsync) GWT.create(CustomFieldSerializerTestService.class);
diff --git a/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestService.java b/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestService.java
index e90d71e..8d0541c 100644
--- a/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestService.java
+++ b/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestService.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
@@ -25,5 +25,8 @@
public interface CustomFieldSerializerTestService extends RemoteService {
ManuallySerializedClass echo(ManuallySerializedClass manuallySerializableClass);
+ ManuallySerializedImmutableClass[] echo(
+ ManuallySerializedImmutableClass[] manuallySerializableImmutables);
+
SerializableSubclass echo(SerializableSubclass serializableClass);
}
diff --git a/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestServiceAsync.java b/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestServiceAsync.java
index 9260623..d0d2163 100644
--- a/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestServiceAsync.java
+++ b/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestServiceAsync.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
@@ -26,5 +26,8 @@
void echo(ManuallySerializedClass manuallySerializableClass,
AsyncCallback callback);
+ void echo(ManuallySerializedImmutableClass[] manuallySerializableImmutables,
+ AsyncCallback callback);
+
void echo(SerializableSubclass serializableClass, AsyncCallback callback);
}
diff --git a/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestSetFactory.java b/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestSetFactory.java
index a075066..71c67c2 100644
--- a/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestSetFactory.java
+++ b/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestSetFactory.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.user.client.rpc;
+import java.util.Date;
+
/**
* Generated test data for the
* {@link com.google.gwt.user.client.rpc.CustomFieldSerializerTest CustomFieldSerializerTest}
@@ -46,6 +48,12 @@
public static class UnserializableSubclass extends ManuallySerializedClass {
}
+ public static ManuallySerializedImmutableClass[] createSerializableImmutablesArray() {
+ ManuallySerializedImmutableClass immutable = new ManuallySerializedImmutableClass(
+ new Date(12345L), new Date(54321L));
+ return new ManuallySerializedImmutableClass[] {immutable, immutable};
+ }
+
public static SerializableSubclass createSerializableSubclass() {
return new SerializableSubclass();
}
diff --git a/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestSetValidator.java b/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestSetValidator.java
index 0d16946..ded6f14 100644
--- a/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestSetValidator.java
+++ b/user/test/com/google/gwt/user/client/rpc/CustomFieldSerializerTestSetValidator.java
@@ -34,6 +34,20 @@
&& manuallySerializedClass.getObj().equals("bye");
}
+ // Must be a non-null array with two == elements
+ public static boolean isValid(
+ ManuallySerializedImmutableClass[] manuallySerializedImmutables) {
+ if (manuallySerializedImmutables == null) {
+ return false;
+ }
+
+ if (manuallySerializedImmutables.length != 2) {
+ return false;
+ }
+
+ return manuallySerializedImmutables[0] == manuallySerializedImmutables[1];
+ }
+
public static boolean isValid(SerializableSubclass serializableSubclass) {
if (serializableSubclass == null) {
return false;
diff --git a/user/test/com/google/gwt/user/client/rpc/ManuallySerializedImmutableClass.java b/user/test/com/google/gwt/user/client/rpc/ManuallySerializedImmutableClass.java
new file mode 100644
index 0000000..baa4518
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/ManuallySerializedImmutableClass.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ * 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.util.Date;
+
+/**
+ * This class is defined outside of the CustomFieldSerializerTestSetFactory
+ * because of a bug where custom field serializers cannot be inner classes and
+ * custom field serializers must be in the same package as the class that they
+ * serializer. Once we fix this bug we can move this class into the test set
+ * factory.
+ */
+public class ManuallySerializedImmutableClass {
+ private final Date endDate;
+ private final Date startDate;
+
+ public ManuallySerializedImmutableClass(Date startDate, Date endDate) {
+ this.startDate = startDate;
+ this.endDate = endDate;
+ }
+
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public Date getStartDate() {
+ return startDate;
+ }
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/ManuallySerializedImmutableClass_CustomFieldSerializer.java b/user/test/com/google/gwt/user/client/rpc/ManuallySerializedImmutableClass_CustomFieldSerializer.java
new file mode 100644
index 0000000..0533db3
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/ManuallySerializedImmutableClass_CustomFieldSerializer.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ * 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.util.Date;
+
+/**
+ * This class is defined outside of the CustomFieldSerializerTestSetFactory
+ * because of a bug where custom field serializers cannot be inner classes. Once
+ * we fix this bug we can move this class into the test set factory.
+ */
+public class ManuallySerializedImmutableClass_CustomFieldSerializer {
+ public static void deserialize(SerializationStreamReader streamReader,
+ ManuallySerializedImmutableClass instance) {
+ // immutable; do nothing.
+ }
+
+ public static ManuallySerializedImmutableClass instantiate(
+ SerializationStreamReader streamReader) throws SerializationException {
+ Date start = (Date) streamReader.readObject();
+ Date end = (Date) streamReader.readObject();
+ return new ManuallySerializedImmutableClass(start, end);
+ }
+
+ public static void serialize(SerializationStreamWriter streamWriter,
+ ManuallySerializedImmutableClass instance) throws SerializationException {
+ streamWriter.writeObject(instance.getStartDate());
+ streamWriter.writeObject(instance.getEndDate());
+ }
+}
diff --git a/user/test/com/google/gwt/user/server/rpc/CustomFieldSerializerTestServiceImpl.java b/user/test/com/google/gwt/user/server/rpc/CustomFieldSerializerTestServiceImpl.java
index 309702e..46205ba 100644
--- a/user/test/com/google/gwt/user/server/rpc/CustomFieldSerializerTestServiceImpl.java
+++ b/user/test/com/google/gwt/user/server/rpc/CustomFieldSerializerTestServiceImpl.java
@@ -18,6 +18,7 @@
import com.google.gwt.user.client.rpc.CustomFieldSerializerTestService;
import com.google.gwt.user.client.rpc.CustomFieldSerializerTestSetValidator;
import com.google.gwt.user.client.rpc.ManuallySerializedClass;
+import com.google.gwt.user.client.rpc.ManuallySerializedImmutableClass;
import com.google.gwt.user.client.rpc.CustomFieldSerializerTestSetFactory.SerializableSubclass;
/**
@@ -37,6 +38,15 @@
return unserializableClass;
}
+ public ManuallySerializedImmutableClass[] echo(
+ ManuallySerializedImmutableClass[] manuallySerializableImmutables) {
+ if (!CustomFieldSerializerTestSetValidator.isValid(manuallySerializableImmutables)) {
+ throw new RuntimeException();
+ }
+
+ return manuallySerializableImmutables;
+ }
+
public SerializableSubclass echo(SerializableSubclass serializableClass) {
if (!CustomFieldSerializerTestSetValidator.isValid(serializableClass)) {
throw new RuntimeException();