Updated generator result caching to use lastModifiedTime from CompilationUnit.lastModified().
Removed GeneratorContextExt.getSourceLastModifiedTime().
Removed JRealClassType.getTypeStrongHash().
Added JRealClassType.getLastModifiedTime()

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


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10336 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/GeneratorContextExt.java b/dev/core/src/com/google/gwt/core/ext/GeneratorContextExt.java
index 86f818d..7153684 100644
--- a/dev/core/src/com/google/gwt/core/ext/GeneratorContextExt.java
+++ b/dev/core/src/com/google/gwt/core/ext/GeneratorContextExt.java
@@ -15,7 +15,6 @@
  */
 package com.google.gwt.core.ext;
 
-import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.dev.javac.rebind.CachedRebindResult;
 
 /**
@@ -39,15 +38,6 @@
   CachedRebindResult getCachedGeneratorResult();
   
   /**
-   * Get source last modified time.
-   * <p>
-   * TODO(jbrosenberg): Implement in terms of a getVersion method yet to be
-   * added to TypeOracle, instead of looking for age of a java source file.
-   * This will soon be removed.
-   */
-  long getSourceLastModifiedTime(JClassType sourceType);
-  
-  /**
    * Check whether generator result caching is currently enabled.
    */
   boolean isGeneratorResultCachingEnabled();
diff --git a/dev/core/src/com/google/gwt/core/ext/GeneratorContextExtWrapper.java b/dev/core/src/com/google/gwt/core/ext/GeneratorContextExtWrapper.java
index 3970057..b429da3 100644
--- a/dev/core/src/com/google/gwt/core/ext/GeneratorContextExtWrapper.java
+++ b/dev/core/src/com/google/gwt/core/ext/GeneratorContextExtWrapper.java
@@ -17,7 +17,6 @@
 
 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;
@@ -80,10 +79,6 @@
     return baseContext.getResourcesOracle();
   }
 
-  public long getSourceLastModifiedTime(JClassType sourceType) {
-    return 0L;
-  }
-
   public TypeOracle getTypeOracle() {
     return baseContext.getTypeOracle();
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
index e207a5f..fdee5dc 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
@@ -23,8 +23,7 @@
   /**
    * EXPERIMENTAL and subject to change. Do not use this in production code.
    * 
-   * Generate a hash to be used as a signature for comparing versions of the 
-   * structure of a type.  
+   * Retrieve last modified time for this type.
    */
-  String getTypeStrongHash();
+  long getLastModifiedTime();
 }
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 ca3f0f5..ae94764 100644
--- a/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
+++ b/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
@@ -27,7 +27,6 @@
 import com.google.gwt.core.ext.linker.ArtifactSet;
 import com.google.gwt.core.ext.linker.GeneratedResource;
 import com.google.gwt.core.ext.linker.impl.StandardGeneratedResource;
-import com.google.gwt.core.ext.typeinfo.JArrayType;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.cfg.ModuleDef;
@@ -35,7 +34,6 @@
 import com.google.gwt.dev.javac.rebind.RebindResult;
 import com.google.gwt.dev.javac.rebind.RebindRuleResolver;
 import com.google.gwt.dev.javac.rebind.RebindStatus;
-import com.google.gwt.dev.resource.Resource;
 import com.google.gwt.dev.resource.ResourceOracle;
 import com.google.gwt.dev.util.DiskCache;
 import com.google.gwt.dev.util.Util;
@@ -541,39 +539,6 @@
     return module.getResourcesOracle();
   }
 
-  /**
-   * EXPERIMENTAL and subject to change. Do not use this in production code.
-   * 
-   * Temporary solution to get last modified time for a sourceType. Finds the
-   * the source file, if possible. Note, this won't work for sources contained
-   * in jar files, or for recently generated source files.
-   * 
-   * TODO(jbrosenberg): Replace this method by using a getVersion() method from
-   * TypeOracle (still under development).
-   */
-  public long getSourceLastModifiedTime(JClassType sourceType) {
-
-    while (sourceType instanceof JArrayType) {
-      sourceType = (JClassType) ((JArrayType) sourceType).getComponentType();
-    }
-
-    JClassType enclosingType;
-    while ((enclosingType = sourceType.getEnclosingType()) != null) {
-      sourceType = enclosingType;
-    }
-
-    String sourceName = sourceType.getQualifiedSourceName();
-    String sourcePath = sourceName.replace('.', '/') + ".java";
-
-    Resource sourceResource = module.findSourceFile(sourcePath);
-
-    if (sourceResource == null) {
-      return 0L;
-    }
-
-    return sourceResource.getLastModified();
-  }
-
   public final TypeOracle getTypeOracle() {
     return compilationState.getTypeOracle();
   }
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 0c93ee3..593c76a 100644
--- a/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
+++ b/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
@@ -539,11 +539,11 @@
       // Always add implicit modifiers on interfaces.
       resultType.addModifierBits(Shared.MOD_STATIC | Shared.MOD_ABSTRACT);
     }
