Allow JTypeOracle information to be reused across compiles.

Change-Id: I8afe419c0e41004882a0c9da7fc965fdb5dd12a9
Review-Link: https://gwt-review.googlesource.com/#/c/8418/
diff --git a/dev/BUILD b/dev/BUILD
index fe265e6..44b019f 100644
--- a/dev/BUILD
+++ b/dev/BUILD
@@ -186,6 +186,7 @@
             "core/src/com/google/gwt/dev/CompileTaskOptionsImpl.java",
             "core/src/com/google/gwt/dev/GwtCreateMap.java",
             "core/src/com/google/gwt/dev/GwtVersion.java",
+            "core/src/com/google/gwt/dev/MinimalRebuildCache.java",
             "core/src/com/google/gwt/dev/Permutation.java",
             "core/src/com/google/gwt/dev/PrecompileTaskOptions.java",
             "core/src/com/google/gwt/dev/PrecompileTaskOptionsImpl.java",
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/ModuleState.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/ModuleState.java
index d63c706..20fda87 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/ModuleState.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/ModuleState.java
@@ -78,7 +78,7 @@
    */
   boolean recompile(Map<String, String> bindingProperties, AtomicReference<Progress> progress) {
     try {
-      current.set(recompiler.compile(bindingProperties, progress));
+      current.set(recompiler.recompile(bindingProperties, progress));
       return true;
     } catch (UnableToCompleteException e) {
       logger.log(TreeLogger.Type.WARN, "continuing to serve previous version");
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
index 4518f21..abef180 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
@@ -25,6 +25,7 @@
 import com.google.gwt.dev.CompilerOptions;
 import com.google.gwt.dev.IncrementalBuilder;
 import com.google.gwt.dev.IncrementalBuilder.BuildResultStatus;
+import com.google.gwt.dev.MinimalRebuildCache;
 import com.google.gwt.dev.cfg.BindingProperty;
 import com.google.gwt.dev.cfg.ConfigProps;
 import com.google.gwt.dev.cfg.ConfigurationProperty;
@@ -56,6 +57,7 @@
   private final TreeLogger logger;
   private String serverPrefix;
   private int compilesDone = 0;
+  private MinimalRebuildCache minimalRebuildCache;
 
   // after renaming
   private AtomicReference<String> moduleName = new AtomicReference<String>(null);
@@ -77,10 +79,22 @@
     compilerContext = compilerContextBuilder.build();
   }
 
-  synchronized CompileDir compile(Map<String, String> bindingProperties,
-      AtomicReference<Progress> progress)
+  CompileDir recompile(Map<String, String> bindingProperties, AtomicReference<Progress> progress)
       throws UnableToCompleteException {
+    if (options.shouldCompilePerFile()) {
+      return compile(bindingProperties, progress, minimalRebuildCache);
+    }
+    return compile(bindingProperties, progress, new MinimalRebuildCache());
+  }
 
+  synchronized CompileDir compile(Map<String, String> bindingProperties,
+      AtomicReference<Progress> progress) throws UnableToCompleteException {
+    return compile(bindingProperties, progress, new MinimalRebuildCache());
+  }
+
+  private synchronized CompileDir compile(Map<String, String> bindingProperties,
+      AtomicReference<Progress> progress, MinimalRebuildCache minimalRebuildCache)
+      throws UnableToCompleteException {
     if (compilesDone == 0) {
       System.setProperty("java.awt.headless", "true");
       if (System.getProperty("gwt.speedtracerlog") == null) {
@@ -112,7 +126,8 @@
 
         success = compileIncremental(compileLogger, compileDir);
       } else {
-        success = compileMonolithic(compileLogger, bindingProperties, compileDir, progress);
+        success = compileMonolithic(compileLogger, bindingProperties, compileDir, progress,
+            minimalRebuildCache);
       }
     } finally {
       try {
@@ -128,6 +143,9 @@
       throw new UnableToCompleteException();
     }
 
+    // keep the minimal rebuild cache for the next compile
+    this.minimalRebuildCache = minimalRebuildCache;
+
     long elapsedTime = System.currentTimeMillis() - startTime;
     compileLogger.log(TreeLogger.Type.INFO,
         String.format("%.3fs total -- Compile completed", elapsedTime / 1000d));
@@ -208,7 +226,8 @@
   }
 
   private boolean compileMonolithic(TreeLogger compileLogger, Map<String, String> bindingProperties,
-      CompileDir compileDir, AtomicReference<Progress> progress) throws UnableToCompleteException {
+      CompileDir compileDir, AtomicReference<Progress> progress, MinimalRebuildCache rebuildCache)
+      throws UnableToCompleteException {
 
     progress.set(new Progress.Compiling(moduleName.get(), compilesDone, 0, 2, "Loading modules"));
 
@@ -226,7 +245,7 @@
     CompilerOptions runOptions = new CompilerOptionsImpl(compileDir, newModuleName, options);
     compilerContext = compilerContextBuilder.options(runOptions).build();
 
-    boolean success = new Compiler(runOptions).run(compileLogger, module);
+    boolean success = new Compiler(runOptions, rebuildCache).run(compileLogger, module);
     if (success) {
       publishedCompileDir = compileDir;
     }
diff --git a/dev/core/src/com/google/gwt/dev/Compiler.java b/dev/core/src/com/google/gwt/dev/Compiler.java
index 320e8b1..8b46fe7 100644
--- a/dev/core/src/com/google/gwt/dev/Compiler.java
+++ b/dev/core/src/com/google/gwt/dev/Compiler.java
@@ -123,9 +123,14 @@
   private final CompilerOptionsImpl options;
 
   public Compiler(CompilerOptions compilerOptions) {
+    this(compilerOptions, new MinimalRebuildCache());
+  }
+
+  public Compiler(CompilerOptions compilerOptions, MinimalRebuildCache minimalRebuildCache) {
     this.options = new CompilerOptionsImpl(compilerOptions);
     this.compilerContextBuilder = new CompilerContext.Builder();
-    this.compilerContext = compilerContextBuilder.options(options).build();
+    this.compilerContext = compilerContextBuilder.options(options)
+        .minimalRebuildCache(minimalRebuildCache).build();
   }
 
   public boolean run(TreeLogger logger) throws UnableToCompleteException {
diff --git a/dev/core/src/com/google/gwt/dev/CompilerContext.java b/dev/core/src/com/google/gwt/dev/CompilerContext.java
index 5155d9d..410d265 100644
--- a/dev/core/src/com/google/gwt/dev/CompilerContext.java
+++ b/dev/core/src/com/google/gwt/dev/CompilerContext.java
@@ -46,6 +46,7 @@
 
     private ResourceOracle buildResourceOracle;
     private boolean compileMonolithic = true;
+    private MinimalRebuildCache minimalRebuildCache = new MinimalRebuildCache();
     private CompilationErrorsIndex globalCompilationErrorsIndex;
     private CompilationErrorsIndex libraryCompilationErrorsIndex;
     private LibraryGroup libraryGroup = new ImmutableLibraryGroup();
@@ -63,6 +64,7 @@
 
       CompilerContext compilerContext = new CompilerContext();
       compilerContext.buildResourceOracle = buildResourceOracle;
+      compilerContext.minimalRebuildCache = minimalRebuildCache;
       compilerContext.libraryWriter = libraryWriter;
       compilerContext.libraryGroup = libraryGroup;
       compilerContext.module = module;
@@ -110,6 +112,12 @@
       return this;
     }
 
+    public Builder minimalRebuildCache(MinimalRebuildCache minimalRebuildCache) {
+      assert minimalRebuildCache != null;
+      this.minimalRebuildCache = minimalRebuildCache;
+      return this;
+    }
+
     public Builder unitCache(UnitCache unitCache) {
       this.unitCache = unitCache;
       return this;
@@ -169,6 +177,8 @@
    * they should be grouped together instead of floating free here.
    */
   private boolean compileMonolithic = true;
+
+  private MinimalRebuildCache minimalRebuildCache = new MinimalRebuildCache();
   private LibraryGroup libraryGroup = new ImmutableLibraryGroup();
   private LibraryWriter libraryWriter = new NullLibraryWriter();
   private CompilationErrorsIndex localCompilationErrorsIndex = new CompilationErrorsIndexImpl();
@@ -188,6 +198,10 @@
     return buildResourceOracle;
   }
 
+  public MinimalRebuildCache getMinimalRebuildCache() {
+    return minimalRebuildCache;
+  }
+
   /**
    * Returns the immutable compilation errors index that provides a combined view of compilation
    * errors for both the current compile as well as previously compiled libraries.
diff --git a/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java b/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java
new file mode 100644
index 0000000..72a0059
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 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.dev;
+
+import com.google.gwt.dev.jjs.ast.JTypeOracle.ImmediateTypeRelations;
+
+import java.io.Serializable;
+
+/**
+ * MinimalRebuildCache contains compiler information that can be persisted between compiles to
+ * decrease compilation time.
+ */
+public class MinimalRebuildCache implements Serializable {
+  private ImmediateTypeRelations immediateTypeRelations = new ImmediateTypeRelations();
+
+  public ImmediateTypeRelations getImmediateTypeRelations() {
+    return immediateTypeRelations;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/AstConstructor.java b/dev/core/src/com/google/gwt/dev/jjs/AstConstructor.java
index 744dfdf..df06c8d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/AstConstructor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/AstConstructor.java
@@ -18,6 +18,7 @@
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.dev.CompilerContext;
+import com.google.gwt.dev.MinimalRebuildCache;
 import com.google.gwt.dev.PrecompileTaskOptions;
 import com.google.gwt.dev.cfg.ConfigProps;
 import com.google.gwt.dev.javac.CompilationState;
@@ -49,7 +50,8 @@
 
     InternalCompilerException.preload();
 
-    CompilerContext compilerContext = new CompilerContext.Builder().options(options).build();
+    CompilerContext compilerContext = new CompilerContext.Builder().options(options)
+        .minimalRebuildCache(new MinimalRebuildCache()).build();
 
     RebindPermutationOracle rpo = new RebindPermutationOracle() {
       @Override
@@ -73,7 +75,7 @@
       }
     };
 
-    JProgram jprogram = new JProgram();
+    JProgram jprogram = new JProgram(compilerContext.getMinimalRebuildCache());
     JsProgram jsProgram = new JsProgram();
     UnifyAst unifyAst = new UnifyAst(logger, compilerContext, jprogram, jsProgram, rpo);
     unifyAst.buildEverything();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index a21e4c2..e41a2b3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -827,7 +827,7 @@
 
     protected abstract void checkEntryPoints(String[] additionalRootTypes);
 
-    protected abstract void createJProgram();
+    protected abstract void createJProgram(CompilerContext compilerContext);
 
     /**
      * Takes as input a CompilationState and transforms that into a unified by not yet resolved Java
@@ -863,7 +863,7 @@
          */
 
         // (1) Initialize local state
-        createJProgram();
+        createJProgram(compilerContext);
         // Synchronize JTypeOracle with compile optimization behavior.
         jprogram.typeOracle.setOptimize(
             options.getOptimizationLevel() > OptionOptimize.OPTIMIZE_LEVEL_DRAFT);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompiler.java
index cc05df7..edec35a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompiler.java
@@ -165,8 +165,8 @@
     }
 
     @Override
-    protected void createJProgram() {
-      jprogram = new JProgram(false);
+    protected void createJProgram(CompilerContext compilerContext) {
+      jprogram = new JProgram(compilerContext.getMinimalRebuildCache(), false);
     }
 
     @VisibleForTesting
diff --git a/dev/core/src/com/google/gwt/dev/jjs/MonolithicJavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/MonolithicJavaToJavaScriptCompiler.java
index e93fa33..427181f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/MonolithicJavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/MonolithicJavaToJavaScriptCompiler.java
@@ -261,8 +261,8 @@
     }
 
     @Override
-    protected void createJProgram() {
-      jprogram = new JProgram();
+    protected void createJProgram(CompilerContext compilerContext) {
+      jprogram = new JProgram(compilerContext.getMinimalRebuildCache());
     }
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index bdd0316..52ef015 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.MinimalRebuildCache;
 import com.google.gwt.dev.jjs.Correlation.Literal;
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.SourceInfo;
@@ -377,14 +378,14 @@
     pinnedMethods.add(method);
   }
 
-  public JProgram() {
+  public JProgram(MinimalRebuildCache minimalRebuildCache) {
     super(SourceOrigin.UNKNOWN);
-    typeOracle = new JTypeOracle(this, true);
+    typeOracle = new JTypeOracle(this, minimalRebuildCache, true);
   }
 
-  public JProgram(boolean hasWholeWorldKnowledge) {
+  public JProgram(MinimalRebuildCache minimalRebuildCache, boolean hasWholeWorldKnowledge) {
     super(SourceOrigin.UNKNOWN);
-    typeOracle = new JTypeOracle(this, hasWholeWorldKnowledge);
+    typeOracle = new JTypeOracle(this, minimalRebuildCache, hasWholeWorldKnowledge);
   }
 
   public void addEntryMethod(JMethod entryPoint) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index fb03c8f..45d4892 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.MinimalRebuildCache;
 import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
 import com.google.gwt.dev.util.arg.JsInteropMode;
 import com.google.gwt.dev.util.collect.HashMap;
@@ -485,14 +486,16 @@
   /**
    * Constructs a new JTypeOracle.
    */
-  public JTypeOracle(ArrayTypeCreator arrayTypeCreator, boolean hasWholeWorldKnowledge) {
-    this.immediateTypeRelations = new ImmediateTypeRelations();
+  public JTypeOracle(ArrayTypeCreator arrayTypeCreator, MinimalRebuildCache minimalRebuildCache,
+      boolean hasWholeWorldKnowledge) {
+    this.immediateTypeRelations = minimalRebuildCache.getImmediateTypeRelations();
     this.arrayTypeCreator = arrayTypeCreator;
     this.hasWholeWorldKnowledge = hasWholeWorldKnowledge;
   }
 
   /**
-   * True if the type is a JSO or interface implemented by JSO..
+   * True if the type is a JSO or interface implemented by JSO or a JsType without
+   * prototype.
    */
   public boolean canBeJavaScriptObject(JType type) {
     if (type instanceof JNonNullType) {
@@ -502,8 +505,7 @@
   }
 
   /**
-   * True if the type is a JSO or interface implemented by JSO or a JsType without
-   * prototype.
+   * True if the type is a JSO or interface implemented by JSO or a JsType without prototype.
    */
   public boolean canCrossCastLikeJso(JType type) {
     JDeclaredType dtype = getNearestJsType(type, false);
@@ -525,7 +527,7 @@
       return true;
     }
     if (type instanceof JInterfaceType && isImplementedMap.get(type.getName()) != null) {
-      for (JReferenceType impl : getTypes(isImplementedMap, (JReferenceType) type)) {
+      for (JReferenceType impl : getTypes(isImplementedMap, type.getName())) {
         if (isInstantiatedType((JClassType) impl)) {
           return true;
         }
@@ -692,6 +694,14 @@
     return false;
   }
 
+  public void updateImmediateTypeRelations(Set<JDeclaredType> changedTypes,
+      Set<JDeclaredType> deletedTypes) {
+    deleteImmediateTypeRelations(deletedTypes);
+    deleteImmediateTypeRelations(changedTypes);
+    recordImmediateTypeRelations(changedTypes);
+    computeExtendedTypeRelations();
+  }
+
   public void computeBeforeAST(StandardTypes standardTypes,
       Collection<JDeclaredType> declaredTypes) {
     this.standardTypes = standardTypes;
@@ -813,8 +823,8 @@
         // Class arrays reuse their leaf type super hierarchy.
         JDeclaredType leafType = (JDeclaredType) arrayType.getLeafType();
         for (JReferenceType leafSuperType : getSuperHierarchyTypes(leafType)) {
-          JArrayType superArrayType = arrayTypeCreator.getOrCreateArrayType(
-              leafSuperType, arrayType.getDims());
+          JArrayType superArrayType =
+              arrayTypeCreator.getOrCreateArrayType(leafSuperType, arrayType.getDims());
           superHierarchyTypes.add(superArrayType);
         }
       }
@@ -823,13 +833,13 @@
 
     Set<JReferenceType> superHierarchyTypes = Sets.newHashSet();
     if (superClassMap.containsKey(type.getName())) {
-      superHierarchyTypes.addAll(getTypes(superClassMap, type));
+      superHierarchyTypes.addAll(getTypes(superClassMap, type.getName()));
     }
     if (superInterfaceMap.containsKey(type.getName())) {
-      superHierarchyTypes.addAll(getTypes(superInterfaceMap, type));
+      superHierarchyTypes.addAll(getTypes(superInterfaceMap, type.getName()));
     }
     if (implementsMap.containsKey(type.getName())) {
-      superHierarchyTypes.addAll(getTypes(implementsMap, type));
+      superHierarchyTypes.addAll(getTypes(implementsMap, type.getName()));
     }
     superHierarchyTypes.add(type);
 
@@ -896,8 +906,14 @@
     if (referenceType instanceof JNullType) {
       return true;
     }
+    return isJavaScriptObject(referenceType.getName());
+  }
 
-    String typeName = referenceType.getName();
+  // Note: This method does not account for null types and only relies on static 
+  // class inheritance and does not account for any changes due to optimizations.
+  // Therefore this method should be kept private since callers need to be aware
+  // of this semantic difference.
+  private boolean isJavaScriptObject(String typeName) {
     if (typeName.equals(JProgram.JAVASCRIPTOBJECT)) {
       return true;
     }
@@ -1009,17 +1025,17 @@
   }
 
   /**
-   * Returns true if qType is a subclass of type, directly or indirectly.
+   * Returns true if possibleSubType is a subclass of type, directly or indirectly.
    */
-  public boolean isSubClass(JClassType type, JClassType qType) {
-    return get(subClassMap, type.getName()).contains(qType.getName());
+  public boolean isSubClass(JClassType type, JClassType possibleSubType) {
+    return get(subClassMap, type.getName()).contains(possibleSubType.getName());
   }
 
   /**
-   * Returns true if qType is a superclass of type, directly or indirectly.
+   * Returns true if possibleSuperClass is a superclass of type, directly or indirectly.
    */
-  public boolean isSuperClass(JClassType type, JClassType qType) {
-    return isSuperClass(type.getName(), qType.getName());
+  public boolean isSuperClass(JClassType type, JClassType possibleSuperClass) {
+    return isSuperClass(type.getName(), possibleSuperClass.getName());
   }
 
   /**
@@ -1078,7 +1094,18 @@
     getOrCreate(map, key).add(value);
   }
 
-  private void recordImmediateTypeRelations(Collection<JDeclaredType> types) {
+  private void deleteImmediateTypeRelations(Set<JDeclaredType> types) {
+    for (JDeclaredType type : types) {
+      if (type instanceof JClassType) {
+        immediateTypeRelations.superClassesByClass.remove(type.getName());
+        immediateTypeRelations.implementedIntfsByClass.remove(type.getName());
+      } else if (type instanceof JInterfaceType) {
+        immediateTypeRelations.superIntfsByIntf.remove(type.getName());
+      }
+    }
+  }
+
+  private void recordImmediateTypeRelations(Iterable<JDeclaredType> types) {
     referenceTypesByName.clear();
     for (JReferenceType type : types) {
       referenceTypesByName.put(type.getName(), type);
@@ -1173,16 +1200,15 @@
     dualImpls.clear();
     // Create dual mappings for any jso interface with a Java implementor.
     for (String jsoIntfName : jsoSingleImpls.keySet()) {
-      JInterfaceType jsoIntf = (JInterfaceType) referenceTypesByName.get(jsoIntfName);
-      Set<JReferenceType> implementors = getTypes(isImplementedMap, jsoIntf);
-      for (JReferenceType implementor : implementors) {
+      Set<String> implementors = get(isImplementedMap, jsoIntfName);
+      for (String implementor : implementors) {
         if (!hasWholeWorldKnowledge || !isJavaScriptObject(implementor)) {
           // Assume always dualImpl for separate compilation. Due to the nature of separate
           // compilation, the compiler can not know if a specific interface is implemented in a
           // different module unless it is a monolithic whole world compile.
           // TODO(rluble): Jso devirtualization should be an normalization pass before optimization
           // JTypeOracle should be mostly unaware of JSOs.
-          dualImpls.add(jsoIntf.getName());
+          dualImpls.add(jsoIntfName);
           break;
         }
       }
@@ -1389,7 +1415,7 @@
      */
     for (JInterfaceType intf : type.getImplements()) {
       computeVirtualUpRefs(type, intf);
-      for (JReferenceType superIntf : getTypes(superInterfaceMap, intf)) {
+      for (JReferenceType superIntf : getTypes(superInterfaceMap, intf.getName())) {
         computeVirtualUpRefs(type, (JInterfaceType) superIntf);
       }
     }
@@ -1464,12 +1490,12 @@
   }
 
   private Set<JReferenceType> getTypes(Map<String, Set<String>> typeNamesByTypeName,
-      JReferenceType type) {
-    Set<String> typeNames = get(typeNamesByTypeName, type.getName());
+      String typeName) {
+    Set<String> typeNames = get(typeNamesByTypeName, typeName);
     IdentityHashSet<JReferenceType> types = new IdentityHashSet<JReferenceType>();
 
-    for (String typeName : typeNames) {
-      JReferenceType referenceType = referenceTypesByName.get(typeName);
+    for (String localTypeName : typeNames) {
+      JReferenceType referenceType = referenceTypesByName.get(localTypeName);
       assert referenceType != null;
       types.add(referenceType);
     }
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java b/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
index 7412912..9a03166 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
@@ -16,8 +16,10 @@
 
 package com.google.gwt.dev.jjs;
 
+import com.google.gwt.dev.MinimalRebuildCache;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JNonNullType;
 import com.google.gwt.dev.jjs.ast.JNullType;
@@ -29,8 +31,11 @@
 
 import junit.framework.TestCase;
 
+import org.junit.Assert;
+
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -244,6 +249,28 @@
     assertSuperHierarchy(arrayOfArrayOfB, arrayOfArrayOfB, arrayOfArrayOfBase, arrayOfArrayOfObject,
         arrayOfArrayOfIntfI, arrayOfArrayOfIntfIBase, classObject);
     assertSuperHierarchy(arrayOfInt, arrayOfInt, classObject);
+
+    intfCollection = createInterface("java.util.Collection");
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType> newHashSet(classArrayList, intfList, intfCollection, classObject),
+        Sets.<JDeclaredType> newHashSet(intfIterable));
+
+    assertSuperHierarchy(intfCollection, intfCollection);
+    assertSuperHierarchy(intfList, intfList, intfCollection);
+    assertSuperHierarchy(classArrayList, classArrayList, intfList, classObject,
+        intfCollection);
+
+    classA = createClass("A", classObject, false, false);
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType> newHashSet(classA, classObject),
+        Sets.<JDeclaredType> newHashSet(classBase));
+    assertSuperHierarchy(classA, classA, classObject);
+
+    JClassType classASub = createClass("ASub", classA, false, false);
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType> newHashSet(classASub, classA, classObject),
+        Sets.<JDeclaredType> newHashSet());
+    assertSuperHierarchy(classASub, classASub, classA, classObject);
   }
 
   public void testJavahSignatures() {
@@ -272,9 +299,126 @@
     assertSame(arrayOfA, program.strongerType(intfCloneable, arrayOfA));
   }
 
+  public void testUpdateTypeInformation_isJavaScriptObject() {
+    program.typeOracle.computeBeforeAST(StandardTypes.createFrom(program),
+        program.getDeclaredTypes());
+
+    JClassType jso = createClass("SomeJSO", classJso, false, false);
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType> newHashSet(jso),
+        Collections.<JDeclaredType> emptySet());
+    Assert.assertTrue(program.typeOracle.isJavaScriptObject(jso));
+    program.typeOracle.updateImmediateTypeRelations(
+        Collections.<JDeclaredType> emptySet(),
+        Sets.<JDeclaredType> newHashSet(jso));
+    Assert.assertFalse(program.typeOracle.isJavaScriptObject(jso));
+
+    jso = createClass("SomeJSO", classJso, false, false);
+    JClassType jsoChild = createClass("SomeJSOChild", jso, false, false);
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType> newHashSet(jso, jsoChild),
+        Collections.<JDeclaredType> emptySet());
+    Assert.assertTrue(program.typeOracle.isJavaScriptObject(jsoChild));
+    jsoChild = createClass("SomeJSOChild", classObject, false, false);
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType> newHashSet(jsoChild),
+        Collections.<JDeclaredType> emptySet());
+    Assert.assertFalse(program.typeOracle.isJavaScriptObject(jsoChild));
+
+    jso = createClass("SomeJSO", classJso, false, false);
+    jsoChild = createClass("SomeJSOChild", jso, false, false);
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType> newHashSet(jsoChild),
+        Collections.<JDeclaredType> emptySet());
+    Assert.assertTrue(program.typeOracle.isJavaScriptObject(jsoChild));
+    Assert.assertTrue(program.typeOracle.isJavaScriptObject(jso));
+    jso = createClass("SomeJSO", classObject, false, false);
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType> newHashSet(jso),
+        Collections.<JDeclaredType> emptySet());
+    Assert.assertFalse(program.typeOracle.isJavaScriptObject(jsoChild));
+    Assert.assertFalse(program.typeOracle.isJavaScriptObject(jso));
+  }
+
+  public void testUpdateTypeInformation_JSODualImpl() {
+    program.typeOracle.computeBeforeAST(StandardTypes.createFrom(program),
+        program.getDeclaredTypes());
+
+    Assert.assertFalse(program.typeOracle.isDualJsoInterface(intfJ));
+
+    JClassType javaIntfImplementor = createClass("JavaImplementor", classObject, false, false);
+    javaIntfImplementor.addImplements(intfJ);
+
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType>newHashSet(javaIntfImplementor),
+        Collections.<JDeclaredType>emptySet());
+    Assert.assertTrue(program.typeOracle.isDualJsoInterface(intfJ));
+    program.typeOracle.updateImmediateTypeRelations(
+        Collections.<JDeclaredType>emptySet(),
+        Sets.<JDeclaredType>newHashSet(javaIntfImplementor));
+    Assert.assertFalse(program.typeOracle.isDualJsoInterface(intfJ));
+  }
+
+  public void testUpdateTypeInformation_SubClasses() {
+    JClassType baseAll = createClass("IncrementalBaseAll", classObject, false, false);
+    JClassType sub1 = createClass("IncrementalSub1", baseAll, false, false);
+    JClassType sub2 = createClass("IncrementalSub2", baseAll, false, false);
+    JClassType sub1_2 = createClass("IncrementalSub1_2", sub1, false, false);
+
+    Assert.assertFalse(program.typeOracle.isSubClass(baseAll, sub1));
+
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType>newHashSet(baseAll, sub1, sub2, sub1_2),
+        Collections.<JDeclaredType>emptySet());
+
+    Assert.assertTrue(program.typeOracle.isSubClass(baseAll, sub1));
+    Assert.assertTrue(program.typeOracle.isSubClass(baseAll, sub2));
+    Assert.assertTrue(program.typeOracle.isSubClass(baseAll, sub1_2));
+    Assert.assertTrue(program.typeOracle.isSubClass(sub1, sub1_2));
+
+    sub1_2 = createClass("IncrementalSub1_2", baseAll, false, false);
+
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType>newHashSet(baseAll, sub2, sub1_2),
+        Sets.<JDeclaredType>newHashSet(sub1));
+
+    Assert.assertFalse(program.typeOracle.isSubClass(baseAll, sub1));
+    Assert.assertTrue(program.typeOracle.isSubClass(baseAll, sub2));
+    Assert.assertTrue(program.typeOracle.isSubClass(baseAll, sub1_2));
+  }
+
+  public void testUpdateTypeInformation_SuperClasses() {
+    JClassType baseAll = createClass("IncrementalBaseAll", classObject, false, false);
+    JClassType sub1 = createClass("IncrementalSub1", baseAll, false, false);
+    JClassType sub2 = createClass("IncrementalSub2", baseAll, false, false);
+    JClassType sub1_2 = createClass("IncrementalSub1_2", sub1, false, false);
+
+    Assert.assertFalse(program.typeOracle.isSuperClass(sub1, baseAll));
+
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType>newHashSet(baseAll, sub1, sub2, sub1_2),
+        Collections.<JDeclaredType>emptySet());
+
+    Assert.assertTrue(program.typeOracle.isSuperClass(sub1, baseAll));
+    Assert.assertTrue(program.typeOracle.isSuperClass(sub2, baseAll));
+    Assert.assertTrue(program.typeOracle.isSuperClass(sub1_2, baseAll));
+    Assert.assertTrue(program.typeOracle.isSuperClass(sub1_2, sub1));
+
+    sub1_2 = createClass("IncrementalSub1_2", baseAll, false, false);
+
+    program.typeOracle.updateImmediateTypeRelations(
+        Sets.<JDeclaredType>newHashSet(baseAll, sub2, sub1_2),
+        Sets.<JDeclaredType>newHashSet(sub1));
+
+    Assert.assertFalse(program.typeOracle.isSuperClass(sub1, baseAll));
+    Assert.assertTrue(program.typeOracle.isSuperClass(sub2, baseAll));
+    Assert.assertFalse(program.typeOracle.isSuperClass(sub1_2, sub1));
+    Assert.assertTrue(program.typeOracle.isSuperClass(sub1_2, baseAll));
+  }
+
   @Override
   protected void setUp() {
-    createSampleProgram();
+    createSampleProgram(new MinimalRebuildCache());
   }
 
   private void assertSuperHierarchy(JReferenceType type, JReferenceType... superHierarchyTypes) {
@@ -296,9 +440,9 @@
     return x;
   }
 
