Do not visit the fields of enumerated types and do not consider nested types that are not visible to a class in the parent package.
Review by: spoon (desk check)
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2872 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 6478697..3a9bf67 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -497,6 +497,22 @@
return isSpeculative ? TreeLogger.WARN : TreeLogger.ERROR;
}
+ /**
+ * Returns <code>true</code> if the query type is accessible to classes in
+ * the same package.
+ */
+ private static boolean isAccessibleToClassesInSamePackage(JClassType type) {
+ if (type.isPrivate()) {
+ return false;
+ }
+
+ if (type.isMemberType()) {
+ return isAccessibleToClassesInSamePackage(type.getEnclosingType());
+ }
+
+ return true;
+ }
+
private static void logSerializableTypes(TreeLogger logger,
Set<JClassType> fieldSerializableTypes) {
TreeLogger localLogger = logger.branch(TreeLogger.DEBUG, "Identified "
@@ -943,7 +959,8 @@
final boolean checkTypeInstantiableNoSubtypes(TreeLogger logger,
JClassType type, boolean isSpeculative, Path path) {
if (qualifiesForSerialization(logger, type, isSpeculative, path)) {
- return checkFields(logger, type, isSpeculative, path);
+ return type.isEnum() != null
+ || checkFields(logger, type, isSpeculative, path);
}
return false;
@@ -955,6 +972,8 @@
/**
* Returns <code>true</code> if the type qualifies for serialization.
+ *
+ * Default access to allow for testing.
*/
boolean qualifiesForSerialization(TreeLogger logger, JClassType type,
boolean isSpeculative, Path parent) {
@@ -985,30 +1004,6 @@
} else {
assert (typeInfo.isAutoSerializable());
- if (type.isEnum() != null) {
- if (type.isLocalType()) {
- /*
- * Quietly ignore local enum types.
- */
- return false;
- } else {
- /*
- * 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 !type.isPrivate();
- }
- }
-
- if (type.isPrivate()) {
- /*
- * Quietly ignore private types since these cannot be instantiated from
- * the generated field serializers.
- */
- return false;
- }
-
/*
* Speculative paths log at DEBUG level, non-speculative ones log at WARN
* level.
@@ -1016,7 +1011,18 @@
TreeLogger.Type logLevel = isSpeculative ? TreeLogger.DEBUG
: TreeLogger.WARN;
+ if (!isAccessibleToClassesInSamePackage(type)) {
+ // Class is not visible to a serializer class in the same package
+ logger.branch(
+ logLevel,
+ type.getParameterizedQualifiedSourceName()
+ + " is not accessible from a class in its same package; it will be excluded from the set of serializable types",
+ null);
+ return false;
+ }
+
if (type.isLocalType()) {
+ // Local types cannot be serialized
logger.branch(
logLevel,
type.getParameterizedQualifiedSourceName()
@@ -1026,6 +1032,7 @@
}
if (type.isMemberType() && !type.isStatic()) {
+ // Non-static member types cannot be serialized
logger.branch(
logLevel,
type.getParameterizedQualifiedSourceName()
@@ -1034,18 +1041,26 @@
return false;
}
- if (type.isAbstract()) {
- // Abstract types will be picked up if there is an instantiable subtype.
- return false;
- }
+ if (type.isEnum() == null) {
+ if (type.isAbstract()) {
+ // Abstract types will be picked up if there is an instantiable
+ // subtype.
+ return false;
+ }
- if (!type.isDefaultInstantiable()) {
- // Warn and return false.
- logger.log(
- logLevel,
- "Was not default instantiable (it must have a zero-argument constructor or no constructors at all)",
- null);
- return false;
+ if (!type.isDefaultInstantiable()) {
+ // Warn and return false.
+ logger.log(
+ logLevel,
+ "Was not default instantiable (it must have a zero-argument constructor or no constructors at all)",
+ null);
+ return false;
+ }
+ } else {
+ /*
+ * Enums are always instantiable regardless of abstract or default
+ * instantiability.
+ */
}
}
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 bec1bbd..120907c 100644
--- a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
+++ b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
@@ -431,6 +431,179 @@
}
/**
+ * Tests the rules that govern whether a type qualifies for serialization.
+ *
+ * @throws UnableToCompleteException
+ * @throws NotFoundException
+ */
+ public void testClassQualifiesForSerialization()
+ throws UnableToCompleteException, NotFoundException {
+ Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+ addStandardClasses(units);
+
+ {
+ StringBuilder code = new StringBuilder();
+ code.append("public class NotSerializable {\n");
+ code.append("}\n");
+ units.add(createMockCompilationUnit("NotSerializable", code));
+ }
+
+ {
+ StringBuilder code = new StringBuilder();
+ code.append("import java.io.Serializable;\n");
+ code.append("public class AutoSerializable {\n");
+ code.append(" interface IFoo extends Serializable {};\n");
+ code.append(" IFoo createFoo() { return new IFoo(){};}\n");
+ code.append("}\n");
+ units.add(createMockCompilationUnit("AutoSerializable", code));
+ }
+
+ {
+ StringBuilder code = new StringBuilder();
+ code.append("import java.io.Serializable;\n");
+ code.append("public class OuterClass {\n");
+ code.append(" static class StaticNested implements Serializable {};\n");
+ code.append(" class NonStaticNested implements Serializable {};\n");
+ code.append("}\n");
+ units.add(createMockCompilationUnit("OuterClass", code));
+ }
+
+ {
+ StringBuilder code = new StringBuilder();
+ code.append("import java.io.Serializable;\n");
+ code.append("public abstract class AbstractSerializableClass implements Serializable {\n");
+ code.append("}\n");
+ units.add(createMockCompilationUnit("AbstractSerializableClass", code));
+ }
+
+ {
+ StringBuilder code = new StringBuilder();
+ code.append("import java.io.Serializable;\n");
+ code.append("public class NonDefaultInstantiableSerializable implements Serializable {\n");
+ code.append(" NonDefaultInstantiableSerializable(int i) {}\n");
+ code.append("}\n");
+ units.add(createMockCompilationUnit("NonDefaultInstantiableSerializable",
+ code));
+ }
+
+ {
+ StringBuilder code = new StringBuilder();
+ code.append("import java.io.Serializable;\n");
+ code.append("public class PublicOuterClass {\n");
+ code.append(" private static class PrivateStaticInner {\n");
+ code.append(" public static class PublicStaticInnerInner implements Serializable {\n");
+ code.append(" }\n");
+ code.append(" }\n");
+ code.append(" static class DefaultStaticInner {\n");
+ code.append(" static class DefaultStaticInnerInner implements Serializable {\n");
+ code.append(" }\n");
+ code.append(" }\n");
+ code.append("}\n");
+ units.add(createMockCompilationUnit("PublicOuterClass", code));
+ }
+
+ {
+ StringBuilder code = new StringBuilder();
+ code.append("public enum EnumWithSubclasses {\n");
+ code.append(" A {\n");
+ code.append(" @Override\n");
+ code.append(" public String value() {\n");
+ code.append(" return \"X\";\n");
+ code.append(" }\n");
+ code.append(" },\n");
+ code.append(" B {\n");
+ code.append(" @Override\n");
+ code.append(" public String value() {\n");
+ code.append(" return \"Y\";\n");
+ code.append(" };\n");
+ code.append(" };\n");
+ code.append(" public abstract String value();\n");
+ code.append("};\n");
+ units.add(createMockCompilationUnit("EnumWithSubclasses", code));
+ }
+
+ {
+ StringBuilder code = new StringBuilder();
+ code.append("public enum EnumWithNonDefaultCtors {\n");
+ code.append(" A(\"X\"), B(\"Y\");\n");
+ code.append(" String value;");
+ code.append(" private EnumWithNonDefaultCtors(String value) {\n");
+ code.append(" this.value = value;\n");
+ code.append(" }\n");
+ code.append("};\n");
+ units.add(createMockCompilationUnit("EnumWithNonDefaultCtors", code));
+ }
+
+ {
+ StringBuilder code = new StringBuilder();
+ code.append("package java.lang;\n");
+ code.append("import java.io.Serializable;\n");
+ code.append("public class Enum<E extends Enum<E>> implements Serializable {\n");
+ code.append(" protected Enum(String name, int ordinal) {\n");
+ code.append(" }\n");
+ code.append("}\n");
+ units.add(createMockCompilationUnit("java.lang.Enum", code));
+ }
+
+ TreeLogger logger = createLogger();
+ TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, units);
+ SerializableTypeOracleBuilder sob = new SerializableTypeOracleBuilder(
+ logger, to);
+
+ // Does not qualify because it is not declared to be auto or manually
+ // serializable
+ JClassType notSerializable = to.getType("NotSerializable");
+ assertFalse(sob.qualifiesForSerialization(logger, notSerializable, false,
+ null));
+
+ // Local types should not qualify for serialization
+ JClassType iFoo = to.getType("AutoSerializable.IFoo");
+ assertFalse(sob.qualifiesForSerialization(logger, iFoo.getSubtypes()[0],
+ false, null));
+
+ // Static nested types qualify for serialization
+ JClassType staticNested = to.getType("OuterClass.StaticNested");
+ assertTrue(sob.qualifiesForSerialization(logger, staticNested, false, null));
+
+ // Non-static nested types do not qualify for serialization
+ JClassType nonStaticNested = to.getType("OuterClass.NonStaticNested");
+ assertFalse(sob.qualifiesForSerialization(logger, nonStaticNested, false,
+ null));
+
+ // Abstract classes that implement Serializable should not qualify
+ JClassType abstractSerializableClass = to.getType("AbstractSerializableClass");
+ assertFalse(sob.qualifiesForSerialization(logger,
+ abstractSerializableClass, false, null));
+
+ // Non-default instantiable types should not qualify
+ JClassType nonDefaultInstantiableSerializable = to.getType("NonDefaultInstantiableSerializable");
+ assertFalse(sob.qualifiesForSerialization(logger,
+ nonDefaultInstantiableSerializable, false, null));
+
+ // SPublicStaticInnerInner is not accessible to classes in its package
+ JClassType publicStaticInnerInner = to.getType("PublicOuterClass.PrivateStaticInner.PublicStaticInnerInner");
+ assertFalse(sob.qualifiesForSerialization(logger, publicStaticInnerInner,
+ false, null));
+
+ // DefaultStaticInnerInner is visible to classes in its package
+ JClassType defaultStaticInnerInner = to.getType("PublicOuterClass.DefaultStaticInner.DefaultStaticInnerInner");
+ assertTrue(sob.qualifiesForSerialization(logger, defaultStaticInnerInner,
+ false, null));
+
+ // Enum with subclasses should qualify, but their subtypes should not
+ JClassType enumWithSubclasses = to.getType("EnumWithSubclasses");
+ assertTrue(sob.qualifiesForSerialization(logger, enumWithSubclasses, false,
+ null));
+ assertFalse(sob.qualifiesForSerialization(logger,
+ enumWithSubclasses.getSubtypes()[0], false, null));
+
+ // Enum that are not default instantiable should qualify
+ JClassType enumWithNonDefaultCtors = to.getType("EnumWithNonDefaultCtors");
+ assertTrue(sob.qualifiesForSerialization(logger, enumWithNonDefaultCtors,
+ false, null));
+ }
+
+ /**
* Tests that both the generic and raw forms of type that has a type parameter
* that erases to object are not serializable.
*