-    
+
     /*
-     * Add a reference to the byteCode
+     * Add lastModified time from compilation unit
      */
-    resultType.addByteCode(typeData.byteCode);
+    resultType.addLastModifiedTime(typeData.lastModifiedTime);
 
     return resultType;
   }
diff --git a/dev/core/src/com/google/gwt/dev/javac/rebind/CachedPropertyInformation.java b/dev/core/src/com/google/gwt/dev/javac/rebind/CachedPropertyInformation.java
index b18a3d9..bddb4a7 100644
--- a/dev/core/src/com/google/gwt/dev/javac/rebind/CachedPropertyInformation.java
+++ b/dev/core/src/com/google/gwt/dev/javac/rebind/CachedPropertyInformation.java
@@ -85,10 +85,12 @@
           SelectionProperty currProp = 
             oracle.getSelectionProperty(logger, selProp.getName());
           if (!currProp.getCurrentValue().equals(selProp.getCurrentValue())) {
+            logger.log(TreeLogger.TRACE, "Found changed property: " + selProp.getName());
             return false;
           }
         }
       } catch (BadPropertyValueException e) {
+        logger.log(TreeLogger.TRACE, "Found problem checking property", e);
         return false;
       }
     }
@@ -99,10 +101,14 @@
           ConfigurationProperty currProp = 
             oracle.getConfigurationProperty(configProp.getName());
           if (!currProp.equals(configProp)) {
+            logger.log(TreeLogger.TRACE, 
+                "Found changed configuration property: " + configProp.getName());
             return false;
           }
         }
       } catch (BadPropertyValueException e) {
+        logger.log(TreeLogger.TRACE, 
+            "Found problem checking configuration property", e);
         return false;
       }
     }
diff --git a/dev/core/src/com/google/gwt/dev/javac/typemodel/JRealClassType.java b/dev/core/src/com/google/gwt/dev/javac/typemodel/JRealClassType.java
index eb4c3d8..a780b07 100644
--- a/dev/core/src/com/google/gwt/dev/javac/typemodel/JRealClassType.java
+++ b/dev/core/src/com/google/gwt/dev/javac/typemodel/JRealClassType.java
@@ -19,7 +19,6 @@
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.dev.util.StringInterner;
-import com.google.gwt.dev.util.Util;
 import com.google.gwt.dev.util.collect.IdentitySets;
 import com.google.gwt.dev.util.collect.Lists;
 
@@ -52,8 +51,6 @@
   private String lazyQualifiedBinaryName;
 
   private String lazyQualifiedName;
-  
-  private String lazyTypeStrongHash;
 
   private final Members members = new Members(this);
 
@@ -66,8 +63,8 @@
   private final TypeOracle oracle;
 
   private JClassType superclass;
-  
-  private byte[] byteCode;
+
+  private long lastModifiedTime;
 
   /**
    * Create a class type that reflects an actual type.
@@ -80,8 +77,8 @@
    * @param name
    * @param isInterface
    */
-  JRealClassType(TypeOracle oracle, JPackage declaringPackage,
-      String enclosingTypeName, String name, boolean isInterface) {
+  JRealClassType(TypeOracle oracle, JPackage declaringPackage, String enclosingTypeName,
+      String name, boolean isInterface) {
     this.oracle = oracle;
     this.declaringPackage = declaringPackage;
     this.name = StringInterner.get().intern(name);
@@ -103,9 +100,9 @@
     }
     oracle.addNewType(this);
   }
-  
-  public void addByteCode(byte[] byteCode) {
-    this.byteCode = byteCode;
+
+  public void addLastModifiedTime(long lastModifiedTime) {
+    this.lastModifiedTime = lastModifiedTime;
   }
 
   @Override
@@ -144,8 +141,7 @@
   }
 
   @Override