-  private void createSampleProgram() {
+  private void createSampleProgram(MinimalRebuildCache minimalRebuildCache) {
     // Make the program itself
-    program = new JProgram();
+    program = new JProgram(minimalRebuildCache);
     typeOracle = program.typeOracle;
     synthSource = SourceOrigin.UNKNOWN;
 
diff --git a/dev/core/test/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompilerTest.java b/dev/core/test/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompilerTest.java
index ffc4797..008abf1 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompilerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/LibraryJavaToJavaScriptCompilerTest.java
@@ -22,6 +22,7 @@
 import com.google.gwt.core.ext.linker.ArtifactSet;
 import com.google.gwt.dev.CompilerContext;
 import com.google.gwt.dev.CompilerOptionsImpl;
+import com.google.gwt.dev.MinimalRebuildCache;
 import com.google.gwt.dev.PrecompileTaskOptions;
 import com.google.gwt.dev.cfg.BindingProperty;
 import com.google.gwt.dev.cfg.Condition;
@@ -298,7 +299,7 @@
   public void testBuildLocalRuntimeRebindRules() throws UnableToCompleteException {
     // Sets up environment.
     Set<String> allRootTypes = Sets.newHashSet();
-    compiler.jprogram = new JProgram();
+    compiler.jprogram = new JProgram(new MinimalRebuildCache());
     Map<String, String> runtimeRebindRuleSourcesByShortName =
         RuntimeRebindRuleGenerator.RUNTIME_REBIND_RULE_SOURCES_BY_SHORT_NAME;
     Rules rules = new Rules();
@@ -372,7 +373,7 @@
     ConfigurationProperty emulateStackProperty =
         properties.createConfiguration("emulateStack", false);
     emulateStackProperty.setValue("TRUE");
-    compiler.jprogram = new JProgram();
+    compiler.jprogram = new JProgram(new MinimalRebuildCache());
 
     // Builds property provider classes and a property provider registrator to register them.
     precompiler.buildPropertyProviderRegistrator(allRootTypes,
diff --git a/dev/core/test/com/google/gwt/dev/js/JsStackEmulatorTest.java b/dev/core/test/com/google/gwt/dev/js/JsStackEmulatorTest.java
index 81c0357..d6a5d1f 100644
--- a/dev/core/test/com/google/gwt/dev/js/JsStackEmulatorTest.java
+++ b/dev/core/test/com/google/gwt/dev/js/JsStackEmulatorTest.java
@@ -19,6 +19,7 @@
 import com.google.gwt.core.ext.linker.SymbolData;
 import com.google.gwt.core.ext.linker.impl.StandardSymbolData;
 import com.google.gwt.dev.CompilerContext;
+import com.google.gwt.dev.MinimalRebuildCache;
 import com.google.gwt.dev.PrecompileTaskOptions;
 import com.google.gwt.dev.PrecompileTaskOptionsImpl;
 import com.google.gwt.dev.cfg.BindingProperty;
@@ -227,7 +228,8 @@
     PrecompileTaskOptions options = new PrecompileTaskOptionsImpl();
     options.setOutput(JsOutputOption.PRETTY);
     options.setRunAsyncEnabled(false);
-    CompilerContext context = new CompilerContext.Builder().options(options).build();
+    CompilerContext context = new CompilerContext.Builder().options(options)
+        .minimalRebuildCache(new MinimalRebuildCache()).build();
 
     ConfigProps config = new ConfigProps(Arrays.asList(recordFileNamesProp,
         recordLineNumbersProp));
diff --git a/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java b/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java
index 4975a71..b00f008 100644
--- a/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java
+++ b/user/src/com/google/gwt/validation/rebind/GwtSpecificValidatorCreator.java
@@ -27,7 +27,12 @@
 import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.shared.impl.StringCase;
-import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JBooleanLiteral;
+import com.google.gwt.dev.jjs.ast.JCharLiteral;
+import com.google.gwt.dev.jjs.ast.JDoubleLiteral;
+import com.google.gwt.dev.jjs.ast.JFloatLiteral;
+import com.google.gwt.dev.jjs.ast.JIntLiteral;
+import com.google.gwt.dev.jjs.ast.JLongLiteral;
 import com.google.gwt.thirdparty.guava.common.base.Function;
 import com.google.gwt.thirdparty.guava.common.base.Functions;
 import com.google.gwt.thirdparty.guava.common.base.Joiner;
@@ -138,7 +143,6 @@
    */
   public static String asLiteral(Object value) throws IllegalArgumentException {
     Class<?> clazz = value.getClass();
-    JProgram jProgram = new JProgram();
 
     if (clazz.isArray()) {
       StringBuilder sb = new StringBuilder();
@@ -160,27 +164,27 @@
     }
 
     if (value instanceof Boolean) {
-      return jProgram.getLiteralBoolean(((Boolean) value).booleanValue())
+      return JBooleanLiteral.get(((Boolean) value).booleanValue())
           .toSource();
     } else if (value instanceof Byte) {
-      return jProgram.getLiteralInt(((Byte) value).byteValue()).toSource();
+      return JIntLiteral.get(((Byte) value).byteValue()).toSource();
     } else if (value instanceof Character) {
-      return jProgram.getLiteralChar(((Character) value).charValue())
+      return JCharLiteral.get(((Character) value).charValue())
           .toSource();
     } else if (value instanceof Class<?>) {
       return ((Class<?>) ((Class<?>) value)).getCanonicalName() + ".class";
     } else if (value instanceof Double) {
-      return jProgram.getLiteralDouble(((Double) value).doubleValue())
+      return JDoubleLiteral.get(((Double) value).doubleValue())
           .toSource();
     } else if (value instanceof Enum) {
       return value.getClass().getCanonicalName() + "."
           + ((Enum<?>) value).name();
     } else if (value instanceof Float) {
-      return jProgram.getLiteralFloat(((Float) value).floatValue()).toSource();
+      return JFloatLiteral.get(((Float) value).floatValue()).toSource();
     } else if (value instanceof Integer) {
-      return jProgram.getLiteralInt(((Integer) value).intValue()).toSource();
+      return JIntLiteral.get(((Integer) value).intValue()).toSource();
     } else if (value instanceof Long) {
-      return jProgram.getLiteralLong(((Long) value).intValue()).toSource();
+      return JLongLiteral.get(((Long) value).intValue()).toSource();
     } else if (value instanceof String) {
       return '"' + Generator.escape((String) value) + '"';
     } else {
diff --git a/user/test/com/google/gwt/dev/StrictModeTest.java b/user/test/com/google/gwt/dev/StrictModeTest.java
index 9671956..b9ef065 100644
--- a/user/test/com/google/gwt/dev/StrictModeTest.java
+++ b/user/test/com/google/gwt/dev/StrictModeTest.java
@@ -47,7 +47,8 @@
   private CompilerContext compilerContext = new CompilerContext();
 
   public StrictModeTest() {
-    compilerContext = compilerContextBuilder.build();
+    compilerContext = compilerContextBuilder.minimalRebuildCache(new MinimalRebuildCache())
+        .build();
   }
 
   /**