Generator Result Caching for RPC, with some refinements to the underlying framework

Review at http://gwt-code-reviews.appspot.com/1243801


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9481 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/GeneratorContextExtWrapper.java b/dev/core/src/com/google/gwt/core/ext/GeneratorContextExtWrapper.java
new file mode 100644
index 0000000..9eed7ac
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/GeneratorContextExtWrapper.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2010 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.core.ext;
+
+import com.google.gwt.core.ext.linker.Artifact;
+import com.google.gwt.core.ext.linker.GeneratedResource;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.javac.rebind.CachedRebindResult;
+import com.google.gwt.dev.resource.ResourceOracle;
+
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+/**
+ * EXPERIMENTAL and subject to change. Do not use this in production code.
+ * <p> 
+ * A wrapper to access a base {@link GeneratorContext} instance as a 
+ * {@link GeneratorContextExt} instance.  Methods from the 
+ * {@link GeneratorContext} interface are passed through to the baseContext, 
+ * while methods from the {@link GeneratorContextExt} interface are given 
+ * default stub implementations.
+ */
+public class GeneratorContextExtWrapper implements GeneratorContextExt {
+ 
+  /**
+   * Get a new instance wrapped from a base {@link GeneratorContext} 
+   * implementation.
+   */
+  public static GeneratorContextExt newInstance(GeneratorContext baseContext) {
+    return new GeneratorContextExtWrapper(baseContext);
+  } 
+  
+  private final GeneratorContext baseContext;
+  
+  public GeneratorContextExtWrapper(GeneratorContext baseContext) {
+    this.baseContext = baseContext;
+  }
+
+  public void commit(TreeLogger logger, PrintWriter pw) {
+    baseContext.commit(logger, pw);
+  }
+
+  public void commitArtifact(TreeLogger logger, Artifact<?> artifact)
+      throws UnableToCompleteException {
+    baseContext.commitArtifact(logger, artifact);
+  }
+
+  public GeneratedResource commitResource(TreeLogger logger, OutputStream os)
+      throws UnableToCompleteException {
+    return baseContext.commitResource(logger, os);
+  }
+
+  public CachedRebindResult getCachedGeneratorResult() {
+    return null;
+  }
+
+  public PropertyOracle getPropertyOracle() {
+    return baseContext.getPropertyOracle();
+  }
+
+  public ResourceOracle getResourcesOracle() {
+    return baseContext.getResourcesOracle();
+  }
+
+  public long getSourceLastModifiedTime(JClassType sourceType) {
+    return 0L;
+  }
+
+  public TypeOracle getTypeOracle() {
+    return baseContext.getTypeOracle();
+  }
+
+  public boolean isGeneratorResultCachingEnabled() {
+    return false;
+  }
+
+  public boolean reuseTypeFromCacheIfAvailable(String typeName) {
+    return false;
+  }
+
+  public PrintWriter tryCreate(
+      TreeLogger logger, String packageName, String simpleName) {
+    return baseContext.tryCreate(logger, packageName, simpleName);
+  }
+
+  public OutputStream tryCreateResource(TreeLogger logger, String partialPath)
+      throws UnableToCompleteException {
+    return baseContext.tryCreateResource(logger, partialPath);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/GeneratorExt.java b/dev/core/src/com/google/gwt/core/ext/GeneratorExt.java
index 2a1313c..ed8c976 100644
--- a/dev/core/src/com/google/gwt/core/ext/GeneratorExt.java
+++ b/dev/core/src/com/google/gwt/core/ext/GeneratorExt.java
@@ -16,49 +16,25 @@
 package com.google.gwt.core.ext;
 
 import com.google.gwt.dev.javac.rebind.RebindResult;
-import com.google.gwt.dev.javac.rebind.RebindStatus;
 
 /**
  * EXPERIMENTAL and subject to change. Do not use this in production code.
  * <p>
- * Adds a new {@link #generateIfNecessary} method.
+ * Adds a new {@link #generateIncrementally} method.
  * <p> 
  * TODO(jbrosenberg): Merge this into {@link Generator} directly, once the api
  * has stabilized and we can remove the "experimental" moniker.
  */
 public abstract class GeneratorExt extends Generator {
-  
+ 
   /**
-   * A wrapper class for using old style {@link Generator} implementations where
-   * a GeneratorExt instance is needed.
-   */
-  private static class BaseGeneratorWrapper extends GeneratorExt {
-    final Generator baseGenerator;
-    
-    public BaseGeneratorWrapper(Generator baseGenerator) {
-      this.baseGenerator = baseGenerator;
-    }
-    
-    @Override
-    public String generate(TreeLogger logger, GeneratorContext context,
-        String typeName) throws UnableToCompleteException {
-      return this.baseGenerator.generate(logger, context, typeName);
-    }
-  }
-  
-  /**
-   * Get a new instance wrapped from an old style {@link Generator} 
-   * implementation.
-   */
-  public static GeneratorExt getWrappedInstance(Generator baseGenerator) {
-    return new BaseGeneratorWrapper(baseGenerator);
-  }
-  
-  /**
-   * A default implementation of the abstract method defined in the old style
-   * {@link Generator}.
+   * A default implementation of the abstract method defined in the base
+   * {@link Generator} class.  This will wrap a call to
+   * {@link #generateIncrementally}, and attempt no caching.  This supports
+   * backwards compatibility for applications or other generators which call 
+   * this generator directly, as outside of the normal internal rebind process.
    * <p>
-   * Note, it is recommended that {@link #generateIncrementally} be used instead.
+   * It is recommended that {@link #generateIncrementally} be used instead.
    * 
    * @return the name of a subclass to substitute for the requested class, or
    *         return <code>null</code> to cause the requested type itself to be
@@ -67,8 +43,13 @@
   @Override
   public String generate(TreeLogger logger, GeneratorContext context,
       String typeName) throws UnableToCompleteException {
-    // to override (implementing generateIncrementally instead is recommended)
-    return null;
+    
+    // wrap the passed in context
+    GeneratorContextExt contextExt = 
+      GeneratorContextExtWrapper.newInstance(context);
+    
+    RebindResult result = generateIncrementally(logger, contextExt, typeName);
+    return result.getReturnedTypeName();
   }
   
   /**
@@ -76,37 +57,21 @@
    * type.  The generator can use information from the context to determine
    * whether it needs to regenerate everything, or whether it can selectively
    * regenerate a subset of its output, or whether it can return quickly to
-   * allow use of all previously cached objects.  It will return a 
-   * {@link RebindResult}, which contains a {@link RebindStatus} field 
-   * indicating whether to use previously cached artifacts, newly generated 
-   * ones, or a partial mixture of both cached and newly generated objects.  
+   * allow reuse of all previously cached objects.  It will return a 
+   * {@link RebindResult}, which contains a
+   * {@link com.google.gwt.dev.javac.rebind.RebindStatus} field indicating
+   * whether to use previously cached artifacts, newly generated ones, or a
+   * partial mixture of both cached and newly generated objects.
    * <p>
    * The result also includes a field for the name of the subclass to 
    * substitute for the requested class.
    * <p>
-   * For backwards compatibility, the default implementation calls the old-style
-   * generate() method, and doesn't attempt any generator result caching.
-   * <p>
    * The generator throws an <code>UnableToCompleteException</code> if for 
    * any reason it cannot complete successfully.
    * 
-   * @return a GeneratorResult
+   * @return a RebindResult
    */
-  public RebindResult generateIncrementally(TreeLogger logger, 
+  public abstract RebindResult generateIncrementally(TreeLogger logger, 
       GeneratorContextExt context, String typeName) 
-      throws UnableToCompleteException {
-    
-    // to override (default implementation calls unconditional generate() method)
-    
-    RebindStatus status;
-    String resultTypeName = generate(logger, context, typeName);
-    if (resultTypeName == null) {
-      status = RebindStatus.USE_EXISTING;
-      resultTypeName = typeName;
-    } else {
-      status = RebindStatus.USE_ALL_NEW_WITH_NO_CACHING;
-    }
-    
-    return new RebindResult(status, resultTypeName);
-  }
+      throws UnableToCompleteException;
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/GeneratorExtWrapper.java b/dev/core/src/com/google/gwt/core/ext/GeneratorExtWrapper.java
new file mode 100644
index 0000000..bc8a250
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/GeneratorExtWrapper.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010 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.core.ext;
+
+import com.google.gwt.dev.javac.rebind.RebindResult;
+import com.google.gwt.dev.javac.rebind.RebindStatus;
+
+/**
+ * EXPERIMENTAL and subject to change. Do not use this in production code.
+ * <p>  
+ * A wrapper class for using base {@link Generator} implementations where
+ * a {@link GeneratorExt} instance is needed.
+ */
+public class GeneratorExtWrapper extends GeneratorExt {
+ 
+  /**
+   * Get a new instance wrapped from a base {@link Generator} implementation.
+   */
+  public static GeneratorExt newInstance(Generator baseGenerator) {
+    return new GeneratorExtWrapper(baseGenerator);
+  }
+
+  private final Generator baseGenerator;
+ 
+  public GeneratorExtWrapper(Generator baseGenerator) {
+    this.baseGenerator = baseGenerator;
+  }
+ 
+  /**
+   * Pass through to the base generator's generate method.
+   */
+  @Override
+  public String generate(TreeLogger logger, GeneratorContext context,
+      String typeName) throws UnableToCompleteException {
+    return this.baseGenerator.generate(logger, context, typeName);
+  }
+
+  /**
+   * Call base generator's generate method, and don't attempt any caching.
+   */
+  @Override
+  public RebindResult generateIncrementally(TreeLogger logger, 
+      GeneratorContextExt context, String typeName) 
+      throws UnableToCompleteException {
+
+    RebindStatus status;
+    String resultTypeName = generate(logger, context, typeName);
+    if (resultTypeName == null) {
+      status = RebindStatus.USE_EXISTING;
+      resultTypeName = typeName;
+    } else {
+      status = RebindStatus.USE_ALL_NEW_WITH_NO_CACHING;
+    }
+
+    return new RebindResult(status, resultTypeName);
+  }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java b/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
index bc7d51c..1b3e4b7 100644
--- a/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
+++ b/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
@@ -19,6 +19,7 @@
 import com.google.gwt.core.ext.GeneratorContext;
 import com.google.gwt.core.ext.GeneratorContextExt;
 import com.google.gwt.core.ext.GeneratorExt;
+import com.google.gwt.core.ext.GeneratorExtWrapper;
 import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
@@ -332,7 +333,7 @@
    * Adds all available cached generated units to the context.  Existing units
    * for a given type will not be overwritten.
    */
-  public void addGeneratedUnitsFromCachedRebindResult() {
+  public void addGeneratedUnitsFromCache() {
     if (cachedRebindResult != null 
         && cachedRebindResult.getGeneratedUnits() != null) {
       addGeneratedUnits(cachedRebindResult.getGeneratedUnits());
@@ -392,7 +393,7 @@
   /**
    * Commits all available cached Artifacts to the context.
    */
-  public void commitArtifactsFromCachedRebindResult(TreeLogger logger) {
+  public void commitArtifactsFromCache(TreeLogger logger) {
     if (cachedRebindResult != null 
         && cachedRebindResult.getArtifacts() != null) {
       for (Artifact<?> art : cachedRebindResult.getArtifacts()) {
@@ -654,7 +655,7 @@
       if (generator instanceof GeneratorExt) {
         generatorExt = (GeneratorExt) generator;
       } else {
-        generatorExt = GeneratorExt.getWrappedInstance(generator);
+        generatorExt = GeneratorExtWrapper.newInstance(generator);
       }
       
       RebindResult result;
@@ -686,7 +687,7 @@
   /**
    * Set previously cached rebind result for currently active generator.
    */
-  public void setCachedRebindResult(CachedRebindResult cachedRebindResult) {
+  public void setCachedGeneratorResult(CachedRebindResult cachedRebindResult) {
     this.cachedRebindResult = cachedRebindResult;
   }
   
diff --git a/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java b/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
index 5736eb5..50bb0c2 100644
--- a/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
+++ b/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
@@ -67,7 +67,7 @@
         
         CachedRebindResult cachedResult = rebindCacheGet(rule, typeName);
         if (cachedResult != null) {
-          genCtx.setCachedRebindResult(cachedResult);
+          genCtx.setCachedGeneratorResult(cachedResult);
         }
         
         // realize the rule (call a generator, or do type replacement, etc.)
@@ -189,8 +189,8 @@
           // use all cached results
           assert (cachedResult != null);
           
-          genCtx.commitArtifactsFromCachedRebindResult(logger);
-          genCtx.addGeneratedUnitsFromCachedRebindResult();
+          genCtx.commitArtifactsFromCache(logger);
+          genCtx.addGeneratedUnitsFromCache();
           
           // use cached type name
           resultTypeName = cachedResult.getReturnedTypeName();
diff --git a/user/src/com/google/gwt/rpc/rebind/RpcProxyCreator.java b/user/src/com/google/gwt/rpc/rebind/RpcProxyCreator.java
index 644501d..19e33bc 100644
--- a/user/src/com/google/gwt/rpc/rebind/RpcProxyCreator.java
+++ b/user/src/com/google/gwt/rpc/rebind/RpcProxyCreator.java
@@ -19,7 +19,7 @@
 import com.google.gwt.core.client.impl.ArtificialRescue;
 import com.google.gwt.core.client.impl.Impl;
 import com.google.gwt.core.client.impl.ArtificialRescue.Rescue;
-import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.GeneratorContextExt;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JArrayType;
@@ -118,7 +118,7 @@
   }
 
   @Override
-  protected void generateTypeHandlers(TreeLogger logger, GeneratorContext ctx,
+  protected void generateTypeHandlers(TreeLogger logger, GeneratorContextExt ctx,
       SerializableTypeOracle serializationSto,
       SerializableTypeOracle deserializationSto)
       throws UnableToCompleteException {
@@ -271,7 +271,7 @@
 
   @Override
   protected String writeSerializationPolicyFile(TreeLogger logger,
-      GeneratorContext ctx, SerializableTypeOracle serializationSto,
+      GeneratorContextExt ctx, SerializableTypeOracle serializationSto,
       SerializableTypeOracle deserializationSto)
       throws UnableToCompleteException {
 
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 4921dd4..da4e52e 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
@@ -19,7 +19,7 @@
 import com.google.gwt.core.client.impl.Impl;
 import com.google.gwt.core.ext.BadPropertyValueException;
 import com.google.gwt.core.ext.ConfigurationProperty;
-import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.GeneratorContextExt;
 import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
@@ -248,7 +248,7 @@
    *
    * @throws UnableToCompleteException
    */
-  public String create(TreeLogger logger, GeneratorContext context)
+  public String create(TreeLogger logger, GeneratorContextExt context)
       throws UnableToCompleteException {
     TypeOracle typeOracle = context.getTypeOracle();
 
@@ -690,7 +690,7 @@
   }
 
   protected void generateTypeHandlers(TreeLogger logger,
-      GeneratorContext context, SerializableTypeOracle typesSentFromBrowser,
+      GeneratorContextExt context, SerializableTypeOracle typesSentFromBrowser,
       SerializableTypeOracle typesSentToBrowser)
       throws UnableToCompleteException {
     Event event = SpeedTracerLogger.start(CompilerEventType.GENERATOR_RPC_TYPE_SERIALIZER);
@@ -729,7 +729,7 @@
   }
 
   protected String writeSerializationPolicyFile(TreeLogger logger,
-      GeneratorContext ctx, SerializableTypeOracle serializationSto,
+      GeneratorContextExt ctx, SerializableTypeOracle serializationSto,
       SerializableTypeOracle deserializationSto)
       throws UnableToCompleteException {
     try {
@@ -830,7 +830,7 @@
   }
 
   private void emitPolicyFileArtifact(TreeLogger logger,
-      GeneratorContext context, String partialPath)
+      GeneratorContextExt context, String partialPath)
       throws UnableToCompleteException {
     try {
       String qualifiedSourceName = serviceIntf.getQualifiedSourceName();
@@ -881,7 +881,7 @@
     return ResponseReader.OBJECT;
   }
 
-  private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext ctx,
+  private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContextExt ctx,
       JClassType serviceAsync) {
     JPackage serviceIntfPkg = serviceAsync.getPackage();
     String packageName = serviceIntfPkg == null ? "" : serviceIntfPkg.getName();
diff --git a/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java b/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java
index ded7cda..67b0c31 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java
@@ -15,23 +15,25 @@
  */
 package com.google.gwt.user.rebind.rpc;
 
-import com.google.gwt.core.ext.Generator;
-import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.GeneratorContextExt;
+import com.google.gwt.core.ext.GeneratorExt;
 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.TypeOracle;
+import com.google.gwt.dev.javac.rebind.RebindResult;
+import com.google.gwt.dev.javac.rebind.RebindStatus;
 
 /**
  * Generator for producing the asynchronous version of a
  * {@link com.google.gwt.user.client.rpc.RemoteService RemoteService} interface.
  */
-public class ServiceInterfaceProxyGenerator extends Generator {
-
+public class ServiceInterfaceProxyGenerator extends GeneratorExt {
+ 
   @Override
-  public String generate(TreeLogger logger, GeneratorContext ctx,
+  public RebindResult generateIncrementally(TreeLogger logger, GeneratorContextExt ctx,
       String requestedClass) throws UnableToCompleteException {
-
+    
     TypeOracle typeOracle = ctx.getTypeOracle();
     assert (typeOracle != null);
 
@@ -54,7 +56,16 @@
         "Generating client proxy for remote service interface '"
             + remoteService.getQualifiedSourceName() + "'", null);
 
-    return proxyCreator.create(proxyLogger, ctx);
+    String returnTypeName = proxyCreator.create(proxyLogger, ctx);
+    
+    /*
+     * Return with RebindStatus.USE_PARTIAL_CACHED, since we are implementing an
+     * incremental scheme, which allows us to use a mixture of previously cached
+     * and newly generated compilation units and artifacts.  For example, the
+     * field serializers only need to be generated fresh if their source type
+     * has changed (or if no previously cached version exists).
+     */
+    return new RebindResult(RebindStatus.USE_PARTIAL_CACHED, returnTypeName);
   }
 
   protected ProxyCreator createProxyCreator(JClassType remoteService) {
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 ebb31d8..0f7726a 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
@@ -21,7 +21,7 @@
 import com.google.gwt.core.client.JsArrayString;
 import com.google.gwt.core.ext.BadPropertyValueException;
 import com.google.gwt.core.ext.ConfigurationProperty;
-import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.GeneratorContextExt;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
@@ -29,6 +29,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.rebind.CachedRebindResult;
 import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
@@ -99,7 +100,7 @@
     }
   }
 
-  private final GeneratorContext context;
+  private final GeneratorContextExt context;
 
   private final SerializableTypeOracle deserializationOracle;
 
@@ -121,7 +122,7 @@
 
   public TypeSerializerCreator(TreeLogger logger,
       SerializableTypeOracle serializationOracle,
-      SerializableTypeOracle deserializationOracle, GeneratorContext context,
+      SerializableTypeOracle deserializationOracle, GeneratorContextExt context,
       String typeSerializerClassName, String typeSerializerSimpleName)
       throws UnableToCompleteException {
     this.context = context;
@@ -202,44 +203,52 @@
 
     return typeSerializerClassName;
   }
-
+ 
   /*
    * Create a field serializer for a type if it does not have a custom
    * serializer.
    */
-  private void createFieldSerializer(TreeLogger logger, GeneratorContext ctx,
+  private void createFieldSerializer(TreeLogger logger, GeneratorContextExt ctx,
       JType type) {
     Event event = SpeedTracerLogger.start(CompilerEventType.GENERATOR_RPC_FIELD_SERIALIZER);
-    assert (type != null);
-    assert (serializationOracle.isSerializable(type) || deserializationOracle.isSerializable(type));
-
-    JParameterizedType parameterizedType = type.isParameterized();
-    if (parameterizedType != null) {
-      createFieldSerializer(logger, ctx, parameterizedType.getRawType());
-      return;
+    try {
+      assert (type != null);
+      assert (serializationOracle.isSerializable(type) || deserializationOracle.isSerializable(type));
+  
+      JParameterizedType parameterizedType = type.isParameterized();
+      if (parameterizedType != null) {
+        createFieldSerializer(logger, ctx, parameterizedType.getRawType());
+        return;
+      }
+  
+      /*
+       * Only a JClassType can reach this point in the code. JPrimitives have been
+       * removed because their serialization is built in, interfaces have been
+       * removed because they are not an instantiable type and parameterized types
+       * have been broken down into their raw types.
+       */
+      assert (type.isClass() != null || type.isArray() != null);
+      
+      if (findCacheableFieldSerializerAndMarkForReuseIfAvailable(ctx, type)) {
+        // skip generation of field serializer
+        return;
+      }
+  
+      JClassType customFieldSerializer = SerializableTypeOracleBuilder.findCustomFieldSerializer(
+          typeOracle, type);
+      FieldSerializerCreator creator = new FieldSerializerCreator(typeOracle,
+          serializationOracle, deserializationOracle, (JClassType) type,
+          customFieldSerializer);
+      creator.realize(logger, ctx);
+    } finally {
+      event.end();
     }
-
-    /*
-     * Only a JClassType can reach this point in the code. JPrimitives have been
-     * removed because their serialization is built in, interfaces have been
-     * removed because they are not an instantiable type and parameterized types
-     * have been broken down into their raw types.
-     */
-    assert (type.isClass() != null || type.isArray() != null);
-
-    JClassType customFieldSerializer = SerializableTypeOracleBuilder.findCustomFieldSerializer(
-        typeOracle, type);
-    FieldSerializerCreator creator = new FieldSerializerCreator(typeOracle,
-        serializationOracle, deserializationOracle, (JClassType) type,
-        customFieldSerializer);
-    creator.realize(logger, ctx);
-    event.end();
   }
 
   /*
    * Create all of the necessary field serializers.
    */
-  private void createFieldSerializers(TreeLogger logger, GeneratorContext ctx) {
+  private void createFieldSerializers(TreeLogger logger, GeneratorContextExt ctx) {
     JType[] types = getSerializableTypes();
     int typeCount = types.length;
     for (int typeIndex = 0; typeIndex < typeCount; ++typeIndex) {
@@ -249,7 +258,51 @@
       createFieldSerializer(logger, ctx, type);
     }
   }
-
+  
+  /*
+   * check whether we can use a previously generated version of a 
+   * FieldSerializer.  If so, mark it for reuse, and return true.
+   * Otherwise return false.
+   */
+  private boolean findCacheableFieldSerializerAndMarkForReuseIfAvailable(
+      GeneratorContextExt ctx, JType type) {
+    
+    CachedRebindResult lastResult = ctx.getCachedGeneratorResult();
+    if (lastResult == null || !ctx.isGeneratorResultCachingEnabled()) {
+      return false;
+    }
+    
+    String fieldSerializerName = 
+      SerializationUtils.getStandardSerializerName((JClassType) type);
+    
+    if (type instanceof JClassType) {
+      // check that it is available for reuse
+      if (!lastResult.isTypeCached(fieldSerializerName)) {
+        return false;
+      }
+    } else {
+      return false;
+    }
+          
+    try {
+      /*
+       * TODO(jbrosenberg): Change this check to use getVersion() from 
+       * TypeOracle, once that is available.
+       */
+      long lastModified = ctx.getSourceLastModifiedTime((JClassType) type);
+      
+      if (lastModified != 0L &&
+          lastModified < lastResult.getTimeGenerated()) {
+        
+        // use cached version  
+        return ctx.reuseTypeFromCacheIfAvailable(fieldSerializerName);
+      }
+    } catch (RuntimeException ex) {
+      // could get an exception checking modified time
+    }
+      
+    return false;
+  }
   private String[] getPackageAndClassName(String fullClassName) {
     String className = fullClassName;
     String packageName = "";
@@ -265,7 +318,7 @@
     return serializableTypes;
   }
 
-  private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext ctx) {
+  private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContextExt ctx) {
     String name[] = getPackageAndClassName(typeSerializerClassName);
     String packageName = name[0];
     String className = name[1];