-  public JConstructor getConstructor(JType[] paramTypes)
-      throws NotFoundException {
+  public JConstructor getConstructor(JType[] paramTypes) throws NotFoundException {
     return members.getConstructor(paramTypes);
   }
 
@@ -200,8 +196,12 @@
   }
 
   @Override
-  public JMethod getMethod(String name, JType[] paramTypes)
-      throws NotFoundException {
+  public long getLastModifiedTime() {
+    return lastModifiedTime;
+  }
+
+  @Override
+  public JMethod getMethod(String name, JType[] paramTypes) throws NotFoundException {
     return members.getMethod(name, paramTypes);
   }
 
@@ -286,27 +286,6 @@
   public JClassType getSuperclass() {
     return superclass;
   }
-  
-  /**
-   * EXPERIMENTAL and subject to change. Do not use this in production code.
-   * 
-   * Generate a hash to be used as a signature for comparing versions of the 
-   * structure of a type.
-   * 
-   * TODO(jbrosenberg): Note, this implementation is based on the entire byte 
-   * code for a class, which is probably overkill, since we only need a hash 
-   * based on the type's structure (but not all of its code).  Need to come up
-   * with an efficient way to compute a hash of a type's structure.  For now,
-   * using the raw bytes directly is quick relative to making multiple api calls
-   * into type oracle to determine the type's structure.
-   */ 
-  public String getTypeStrongHash() {
-    if (lazyTypeStrongHash != null) {
-      return lazyTypeStrongHash;
-    }
-    lazyTypeStrongHash = Util.computeStrongName(byteCode);
-    return lazyTypeStrongHash;
-  }
 
   @Override
   public boolean isAbstract() {
@@ -528,8 +507,7 @@
     }
   }
 
-  void addAnnotations(
-      Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
+  void addAnnotations(Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
     annotations.addAnnotations(declaredAnnotations);
   }
 
diff --git a/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java b/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
index 8ba1556..9429d04 100644
--- a/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
+++ b/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
@@ -122,9 +122,9 @@
 
   private static final String FILE_PROTOCOL = "file";
   private static final String JAR_PROTOCOL = "jar";
-  private static final String CACHED_PROPERTY_INFORMATION = "cpi";
-  private static final String CACHED_RESOURCE_INFORMATION = "cri";
-  private static final String CACHED_TYPE_INFORMATION = "cti";
+  private static final String CACHED_PROPERTY_INFORMATION = "cached-property-info";
+  private static final String CACHED_RESOURCE_INFORMATION = "cached-resource-info";
+  private static final String CACHED_TYPE_INFORMATION = "cached-type-info";
   private static final String INSTANCE_NAME = "_instance0";
 
   /**
@@ -302,22 +302,18 @@
       return resolvedResources;
     }
 
-    public Map<String, String> getTypeSignatures() {
+    public Map<String, Long> getTypeLastModifiedTimes() {
       if (!canBeCacheable) {
         return null;
       }
-      Map<String, String> typeSignatures = new HashMap<String, String>();
+      Map<String, Long> typeLastModifiedTimeMap = new HashMap<String, Long>();
       for (JClassType type : types) {
         String typeName = type.getQualifiedSourceName();
-        if (type instanceof JRealClassType) {
-          JRealClassType sourceRealType = (JRealClassType) type;
-          String typeSignature = sourceRealType.getTypeStrongHash();
-          typeSignatures.put(typeName, typeSignature);
-        } else {
-          typeSignatures.put(typeName, "");
-        }
+        assert type instanceof JRealClassType;
+        JRealClassType sourceRealType = (JRealClassType) type;
+        typeLastModifiedTimeMap.put(typeName, sourceRealType.getLastModifiedTime());
       }
-      return typeSignatures;
+      return typeLastModifiedTimeMap;
     }
 
     /*
@@ -337,9 +333,26 @@
      * Do a series of checks to see if we can use a previously cached result,
      * and if so, we can skip further execution and return immediately.
      */
+    boolean useCache = false;
     if (checkPropertyCacheability(logger, generatorContext)
-        && checkSourceTypeCacheability(generatorContext)
+        && checkSourceTypeCacheability(logger, generatorContext)
         && checkDependentResourceCacheability(logger, generatorContext, null)) {
+      useCache = true;
+    }
+    
+    if (logger.isLoggable(TreeLogger.TRACE)) { 
+      if (generatorContext.isGeneratorResultCachingEnabled()) {
+        String msg;
+        if (useCache) {
+          msg = "Reusing cached client bundle for " + typeName;
+        } else {
+          msg = "Can't use cached client bundle for " + typeName;
+        }
+        logger.log(TreeLogger.TRACE, msg);
+      }
+    }
+      
+    if (useCache) {
       return new RebindResult(RebindStatus.USE_ALL_CACHED, typeName);
     }
       
@@ -470,8 +483,8 @@
           requirements.getPermutationAxes(), 
           requirements.getConfigurationPropertyNames());
 
