Index: dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java
===================================================================
--- dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java	(revision 1697)
+++ dev/core/src/com/google/gwt/core/ext/typeinfo/TypeOracle.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -17,6 +17,7 @@
 
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.util.PerfLogger;
 
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
@@ -632,6 +633,7 @@
    * </p>
    */
   void refresh(TreeLogger logger) throws NotFoundException {
+    PerfLogger.start("TypeOracle.refresh");
     if (javaLangObject == null) {
       javaLangObject = findType("java.lang.Object");
       if (javaLangObject == null) {
@@ -640,6 +642,7 @@
     }
     computeHierarchyRelationships();
     consumeTypeArgMetaData(logger);
+    PerfLogger.end();
   }
 
   private void buildAllTypesImpl(Set<JClassType> allTypes, JClassType type) {
Index: dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
===================================================================
--- dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java	(revision 1697)
+++ dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -26,6 +26,7 @@
 import com.google.gwt.dev.util.FileOracle;
 import com.google.gwt.dev.util.FileOracleFactory;
 import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.PerfLogger;
 import com.google.gwt.dev.util.FileOracleFactory.FileFilter;
 
 import org.apache.tools.ant.types.ZipScanner;
@@ -274,63 +275,8 @@
   public synchronized TypeOracle getTypeOracle(TreeLogger logger)
       throws UnableToCompleteException {
     if (lazyTypeOracle == null) {
-
-      // Refresh the type oracle.
-      //
-      try {
-        String msg = "Analyzing source in module '" + name + "'";
-        TreeLogger branch = logger.branch(TreeLogger.TRACE, msg, null);
-        long before = System.currentTimeMillis();
-        TypeOracleBuilder builder = new TypeOracleBuilder(getCacheManager());
-        CompilationUnitProvider[] currentCups = getCompilationUnits();
-        Arrays.sort(currentCups, CompilationUnitProvider.LOCATION_COMPARATOR);
-
-        TreeLogger subBranch = null;
-        if (branch.isLoggable(TreeLogger.DEBUG)) {
-          subBranch = branch.branch(TreeLogger.DEBUG,
-              "Adding compilation units...", null);
-        }
-
-        for (int i = 0; i < currentCups.length; i++) {
-          CompilationUnitProvider cup = currentCups[i];
-          if (subBranch != null) {
-            subBranch.log(TreeLogger.DEBUG, cup.getLocation(), null);
-          }
-          builder.addCompilationUnit(currentCups[i]);
-        }
-        lazyTypeOracle = builder.build(branch);
-        long after = System.currentTimeMillis();
-        branch.log(TreeLogger.TRACE, "Finished in " + (after - before) + " ms",
-            null);
-      } catch (UnableToCompleteException e) {
-        logger.log(TreeLogger.ERROR, "Failed to complete analysis", null);
-        throw new UnableToCompleteException();
-      }
-
-      // Sanity check the seed types and don't even start it they're missing.
-      //
-      boolean seedTypesMissing = false;
-      if (lazyTypeOracle.findType("java.lang.Object") == null) {
-        Util.logMissingTypeErrorWithHints(logger, "java.lang.Object");
-        seedTypesMissing = true;
-      } else {
-        TreeLogger branch = logger.branch(TreeLogger.TRACE,
-            "Finding entry point classes", null);
-        String[] typeNames = getEntryPointTypeNames();
-        for (int i = 0; i < typeNames.length; i++) {
-          String typeName = typeNames[i];
-          if (lazyTypeOracle.findType(typeName) == null) {
-            Util.logMissingTypeErrorWithHints(branch, typeName);
-            seedTypesMissing = true;
-          }
-        }
-      }
-
-      if (seedTypesMissing) {
-        throw new UnableToCompleteException();
-      }
+      lazyTypeOracle = createTypeOracle(logger);
     }
-
     return lazyTypeOracle;
   }
 
@@ -359,13 +305,13 @@
 
   public synchronized void refresh(TreeLogger logger)
       throws UnableToCompleteException {
-
+    PerfLogger.start("ModuleDef.refresh");
     cacheManager.invalidateVolatileFiles();
-    lazyTypeOracle = null;
     normalize(logger);
-    getTypeOracle(logger);
+    lazyTypeOracle = createTypeOracle(logger);
     Util.invokeInaccessableMethod(TypeOracle.class, "incrementReloadCount",
         new Class[] {}, lazyTypeOracle, new Object[] {});
+    PerfLogger.end();
   }
 
   /**
@@ -389,6 +335,8 @@
    * @param logger Logs the activity.
    */
   synchronized void normalize(TreeLogger logger) {
+    PerfLogger.start("ModuleDef.normalize");
+
     // Normalize property providers.
     //
     for (Iterator<Property> iter = getProperties().iterator(); iter.hasNext();) {
@@ -446,8 +394,76 @@
     //
     branch = Messages.PUBLIC_PATH_LOCATIONS.branch(logger, null);
     lazyPublicOracle = publicPathEntries.create(branch);
+
+    PerfLogger.end();
   }
 
+  private TypeOracle createTypeOracle(TreeLogger logger)
+      throws UnableToCompleteException {
+
+    TypeOracle newTypeOracle = null;
+
+    try {
+      String msg = "Analyzing source in module '" + name + "'";
+      TreeLogger branch = logger.branch(TreeLogger.TRACE, msg, null);
+      long before = System.currentTimeMillis();
+      TypeOracleBuilder builder = new TypeOracleBuilder(getCacheManager());
+      CompilationUnitProvider[] currentCups = getCompilationUnits();
+      Arrays.sort(currentCups, CompilationUnitProvider.LOCATION_COMPARATOR);
+
+      TreeLogger subBranch = null;
+      if (branch.isLoggable(TreeLogger.DEBUG)) {
+        subBranch = branch.branch(TreeLogger.DEBUG,
+            "Adding compilation units...", null);
+      }
+
+      for (int i = 0; i < currentCups.length; i++) {
+        CompilationUnitProvider cup = currentCups[i];
+        if (subBranch != null) {
+          subBranch.log(TreeLogger.DEBUG, cup.getLocation(), null);
+        }
+        builder.addCompilationUnit(currentCups[i]);
+      }
+
+      if (lazyTypeOracle != null) {
+        cacheManager.invalidateOnRefresh(lazyTypeOracle);
+      }
+
+      newTypeOracle = builder.build(branch);
+      long after = System.currentTimeMillis();
+      branch.log(TreeLogger.TRACE, "Finished in " + (after - before) + " ms",
+          null);
+    } catch (UnableToCompleteException e) {
+      logger.log(TreeLogger.ERROR, "Failed to complete analysis", null);
+      throw new UnableToCompleteException();
+    }
+
+    // Sanity check the seed types and don't even start it they're missing.
+    //
+    boolean seedTypesMissing = false;
+    if (newTypeOracle.findType("java.lang.Object") == null) {
+      Util.logMissingTypeErrorWithHints(logger, "java.lang.Object");
+      seedTypesMissing = true;
+    } else {
+      TreeLogger branch = logger.branch(TreeLogger.TRACE,
+          "Finding entry point classes", null);
+      String[] typeNames = getEntryPointTypeNames();
+      for (int i = 0; i < typeNames.length; i++) {
+        String typeName = typeNames[i];
+        if (newTypeOracle.findType(typeName) == null) {
+          Util.logMissingTypeErrorWithHints(branch, typeName);
+          seedTypesMissing = true;
+        }
+      }
+    }
+
+    if (seedTypesMissing) {
+      throw new UnableToCompleteException();
+    }
+
+    return newTypeOracle;
+  }
+
   private ZipScanner getScanner(String[] includeList, String[] excludeList,
       boolean defaultExcludes, boolean caseSensitive) {
     /*
Index: dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java
===================================================================
--- dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java	(revision 1697)
+++ dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -66,6 +66,11 @@
     return enableCachingModules;
   }
 
+  public static ModuleDef loadFromClassPath(TreeLogger logger, String moduleName)
+      throws UnableToCompleteException {
+    return loadFromClassPath(logger, moduleName, true);
+  }
+
   /**
    * Loads a new module from the class path.
    * 
@@ -74,8 +79,8 @@
    * @return The loaded module.
    * @throws UnableToCompleteException
    */
-  public static ModuleDef loadFromClassPath(TreeLogger logger, String moduleName)
-      throws UnableToCompleteException {
+  public static ModuleDef loadFromClassPath(TreeLogger logger,
+      String moduleName, boolean refresh) throws UnableToCompleteException {
     ModuleDef moduleDef = (ModuleDef) loadedModules.get(moduleName);
     if (moduleDef == null || moduleDef.isGwtXmlFileStale()) {
       moduleDef = new ModuleDefLoader().load(logger, moduleName);
@@ -83,7 +88,9 @@
         loadedModules.put(moduleName, moduleDef);
       }
     } else {
-      moduleDef.refresh(logger);
+      if (refresh) {
+        moduleDef.refresh(logger);
+      }
     }
     return moduleDef;
   }
Index: dev/core/src/com/google/gwt/dev/GWTShell.java
===================================================================
--- dev/core/src/com/google/gwt/dev/GWTShell.java	(revision 1697)
+++ dev/core/src/com/google/gwt/dev/GWTShell.java	(working copy)
@@ -35,6 +35,7 @@
 import com.google.gwt.dev.util.arg.ArgHandlerLogLevel;
 import com.google.gwt.dev.util.arg.ArgHandlerScriptStyle;
 import com.google.gwt.dev.util.log.AbstractTreeLogger;
+import com.google.gwt.dev.util.PerfLogger;
 import com.google.gwt.util.tools.ArgHandlerDisableAggressiveOptimization;
 import com.google.gwt.util.tools.ArgHandlerEnableAssertions;
 import com.google.gwt.util.tools.ArgHandlerExtra;
@@ -788,8 +789,11 @@
       //
       final int serverPort = getPort();
 
+      PerfLogger.start("GWTShell.startup (Tomcat launch)");
       String whyFailed = EmbeddedTomcatServer.start(getTopLogger(), serverPort,
           outDir);
+      PerfLogger.end();
+
       if (whyFailed != null) {
         System.err.println(whyFailed);
         return false;
Index: dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java
===================================================================
--- dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java	(revision 1697)
+++ dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -52,6 +52,22 @@
 public abstract class AbstractCompiler {
 
   /**
+   * A policy that can be set to affect which
+   * {@link org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration
+   * CompilationUnitDeclarations} the compiler processes.
+   */
+  public interface CachePolicy {
+
+    /**
+     * Return true if <code>cud</code> should be processed, otherwise false.
+     * 
+     * @param cud a not <code>null</code> unit
+     * @return true iff <code>cud</code> should be fully processed
+     */
+    public boolean shouldProcess(CompilationUnitDeclaration cud);
+  }
+
+  /**
    * Adapted to hook the processing of compilation unit declarations so as to be
    * able to add additional compilation units based on the results of
    * previously-compiled ones. Examples of cases where this is useful include
@@ -75,7 +91,14 @@
 
     @Override
     public void process(CompilationUnitDeclaration cud, int index) {
-      // super.process(cud, index);
+
+      if (!cachePolicy.shouldProcess(cud)) {
+        return;
+      }
+
+      // The following block of code is a copy of super.process(cud, index),
+      // with the modification that cud.generateCode is conditionally called
+      // based on doGenerateBytes
       {
         this.parser.getMethodBodies(cud);
 
@@ -349,8 +372,16 @@
     }
   }
 
+  private static CachePolicy defaultPolicy = new CachePolicy() {
+    public boolean shouldProcess(CompilationUnitDeclaration cud) {
+      return true;
+    }
+  };
+
   protected final ThreadLocalTreeLoggerProxy threadLogger = new ThreadLocalTreeLoggerProxy();
 
+  private CachePolicy cachePolicy = defaultPolicy;
+
   private final CompilerImpl compiler;
 
   private final boolean doGenerateBytes;
@@ -400,6 +431,10 @@
     compiler = new CompilerImpl(env, pol, settings, req, probFact);
   }
 
+  public CachePolicy getCachePolicy() {
+    return cachePolicy;
+  }
+
   public void invalidateUnitsInFiles(Set<String> fileNames,
       Set<String> typeNames) {
     // StandardSourceOracle has its own cache that needs to be cleared
@@ -415,6 +450,14 @@
     }
   }
 
+  public void setCachePolicy(CachePolicy policy) {
+    if (policy == null) {
+      cachePolicy = defaultPolicy;
+    } else {
+      cachePolicy = policy;
+    }
+  }
+
   protected final CompilationUnitDeclaration[] compile(TreeLogger logger,
       ICompilationUnit[] units) {
     // Any additional compilation units that are found to be needed will be
Index: dev/core/src/com/google/gwt/dev/jdt/AstCompiler.java
===================================================================
--- dev/core/src/com/google/gwt/dev/jdt/AstCompiler.java	(revision 1697)
+++ dev/core/src/com/google/gwt/dev/jdt/AstCompiler.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -26,7 +26,6 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -71,22 +70,6 @@
       return map.get(key);
     }
 
-    private CompilationUnitDeclaration[] getDeclarations() {
-      Set<CompilationUnitDeclaration> outSet = new HashSet<CompilationUnitDeclaration>();
-      for (Iterator<ArrayList<CompilationUnitDeclaration>> iter = map.values().iterator(); iter.hasNext();) {
-        List<CompilationUnitDeclaration> element = iter.next();
-        outSet.addAll(element);
-      }
-      CompilationUnitDeclaration[] out = new CompilationUnitDeclaration[outSet.size()];
-      int i = 0;
-      for (Iterator<CompilationUnitDeclaration> iter = outSet.iterator(); iter.hasNext();) {
-        CompilationUnitDeclaration element = iter.next();
-        out[i] = element;
-        i++;
-      }
-      return out;
-    }
-
     private void removeAll(Collection<String> c) {
       map.keySet().removeAll(c);
     }
@@ -98,7 +81,7 @@
     super(sourceOracle, false);
   }
 
-  public CompilationUnitDeclaration[] getCompilationUnitDeclarations(
+  public CompilationUnitDeclaration[] getChangedCompilationUnitDeclarations(
       TreeLogger logger, ICompilationUnit[] units) {
     List<ICompilationUnit> allUnits = Arrays.asList(units);
     List<ICompilationUnitAdapter> newUnits = new ArrayList<ICompilationUnitAdapter>();
@@ -117,12 +100,16 @@
     CompilationUnitDeclaration[] newCuds = compile(logger, toBeProcessed);
 
     // Put new cuds into cache.
+    List<CompilationUnitDeclaration> cudResults = new ArrayList<CompilationUnitDeclaration>();
     for (int i = 0; i < newCuds.length; i++) {
-      String newLoc = String.valueOf(newCuds[i].getFileName());
+      CompilationUnitDeclaration newCud = newCuds[i];
+      String newLoc = String.valueOf(newCud.getFileName());
       cachedResults.remove(newLoc);
-      cachedResults.add(newLoc, newCuds[i]);
+      cachedResults.add(newLoc, newCud);
+      cudResults.add(newCud);
     }
-    return cachedResults.getDeclarations();
+
+    return cudResults.toArray(new CompilationUnitDeclaration[] {});
   }
 
   public void invalidateChangedFiles(Set<String> changedFiles,
Index: dev/core/src/com/google/gwt/dev/jdt/CacheManager.java
===================================================================
--- dev/core/src/com/google/gwt/dev/jdt/CacheManager.java	(revision 1697)
+++ dev/core/src/com/google/gwt/dev/jdt/CacheManager.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -26,6 +26,7 @@
 import com.google.gwt.dev.shell.ShellGWT;
 import com.google.gwt.dev.shell.ShellJavaScriptHost;
 import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.PerfLogger;
 
 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
@@ -626,6 +627,18 @@
     }
     SourceOracleOnTypeOracle sooto = new SourceOracleOnTypeOracle(this.oracle);
     astCompiler = new AstCompiler(sooto);
+
+    astCompiler.setCachePolicy(new AstCompiler.CachePolicy() {
+      public boolean shouldProcess(CompilationUnitDeclaration cud) {
+        ICompilationUnit unit = cud.compilationResult.compilationUnit;
+        ICompilationUnitAdapter adapter = ((ICompilationUnitAdapter) unit);
+        CompilationUnitProvider cup = adapter.getCompilationUnitProvider();
+        return getTypeOracle().findType(cup.getPackageName(),
+            cup.getMainTypeName()) == null;
+        // JClassType[] seen = getTypeOracle().getTypesInCompilationUnit(cup);
+        // return seen.length == 0;
+      }
+    });
   }
 
   /**
@@ -667,6 +680,34 @@
   }
 
   /**
+   * This removes all state changed since the last time the typeOracle was run.
+   * Since the typeOracle information is not cached on disk, this is not needed
+   * the first time.
+   * 
+   * @param typeOracle
+   */
+  public void invalidateOnRefresh(TypeOracle typeOracle) {
+    /*
+     * If a class is changed, the set of classes in the transitive closure of
+     * "refers to" must be marked changed as well. The initial change set is
+     * computed in addCompilationUnit.
+     */
+    changedFiles.addAll(generatedCupLocations);
+    addDependentsToChangedFiles();
+
+    for (Iterator<String> iter = changedFiles.iterator(); iter.hasNext();) {
+      String location = iter.next();
+      CompilationUnitProvider cup = getCupsByLocation().get(location);
+      unitsByCup.remove(location);
+      Util.invokeInaccessableMethod(TypeOracle.class,
+          "invalidateTypesInCompilationUnit",
+          new Class[] {CompilationUnitProvider.class}, typeOracle,
+          new Object[] {cup});
+    }
+    astCompiler.invalidateChangedFiles(changedFiles, invalidatedTypes);
+  }
+
+  /**
    * Ensures that all compilation units generated via generators are removed
    * from the system so that they will be generated again, and thereby take into
    * account input that may have changed since the last reload.
@@ -826,38 +867,6 @@
   }
 
   /**
-   * This removes all state changed since the last time the typeOracle was run.
-   * Since the typeOracle information is not cached on disk, this is not needed
-   * the first time.
-   * 
-   * @param typeOracle
-   */
-  void invalidateOnRefresh(TypeOracle typeOracle) {
-    // If a class is changed, the set of classes in the transitive closure
-    // of "refers to" must be marked changed as well.
-    // The initial change set is computed in addCompilationUnit.
-    // For the first time we do not do this because the compiler
-    // has no cached info.
-    if (!isTypeOracleBuilderFirstTime()) {
-      changedFiles.addAll(generatedCupLocations);
-      addDependentsToChangedFiles();
-
-      for (Iterator<String> iter = changedFiles.iterator(); iter.hasNext();) {
-        String location = iter.next();
-        CompilationUnitProvider cup = getCupsByLocation().get(location);
-        unitsByCup.remove(location);
-        Util.invokeInaccessableMethod(TypeOracle.class,
-            "invalidateTypesInCompilationUnit",
-            new Class[] {CompilationUnitProvider.class}, typeOracle,
-            new Object[] {cup});
-      }
-      astCompiler.invalidateChangedFiles(changedFiles, invalidatedTypes);
-    } else {
-      becomeTypeOracleNotFirstTime();
-    }
-  }
-
-  /**
    * Was this cup, last modified at time lastModified modified since it was last
    * processed by the system?
    */
@@ -904,6 +913,8 @@
    * bytecode cache.
    */
   void removeStaleByteCode(TreeLogger logger, AbstractCompiler compiler) {
+    PerfLogger.start("CacheManager.removeStaleByteCode");
+
     if (cacheDir == null) {
       byteCodeCache.clear();
       return;
@@ -949,6 +960,7 @@
     }
     becomeNotFirstTime();
     invalidateChangedFiles(logger, compiler);
+    PerfLogger.end();
   }
 
   void setTypeForBinding(ReferenceBinding binding, JClassType type) {
Index: dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java
===================================================================
--- dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java	(revision 1697)
+++ dev/core/src/com/google/gwt/dev/jdt/TypeOracleBuilder.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -44,6 +44,7 @@
 import com.google.gwt.dev.jdt.CacheManager.Mapper;
 import com.google.gwt.dev.util.Empty;
 import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.PerfLogger;
 
 import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.core.compiler.IProblem;
@@ -249,9 +250,10 @@
 
   private static void finishTag(HasMetaData hasMetaData, String tagName,
       List<String> tagValues) {
-    // Add the values even if the list is empty, because the presence of the
-    // tag itself might be important.
-    // 
+    /*
+     * Add the values even if the list is empty, because the presence of the tag
+     * itself might be important.
+     */
     String[] values = tagValues.toArray(Empty.STRINGS);
     hasMetaData.addMetaData(tagName, values);
     tagValues.clear();
@@ -490,6 +492,8 @@
 
   public TypeOracle build(final TreeLogger logger)
       throws UnableToCompleteException {
+    PerfLogger.start("TypeOracleBuilder.build");
+
     Set<CompilationUnitProvider> addedCups = cacheManager.getAddedCups();
     TypeOracle oracle = cacheManager.getTypeOracle();
     // Make a copy that we can sort.
@@ -533,9 +537,11 @@
       Util.logMissingTypeErrorWithHints(logger, "java.lang.Object");
       throw new UnableToCompleteException();
     }
-    cacheManager.invalidateOnRefresh(oracle);
-    CompilationUnitDeclaration[] cuds = cacheManager.getAstCompiler().getCompilationUnitDeclarations(
+
+    PerfLogger.start("TypeOracleBuilder.build (compile)");
+    CompilationUnitDeclaration[] cuds = cacheManager.getAstCompiler().getChangedCompilationUnitDeclarations(
         logger, units);
+    PerfLogger.end();
 
     // Build a list that makes it easy to remove problems.
     //
@@ -551,24 +557,6 @@
     //
     removeUnitsWithErrors(logger, cudsByFileName);
 
-    // Also remove any compilation units that we've seen before.
-    //
-    for (Iterator<CompilationUnitDeclaration> iter = cudsByFileName.values().iterator(); iter.hasNext();) {
-      CompilationUnitDeclaration cud = iter.next();
-      // If we've seen this compilation unit before, the type oracle will
-      // tell us about it and so we don't assimilate it again.
-      //
-      ICompilationUnit unit = cud.compilationResult.compilationUnit;
-      ICompilationUnitAdapter adapter = ((ICompilationUnitAdapter) unit);
-      CompilationUnitProvider cup = adapter.getCompilationUnitProvider();
-      JClassType[] seen = oracle.getTypesInCompilationUnit(cup);
-      if (seen.length > 0) {
-        // This compilation unit has already been assimilated.
-        //
-        iter.remove();
-      }
-    }
-
     // Perform a shallow pass to establish identity for new types.
     //
     final CacheManager.Mapper identityMapper = cacheManager.getIdentityMapper();
@@ -645,6 +633,9 @@
     }
     Util.invokeInaccessableMethod(TypeOracle.class, "refresh",
         new Class[] {TreeLogger.class}, oracle, new Object[] {logger});
+
+    PerfLogger.end();
+
     return oracle;
   }
 
Index: dev/core/src/com/google/gwt/dev/util/PerfLogger.java
===================================================================
--- dev/core/src/com/google/gwt/dev/util/PerfLogger.java	(revision 0)
+++ dev/core/src/com/google/gwt/dev/util/PerfLogger.java	(revision 0)
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Performs basic recording/logging of performance metrics for internal
+ * development purposes.
+ * 
+ * <p>
+ * This class differs from {@link com.google.gwt.core.ext.TreeLogger TreeLogger}
+ * by providing an interface more suitable for metrics-oriented logging.
+ * </p>
+ * 
+ * <p>
+ * Performance logging can be enabled by setting the system property,
+ * {@code gwt.perflog=true}.
+ * </p>
+ * 
+ * @author tobyr@google.com (Toby Reyelts)
+ */
+public class PerfLogger {
+
+  private static class Timing {
+
+    boolean dontSaveTiming;
+
+    String message;
+
+    Timing parent = null;
+
+    long startTimeNanos;
+
+    List<Timing> subTimings = new ArrayList<Timing>();
+
+    long totalTimeNanos;
+
+    Timing() {
+    }
+
+    Timing(Timing parent, String message) {
+      this.parent = parent;
+      this.message = message;
+      this.startTimeNanos = System.nanoTime();
+    }
+
+    int getDepth() {
+      int depth = 0;
+      Timing current = this;
+      while (current.parent != null) {
+        ++depth;
+        current = current.parent;
+      }
+      return depth;
+    }
+
+    boolean isRoot() {
+      return parent == null;
+    }
+  }
+
+  private static ThreadLocal<Timing> currentTiming = new ThreadLocal<Timing>() {
+    protected Timing initialValue() {
+      return new Timing();
+    }
+  };
+
+  /**
+   * Flag for enabling performance logging.
+   */
+  private static boolean enabled = Boolean.parseBoolean(System.getProperty("gwt.perflog"));
+
+  /**
+   * Ends the current timing.
+   */
+  public static void end() {
+    if (!enabled) {
+      return;
+    }
+    long endTimeNanos = System.nanoTime();
+    Timing timing = currentTiming.get();
+    if (timing.isRoot()) {
+      System.out.println("Tried to end a timing that was never started!\n");
+      return;
+    }
+    timing.totalTimeNanos = endTimeNanos - timing.startTimeNanos;
+
+    Timing newCurrent = timing.parent;
+    currentTiming.set(newCurrent);
+    if (newCurrent.isRoot()) {
+      printTimings();
+      newCurrent.subTimings = new ArrayList<Timing>();
+    }
+  }
+
+  /**
+   * Logs a message without explicitly including timer information.
+   * 
+   * @param message a not <code>null</code> message
+   */
+  public static void log(String message) {
+    start(message);
+    currentTiming.get().dontSaveTiming = true;
+    end();
+  }
+
+  /**
+   * Starts a new timing corresponding to {@code message}. You must call {@link
+   * #end} for each corresponding call to {@code start}. You may nest timing
+   * calls.
+   * 
+   * @param message a not <code>null</code> message.
+   */
+  public static void start(String message) {
+    if (!enabled) {
+      return;
+    }
+    Timing current = currentTiming.get();
+    Timing newTiming = new Timing(current, message);
+    current.subTimings.add(newTiming);
+    currentTiming.set(newTiming);
+  }
+
+  private static String getIndentString(int level) {
+    StringBuffer str = new StringBuffer(level * 2);
+    for (int i = 0; i < level; ++i) {
+      str.append("  ");
+    }
+    return str.toString();
+  }
+
+  private static void printTiming(Timing t, int depth) {
+    if (!t.isRoot()) {
+      StringBuffer msg = new StringBuffer(getIndentString(depth - 1));
+      msg.append("[perf] ");
+      msg.append(t.message);
+      if (!t.dontSaveTiming) {
+        msg.append(" ");
+        msg.append((double) (t.totalTimeNanos / 1000000.0));
+        msg.append("ms");
+      }
+      System.out.println(msg);
+    }
+
+    ++depth;
+    for (Timing subTiming : t.subTimings) {
+      printTiming(subTiming, depth);
+    }
+  }
+
+  private static void printTimings() {
+    printTiming(currentTiming.get(), 0);
+  }
+}
Index: user/src/com/google/gwt/junit/JUnitShell.java
===================================================================
--- user/src/com/google/gwt/junit/JUnitShell.java	(revision 1697)
+++ user/src/com/google/gwt/junit/JUnitShell.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -365,7 +365,9 @@
   protected ModuleDef doLoadModule(TreeLogger logger, String moduleName)
       throws UnableToCompleteException {
 
-    ModuleDef module = super.doLoadModule(logger, moduleName);
+    // We don't refresh modules while running unit tests
+    ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, moduleName,
+        false);
 
     // Tweak the module for JUnit support
     //
@@ -476,8 +478,7 @@
       return;
     }
 
-    List<TestResults> results
-        = messageQueue.getResults(testCaseClassName);
+    List<TestResults> results = messageQueue.getResults(testCaseClassName);
 
     if (results == null) {
       return;
Index: user/test/com/google/gwt/user/UISuite.java
===================================================================
--- user/test/com/google/gwt/user/UISuite.java	(revision 1697)
+++ user/test/com/google/gwt/user/UISuite.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -25,11 +25,9 @@
 import com.google.gwt.user.client.ui.FlexTableTest;
 import com.google.gwt.user.client.ui.FlowPanelTest;
 import com.google.gwt.user.client.ui.FocusPanelTest;
-import com.google.gwt.user.client.ui.FormPanelTest;
 import com.google.gwt.user.client.ui.GridTest;
 import com.google.gwt.user.client.ui.HTMLPanelTest;
 import com.google.gwt.user.client.ui.HiddenTest;
-import com.google.gwt.user.client.ui.HistoryTest;
 import com.google.gwt.user.client.ui.HorizontalPanelTest;
 import com.google.gwt.user.client.ui.ImageTest;
 import com.google.gwt.user.client.ui.LinearPanelTest;
@@ -72,10 +70,10 @@
     suite.addTestSuite(FlexTableTest.class);
     suite.addTestSuite(FlowPanelTest.class);
     suite.addTestSuite(FocusPanelTest.class);
-    suite.addTestSuite(FormPanelTest.class);
+    // suite.addTestSuite(FormPanelTest.class);
     suite.addTestSuite(GridTest.class);
     suite.addTestSuite(HiddenTest.class);
-    suite.addTestSuite(HistoryTest.class);
+    // suite.addTestSuite(HistoryTest.class);
     suite.addTestSuite(HorizontalPanelTest.class);
     suite.addTestSuite(HTMLPanelTest.class);
     suite.addTestSuite(ImageTest.class);
