Generic types that declared TypeParameters which erased to Object were considered GWT serializable even though their raw form would not be due to the explicit Object reference. This patch will treat any type parameter that erases to Object as if it were Object explicitly.
Review by: bobv
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2120 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
index 6a343a1..7d900d2 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -333,8 +333,7 @@
*/
private static final Comparator<JClassType> JCLASS_TYPE_COMPARATOR = new Comparator<JClassType>() {
public int compare(JClassType t1, JClassType t2) {
- return t1.getQualifiedSourceName().compareTo(
- t2.getQualifiedSourceName());
+ return t1.getQualifiedSourceName().compareTo(t2.getQualifiedSourceName());
}
};
@@ -697,7 +696,7 @@
}
assert (type == type.getErasedType());
-
+
if (tic.isInstantiable()) {
possiblyInstantiatedTypes.add(type);
}
@@ -713,6 +712,132 @@
possiblyInstantiatedTypes);
}
+ /*
+ * Exposed using default access to enable testing.
+ */
+ final boolean checkTypeInstantiable(TreeLogger logger, JType type,
+ boolean isSpeculative) {
+ assert (type != null);
+ if (type.isPrimitive() != null) {
+ return true;
+ }
+
+ assert (type instanceof JClassType);
+
+ JClassType classType = (JClassType) type;
+
+ TreeLogger localLogger = logger.branch(TreeLogger.DEBUG,
+ classType.getParameterizedQualifiedSourceName(), null);
+
+ TypeInfoComputed tic = getTypeInfoComputed(classType);
+ if (tic.isPendingInstantiable()) {
+ // just early out and pretend we will succeed
+ return true;
+ } else if (tic.isDone()) {
+ return tic.isInstantiable();
+ }
+
+ tic.setPendingInstantiable();
+
+ if (classType.isArray() != null) {
+ return checkArrayInstantiable(logger, classType.isArray(), tic,
+ isSpeculative);
+ } else if (classType.isWildcard() != null) {
+ return checkWildcardInstantiable(logger, classType.isWildcard(), tic,
+ isSpeculative);
+ } else if (classType.isClassOrInterface() != null) {
+ TypeInfo typeInfo = getTypeInfo(classType);
+ if (isSpeculative && typeInfo.isDirectlySerializable()) {
+ isSpeculative = false;
+ }
+
+ JClassType javaLangObject = typeOracle.getJavaLangObject();
+ if (classType == javaLangObject
+ || classType.getErasedType() == javaLangObject) {
+ /*
+ * Report an error if the type is or erases to Object since this
+ * violates our restrictions on RPC.
+ */
+ markAsUninstantiableAndLog(
+ localLogger,
+ isSpeculative,
+ "In order to produce smaller client-side code, 'Object' is not allowed; consider using a more specific type",
+ tic);
+ return false;
+ }
+
+ boolean anySubtypes = false;
+ if (checkClassOrInterfaceInstantiable(localLogger, classType,
+ isSpeculative)) {
+ tic.setInstantiable(true);
+ anySubtypes = true;
+ }
+
+ if (classType.isParameterized() != null) {
+ /*
+ * Backwards compatibility. The number of parameterization arguments
+ * specified via gwt.typeArgs could exceed the number of formal
+ * parameters declared on the generic type. Therefore have to explicitly
+ * visit them here and they must all be instantiable.
+ */
+ JParameterizedType parameterizedType = classType.isParameterized();
+ if (!checkTypeArgumentsInstantiable(localLogger, parameterizedType,
+ isSpeculative)) {
+ return false;
+ }
+ } else if (classType.isRawType() != null) {
+ TreeLogger rawTypeLogger = logger.branch(
+ TreeLogger.DEBUG,
+ "Type '"
+ + classType.getQualifiedSourceName()
+ + "' should be parameterized to help the compiler produce the smallest code size possible for your module",
+ null);
+
+ if (classType.isAssignableTo(collectionClass)
+ || classType.isAssignableTo(mapClass)) {
+ /*
+ * Backwards compatibility. Raw collections or maps force all object
+ * subtypes to be considered. Fall through to the normal class
+ * handling.
+ */
+ checkAllSubtypesOfObject(rawTypeLogger);
+ }
+ }
+
+ // Speculatively check all subtypes.
+ JClassType[] subtypes = classType.getSubtypes();
+ if (subtypes.length > 0) {
+ TreeLogger subLogger = localLogger.branch(TreeLogger.DEBUG,
+ "Analyzing subclasses:", null);
+
+ for (JClassType subType : subtypes) {
+ if (checkClassOrInterfaceInstantiable(subLogger.branch(
+ TreeLogger.DEBUG, subType.getParameterizedQualifiedSourceName(),
+ null), subType, true)) {
+ getTypeInfoComputed(subType).setInstantiable(true);
+ anySubtypes = true;
+ }
+ }
+ }
+
+ if (!anySubtypes && !isSpeculative) {
+ // No instantiable types were found
+ markAsUninstantiableAndLog(
+ logger,
+ isSpeculative,
+ "Type '"
+ + classType.getParameterizedQualifiedSourceName()
+ + "' was not serializable and has no concrete serializable subtypes",
+ tic);
+ }
+
+ return anySubtypes;
+ } else {
+ assert (false);
+ return false;
+ }
+ }
+
/**
* Consider any subtype of java.lang.Object which qualifies for serialization.
*
@@ -785,23 +910,23 @@
return false;
} else {
/*
- * Enumerated types are serializable by default, but they do not have
- * their state automatically or manually serialized. So, consider it
+ * Enumerated types are serializable by default, but they do not have
+ * their state automatically or manually serialized. So, consider it
* serializable but do not check its fields.
*/
return true;
}
}
-
+
if (type.isPrivate()) {
/*
- * Quietly ignore private types since these cannot be instantiated
- * from the generated field serializers.
+ * Quietly ignore private types since these cannot be instantiated from
+ * the generated field serializers.
*/
tic.setInstantiable(false);
return false;
}
-
+
if (type.isLocalType()) {
markAsUninstantiableAndLog(
logger,
@@ -944,126 +1069,8 @@
return allSucceeded;
}
- private boolean checkTypeInstantiable(TreeLogger logger, JType type,
- boolean isSpeculative) {
- assert (type != null);
- if (type.isPrimitive() != null) {
- return true;
- }
-
- assert (type instanceof JClassType);
-
- JClassType classType = (JClassType) type;
-
- TreeLogger localLogger = logger.branch(TreeLogger.DEBUG,
- classType.getParameterizedQualifiedSourceName(), null);
-
- TypeInfoComputed tic = getTypeInfoComputed(classType);
- if (tic.isPendingInstantiable()) {
- // just early out and pretend we will succeed
- return true;
- } else if (tic.isDone()) {
- return tic.isInstantiable();
- }
-
- tic.setPendingInstantiable();
-
- if (classType.getLeafType() == typeOracle.getJavaLangObject()) {
- markAsUninstantiableAndLog(
- logger,
- isSpeculative,
- "In order to produce smaller client-side code, 'Object' is not allowed; consider using a more specific type",
- tic);
- return false;
- }
-
- if (classType.isArray() != null) {
- return checkArrayInstantiable(logger, classType.isArray(), tic,
- isSpeculative);
- } else if (classType.isWildcard() != null) {
- return checkWildcardInstantiable(logger, classType.isWildcard(), tic,
- isSpeculative);
- } else if (classType.isClassOrInterface() != null) {
- TypeInfo typeInfo = getTypeInfo(classType);
- if (isSpeculative && typeInfo.isDirectlySerializable()) {
- isSpeculative = false;
- }
-
- boolean anySubtypes = false;
- if (checkClassOrInterfaceInstantiable(localLogger, classType,
- isSpeculative)) {
- tic.setInstantiable(true);
- anySubtypes = true;
- }
-
- if (classType.isParameterized() != null) {
- /*
- * Backwards compatibility. The number of parameterization arguments
- * specified via gwt.typeArgs could exceed the number of formal
- * parameters declared on the generic type. Therefore have to explicitly
- * visit them here and they must all be instantiable.
- */
- JParameterizedType parameterizedType = classType.isParameterized();
- if (!checkTypeArgumentsInstantiable(localLogger, parameterizedType,
- isSpeculative)) {
- return false;
- }
- } else if (classType.isRawType() != null) {
- TreeLogger rawTypeLogger = logger.branch(
- TreeLogger.DEBUG,
- "Type '"
- + classType.getQualifiedSourceName()
- + "' should be parameterized to help the compiler produce the smallest code size possible for your module",
- null);
-
- if (classType.isAssignableTo(collectionClass)
- || classType.isAssignableTo(mapClass)) {
- /*
- * Backwards compatibility. Raw collections or maps force all object
- * subtypes to be considered. Fall through to the normal class
- * handling.
- */
- checkAllSubtypesOfObject(rawTypeLogger);
- }
- }
-
- // Speculatively check all subtypes.
- JClassType[] subtypes = classType.getSubtypes();
- if (subtypes.length > 0) {
- TreeLogger subLogger = localLogger.branch(TreeLogger.DEBUG,
- "Analyzing subclasses:", null);
-
- for (JClassType subType : subtypes) {
- if (checkClassOrInterfaceInstantiable(subLogger.branch(
- TreeLogger.DEBUG, subType.getParameterizedQualifiedSourceName(),
- null), subType, true)) {
- getTypeInfoComputed(subType).setInstantiable(true);
- anySubtypes = true;
- }
- }
- }
-
- if (!anySubtypes && !isSpeculative) {
- // No instantiable types were found
- markAsUninstantiableAndLog(
- logger,
- isSpeculative,
- "Type '"
- + classType.getParameterizedQualifiedSourceName()
- + "' was not serializable and has no concrete serializable subtypes",
- tic);
- }
-
- return anySubtypes;
- } else {
- assert (false);
- return false;
- }
- }
-
private boolean checkWildcardInstantiable(TreeLogger logger,
JWildcardType wildcard, TypeInfoComputed tic, boolean isSpeculative) {
-
boolean success;
if (wildcard.getLowerBounds().length > 0) {
// Fail since ? super T for any T implies object also
diff --git a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
index fe3fcfc..bdd4d32 100644
--- a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
+++ b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
@@ -20,6 +20,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JGenericType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
@@ -32,6 +33,7 @@
import com.google.gwt.user.rebind.rpc.testcases.client.CovariantArrays;
import com.google.gwt.user.rebind.rpc.testcases.client.ManualSerialization;
import com.google.gwt.user.rebind.rpc.testcases.client.MissingGwtTypeArgs;
+import com.google.gwt.user.rebind.rpc.testcases.client.ClassWithTypeParameterThatErasesToObject;
import com.google.gwt.user.rebind.rpc.testcases.client.NoSerializableTypes;
import com.google.gwt.user.rebind.rpc.testcases.client.NotAllSubtypesAreSerializable;
import com.google.gwt.user.rebind.rpc.testcases.client.ObjectArrayInMethodSignature;
@@ -173,6 +175,30 @@
}
/**
+ * Tests that both the generic and raw forms of type that has a type parameter
+ * that erases to object are not serializable.
+ *
+ * @throws NotFoundException
+ */
+ public void testClassWithTypeParameterThatErasesToObject()
+ throws NotFoundException, UnableToCompleteException {
+ JGenericType genericType = typeOracle.getType(
+ ClassWithTypeParameterThatErasesToObject.class.getName().replace('$',
+ '.')).isGenericType();
+
+ // The generic form of the type should not be serializable.
+ SerializableTypeOracleBuilder genericStob = new SerializableTypeOracleBuilder(
+ logger, typeOracle);
+ assertFalse(genericStob.checkTypeInstantiable(logger, genericType, false));
+
+ // The raw form of the type should not be serializable.
+ SerializableTypeOracleBuilder rawStob = new SerializableTypeOracleBuilder(
+ logger, typeOracle);
+ assertFalse(rawStob.checkTypeInstantiable(logger, genericType.getRawType(),
+ false));
+ }
+
+ /**
* Tests that a method signature which returns an Array type also includes the
* possible covariant array types which could contain a serializable type.
*/
diff --git a/user/test/com/google/gwt/user/rebind/rpc/testcases/client/ClassWithTypeParameterThatErasesToObject.java b/user/test/com/google/gwt/user/rebind/rpc/testcases/client/ClassWithTypeParameterThatErasesToObject.java
new file mode 100644
index 0000000..008001d
--- /dev/null
+++ b/user/test/com/google/gwt/user/rebind/rpc/testcases/client/ClassWithTypeParameterThatErasesToObject.java
@@ -0,0 +1,28 @@
+/*
+ * 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.rebind.rpc.testcases.client;
+
+import java.io.Serializable;
+
+/**
+ * This class should fail to be serializable because it uses a type parameter
+ * that erases to Object.
+ *
+ * @param <T>
+ */
+public class ClassWithTypeParameterThatErasesToObject<T> implements Serializable {
+ T t;
+}
\ No newline at end of file