-      // remember the type signatures for required source types
-      Map<String, String> cti = requirements.getTypeSignatures();
+      // remember the last modified times for required source types
+      Map<String, Long> cti = requirements.getTypeLastModifiedTimes();
 
       // remember the required resources
       Map<String, URL> cri = requirements.getResolvedResources();
@@ -543,23 +556,26 @@
   }
 
   /**
-   * Check that the map of cached type signatures matches those from the current
+   * Check that the cached last modified times match those from the current
    * typeOracle.
    */
-  private boolean checkCachedTypeSignatures(
-      GeneratorContextExt generatorContext, Map<String, String> typeSignatures) {
+  private boolean checkCachedTypeLastModifiedTimes(TreeLogger logger,
+      GeneratorContextExt generatorContext, Map<String, Long> typeLastModifiedTimes) {
     
     TypeOracle oracle = generatorContext.getTypeOracle();
     
-    for (String sourceTypeName : typeSignatures.keySet()) {
+    for (String sourceTypeName : typeLastModifiedTimes.keySet()) {
       JClassType sourceType = oracle.findType(sourceTypeName);
-      if (sourceType == null || !(sourceType instanceof JRealClassType)) {
+      if (sourceType == null) {
+        logger.log(TreeLogger.TRACE, 
+            "Found previously dependent type that's no longer present: " + sourceTypeName);
         return false;
       }
+      assert sourceType instanceof JRealClassType;
       JRealClassType sourceRealType = (JRealClassType) sourceType;
       
-      String signature = sourceRealType.getTypeStrongHash();
-      if (!signature.equals(typeSignatures.get(sourceTypeName))) {
+      if (sourceRealType.getLastModifiedTime() != typeLastModifiedTimes.get(sourceTypeName)) {
+        logger.log(TreeLogger.TRACE, "Found dependent type that has changed: " + sourceTypeName);
         return false;
       }
     }
@@ -597,10 +613,13 @@
           resourceContext, resourceName);
       if (currentUrl == null || resolvedUrl == null
           || !resolvedUrl.toExternalForm().equals(currentUrl.toExternalForm())) {
+        logger.log(TreeLogger.TRACE,
+            "Found dependent resource that has moved or no longer exists: " + resourceName);
         return false;
       }
       
       if (!checkDependentResourceUpToDate(lastTimeGenerated, resolvedUrl)) {
+        logger.log(TreeLogger.TRACE, "Found dependent resource that has changed: " + resourceName);
         return false;
       }
     }
@@ -714,7 +733,7 @@
   /*
    * Check source types for cacheability
    */
-  private boolean checkSourceTypeCacheability(GeneratorContextExt genContext) {
+  private boolean checkSourceTypeCacheability(TreeLogger logger, GeneratorContextExt genContext) {
 
     CachedRebindResult lastRebindResult = genContext.getCachedGeneratorResult();
 
@@ -729,11 +748,11 @@
      * since the previous cached result was generated.
      */
     @SuppressWarnings("unchecked")
-    Map<String, String> cachedTypeSignatures = (Map<String, String>)
+    Map<String, Long> cachedTypeLastModifiedTimes = (Map<String, Long>)
       lastRebindResult.getClientData(CACHED_TYPE_INFORMATION);
 
-    return cachedTypeSignatures != null 
-      && checkCachedTypeSignatures(genContext, cachedTypeSignatures);
+    return cachedTypeLastModifiedTimes != null 
+      && checkCachedTypeLastModifiedTimes(logger, genContext, cachedTypeLastModifiedTimes);
   }
 
   /**
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 8b1d349..8446a30 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
@@ -36,6 +36,7 @@
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.generator.NameFactory;
+import com.google.gwt.dev.javac.rebind.CachedClientDataMap;
 import com.google.gwt.dev.util.Util;
 import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
@@ -204,6 +205,8 @@
 
   private boolean elideTypeNames;
 
+  private Map<String, Long> cachedTypeLastModifiedTimes = null;
+
   /**
    * The possibly obfuscated type signatures used to represent a type.
    */
@@ -344,6 +347,12 @@
     return getProxyQualifiedName();
   }
 
+  public void updateResultCacheData(CachedClientDataMap clientData) {
+    if (cachedTypeLastModifiedTimes != null) {
+      clientData.put(TypeSerializerCreator.CACHED_TYPE_INFO_KEY, cachedTypeLastModifiedTimes);
+    }
+  }
+
   protected void addRoots(TreeLogger logger, TypeOracle typeOracle,
       SerializableTypeOracleBuilder typesSentFromBrowserBuilder,
       SerializableTypeOracleBuilder typesSentToBrowserBuilder) throws UnableToCompleteException {
@@ -645,6 +654,8 @@
 
     typeStrings = new HashMap<JType, String>(tsc.getTypeStrings());
     typeStrings.put(serviceIntf, TypeNameObfuscator.SERVICE_INTERFACE_ID);
+
+    cachedTypeLastModifiedTimes = tsc.getTypeLastModifiedTimeMap();
   }
 
   protected String getProxySimpleName() {
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 b84941e..c49244c 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java
@@ -21,6 +21,7 @@
 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.CachedClientDataMap;
 import com.google.gwt.dev.javac.rebind.RebindResult;
 import com.google.gwt.dev.javac.rebind.RebindStatus;
 
@@ -58,14 +59,21 @@
 
     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);
+    if (ctx.isGeneratorResultCachingEnabled()) {
+      // Remember the type info that we care about for cacheability testing.
+      CachedClientDataMap clientData = new CachedClientDataMap();
+      proxyCreator.updateResultCacheData(clientData);
+
+      /*
+       * Return with RebindStatus.USE_PARTIAL_CACHED, since we are allowing
+       * generator result caching for field serializers, but other generated
+       * types cannot be cached effectively.
+       */
+      return new RebindResult(RebindStatus.USE_PARTIAL_CACHED, returnTypeName, clientData);
+    } else {
+      // If we can't be cacheable, don't return a cacheable result
+      return new RebindResult(RebindStatus.USE_ALL_NEW_WITH_NO_CACHING, 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 436d1c6..288b7cc 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
@@ -24,9 +24,13 @@
 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;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JMethod;
 import com.google.gwt.core.ext.typeinfo.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JRawType;
+import com.google.gwt.core.ext.typeinfo.JRealClassType;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.javac.rebind.CachedRebindResult;
@@ -59,6 +63,12 @@
 public class TypeSerializerCreator {
 
   /**
+   * A key for storing cached type information for use with generator result
+   * caching.
+   */
+  public static final String CACHED_TYPE_INFO_KEY = "cached-type-info";
+
+  /**
    * Configuration property to use type indices instead of type signatures.
    */
   public static final String GWT_ELIDE_TYPE_NAMES_FROM_RPC = "gwt.elideTypeNamesFromRPC";
@@ -117,6 +127,8 @@
 
   private final String typeSerializerSimpleName;
 
+  private final Map<String, Long> typeLastModifiedTimeMap;
+
   private final Map<JType, String> typeStrings = new IdentityHashMap<JType, String>();
 
   public TypeSerializerCreator(TreeLogger logger, SerializableTypeOracle serializationOracle,
@@ -155,6 +167,16 @@
           + " was not defined. Is RemoteService.gwt.xml inherited?");
       throw new UnableToCompleteException();
     }
+
+    if (context.isGeneratorResultCachingEnabled()) {
+      typeLastModifiedTimeMap = new HashMap<String, Long>();
+    } else {
+      typeLastModifiedTimeMap = null;
+    }
+  }
+
+  public Map<String, Long> getTypeLastModifiedTimeMap() {
+    return typeLastModifiedTimeMap;
   }
 
   public Map<JType, String> getTypeStrings() {
@@ -224,11 +246,19 @@
        */
       assert (type.isClass() != null || type.isArray() != null);
 
-      if (findCacheableFieldSerializerAndMarkForReuseIfAvailable(ctx, type)) {
-        // skip generation of field serializer
-        return;
+      if (ctx.isGeneratorResultCachingEnabled()) {
+        // get the last modified time for our type, and remember it
+        typeLastModifiedTimeMap.put(type.getQualifiedSourceName(), getLastModifiedTime(type));
+
+        // check the cache for a valid field serializer for the current type
+        if (findCacheableFieldSerializerAndMarkForReuseIfAvailable(logger, ctx, type)) {
+          // we can skip re-generation of the field serializer for the current
+          // type
+          return;
+        }
       }
 
+      // generate a new field serializer
       JClassType customFieldSerializer =
           SerializableTypeOracleBuilder.findCustomFieldSerializer(typeOracle, type);
       FieldSerializerCreator creator =
@@ -259,8 +289,8 @@
    * FieldSerializer. If so, mark it for reuse, and return true. Otherwise
    * return false.
    */
-  private boolean findCacheableFieldSerializerAndMarkForReuseIfAvailable(GeneratorContextExt ctx,
-      JType type) {
+  private boolean findCacheableFieldSerializerAndMarkForReuseIfAvailable(TreeLogger logger,
+      GeneratorContextExt ctx, JType type) {
 
     CachedRebindResult lastResult = ctx.getCachedGeneratorResult();
     if (lastResult == null || !ctx.isGeneratorResultCachingEnabled()) {
@@ -278,23 +308,53 @@
       return false;
     }
 
-    try {
-      /*
-       * TODO(jbrosenberg): Change this check to use getVersion() from
-       * TypeOracle, once that is available.
-       */
-      long lastModified = ctx.getSourceLastModifiedTime((JClassType) type);
+    @SuppressWarnings("unchecked")
+    Map<String, Long> cachedLastModifiedTimes =
+        (Map<String, Long>) lastResult.getClientData(CACHED_TYPE_INFO_KEY);
+    String sourceName = type.getQualifiedSourceName();
 
-      if (lastModified != 0L && lastModified < lastResult.getTimeGenerated()) {
-
-        // use cached version
-        return ctx.reuseTypeFromCacheIfAvailable(fieldSerializerName);
-      }
-    } catch (RuntimeException ex) {
-      // could get an exception checking modified time
+    assert cachedLastModifiedTimes != null;
+    assert typeLastModifiedTimeMap.get(sourceName) != null;
+    boolean foundMatch = false;
+    if (typeLastModifiedTimeMap.get(sourceName).equals(cachedLastModifiedTimes.get(sourceName))) {
+      // use cached version, if available
+      foundMatch = ctx.reuseTypeFromCacheIfAvailable(fieldSerializerName);
     }
 
-    return false;
+    if (logger.isLoggable(TreeLogger.TRACE)) {
+      String msg;
+      if (foundMatch) {
+        msg = "Reusing cached field serializer for " + type.getQualifiedSourceName();
+      } else {
+        msg = "Can't reuse cached field serializer for " + type.getQualifiedSourceName();
+      }
+      logger.log(TreeLogger.TRACE, msg);
+    }
+
+    return foundMatch;
+  }
+
+  private long getLastModifiedTime(JType type) {
+    JType typeToCheck;
+    if (type instanceof JArrayType) {
+      typeToCheck = ((JArrayType) type).getLeafType();
+    } else if (type instanceof JRawType) {
+      typeToCheck = ((JRawType) type).getGenericType();
+    } else {
+      assert type instanceof JRealClassType;
+      typeToCheck = type;
+    }
+
+    long lastModifiedTime;
+    if (typeToCheck instanceof JRealClassType) {
+      lastModifiedTime = ((JRealClassType) typeToCheck).getLastModifiedTime();
+    } else {
+      // we have a type that is an array with a primitive leafType
+      assert typeToCheck instanceof JPrimitiveType;
+      lastModifiedTime = Long.MAX_VALUE;
+    }
+
+    return lastModifiedTime;
   }
 
   private String[] getPackageAndClassName(String fullClassName) {
@@ -335,10 +395,6 @@
     return composerFactory.createSourceWriter(ctx, printWriter);
   }
 
-  /**
-   * @param type
-   * @return
-   */
   private String getTypeString(JType type) {
     String typeString =
         SerializationUtils.getRpcTypeName(type) + "/"
diff --git a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
index 4a4227a..f7dcf56 100644
--- a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
+++ b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
@@ -107,10 +107,6 @@
       return null;
     }
 
-    public long getSourceLastModifiedTime(JClassType sourceType) {
-      return 0;
-    }
-
     public TypeOracle getTypeOracle() {
       return typeOracle;
     }