When generating serializers and deserializers for RPC, don't support
sending types *to* the browser that are only sent *from* the browser,
and vice versa.

Review by: kprobst

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4069 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java b/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
index 63e4a4e..ed74fe3 100644
--- a/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
+++ b/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
@@ -101,6 +101,10 @@
   private static final JClassType[] NO_JCLASSES = new JClassType[0];
   private static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\s");
 
+  /**
+   * Returns the binary name of a type. This is the same name that would be
+   * returned by {@link Class#getName()} for this type.
+   */
   public static String computeBinaryClassName(JType type) {
     JPrimitiveType primitiveType = type.isPrimitive();
     if (primitiveType != null) {
diff --git a/dev/core/src/com/google/gwt/dev/js/JsParser.java b/dev/core/src/com/google/gwt/dev/js/JsParser.java
index 3ddf964..32a5fed 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsParser.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsParser.java
@@ -884,6 +884,9 @@
 
       case TokenStream.NULL:
         return program.getNullLiteral();
+        
+      case TokenStream.UNDEFINED:
+        return program.getUndefinedLiteral();
 
       default:
         throw new JsParserException("Unknown primary: " + node.getIntDatum());
diff --git a/user/src/com/google/gwt/user/rebind/rpc/FieldSerializerCreator.java b/user/src/com/google/gwt/user/rebind/rpc/FieldSerializerCreator.java
index a0bcd41..e21bf52 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/FieldSerializerCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/FieldSerializerCreator.java
@@ -24,6 +24,8 @@
 import com.google.gwt.core.ext.typeinfo.JField;
 import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
 import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.javac.TypeOracleMediator;
 import com.google.gwt.user.client.rpc.SerializationException;
 import com.google.gwt.user.client.rpc.SerializationStreamReader;
 import com.google.gwt.user.client.rpc.SerializationStreamWriter;
@@ -49,34 +51,41 @@
 
   private final JField[] serializableFields;
 
-  private final SerializableTypeOracle serializationOracle;
-
   private SourceWriter sourceWriter;
 
+  private final SerializableTypeOracle typesSentFromBrowser;
+
+  private final SerializableTypeOracle typesSentToBrowser;
+
+  private final TypeOracle typeOracle;
+
   /**
    * Constructs a field serializer for the class.
-   * 
-   * @param serializationOracle
-   * @param requestedClass
    */
-  public FieldSerializerCreator(SerializableTypeOracle serializationOracle,
-      JClassType requestedClass) {
+  public FieldSerializerCreator(TypeOracle typeOracle,
+      SerializableTypeOracle typesSentFromBrowser,
+      SerializableTypeOracle typesSentToBrowser, JClassType requestedClass) {
     assert (requestedClass != null);
     assert (requestedClass.isClass() != null || requestedClass.isArray() != null);
 
-    this.serializationOracle = serializationOracle;
+    this.typeOracle = typeOracle;
+    this.typesSentFromBrowser = typesSentFromBrowser;
+    this.typesSentToBrowser = typesSentToBrowser;
     serializableClass = requestedClass;
-    serializableFields = serializationOracle.getSerializableFields(requestedClass);
+    serializableFields = SerializationUtils.getSerializableFields(typeOracle,
+        requestedClass);
   }
 
   public String realize(TreeLogger logger, GeneratorContext ctx) {
     assert (ctx != null);
-    assert (serializationOracle.isSerializable(serializableClass));
+    assert (typesSentFromBrowser.isSerializable(serializableClass) || typesSentToBrowser.isSerializable(serializableClass));
 
     logger = logger.branch(TreeLogger.DEBUG,
         "Generating a field serializer for type '"
             + serializableClass.getQualifiedSourceName() + "'", null);
-    String fieldSerializerName = serializationOracle.getFieldSerializerName(serializableClass);
+
+    String fieldSerializerName = SerializationUtils.getFieldSerializerName(
+        typeOracle, serializableClass);
 
     sourceWriter = getSourceWriter(logger, ctx);
     if (sourceWriter == null) {
@@ -111,7 +120,8 @@
   }
 
   private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext ctx) {
-    String qualifiedSerializerName = serializationOracle.getFieldSerializerName(serializableClass);
+    String qualifiedSerializerName = SerializationUtils.getFieldSerializerName(
+        typeOracle, serializableClass);
     int packageNameEnd = qualifiedSerializerName.lastIndexOf('.');
     String className;
     String packageName;
@@ -137,8 +147,8 @@
   private void maybeSuppressLongWarnings(JType fieldType) {
     if (fieldType == JPrimitiveType.LONG) {
       /**
-       * Accessing long from JSNI causes a error, but field serializers need
-       * to be able to do just that in order to bypass java accessibility
+       * Accessing long from JSNI causes a error, but field serializers need to
+       * be able to do just that in order to bypass java accessibility
        * restrictions.
        */
       sourceWriter.println("@" + UnsafeNativeLong.class.getName());
@@ -281,8 +291,10 @@
     sourceWriter.println();
 
     JClassType superClass = serializableClass.getSuperclass();
-    if (superClass != null && serializationOracle.isSerializable(superClass)) {
-      String fieldSerializerName = serializationOracle.getFieldSerializerName(superClass);
+    if (superClass != null
+        && (typesSentFromBrowser.isSerializable(superClass) || typesSentToBrowser.isSerializable(superClass))) {
+      String fieldSerializerName = SerializationUtils.getFieldSerializerName(
+          typeOracle, superClass);
       sourceWriter.print(fieldSerializerName);
       sourceWriter.println(".deserialize(streamReader, instance);");
     }
@@ -311,8 +323,10 @@
     sourceWriter.println();
 
     JClassType superClass = serializableClass.getSuperclass();
-    if (superClass != null && serializationOracle.isSerializable(superClass)) {
-      String fieldSerializerName = serializationOracle.getFieldSerializerName(superClass);
+    if (superClass != null
+        && (typesSentFromBrowser.isSerializable(superClass) || typesSentToBrowser.isSerializable(superClass))) {
+      String fieldSerializerName = SerializationUtils.getFieldSerializerName(
+          typeOracle, superClass);
       sourceWriter.print(fieldSerializerName);
       sourceWriter.println(".serialize(streamWriter, instance);");
     }
@@ -384,7 +398,7 @@
     sourceWriter.indent();
 
     sourceWriter.print("return instance.@");
-    sourceWriter.print(serializationOracle.getSerializedTypeName(serializableClass));
+    sourceWriter.print(TypeOracleMediator.computeBinaryClassName(serializableClass));
     sourceWriter.print("::");
     sourceWriter.print(fieldName);
     sourceWriter.println(";");
@@ -415,7 +429,7 @@
     sourceWriter.indent();
 
     sourceWriter.print("instance.@");
-    sourceWriter.print(serializationOracle.getSerializedTypeName(serializableClass));
+    sourceWriter.print(TypeOracleMediator.computeBinaryClassName(serializableClass));
     sourceWriter.print("::");
     sourceWriter.print(fieldName);
     sourceWriter.println(" = value;");
diff --git a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
index 75615ff..77726a2 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
@@ -30,6 +30,7 @@
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.generator.GenUtil;
 import com.google.gwt.dev.generator.NameFactory;
+import com.google.gwt.dev.javac.TypeOracleMediator;
 import com.google.gwt.dev.util.Util;
 import com.google.gwt.http.client.Request;
 import com.google.gwt.http.client.RequestBuilder;
@@ -49,8 +50,11 @@
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Creates a client-side proxy for a
@@ -69,8 +73,10 @@
    * methods.
    */
   private static void addRemoteServiceRootTypes(TreeLogger logger,
-      TypeOracle typeOracle, SerializableTypeOracleBuilder stob,
-      JClassType remoteService) throws NotFoundException {
+      TypeOracle typeOracle,
+      SerializableTypeOracleBuilder typesSentFromBrowser,
+      SerializableTypeOracleBuilder typesSentToBrowser, JClassType remoteService)
+      throws NotFoundException {
     logger = logger.branch(TreeLogger.DEBUG, "Analyzing '"
         + remoteService.getParameterizedQualifiedSourceName()
         + "' for serializable types", null);
@@ -89,7 +95,7 @@
         TreeLogger returnTypeLogger = methodLogger.branch(TreeLogger.DEBUG,
             "Return type: " + returnType.getParameterizedQualifiedSourceName(),
             null);
-        stob.addRootType(returnTypeLogger, returnType);
+        typesSentToBrowser.addRootType(returnTypeLogger, returnType);
       }
 
       JParameter[] params = method.getParameters();
@@ -97,7 +103,7 @@
         TreeLogger paramLogger = methodLogger.branch(TreeLogger.DEBUG,
             "Parameter: " + param.toString(), null);
         JType paramType = param.getType();
-        stob.addRootType(paramLogger, paramType);
+        typesSentFromBrowser.addRootType(paramLogger, paramType);
       }
 
       JType[] exs = method.getThrows();
@@ -114,7 +120,7 @@
                 null);
           }
 
-          stob.addRootType(throwsLogger, ex);
+          typesSentToBrowser.addRootType(throwsLogger, ex);
         }
       }
     }
@@ -138,6 +144,20 @@
     stob.addRootType(logger, icseType);
   }
 
+  /**
+   * Take the union of two type arrays, and then sort the results
+   * alphabetically.
+   */
+  private static JType[] unionOfTypeArrays(JType[] types1, JType[] types2) {
+    Set<JType> typesList = new HashSet<JType>();
+    typesList.addAll(Arrays.asList(types1));
+    typesList.addAll(Arrays.asList(types2));
+    JType[] serializableTypes = typesList.toArray(new JType[0]);
+    Arrays.sort(serializableTypes,
+        SerializableTypeOracleBuilder.JTYPE_COMPARATOR);
+    return serializableTypes;
+  }
+
   private JClassType serviceIntf;
 
   {
@@ -207,12 +227,16 @@
         serviceIntf, serviceAsync);
 
     // Determine the set of serializable types
-    SerializableTypeOracleBuilder stob = new SerializableTypeOracleBuilder(
+    SerializableTypeOracleBuilder typesSentFromBrowserBuilder = new SerializableTypeOracleBuilder(
+        logger, context.getPropertyOracle(), typeOracle);
+    SerializableTypeOracleBuilder typesSentToBrowserBuilder = new SerializableTypeOracleBuilder(
         logger, context.getPropertyOracle(), typeOracle);
     try {
-      addRequiredRoots(logger, typeOracle, stob);
+      addRequiredRoots(logger, typeOracle, typesSentFromBrowserBuilder);
+      addRequiredRoots(logger, typeOracle, typesSentToBrowserBuilder);
 
-      addRemoteServiceRootTypes(logger, typeOracle, stob, serviceIntf);
+      addRemoteServiceRootTypes(logger, typeOracle,
+          typesSentFromBrowserBuilder, typesSentToBrowserBuilder, serviceIntf);
     } catch (NotFoundException e) {
       logger.log(TreeLogger.ERROR, "", e);
       throw new UnableToCompleteException();
@@ -223,24 +247,28 @@
     // output.
     OutputStream pathInfo = context.tryCreateResource(logger,
         serviceIntf.getQualifiedSourceName() + ".rpc.log");
-    stob.setLogOutputStream(pathInfo);
-    SerializableTypeOracle sto = stob.build(logger);
+    typesSentFromBrowserBuilder.setLogOutputStream(pathInfo);
+    SerializableTypeOracle typesSentFromBrowser = typesSentFromBrowserBuilder.build(logger);
+    SerializableTypeOracle typesSentToBrowser = typesSentToBrowserBuilder.build(logger);
     if (pathInfo != null) {
       context.commitResource(logger, pathInfo).setPrivate(true);
     }
 
-    TypeSerializerCreator tsc = new TypeSerializerCreator(logger, sto, context,
-        sto.getTypeSerializerQualifiedName(serviceIntf));
+    TypeSerializerCreator tsc = new TypeSerializerCreator(logger,
+        typesSentFromBrowser, typesSentToBrowser, context,
+        SerializationUtils.getTypeSerializerQualifiedName(serviceIntf));
     tsc.realize(logger);
 
     String serializationPolicyStrongName = writeSerializationPolicyFile(logger,
-        context, sto);
+        context, typesSentFromBrowser, typesSentToBrowser);
 
-    generateProxyFields(srcWriter, sto, serializationPolicyStrongName);
+    generateProxyFields(srcWriter, typesSentFromBrowser,
+        serializationPolicyStrongName);
 
     generateProxyContructor(javadocAnnotationDeprecationBranch, srcWriter);
 
-    generateProxyMethods(srcWriter, sto, syncMethToAsyncMethMap);
+    generateProxyMethods(srcWriter, typesSentFromBrowser,
+        syncMethToAsyncMethMap);
 
     srcWriter.commit(logger);
 
@@ -275,10 +303,10 @@
       String serializationPolicyStrongName) {
     // Initialize a field with binary name of the remote service interface
     srcWriter.println("private static final String REMOTE_SERVICE_INTERFACE_NAME = \""
-        + serializableTypeOracle.getSerializedTypeName(serviceIntf) + "\";");
+        + TypeOracleMediator.computeBinaryClassName(serviceIntf) + "\";");
     srcWriter.println("private static final String SERIALIZATION_POLICY =\""
         + serializationPolicyStrongName + "\";");
-    String typeSerializerName = serializableTypeOracle.getTypeSerializerQualifiedName(serviceIntf);
+    String typeSerializerName = SerializationUtils.getTypeSerializerQualifiedName(serviceIntf);
     srcWriter.println("private static final " + typeSerializerName
         + " SERIALIZER = new " + typeSerializerName + "();");
     srcWriter.println();
@@ -366,7 +394,7 @@
     for (JParameter param : syncParams) {
       w.println(streamWriterName
           + ".writeString(\""
-          + serializableTypeOracle.getSerializedTypeName(param.getType().getErasedType())
+          + TypeOracleMediator.computeBinaryClassName(param.getType().getErasedType())
           + "\");");
     }
 
@@ -530,7 +558,8 @@
   }
 
   private String writeSerializationPolicyFile(TreeLogger logger,
-      GeneratorContext ctx, SerializableTypeOracle sto)
+      GeneratorContext ctx, SerializableTypeOracle serializationSto,
+      SerializableTypeOracle deserializationSto)
       throws UnableToCompleteException {
     try {
       ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -538,12 +567,21 @@
           SerializationPolicyLoader.SERIALIZATION_POLICY_FILE_ENCODING);
       PrintWriter pw = new PrintWriter(osw);
 
-      JType[] serializableTypes = sto.getSerializableTypes();
+      JType[] serializableTypes = unionOfTypeArrays(
+          serializationSto.getSerializableTypes(),
+          deserializationSto.getSerializableTypes());
+
       for (int i = 0; i < serializableTypes.length; ++i) {
-        JType serializableType = serializableTypes[i];
-        String binaryTypeName = sto.getSerializedTypeName(serializableType);
-        boolean maybeInstantiated = sto.maybeInstantiated(serializableType);
-        pw.println(binaryTypeName + ", " + Boolean.toString(maybeInstantiated));
+        JType type = serializableTypes[i];
+        String binaryTypeName = TypeOracleMediator.computeBinaryClassName(type);
+        pw.print(binaryTypeName);
+        pw.print(", "
+            + Boolean.toString(deserializationSto.isSerializable(type)));
+        pw.print(", "
+            + Boolean.toString(deserializationSto.maybeInstantiated(type)));
+        pw.print(", " + Boolean.toString(serializationSto.isSerializable(type)));
+        pw.println(", "
+            + Boolean.toString(serializationSto.maybeInstantiated(type)));
       }
 
       // Closes the wrapped streams.
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracle.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracle.java
index 1dde20d..942e68a 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracle.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracle.java
@@ -15,8 +15,6 @@
  */
 package com.google.gwt.user.rebind.rpc;
 
-import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.core.ext.typeinfo.JField;
 import com.google.gwt.core.ext.typeinfo.JType;
 
 /**
@@ -25,24 +23,6 @@
  * {@link com.google.gwt.user.client.rpc.RemoteService RemoteService}.
  */
 public interface SerializableTypeOracle {
-  /**
-   * Returns the name of the field serializer for a particular type. This name
-   * can be either the name of a custom field serializer or that of a generated
-   * field serializer. If the type is not serializable then it can return null.
-   * 
-   * @param type the type that is going to be serialized
-   * @return the fully qualified name of the field serializer for the given type
-   */
-  String getFieldSerializerName(JType type);
-
-  /**
-   * Returns the set of fields that are serializable for a given class type.
-   * This method does not consider any superclass fields.
-   * 
-   * @param classType the class for which we want serializable fields
-   * @return array of fields that meet the serialization criteria.
-   */
-  JField[] getSerializableFields(JClassType classType);
 
   /**
    * Returns the list of all types that are considered serializable.
@@ -52,50 +32,6 @@
   JType[] getSerializableTypes();
 
   /**
-   * Returns the serialization signature for a type.
-   * 
-   * @param instanceType
-   * @return a string representing the serialization signature of a type
-   */
-  String getSerializationSignature(JType instanceType);
-
-  /**
-   * Returns the serialized name of a type. The serialized name of a type is the
-   * name that would be returned by {@link Class#getName()}.
-   * 
-   * @param type
-   * @return serialized name of a type
-   */
-  String getSerializedTypeName(JType type);
-
-  /**
-   * Returns the qualified name of the type serializer class for the given
-   * service interface.
-   * 
-   * @param serviceIntf service interface
-   * @return name of the type serializer that handles the service interface
-   */
-  String getTypeSerializerQualifiedName(JClassType serviceIntf);
-
-  /**
-   * Returns the simple name of the type serializer class for the given service
-   * interface.
-   * 
-   * @param serviceIntf service interface
-   * @return the simple name of the type serializer class
-   */
-  String getTypeSerializerSimpleName(JClassType serviceIntf);
-
-  /**
-   * Returns the custom field serializer associated with the given type. If
-   * there is none, null is returned.
-   * 
-   * @param type type that may have a custom field serializer
-   * @return custom field serializer or null if there is none
-   */
-  JClassType hasCustomFieldSerializer(JType type);
-
-  /**
    * Returns true if the type is serializable. If a type is serializable then
    * there is a secondary type called a FieldSerializer that provides the
    * behavior necessary to serialize or deserialize the fields of an instance.
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 3bd7a0f..687c07a 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -711,8 +711,7 @@
 
     logSerializableTypes(logger, fieldSerializableTypes);
 
-    return new SerializableTypeOracleImpl(typeOracle, fieldSerializableTypes,
-        possiblyInstantiatedTypes);
+    return new SerializableTypeOracleImpl(fieldSerializableTypes, possiblyInstantiatedTypes);
   }
 
   /**
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleImpl.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleImpl.java
index 993987d..5baa7f9 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleImpl.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleImpl.java
@@ -15,232 +15,39 @@
  */
 package com.google.gwt.user.rebind.rpc;
 
-import com.google.gwt.core.ext.typeinfo.JArrayType;
 import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.core.ext.typeinfo.JField;
-import com.google.gwt.core.ext.typeinfo.JParameterizedType;
 import com.google.gwt.core.ext.typeinfo.JType;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
-import com.google.gwt.dev.javac.TypeOracleMediator;
-import com.google.gwt.dev.util.Util;
 
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
-import java.util.zip.CRC32;
 
 final class SerializableTypeOracleImpl implements SerializableTypeOracle {
 
-  private static final Comparator<JField> FIELD_COMPARATOR = new Comparator<JField>() {
-    public int compare(JField f1, JField f2) {
-      return f1.getName().compareTo(f2.getName());
-    }
-  };
-
-  private static final String GENERATED_FIELD_SERIALIZER_SUFFIX = "_FieldSerializer";
-  private static final String TYPE_SERIALIZER_SUFFIX = "_TypeSerializer";
-  private static final Set<String> TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES = new HashSet<String>();
-
-  static {
-    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Boolean");
-    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Byte");
-    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Character");
-    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Double");
-    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Exception");
-    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Float");
-    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Integer");
-    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Long");
-    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Object");
-    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Short");
-    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.String");
-    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Throwable");
-  }
-
-  private final Set<JClassType> serializableTypesSet;
-  private final TypeOracle typeOracle;
   private final Set<JClassType> possiblyInstantiatedTypes;
+  private final Set<JClassType> serializableTypesSet;
 
-  public SerializableTypeOracleImpl(TypeOracle typeOracle,
-      Set<JClassType> serializableTypes,
+  public SerializableTypeOracleImpl(Set<JClassType> serializableTypes,
       Set<JClassType> possiblyInstantiatedTypes) {
 
     serializableTypesSet = serializableTypes;
-    this.typeOracle = typeOracle;
-
     this.possiblyInstantiatedTypes = possiblyInstantiatedTypes;
   }
 
-  public String getFieldSerializerName(JType type) {
-    if (!isSerializable(type)) {
-      return null;
-    }
-
-    JClassType customSerializer = hasCustomFieldSerializer(type);
-    if (customSerializer != null) {
-      return customSerializer.getQualifiedSourceName();
-    }
-
-    assert (type.isClassOrInterface() != null || type.isArray() != null);
-    JClassType classType = (JClassType) type;
-    String[] name = Shared.synthesizeTopLevelClassName(classType,
-        GENERATED_FIELD_SERIALIZER_SUFFIX);
-    if (name[0].length() > 0) {
-      String serializerName = name[0] + "." + name[1];
-      if (SerializableTypeOracleBuilder.isInStandardJavaPackage(type.getQualifiedSourceName())) {
-        /*
-         * Don't generate code into java packages. If you do hosted mode
-         * CompilingClassLoader will fail to resolve references to the generated
-         * code.
-         */
-        serializerName = "com.google.gwt.user.client.rpc.core."
-            + serializerName;
-      }
-
-      return serializerName;
-    } else {
-      return name[1];
-    }
-  }
-
-  /**
-   * Returns the fields which qualify for serialization.
-   */
-  public JField[] getSerializableFields(JClassType classType) {
-    assert (classType != null);
-
-    List<JField> fields = new ArrayList<JField>();
-    JField[] declFields = classType.getFields();
-    assert (declFields != null);
-    for (JField field : declFields) {
-      // TODO(mmendez): this is shared with the serializable type oracle
-      // builder, join with that
-      if (field.isStatic() || field.isTransient() || field.isFinal()) {
-        continue;
-      }
-
-      fields.add(field);
-    }
-
-    Collections.sort(fields, FIELD_COMPARATOR);
-    return fields.toArray(new JField[fields.size()]);
-  }
-
-  /**
-   * 
-   */
   public JType[] getSerializableTypes() {
     return serializableTypesSet.toArray(new JType[serializableTypesSet.size()]);
   }
 
-  public String getSerializationSignature(JType type) {
-    CRC32 crc = new CRC32();
-
-    try {
-      generateSerializationSignature(type, crc);
-    } catch (UnsupportedEncodingException e) {
-      throw new RuntimeException(
-          "Could not compute the serialization signature", e);
-    }
-
-    return Long.toString(crc.getValue());
-  }
-
-  public String getSerializedTypeName(JType type) {
-    return TypeOracleMediator.computeBinaryClassName(type);
-  }
-
-  public String getTypeSerializerQualifiedName(JClassType serviceIntf) {
-    if (serviceIntf.isInterface() == null) {
-      throw new IllegalArgumentException(serviceIntf.getQualifiedSourceName()
-          + " is not a service interface");
-    }
-
-    String[] name = Shared.synthesizeTopLevelClassName(serviceIntf,
-        TYPE_SERIALIZER_SUFFIX);
-    if (name[0].length() > 0) {
-      return name[0] + "." + name[1];
-    } else {
-      return name[1];
-    }
-  }
-
-  public String getTypeSerializerSimpleName(JClassType serviceIntf) {
-    if (serviceIntf.isInterface() == null) {
-      throw new IllegalArgumentException(serviceIntf.getQualifiedSourceName()
-          + " is not a service interface");
-    }
-
-    String[] name = Shared.synthesizeTopLevelClassName(serviceIntf,
-        TYPE_SERIALIZER_SUFFIX);
-    return name[1];
-  }
-
-  public JClassType hasCustomFieldSerializer(JType type) {
-    return SerializableTypeOracleBuilder.findCustomFieldSerializer(typeOracle,
-        type);
-  }
-
   /**
-   * Returns <code>true</code> if the type is serializable.
+   * Returns <code>true</code> if the type's fields can be serializede.
    */
   public boolean isSerializable(JType type) {
     return serializableTypesSet.contains(type);
   }
 
+  /**
+   * Returns <code>true</code> if the type can be serialized and then
+   * instantiated on the other side.
+   */
   public boolean maybeInstantiated(JType type) {
     return possiblyInstantiatedTypes.contains(type);
   }
-
-  private boolean excludeImplementationFromSerializationSignature(
-      JType instanceType) {
-    if (TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.contains(instanceType.getQualifiedSourceName())) {
-      return true;
-    }
-
-    return false;
-  }
-
-  private void generateSerializationSignature(JType type, CRC32 crc)
-      throws UnsupportedEncodingException {
-    JParameterizedType parameterizedType = type.isParameterized();
-    if (parameterizedType != null) {
-      generateSerializationSignature(parameterizedType.getRawType(), crc);
-
-      return;
-    }
-
-    String serializedTypeName = getSerializedTypeName(type);
-    crc.update(serializedTypeName.getBytes(Util.DEFAULT_ENCODING));
-
-    if (excludeImplementationFromSerializationSignature(type)) {
-      return;
-    }
-
-    JClassType customSerializer = hasCustomFieldSerializer(type);
-    if (customSerializer != null) {
-      generateSerializationSignature(customSerializer, crc);
-    } else if (type.isArray() != null) {
-      JArrayType isArray = type.isArray();
-      generateSerializationSignature(isArray.getComponentType(), crc);
-    } else if (type.isClassOrInterface() != null) {
-      JClassType isClassOrInterface = type.isClassOrInterface();
-      JField[] fields = getSerializableFields(isClassOrInterface);
-      for (JField field : fields) {
-        assert (field != null);
-
-        crc.update(field.getName().getBytes(Util.DEFAULT_ENCODING));
-        crc.update(getSerializedTypeName(field.getType()).getBytes(
-            Util.DEFAULT_ENCODING));
-      }
-
-      JClassType superClass = isClassOrInterface.getSuperclass();
-      if (superClass != null) {
-        generateSerializationSignature(superClass, crc);
-      }
-    }
-  }
 }
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializationUtils.java b/user/src/com/google/gwt/user/rebind/rpc/SerializationUtils.java
new file mode 100644
index 0000000..6be308f
--- /dev/null
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializationUtils.java
@@ -0,0 +1,244 @@
+/*
+ * 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;
+
+import com.google.gwt.core.ext.typeinfo.JArrayType;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JField;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.javac.TypeOracleMediator;
+import com.google.gwt.dev.util.Util;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.CRC32;
+
+/**
+ * Utilities used for implementing serialization.
+ */
+class SerializationUtils {
+  static final Comparator<JField> FIELD_COMPARATOR = new Comparator<JField>() {
+    public int compare(JField f1, JField f2) {
+      return f1.getName().compareTo(f2.getName());
+    }
+  };
+  static final String GENERATED_FIELD_SERIALIZER_SUFFIX = "_FieldSerializer";
+  static final String TYPE_SERIALIZER_SUFFIX = "_TypeSerializer";
+  static final Set<String> TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES = new HashSet<String>();
+
+  static {
+    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Boolean");
+    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Byte");
+    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Character");
+    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Double");
+    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Exception");
+    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Float");
+    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Integer");
+    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Long");
+    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Object");
+    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Short");
+    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.String");
+    TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Throwable");
+  }
+
+  /**
+   * Returns the name of the field serializer for a particular type. This name
+   * can be either the name of a custom field serializer or that of a generated
+   * field serializer. If the type is not serializable then it can return null.
+   * 
+   * @param type the type that is going to be serialized
+   * 
+   * @return the fully qualified name of the field serializer for the given type
+   */
+  static String getFieldSerializerName(TypeOracle typeOracle, JType type) {
+    JClassType customSerializer = SerializableTypeOracleBuilder.findCustomFieldSerializer(
+        typeOracle, type);
+    if (customSerializer != null) {
+      return customSerializer.getQualifiedSourceName();
+    }
+
+    assert (type.isClassOrInterface() != null || type.isArray() != null);
+    JClassType classType = (JClassType) type;
+    String[] name = Shared.synthesizeTopLevelClassName(classType,
+        SerializationUtils.GENERATED_FIELD_SERIALIZER_SUFFIX);
+    if (name[0].length() > 0) {
+      String serializerName = name[0] + "." + name[1];
+      if (SerializableTypeOracleBuilder.isInStandardJavaPackage(type.getQualifiedSourceName())) {
+        /*
+         * Don't generate code into java packages. If you do hosted mode
+         * CompilingClassLoader will fail to resolve references to the generated
+         * code.
+         */
+        serializerName = "com.google.gwt.user.client.rpc.core."
+            + serializerName;
+      }
+
+      return serializerName;
+    } else {
+      return name[1];
+    }
+  }
+
+  /**
+   * Returns the set of fields that are serializable for a given class type.
+   * This method does not consider any superclass fields.
+   * 
+   * @param classType the class for which we want serializable fields
+   * @return array of fields that meet the serialization criteria.
+   */
+  static JField[] getSerializableFields(TypeOracle typeOracle,
+      JClassType classType) {
+    assert (classType != null);
+
+    List<JField> fields = new ArrayList<JField>();
+    JField[] declFields = classType.getFields();
+    assert (declFields != null);
+    for (JField field : declFields) {
+      // TODO(mmendez): this is shared with the serializable type oracle
+      // builder, join with that
+      if (field.isStatic() || field.isTransient() || field.isFinal()) {
+        continue;
+      }
+
+      fields.add(field);
+    }
+
+    Collections.sort(fields, FIELD_COMPARATOR);
+    return fields.toArray(new JField[fields.size()]);
+  }
+
+  /**
+   * Returns the serialization signature for a type.
+   * 
+   * @param instanceType
+   * @return a string representing the serialization signature of a type
+   */
+  static String getSerializationSignature(TypeOracle typeOracle, JType type)
+      throws RuntimeException {
+    CRC32 crc = new CRC32();
+
+    try {
+      generateSerializationSignature(typeOracle, type, crc);
+    } catch (UnsupportedEncodingException e) {
+      throw new RuntimeException(
+          "Could not compute the serialization signature", e);
+    }
+
+    return Long.toString(crc.getValue());
+  }
+
+  /**
+   * Returns the qualified name of the type serializer class for the given
+   * service interface.
+   * 
+   * @param serviceIntf service interface
+   * @return name of the type serializer that handles the service interface
+   */
+  static String getTypeSerializerQualifiedName(JClassType serviceIntf)
+      throws IllegalArgumentException {
+    if (serviceIntf.isInterface() == null) {
+      throw new IllegalArgumentException(serviceIntf.getQualifiedSourceName()
+          + " is not a service interface");
+    }
+
+    String[] name = Shared.synthesizeTopLevelClassName(serviceIntf,
+        TYPE_SERIALIZER_SUFFIX);
+    if (name[0].length() > 0) {
+      return name[0] + "." + name[1];
+    } else {
+      return name[1];
+    }
+  }
+
+  /**
+   * Returns the simple name of the type serializer class for the given service
+   * interface.
+   * 
+   * @param serviceIntf service interface
+   * @return the simple name of the type serializer class
+   */
+  static String getTypeSerializerSimpleName(TypeOracle typeOracle,
+      JClassType serviceIntf) throws IllegalArgumentException {
+    if (serviceIntf.isInterface() == null) {
+      throw new IllegalArgumentException(serviceIntf.getQualifiedSourceName()
+          + " is not a service interface");
+    }
+
+    String[] name = Shared.synthesizeTopLevelClassName(serviceIntf,
+        TYPE_SERIALIZER_SUFFIX);
+    return name[1];
+  }
+
+  private static boolean excludeImplementationFromSerializationSignature(
+      JType instanceType) {
+    if (TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.contains(instanceType.getQualifiedSourceName())) {
+      return true;
+    }
+
+    return false;
+  }
+
+  private static void generateSerializationSignature(TypeOracle typeOracle,
+      JType type, CRC32 crc) throws UnsupportedEncodingException {
+    JParameterizedType parameterizedType = type.isParameterized();
+    if (parameterizedType != null) {
+      generateSerializationSignature(typeOracle,
+          parameterizedType.getRawType(), crc);
+
+      return;
+    }
+
+    String serializedTypeName = TypeOracleMediator.computeBinaryClassName(type);
+    crc.update(serializedTypeName.getBytes(Util.DEFAULT_ENCODING));
+
+    if (excludeImplementationFromSerializationSignature(type)) {
+      return;
+    }
+
+    JClassType customSerializer = SerializableTypeOracleBuilder.findCustomFieldSerializer(
+        typeOracle, type);
+    if (customSerializer != null) {
+      generateSerializationSignature(typeOracle, customSerializer, crc);
+    } else if (type.isArray() != null) {
+      JArrayType isArray = type.isArray();
+      generateSerializationSignature(typeOracle, isArray.getComponentType(),
+          crc);
+    } else if (type.isClassOrInterface() != null) {
+      JClassType isClassOrInterface = type.isClassOrInterface();
+      JField[] fields = getSerializableFields(typeOracle, isClassOrInterface);
+      for (JField field : fields) {
+        assert (field != null);
+
+        crc.update(field.getName().getBytes(Util.DEFAULT_ENCODING));
+        crc.update(TypeOracleMediator.computeBinaryClassName(field.getType()).getBytes(
+            Util.DEFAULT_ENCODING));
+      }
+
+      JClassType superClass = isClassOrInterface.getSuperclass();
+      if (superClass != null) {
+        generateSerializationSignature(typeOracle, superClass, crc);
+      }
+    }
+  }
+
+}
diff --git a/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java b/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
index 95da14b..55f2b53 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
@@ -25,6 +25,7 @@
 import com.google.gwt.core.ext.typeinfo.JParameterizedType;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.javac.TypeOracleMediator;
 import com.google.gwt.user.client.rpc.SerializationException;
 import com.google.gwt.user.client.rpc.SerializationStreamReader;
 import com.google.gwt.user.client.rpc.SerializationStreamWriter;
@@ -34,7 +35,10 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * This class generates a class with name 'typeSerializerClassName' that is able
@@ -43,10 +47,27 @@
  */
 public class TypeSerializerCreator {
 
+  /**
+   * Default number of types to split createMethodMap entries into. Zero means
+   * no sharding occurs. Stored as a string since it is used as a default
+   * property value.
+   * 
+   * Note that the inliner will likely reassemble the shards if it is used in
+   * web mode, but it isn't needed there anyway.
+   * 
+   * TODO: remove this (and related code) when it is no longer needed.
+   */
+  private static final String DEFAULT_CREATEMETHODMAP_SHARD_SIZE = "0";
+
   private static final String DESERIALIZE_METHOD_SIGNATURE = "public native void deserialize("
       + "SerializationStreamReader streamReader, Object instance, String typeSignature)"
       + " throws SerializationException";
 
+  /**
+   * Java system property name to override the above.
+   */
+  private static final String GWT_CREATEMETHODMAP_SHARD_SIZE = "gwt.typecreator.shard.size";
+
   private static final String INSTANTIATE_METHOD_SIGNATURE = "public native Object instantiate("
       + "SerializationStreamReader streamReader, String typeSignature)"
       + " throws SerializationException";
@@ -55,23 +76,6 @@
       + "SerializationStreamWriter streamWriter, Object instance, String typeSignature)"
       + " throws SerializationException";
 
-  /**
-   * Default number of types to split createMethodMap entries into.  Zero means
-   * no sharding occurs.  Stored as a string since it is used as a default
-   * property value.
-   *
-   * Note that the inliner will likely reassemble the shards if it is used
-   * in web mode, but it isn't needed there anyway.
-   *
-   * TODO: remove this (and related code) when it is no longer needed.
-   */
-  private static final String DEFAULT_CREATEMETHODMAP_SHARD_SIZE = "0";
-
-  /**
-   * Java system property name to override the above.
-   */
-  private static final String GWT_CREATEMETHODMAP_SHARD_SIZE = "gwt.typecreator.shard.size";
-
   private static int shardSize = -1;
 
   private static void computeShardSize(TreeLogger logger)
@@ -86,19 +90,20 @@
         throw new UnableToCompleteException();
       }
     } catch (NumberFormatException e) {
-      logger.log(TreeLogger.ERROR, "Property "
-          + GWT_CREATEMETHODMAP_SHARD_SIZE + " not a number: "
-          + shardSizeProperty, e);
+      logger.log(TreeLogger.ERROR, "Property " + GWT_CREATEMETHODMAP_SHARD_SIZE
+          + " not a number: " + shardSizeProperty, e);
       throw new UnableToCompleteException();
     }
   }
 
   private final GeneratorContext context;
 
-  private final JType[] serializableTypes;
+  private final SerializableTypeOracle deserializationOracle;
 
   private final SerializableTypeOracle serializationOracle;
 
+  private final JType[] serializableTypes;
+
   private final SourceWriter srcWriter;
 
   private final TypeOracle typeOracle;
@@ -106,14 +111,20 @@
   private final String typeSerializerClassName;
 
   public TypeSerializerCreator(TreeLogger logger,
-      SerializableTypeOracle serializationOracle, GeneratorContext context,
+      SerializableTypeOracle serializationOracle,
+      SerializableTypeOracle deserializationOracle, GeneratorContext context,
       String typeSerializerClassName) throws UnableToCompleteException {
     this.context = context;
     this.typeSerializerClassName = typeSerializerClassName;
     this.serializationOracle = serializationOracle;
+    this.deserializationOracle = deserializationOracle;
+
     this.typeOracle = context.getTypeOracle();
 
-    serializableTypes = serializationOracle.getSerializableTypes();
+    Set<JType> typesSet = new HashSet<JType>();
+    typesSet.addAll(Arrays.asList(serializationOracle.getSerializableTypes()));
+    typesSet.addAll(Arrays.asList(deserializationOracle.getSerializableTypes()));
+    serializableTypes = typesSet.toArray(new JType[0]);
 
     srcWriter = getSourceWriter(logger, context);
     if (shardSize < 0) {
@@ -164,7 +175,7 @@
   private void createFieldSerializer(TreeLogger logger, GeneratorContext ctx,
       JType type) {
     assert (type != null);
-    assert (serializationOracle.isSerializable(type));
+    assert (serializationOracle.isSerializable(type) || deserializationOracle.isSerializable(type));
 
     JParameterizedType parameterizedType = type.isParameterized();
     if (parameterizedType != null) {
@@ -172,7 +183,8 @@
       return;
     }
 
-    JClassType customFieldSerializer = serializationOracle.hasCustomFieldSerializer(type);
+    JClassType customFieldSerializer = SerializableTypeOracleBuilder.findCustomFieldSerializer(
+        typeOracle, type);
     if (customFieldSerializer != null) {
       return;
     }
@@ -185,8 +197,8 @@
      */
     assert (type.isClass() != null || type.isArray() != null);
 
-    FieldSerializerCreator creator = new FieldSerializerCreator(
-        serializationOracle, (JClassType) type);
+    FieldSerializerCreator creator = new FieldSerializerCreator(typeOracle,
+        serializationOracle, deserializationOracle, (JClassType) type);
     creator.realize(logger, ctx);
   }
 
@@ -208,7 +220,8 @@
     assert (type.isArray() == null);
 
     return "create_"
-        + serializationOracle.getFieldSerializerName(type).replace('.', '_');
+        + SerializationUtils.getFieldSerializerName(typeOracle, type).replace(
+            '.', '_');
   }
 
   private String[] getPackageAndClassName(String fullClassName) {
@@ -257,8 +270,8 @@
    * @return
    */
   private String getTypeString(JType type) {
-    String typeString = serializationOracle.getSerializedTypeName(type) + "/"
-        + serializationOracle.getSerializationSignature(type);
+    String typeString = TypeOracleMediator.computeBinaryClassName(type) + "/"
+        + SerializationUtils.getSerializationSignature(typeOracle, type);
     return typeString;
   }
 
@@ -272,7 +285,7 @@
   private boolean needsCreateMethod(JType type) {
     // If this type is abstract it will not be serialized into the stream
     //
-    if (!serializationOracle.maybeInstantiated(type)) {
+    if (!deserializationOracle.maybeInstantiated(type)) {
       return false;
     }
 
@@ -280,7 +293,8 @@
       return false;
     }
 
-    JClassType customSerializer = serializationOracle.hasCustomFieldSerializer(type);
+    JClassType customSerializer = SerializableTypeOracleBuilder.findCustomFieldSerializer(
+        typeOracle, type);
     if (customSerializer == null) {
       return false;
     }
@@ -310,7 +324,8 @@
     int n = types.length;
     for (int index = 0; index < n; ++index) {
       JType type = types[index];
-      if (serializationOracle.maybeInstantiated(type)) {
+      if (serializationOracle.maybeInstantiated(type)
+          || deserializationOracle.maybeInstantiated(type)) {
         filteredTypes.add(type);
       }
     }
@@ -326,7 +341,7 @@
     JType[] types = getSerializableTypes();
     for (int typeIndex = 0; typeIndex < types.length; ++typeIndex) {
       JType type = types[typeIndex];
-      assert (serializationOracle.isSerializable(type));
+      assert (serializationOracle.isSerializable(type) || deserializationOracle.isSerializable(type));
 
       if (!needsCreateMethod(type)) {
         continue;
@@ -360,7 +375,8 @@
       boolean needComma = false;
       for (int index = 0; index < types.length; ++index) {
         JType type = types[index];
-        if (!serializationOracle.maybeInstantiated(type)) {
+        if (!serializationOracle.maybeInstantiated(type)
+            && !deserializationOracle.maybeInstantiated(type)) {
           continue;
         }
         if (needComma) {
@@ -369,8 +385,9 @@
           needComma = true;
         }
 
-        srcWriter.print("\"" + serializationOracle.getSerializedTypeName(type)
-            + "\":\"" + serializationOracle.getSerializationSignature(type)
+        srcWriter.print("\"" + TypeOracleMediator.computeBinaryClassName(type)
+            + "\":\""
+            + SerializationUtils.getSerializationSignature(typeOracle, type)
             + "\"");
       }
       srcWriter.println();
@@ -533,10 +550,11 @@
    */
   private void writeTypeMethods(JType type) {
     srcWriter.indent();
-    String serializerName = serializationOracle.getFieldSerializerName(type);
+    String serializerName = SerializationUtils.getFieldSerializerName(
+        typeOracle, type);
 
     // First the initialization method
-    {
+    if (deserializationOracle.maybeInstantiated(type)) {
       srcWriter.print("@");
       if (needsCreateMethod(type)) {
         srcWriter.print(getTypeSerializerClassName());
@@ -548,13 +566,14 @@
       }
       srcWriter.print("(L"
           + SerializationStreamReader.class.getName().replace('.', '/') + ";)");
-      srcWriter.println(",");
     }
+    srcWriter.println(",");
 
-    JClassType customSerializer = serializationOracle.hasCustomFieldSerializer(type);
+    JClassType customSerializer = SerializableTypeOracleBuilder.findCustomFieldSerializer(
+        typeOracle, type);
 
     // Now the deserialization method
-    {
+    if (deserializationOracle.isSerializable(type)) {
       // Assume param type is the concrete type of the serialized type.
       JType paramType = type;
       if (customSerializer != null) {
@@ -567,11 +586,11 @@
       srcWriter.print("::deserialize(L"
           + SerializationStreamReader.class.getName().replace('.', '/') + ";"
           + paramType.getJNISignature() + ")");
-      srcWriter.println(",");
     }
+    srcWriter.println(",");
 
     // Now the serialization method
-    {
+    if (serializationOracle.isSerializable(type)) {
       // Assume param type is the concrete type of the serialized type.
       JType paramType = type;
       if (customSerializer != null) {
diff --git a/user/src/com/google/gwt/user/server/rpc/SerializationPolicy.java b/user/src/com/google/gwt/user/server/rpc/SerializationPolicy.java
index 064d7dd..f4d2f63 100644
--- a/user/src/com/google/gwt/user/server/rpc/SerializationPolicy.java
+++ b/user/src/com/google/gwt/user/server/rpc/SerializationPolicy.java
@@ -21,6 +21,8 @@
  * This is an abstract class for representing the serialization policy for a
  * given module and
  * {@link com.google.gwt.user.client.rpc.RemoteService RemoteService}.
+ * The serialize and deserialize queries are from the perspective
+ * of the server, not the web browser.
  */
 public abstract class SerializationPolicy {
   /**
diff --git a/user/src/com/google/gwt/user/server/rpc/SerializationPolicyLoader.java b/user/src/com/google/gwt/user/server/rpc/SerializationPolicyLoader.java
index 7141141..0a4ca4a 100644
--- a/user/src/com/google/gwt/user/server/rpc/SerializationPolicyLoader.java
+++ b/user/src/com/google/gwt/user/server/rpc/SerializationPolicyLoader.java
@@ -36,7 +36,7 @@
    */
   public static final String SERIALIZATION_POLICY_FILE_ENCODING = "UTF-8";
 
-  private static final String FORMAT_ERROR_MESSAGE = "Expected: className, [true | false]";
+  private static final String FORMAT_ERROR_MESSAGE = "Expected: className, [true | false], [true | false], [true | false], [true | false]";
 
   /**
    * Returns the serialization policy file name from the from the serialization
@@ -99,7 +99,8 @@
       throw new NullPointerException("inputStream");
     }
 
-    Map<Class<?>, Boolean> whitelist = new HashMap<Class<?>, Boolean>();
+    Map<Class<?>, Boolean> whitelistSer = new HashMap<Class<?>, Boolean>();
+    Map<Class<?>, Boolean> whitelistDeser = new HashMap<Class<?>, Boolean>();
 
     InputStreamReader isr = new InputStreamReader(inputStream,
         SERIALIZATION_POLICY_FILE_ENCODING);
@@ -112,24 +113,52 @@
       if (line.length() > 0) {
         String[] components = line.split(",");
 
-        if (components.length != 2) {
+        if (components.length != 2 && components.length != 5) {
           throw new ParseException(FORMAT_ERROR_MESSAGE, lineNum);
         }
 
-        String binaryTypeName = components[0].trim();
-        String instantiable = components[1].trim();
+        for (int i = 0; i < components.length; i++) {
+          components[i] = components[i].trim();
+          if (components[i].length() == 0) {
+            throw new ParseException(FORMAT_ERROR_MESSAGE, lineNum);
+          }
+        }
+        String[] fields = new String[components.length];
 
-        if (binaryTypeName.length() == 0 || instantiable.length() == 0) {
-          throw new ParseException(FORMAT_ERROR_MESSAGE, lineNum);
+        String binaryTypeName = components[0].trim();
+        boolean fieldSer;
+        boolean instantSer;
+        boolean fieldDeser;
+        boolean instantDeser;
+        if (components.length == 2) {
+          fieldSer = fieldDeser = true;
+          instantSer = instantDeser = Boolean.valueOf(components[1]);
+        } else {
+          int idx = 1;
+          // TODO: Validate the instantiable string better.
+          fieldSer = Boolean.valueOf(components[idx++]);
+          instantSer = Boolean.valueOf(components[idx++]);
+          fieldDeser = Boolean.valueOf(components[idx++]);
+          instantDeser = Boolean.valueOf(components[idx++]);
+
+          if (!fieldSer && !fieldDeser) {
+            throw new ParseException("Type " + binaryTypeName
+                + " is neither field serializable nor field deserializable",
+                lineNum);
+          }
         }
 
         ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
 
         try {
-          Class<?> clazz = Class.forName(binaryTypeName, false, 
+          Class<?> clazz = Class.forName(binaryTypeName, false,
               contextClassLoader);
-          // TODO: Validate the instantiable string better.
-          whitelist.put(clazz, Boolean.valueOf(instantiable));
+          if (fieldSer) {
+            whitelistSer.put(clazz, instantSer);
+          }
+          if (fieldDeser) {
+            whitelistDeser.put(clazz, instantDeser);
+          }
         } catch (ClassNotFoundException ex) {
           // Ignore the error, but add it to the list of errors if one was
           // provided.
@@ -143,7 +172,7 @@
       lineNum++;
     }
 
-    return new StandardSerializationPolicy(whitelist);
+    return new StandardSerializationPolicy(whitelistSer, whitelistDeser);
   }
 
   private SerializationPolicyLoader() {
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/StandardSerializationPolicy.java b/user/src/com/google/gwt/user/server/rpc/impl/StandardSerializationPolicy.java
index e7373bf..4c1f51b 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/StandardSerializationPolicy.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/StandardSerializationPolicy.java
@@ -24,17 +24,47 @@
  * Standard implementation of a {@link SerializationPolicy}.
  */
 public class StandardSerializationPolicy extends SerializationPolicy {
-  private final Map<Class<?>, Boolean> whitelist;
+  /**
+   * Field serializable types are primitives and types on the specified
+   * whitelist.
+   */
+  private static boolean isFieldSerializable(Class<?> clazz,
+      Map<Class<?>, Boolean> whitelist) {
+    if (clazz.isPrimitive()) {
+      return true;
+    }
+    return whitelist.containsKey(clazz);
+  }
+
+  /**
+   * Instantiable types are primitives and types on the specified whitelist
+   * which can be instantiated.
+   */
+  private static boolean isInstantiable(Class<?> clazz,
+      Map<Class<?>, Boolean> whitelist) {
+    if (clazz.isPrimitive()) {
+      return true;
+    }
+    Boolean instantiable = whitelist.get(clazz);
+    return (instantiable != null && instantiable);
+  }
+
+  private final Map<Class<?>, Boolean> deserializationWhitelist;
+
+  private final Map<Class<?>, Boolean> serializationWhitelist;
 
   /**
    * Constructs a {@link SerializationPolicy} from a {@link Map}.
    */
-  public StandardSerializationPolicy(Map<Class<?>, Boolean> whitelist) {
-    if (whitelist == null) {
+  public StandardSerializationPolicy(
+      Map<Class<?>, Boolean> serializationWhitelist,
+      Map<Class<?>, Boolean> deserializationWhitelist) {
+    if (serializationWhitelist == null || deserializationWhitelist == null) {
       throw new NullPointerException("whitelist");
     }
 
-    this.whitelist = whitelist;
+    this.serializationWhitelist = serializationWhitelist;
+    this.deserializationWhitelist = deserializationWhitelist;
   }
 
   /*
@@ -44,7 +74,7 @@
    */
   @Override
   public boolean shouldDeserializeFields(Class<?> clazz) {
-    return isFieldSerializable(clazz);
+    return isFieldSerializable(clazz, deserializationWhitelist);
   }
 
   /*
@@ -54,7 +84,7 @@
    */
   @Override
   public boolean shouldSerializeFields(Class<?> clazz) {
-    return isFieldSerializable(clazz);
+    return isFieldSerializable(clazz, serializationWhitelist);
   }
 
   /*
@@ -64,7 +94,7 @@
    */
   @Override
   public void validateDeserialize(Class<?> clazz) throws SerializationException {
-    if (!isInstantiable(clazz)) {
+    if (!isInstantiable(clazz, deserializationWhitelist)) {
       throw new SerializationException(
           "Type '"
               + clazz.getName()
@@ -79,33 +109,11 @@
    */
   @Override
   public void validateSerialize(Class<?> clazz) throws SerializationException {
-    if (!isInstantiable(clazz)) {
+    if (!isInstantiable(clazz, serializationWhitelist)) {
       throw new SerializationException(
           "Type '"
               + clazz.getName()
               + "' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.");
     }
   }
-
-  /**
-   * Field serializable types are primitives and types on the whitelist.
-   */
-  private boolean isFieldSerializable(Class<?> clazz) {
-    if (clazz.isPrimitive()) {
-      return true;
-    }
-    return whitelist.containsKey(clazz);
-  }
-
-  /**
-   * Instantiable types are primitives and types on the whitelist which can be
-   * instantiated.
-   */
-  private boolean isInstantiable(Class<?> clazz) {
-    if (clazz.isPrimitive()) {
-      return true;
-    }
-    Boolean instantiable = whitelist.get(clazz);
-    return (instantiable != null && instantiable);
-  }
 }
diff --git a/user/test/com/google/gwt/user/server/rpc/SerializationPolicyLoaderTest.java b/user/test/com/google/gwt/user/server/rpc/SerializationPolicyLoaderTest.java
index 5ced7cf..4cea99e 100644
--- a/user/test/com/google/gwt/user/server/rpc/SerializationPolicyLoaderTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/SerializationPolicyLoaderTest.java
@@ -39,20 +39,78 @@
   static class B {
   }
 
-  // missing the instantiable attribute
-  private static String POLICY_FILE_MISSING_FIELD = A.class.getName();
-
-  private static String POLICY_FILE_TRIGGERS_CLASSNOTFOUND = "C,false";
-
-  private static String VALID_POLICY_FILE_CONTENTS = A.class.getName()
+  private static final String OLD_VALID_POLICY_FILE_CONTENTS = A.class.getName()
       + ", true";
 
+  // missing the instantiable attribute
+  private static final String POLICY_FILE_MISSING_FIELD = A.class.getName();
+
+  private static final String POLICY_FILE_TRIGGERS_CLASSNOTFOUND = "C,false";
+
+  private static final String VALID_POLICY_FILE_CONTENTS = A.class.getName()
+      + ", true, true, false, false\n" + B.class.getName()
+      + ", false, false, true, false\n";
+
   public static InputStream getInputStreamFromString(String content)
       throws UnsupportedEncodingException {
     return new ByteArrayInputStream(
         content.getBytes(SerializationPolicyLoader.SERIALIZATION_POLICY_FILE_ENCODING));
   }
 
+  /**
+   * Test that a valid policy file will allow the types in the policy to be used
+   * and reject those that are not.
+   * 
+   * @throws ClassNotFoundException
+   * @throws ParseException
+   */
+  public void testLoading() throws IOException, SerializationException,
+      ParseException, ClassNotFoundException {
+
+    InputStream is = getInputStreamFromString(VALID_POLICY_FILE_CONTENTS);
+    List<ClassNotFoundException> notFounds = new ArrayList<ClassNotFoundException>();
+    SerializationPolicy sp = SerializationPolicyLoader.loadFromStream(is,
+        notFounds);
+    assertTrue(notFounds.isEmpty());
+
+    assertTrue(sp.shouldSerializeFields(A.class));
+    sp.validateSerialize(A.class);
+    assertFalse(sp.shouldDeserializeFields(A.class));
+    assertCannotDeserialize(sp, A.class);
+
+    assertFalse(sp.shouldSerializeFields(B.class));
+    assertCannotDeserialize(sp, B.class);
+    assertTrue(sp.shouldDeserializeFields(B.class));
+    assertCannotDeserialize(sp, B.class);
+  }
+
+  /**
+   * Test that a valid policy file will allow the types in the policy to be used
+   * and reject those that are not. Uses the old policy file format, which is no
+   * longer generated as of November 2008.
+   * 
+   * @throws ClassNotFoundException
+   * @throws ParseException
+   */
+  public void testLoadingOldFileFormat() throws IOException,
+      SerializationException, ParseException, ClassNotFoundException {
+
+    InputStream is = getInputStreamFromString(OLD_VALID_POLICY_FILE_CONTENTS);
+    SerializationPolicy sp = SerializationPolicyLoader.loadFromStream(is);
+
+    assertTrue(sp.shouldDeserializeFields(A.class));
+    assertTrue(sp.shouldSerializeFields(A.class));
+
+    assertFalse(sp.shouldDeserializeFields(B.class));
+    assertFalse(sp.shouldSerializeFields(B.class));
+
+    sp.validateDeserialize(A.class);
+    sp.validateSerialize(A.class);
+
+    assertCannotDeserialize(sp, B.class);
+    assertCannotSerialize(sp, B.class);
+  }
+
   public void testPolicyFileMissingField() throws IOException,
       ClassNotFoundException {
     InputStream is = getInputStreamFromString(POLICY_FILE_MISSING_FIELD);
@@ -73,7 +131,7 @@
     } catch (ClassNotFoundException e) {
       // expected to get here
     }
-    
+
     // Test loading without collecting ClassNotFoundExceptions.
     is.reset();
     SerializationPolicyLoader.loadFromStream(is, null);
@@ -86,36 +144,18 @@
     assertNotNull(classNotFoundExceptions.get(0));
   }
 
-  /**
-   * Test that a valid policy file will allow the types in the policy to be used
-   * and reject those that are not.
-   * 
-   * @throws ClassNotFoundException
-   * @throws ParseException
-   */
-  public void testValidSerializationPolicy() throws IOException,
-      SerializationException, ParseException, ClassNotFoundException {
-
-    InputStream is = getInputStreamFromString(VALID_POLICY_FILE_CONTENTS);
-    SerializationPolicy sp = SerializationPolicyLoader.loadFromStream(is);
-    assertTrue(sp.shouldDeserializeFields(A.class));
-    assertTrue(sp.shouldSerializeFields(A.class));
-
-    assertFalse(sp.shouldDeserializeFields(B.class));
-    assertFalse(sp.shouldSerializeFields(B.class));
-
-    sp.validateDeserialize(A.class);
-    sp.validateSerialize(A.class);
-
+  private void assertCannotDeserialize(SerializationPolicy sp, Class<?> cls) {
     try {
-      sp.validateDeserialize(B.class);
+      sp.validateDeserialize(cls);
       fail("Expected SerializationException");
     } catch (SerializationException ex) {
       // should get here
     }
+  }
 
+  private void assertCannotSerialize(SerializationPolicy sp, Class<?> cls) {
     try {
-      sp.validateSerialize(B.class);
+      sp.validateSerialize(cls);
       fail("Expected SerializationException");
     } catch (SerializationException ex) {
       // should get here
diff --git a/user/test/com/google/gwt/user/server/rpc/impl/StandardSerializationPolicyTest.java b/user/test/com/google/gwt/user/server/rpc/impl/StandardSerializationPolicyTest.java
index bae526f..e81ead5 100644
--- a/user/test/com/google/gwt/user/server/rpc/impl/StandardSerializationPolicyTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/impl/StandardSerializationPolicyTest.java
@@ -119,6 +119,6 @@
     java.util.Map map = new HashMap();
     map.put(A.class, Boolean.TRUE);
     map.put(C.class, Boolean.FALSE);
-    return new StandardSerializationPolicy(map);
+    return new StandardSerializationPolicy(map, map);
   }
 }