Support GWT.runAsync by splitting the JavaScript code
into independently downloadable program fragments.

This commit is a merge from /changes/spoon/runAsync,
but with many small cleanups.

Review by: bobv


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3901 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java b/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
index 9760804..093f578 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
@@ -26,16 +26,22 @@
  * result in identical JavaScript.
  */
 public abstract class CompilationResult extends Artifact<CompilationResult> {
-
   protected CompilationResult(Class<? extends Linker> linkerType) {
     super(linkerType);
   }
 
   /**
-   * Returns the JavaScript compilation. The exact form and function of the
-   * JavaScript should be considered opaque.
+   * Returns the JavaScript compilation. The first element of the array contains
+   * the code that should be run when the application starts up. The remaining
+   * elements are loaded via
+   * {@link com.google.gwt.core.client.GWT#runAsync(com.google.gwt.core.client.RunAsyncCallback) GWT.runAsync}.
+   * The linker should provide a function named
+   * <code>__gwtStartLoadingFragment</code> that can takes an integer as argument
+   * and loads that specified code segment. To see how this function is used,
+   * see
+   * {@link com.google.gwt.core.client.AsyncFragmentLoader AsyncFragmentLoader}.
    */
-  public abstract String getJavaScript();
+  public abstract String[] getJavaScript();
 
   /**
    * Provides values for {@link SelectionProperty} instances that are not
@@ -45,9 +51,19 @@
    */
   public abstract SortedSet<SortedMap<SelectionProperty, String>> getPropertyMap();
 
+  /**
+   * Return a string that uniquely identifies this compilation result.  Typically
+   * this is a cryptographic hash of the compiled data.
+   */
+  public abstract String getStrongName();
+
   @Override
   public final int hashCode() {
-    return getJavaScript().hashCode();
+    int hash = 17;
+    for (String js : getJavaScript()) {
+      hash = hash * 37 + js.hashCode();
+    }
+    return hash;
   }
 
   @Override
@@ -69,7 +85,18 @@
 
   @Override
   protected final int compareToComparableArtifact(CompilationResult o) {
-    return getJavaScript().compareTo(o.getJavaScript());
+    String[] js = getJavaScript();
+    String[] otherJs = o.getJavaScript();
+    if (js.length != otherJs.length) {
+      return js.length - otherJs.length;
+    }
+    for (int i = 0; i < js.length; i++) {
+      int diff = js[i].compareTo(otherJs[i]);
+      if (diff != 0) {
+        return diff;
+      }
+    }
+    return 0;
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeLinker.java
index ea3cf05..d3e8f22 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeLinker.java
@@ -44,6 +44,7 @@
     return super.generateSelectionScript(logger, context, artifacts);
   }
 
+  @Override
   public String getDescription() {
     return "Hosted Mode";
   }
@@ -61,8 +62,8 @@
   }
 
   @Override
-  protected String getModulePrefix(TreeLogger logger, LinkerContext context)
-      throws UnableToCompleteException {
+  protected String getModulePrefix(TreeLogger logger, LinkerContext context,
+      String strongName) throws UnableToCompleteException {
     return unsupported(logger);
   }
 
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
index 71d941b..3d78cb9 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
@@ -31,6 +31,8 @@
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -48,6 +50,11 @@
    */
 
   /**
+   * The extension added to demand-loaded fragment files.
+   */
+  protected static final String FRAGMENT_EXTENSION = ".cache.js";
+
+  /**
    * Determines whether or not the URL is relative.
    * 
    * @param src the test url
@@ -84,7 +91,7 @@
     }
   }
 
-  private final Map<CompilationResult, String> compilationPartialPaths = new IdentityHashMap<CompilationResult, String>();
+  private final Map<CompilationResult, String> compilationStrongNames = new IdentityHashMap<CompilationResult, String>();
 
   @Override
   public ArtifactSet link(TreeLogger logger, LinkerContext context,
@@ -92,24 +99,33 @@
     ArtifactSet toReturn = new ArtifactSet(artifacts);
 
     for (CompilationResult compilation : toReturn.find(CompilationResult.class)) {
-      toReturn.add(doEmitCompilation(logger, context, compilation));
+      toReturn.addAll(doEmitCompilation(logger, context, compilation));
     }
 
     toReturn.add(emitSelectionScript(logger, context, artifacts));
     return toReturn;
   }
 
-  protected EmittedArtifact doEmitCompilation(TreeLogger logger,
+  protected Collection<EmittedArtifact> doEmitCompilation(TreeLogger logger,
       LinkerContext context, CompilationResult result)
       throws UnableToCompleteException {
-    StringBuffer b = new StringBuffer();
-    b.append(getModulePrefix(logger, context));
-    b.append(result.getJavaScript());
-    b.append(getModuleSuffix(logger, context));
-    EmittedArtifact toReturn = emitWithStrongName(logger,
-        Util.getBytes(b.toString()), "", getCompilationExtension(logger,
-            context));
-    compilationPartialPaths.put(result, toReturn.getPartialPath());
+    String[] js = result.getJavaScript();
+    byte[][] bytes = new byte[js.length][];
+    bytes[0] = generatePrimaryFragment(logger, context, js[0], result.getStrongName());
+    for (int i = 1; i < js.length; i++) {
+      bytes[i] = Util.getBytes(js[i]);
+    }
+
+    Collection<EmittedArtifact> toReturn = new ArrayList<EmittedArtifact>();
+    toReturn.add(emitBytes(logger, bytes[0], result.getStrongName()
+        + getCompilationExtension(logger, context)));
+    for (int i = 1; i < js.length; i++) {
+      toReturn.add(emitBytes(logger, bytes[i], result.getStrongName() + "-" + i
+          + FRAGMENT_EXTENSION));
+    }
+
+    compilationStrongNames.put(result, result.getStrongName());
+
     return toReturn;
   }
 
@@ -231,9 +247,8 @@
         // Just one distinct compilation; no need to evaluate properties
         Iterator<CompilationResult> iter = compilations.iterator();
         CompilationResult result = iter.next();
-        text.append("strongName = '" + compilationPartialPaths.get(result)
+        text.append("strongName = '" + compilationStrongNames.get(result)
             + "';");
-
       } else {
         for (CompilationResult r : compilations) {
           for (Map<SelectionProperty, String> propertyMap : r.getPropertyMap()) {
@@ -252,7 +267,7 @@
               }
               text.append("'" + propertyMap.get(p) + "'");
             }
-            text.append("], '").append(compilationPartialPaths.get(r)).append(
+            text.append("], '").append(compilationStrongNames.get(r)).append(
                 "');\n");
           }
         }
@@ -316,16 +331,25 @@
    * @return the partial path, or <code>null</code> if the CompilationResult
    *         has not been emitted.
    */
-  protected String getCompilationPartialPath(CompilationResult result) {
-    return compilationPartialPaths.get(result);
+  protected String getCompilationStrongName(CompilationResult result) {
+    return compilationStrongNames.get(result);
   }
 
   protected abstract String getModulePrefix(TreeLogger logger,
-      LinkerContext context) throws UnableToCompleteException;
+      LinkerContext context, String strongName) throws UnableToCompleteException;
 
   protected abstract String getModuleSuffix(TreeLogger logger,
       LinkerContext context) throws UnableToCompleteException;
 
   protected abstract String getSelectionScriptTemplate(TreeLogger logger,
       LinkerContext context) throws UnableToCompleteException;
+
+  private byte[] generatePrimaryFragment(TreeLogger logger,
+      LinkerContext context, String js, String strongName) throws UnableToCompleteException {
+    StringBuffer b = new StringBuffer();
+    b.append(getModulePrefix(logger, context, strongName));
+    b.append(js);
+    b.append(getModuleSuffix(logger, context));
+    return Util.getBytes(b.toString());
+  }
 }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
index e8cdb0d..2b4f0f8 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
@@ -69,14 +69,16 @@
   public static final Comparator<SortedMap<SelectionProperty, String>> MAP_COMPARATOR = new MapComparator();
 
   private final File cacheFile;
-  private transient SoftReference<String> js;
+  private transient SoftReference<String[]> js;
+
   private final SortedSet<SortedMap<SelectionProperty, String>> propertyValues = new TreeSet<SortedMap<SelectionProperty, String>>(
       MAP_COMPARATOR);
   private final String strongName;
 
-  public StandardCompilationResult(String js, String strongName, File cacheFile) {
+  public StandardCompilationResult(String[] js, String strongName,
+      File cacheFile) {
     super(StandardLinkerContext.class);
-    this.js = new SoftReference<String>(js);
+    this.js = new SoftReference<String[]>(js);
     this.strongName = strongName;
     this.cacheFile = cacheFile;
   }
@@ -92,26 +94,31 @@
     propertyValues.add(Collections.unmodifiableSortedMap(map));
   }
 
-  public String getJavaScript() {
-    String toReturn = null;
+  @Override
+  public String[] getJavaScript() {
+    String[] toReturn = null;
     if (js != null) {
       toReturn = js.get();
     }
+
     if (toReturn == null) {
-      toReturn = Util.readFileAsString(cacheFile);
-      if (toReturn == null) {
+      byte[][] bytes = Util.readFileAndSplit(cacheFile);
+      if (bytes == null) {
         throw new RuntimeException("Unexpectedly unable to read JS file '"
             + cacheFile.getAbsolutePath() + "'");
       }
-      js = new SoftReference<String>(toReturn);
+      toReturn = Util.toStrings(bytes);
+      js = new SoftReference<String[]>(toReturn);
     }
     return toReturn;
   }
 
+  @Override
   public SortedSet<SortedMap<SelectionProperty, String>> getPropertyMap() {
     return Collections.unmodifiableSortedSet(propertyValues);
   }
 
+  @Override
   public String getStrongName() {
     return strongName;
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
index fc6e785..bb2a27d 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
@@ -224,16 +224,18 @@
    */
   public StandardCompilationResult getCompilation(TreeLogger logger, File jsFile)
       throws UnableToCompleteException {
-    byte[] bytes = Util.readFileAsBytes(jsFile);
-    if (bytes == null) {
+    byte[][] results = Util.readFileAndSplit(jsFile);
+
+    if (results == null) {
       logger.log(TreeLogger.ERROR, "Unable to read file '"
           + jsFile.getAbsolutePath() + "'");
       throw new UnableToCompleteException();
     }
-    String strongName = Util.computeStrongName(bytes);
+
+    String strongName = Util.computeStrongName(results);
     StandardCompilationResult result = resultsByStrongName.get(strongName);
     if (result == null) {
-      result = new StandardCompilationResult(Util.toString(bytes), strongName,
+      result = new StandardCompilationResult(Util.toStrings(results), strongName,
           jsFile);
       resultsByStrongName.put(result.getStrongName(), result);
       artifacts.add(result);
diff --git a/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java b/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
index 89517d8..ab19c33 100644
--- a/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
@@ -35,7 +35,7 @@
  */
 @LinkerOrder(Order.PRIMARY)
 public class IFrameLinker extends SelectionScriptLinker {
-
+  @Override
   public String getDescription() {
     return "Standard";
   }
@@ -65,13 +65,13 @@
   }
 
   @Override
-  protected String getModulePrefix(TreeLogger logger, LinkerContext context) {
+  protected String getModulePrefix(TreeLogger logger, LinkerContext context,
+      String strongName) {
     DefaultTextOutput out = new DefaultTextOutput(context.isOutputCompact());
     out.print("<html>");
     out.newlineOpt();
 
     // Setup the well-known variables.
-    //
     out.print("<head><script>");
     out.newlineOpt();
     out.print("var $gwt_version = \"" + About.GWT_VERSION_NUM + "\";");
@@ -82,6 +82,17 @@
     out.newlineOpt();
     out.print("var $moduleName, $moduleBase;");
     out.newlineOpt();
+    out.print("function __gwtStartLoadingFragment(frag) {");
+    out.newlineOpt();
+    out.indentIn();
+    out.print("  var script = $doc.createElement('script');");
+    out.newlineOpt();
+    out.print("  script.src = '" + strongName + "-' + frag + '" + FRAGMENT_EXTENSION + "';");
+    out.print("  document.getElementsByTagName('head').item(0).appendChild(script);");
+    out.indentOut();
+    out.newlineOpt();
+    out.print("};");
+    out.newlineOpt();
     out.print("var $stats = $wnd.__gwtStatsEvent ? function(a) {return $wnd.__gwtStatsEvent(a);} : null;");
     out.newlineOpt();
     out.print("$stats && $stats({moduleName:'" + context.getModuleName()
diff --git a/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js b/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js
index 1f35a26..7051125 100644
--- a/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js
+++ b/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js
@@ -295,7 +295,7 @@
         millis:(new Date()).getTime(), 
         type: 'moduleRequested'
       });
-      iframe.contentWindow.location.replace(base + strongName);
+      iframe.contentWindow.location.replace(base + strongName + '.cache.html');
     }
   }
 
diff --git a/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java b/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java
index 50aa425..790c1cc 100644
--- a/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java
@@ -28,6 +28,7 @@
 import com.google.gwt.dev.util.DefaultTextOutput;
 import com.google.gwt.dev.util.Util;
 
+import java.util.Collection;
 import java.util.Set;
 
 /**
@@ -37,6 +38,7 @@
  */
 @LinkerOrder(Order.PRIMARY)
 public class SingleScriptLinker extends SelectionScriptLinker {
+  @Override
   public String getDescription() {
     return "Single Script";
   }
@@ -52,6 +54,19 @@
   }
 
   @Override
+  protected Collection<EmittedArtifact> doEmitCompilation(TreeLogger logger,
+      LinkerContext context, CompilationResult result)
+      throws UnableToCompleteException {
+    if (result.getJavaScript().length != 1) {
+      logger.branch(TreeLogger.ERROR,
+          "The module must not have multiple fragments when using the "
+              + getDescription() + " Linker.", null);
+      throw new UnableToCompleteException();
+    }
+    return super.doEmitCompilation(logger, context, result);
+  }
+
+  @Override
   protected EmittedArtifact emitSelectionScript(TreeLogger logger,
       LinkerContext context, ArtifactSet artifacts)
       throws UnableToCompleteException {
@@ -92,7 +107,14 @@
     }
     CompilationResult result = results.iterator().next();
 
-    out.print(result.getJavaScript());
+    String[] js = result.getJavaScript();
+    if (js.length != 1) {
+      logger = logger.branch(TreeLogger.ERROR,
+          "The module must not have multiple fragments when using the "
+              + getDescription() + " Linker.", null);
+      throw new UnableToCompleteException();
+    }
+    out.print(js[0]);
 
     // Add a callback to the selection script
     out.newlineOpt();
@@ -128,8 +150,8 @@
    * {@link #doEmitCompilation(TreeLogger, LinkerContext, CompilationResult).
    */
   @Override
-  protected String getModulePrefix(TreeLogger logger, LinkerContext context)
-      throws UnableToCompleteException {
+  protected String getModulePrefix(TreeLogger logger, LinkerContext context,
+      String strongName) throws UnableToCompleteException {
     throw new UnableToCompleteException();
   }
 
diff --git a/dev/core/src/com/google/gwt/core/linker/XSLinker.java b/dev/core/src/com/google/gwt/core/linker/XSLinker.java
index ed544c2..8d2d9fc 100644
--- a/dev/core/src/com/google/gwt/core/linker/XSLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/XSLinker.java
@@ -18,31 +18,48 @@
 import com.google.gwt.core.ext.LinkerContext;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.CompilationResult;
+import com.google.gwt.core.ext.linker.EmittedArtifact;
 import com.google.gwt.core.ext.linker.LinkerOrder;
 import com.google.gwt.core.ext.linker.LinkerOrder.Order;
 import com.google.gwt.core.ext.linker.impl.SelectionScriptLinker;
 import com.google.gwt.dev.About;
 import com.google.gwt.dev.util.DefaultTextOutput;
 
+import java.util.Collection;
+
 /**
  * Generates a cross-site compatible bootstrap sequence.
  */
 @LinkerOrder(Order.PRIMARY)
 public class XSLinker extends SelectionScriptLinker {
-
+  @Override
   public String getDescription() {
     return "Cross-Site";
   }
 
   @Override
+  protected Collection<EmittedArtifact> doEmitCompilation(TreeLogger logger,
+      LinkerContext context, CompilationResult result)
+      throws UnableToCompleteException {
+    if (result.getJavaScript().length != 1) {
+      logger.branch(TreeLogger.ERROR,
+          "The module must not have multiple fragments when using the "
+              + getDescription() + " Linker.", null);
+      throw new UnableToCompleteException();
+    }
+    return super.doEmitCompilation(logger, context, result);
+  }
+
+  @Override
   protected String getCompilationExtension(TreeLogger logger,
       LinkerContext context) throws UnableToCompleteException {
     return ".cache.js";
   }
 
   @Override
-  protected String getModulePrefix(TreeLogger logger, LinkerContext context)
-      throws UnableToCompleteException {
+  protected String getModulePrefix(TreeLogger logger, LinkerContext context,
+      String strongName) throws UnableToCompleteException {
     DefaultTextOutput out = new DefaultTextOutput(context.isOutputCompact());
 
     out.print("(function(){");
diff --git a/dev/core/src/com/google/gwt/dev/CompilePerms.java b/dev/core/src/com/google/gwt/dev/CompilePerms.java
index 17b1042..6ead2f6 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePerms.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePerms.java
@@ -158,7 +158,7 @@
     }
   }
 
-  public static String compile(TreeLogger logger, Permutation permutation,
+  public static String[] compile(TreeLogger logger, Permutation permutation,
       UnifiedAst unifiedAst) {
     try {
       return JavaToJavaScriptCompiler.compilePermutation(logger, unifiedAst,
@@ -255,9 +255,9 @@
     PermutationCompiler multiThread = new PermutationCompiler(branch,
         unifiedAst, perms, permsToRun);
     multiThread.go(new ResultsHandler() {
-      public void addResult(Permutation permutation, int permNum, String js)
+      public void addResult(Permutation permutation, int permNum, String[] js)
           throws UnableToCompleteException {
-        Util.writeStringAsFile(branch, makePermFilename(
+        Util.writeStringsAsFile(branch, makePermFilename(
             options.getCompilerWorkDir(), permNum), js);
       }
     });
diff --git a/dev/core/src/com/google/gwt/dev/PermutationCompiler.java b/dev/core/src/com/google/gwt/dev/PermutationCompiler.java
index f89b102..4922b76 100644
--- a/dev/core/src/com/google/gwt/dev/PermutationCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/PermutationCompiler.java
@@ -39,7 +39,7 @@
    * Hands back results as they are finished.
    */
   public interface ResultsHandler {
-    void addResult(Permutation permutation, int permNum, String js)
+    void addResult(Permutation permutation, int permNum, String[] js)
         throws UnableToCompleteException;
   }
 
@@ -62,7 +62,7 @@
   /**
    * Represents the task of compiling a single permutation.
    */
-  private static final class PermutationTask implements Callable<String> {
+  private static final class PermutationTask implements Callable<String[]> {
     private static void logProperties(TreeLogger logger,
         StaticPropertyOracle[] propOracles) {
       for (StaticPropertyOracle propOracle : propOracles) {
@@ -92,7 +92,7 @@
       this.permNum = permNum;
     }
 
-    public String call() throws Exception {
+    public String[] call() throws Exception {
       PerfLogger.start("Permutation #" + permNum);
       try {
         TreeLogger branch = logger.branch(TreeLogger.TRACE, "Permutation #"
@@ -139,14 +139,14 @@
    * A Result for a permutation that succeeded.
    */
   private static final class SuccessResult extends Result {
-    private final String js;
+    private final String[] js;
 
-    public SuccessResult(Permutation perm, int permNum, String js) {
+    public SuccessResult(Permutation perm, int permNum, String[] result) {
       super(perm, permNum);
-      this.js = js;
+      this.js = result;
     }
 
-    public String getJs() {
+    public String[] getJs() {
       return js;
     }
   }
@@ -201,7 +201,7 @@
 
       boolean definitelyFinalThread = (threadCount.get() == 1);
       try {
-        String result = currentTask.call();
+        String[] result = currentTask.call();
         results.add(new SuccessResult(currentTask.getPermutation(),
             currentTask.getPermNum(), result));
       } catch (OutOfMemoryError e) {
@@ -353,7 +353,7 @@
           assert threadCount.get() == 0;
           return;
         } else if (result instanceof SuccessResult) {
-          String js = ((SuccessResult) result).getJs();
+          String[] js = ((SuccessResult) result).getJs();
           handler.addResult(result.getPermutation(), result.getPermNum(), js);
         } else if (result instanceof FailedResult) {
           FailedResult failedResult = (FailedResult) result;
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index 8465ac5..de667a7 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -30,11 +30,12 @@
 import com.google.gwt.dev.jdt.RebindOracle;
 import com.google.gwt.dev.jdt.RebindPermutationOracle;
 import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd;
-import com.google.gwt.dev.jjs.UnifiedAst;
 import com.google.gwt.dev.jjs.JJSOptions;
 import com.google.gwt.dev.jjs.JJSOptionsImpl;
 import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
 import com.google.gwt.dev.jjs.JsOutputOption;
+import com.google.gwt.dev.jjs.UnifiedAst;
+import com.google.gwt.dev.jjs.impl.FragmentLoaderCreator;
 import com.google.gwt.dev.shell.StandardRebindOracle;
 import com.google.gwt.dev.util.Util;
 import com.google.gwt.dev.util.arg.ArgHandlerDisableAggressiveOptimization;
@@ -258,9 +259,10 @@
       DistillerRebindPermutationOracle rpo = new DistillerRebindPermutationOracle(
           module, generatedArtifacts, new PropertyPermutations(
               module.getProperties()), genDir, generatorResourcesDir);
-
+      FragmentLoaderCreator fragmentLoaderCreator = new FragmentLoaderCreator(
+          module.getCompilationState(), module, genDir, generatorResourcesDir, generatedArtifacts);
       WebModeCompilerFrontEnd frontEnd = new WebModeCompilerFrontEnd(
-          module.getCompilationState(), rpo);
+          module.getCompilationState(), rpo, fragmentLoaderCreator);
       UnifiedAst unifiedAst = JavaToJavaScriptCompiler.precompile(logger,
           frontEnd, declEntryPts, jjsOptions, rpo.getPermuationCount() == 1);
 
@@ -316,9 +318,10 @@
       DistillerRebindPermutationOracle rpo = new DistillerRebindPermutationOracle(
           module, generatorArtifacts, new PropertyPermutations(
               module.getProperties()), genDir, generatorResourcesDir);
-
+      FragmentLoaderCreator fragmentLoaderCreator = new FragmentLoaderCreator(
+          module.getCompilationState(), module, genDir, generatorResourcesDir, generatorArtifacts);
       WebModeCompilerFrontEnd frontEnd = new WebModeCompilerFrontEnd(
-          module.getCompilationState(), rpo);
+          module.getCompilationState(), rpo, fragmentLoaderCreator);
       JavaToJavaScriptCompiler.precompile(logger, frontEnd, declEntryPts,
           jjsOptions, true);
       return true;
diff --git a/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java b/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
index e9e1eab..e4baf2c 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
@@ -26,8 +26,10 @@
 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
 import org.eclipse.jdt.internal.compiler.lookup.Scope;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -42,21 +44,22 @@
    * Information about the site at which a rebind request was found, used to
    * report problems.
    */
-  public static class DeferredBindingSite {
+  public static class MessageSendSite {
     public final MessageSend messageSend;
 
     public final Scope scope;
 
-    public DeferredBindingSite(MessageSend messageSend, Scope scope) {
+    public MessageSendSite(MessageSend messageSend, Scope scope) {
       this.messageSend = messageSend;
       this.scope = scope;
     }
   }
 
-  public static final String REBIND_MAGIC_CLASS = "com.google.gwt.core.client.GWT";
+  public static final String MAGIC_CLASS = "com.google.gwt.core.client.GWT";
   public static final String REBIND_MAGIC_METHOD = "create";
+  public static final String ASYNC_MAGIC_METHOD = "runAsync";
 
-  public static void reportRebindProblem(DeferredBindingSite site,
+  public static void reportRebindProblem(MessageSendSite site,
       String message) {
     MessageSend messageSend = site.messageSend;
     Scope scope = site.scope;
@@ -65,52 +68,77 @@
     GWTProblem.recordInCud(messageSend, cud, message, null);
   }
 
-  private final Map<String, DeferredBindingSite> results = new HashMap<String, DeferredBindingSite>();
+  private final Map<String, MessageSendSite> results = new HashMap<String, MessageSendSite>();
 
+  private final List<MessageSendSite> runAsyncCalls = new ArrayList<MessageSendSite>();
+
+  @Override
   public void endVisit(MessageSend messageSend, BlockScope scope) {
     if (messageSend.binding == null) {
       // Some sort of problem.
-      //
       return;
     }
 
     String methodName = String.valueOf(messageSend.selector);
-    if (!methodName.equals(REBIND_MAGIC_METHOD)) {
-      // Not the create() method.
-      //
+    boolean rebindMagicMethod = methodName.equals(REBIND_MAGIC_METHOD);
+    boolean asyncMagicMethod = methodName.equals(ASYNC_MAGIC_METHOD);
+    if (!(rebindMagicMethod || asyncMagicMethod)) {
+      // Not the create() method or the runAsync() method.
       return;
     }
 
     char[][] targetClass = messageSend.binding.declaringClass.compoundName;
     String targetClassName = CharOperation.toString(targetClass);
-    if (!targetClassName.equals(REBIND_MAGIC_CLASS)) {
+    if (!targetClassName.equals(MAGIC_CLASS)) {
       // Not being called on the Rebind class.
       return;
     }
 
-    DeferredBindingSite site = new DeferredBindingSite(messageSend, scope);
+    MessageSendSite site = new MessageSendSite(messageSend, scope);
 
     Expression[] args = messageSend.arguments;
-    if (args.length != 1) {
-      reportRebindProblem(site, "GWT.create() should take exactly one argument");
+    if (rebindMagicMethod) {
+      if (args.length != 1) {
+        reportRebindProblem(site,
+            "GWT.create() should take exactly one argument");
+        return;
+      }
+
+      if (!(args[0] instanceof ClassLiteralAccess)) {
+        reportRebindProblem(site,
+            "Only class literals may be used as arguments to GWT.create()");
+        return;
+      }
+    } else {
+      assert asyncMagicMethod;
+      if (args.length != 1) {
+        reportRebindProblem(site,
+            "GWT.runAsync() should take exactly one argument");
+        return;
+      }
+    }
+
+    if (asyncMagicMethod) {
+      runAsyncCalls.add(new MessageSendSite(messageSend, scope));
       return;
     }
 
-    Expression arg = args[0];
-    if (!(arg instanceof ClassLiteralAccess)) {
-      reportRebindProblem(site,
-          "Only class literals may be used as arguments to GWT.create()");
-      return;
-    }
-
-    ClassLiteralAccess cla = (ClassLiteralAccess) arg;
+    ClassLiteralAccess cla = (ClassLiteralAccess) args[0];
     String typeName = String.valueOf(cla.targetType.readableName());
+
     if (!results.containsKey(typeName)) {
       results.put(typeName, site);
     }
   }
 
-  public Map<String, DeferredBindingSite> getSites() {
+  /**
+   * Return the calls to GWT.runAsync() that were seen.
+   */
+  public List<MessageSendSite> getRunAsyncSites() {
+    return runAsyncCalls;
+  }
+
+  public Map<String, MessageSendSite> getSites() {
     return Collections.unmodifiableMap(results);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java b/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
index 8566a6d..28185c6 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
@@ -20,7 +20,8 @@
 import com.google.gwt.dev.javac.CompilationState;
 import com.google.gwt.dev.javac.CompiledClass;
 import com.google.gwt.dev.javac.JdtCompiler.CompilationUnitAdapter;
-import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor.DeferredBindingSite;
+import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor.MessageSendSite;
+import com.google.gwt.dev.jjs.impl.FragmentLoaderCreator;
 import com.google.gwt.dev.util.Empty;
 import com.google.gwt.dev.util.JsniRef;
 import com.google.gwt.dev.util.Util;
@@ -41,12 +42,21 @@
  */
 public class WebModeCompilerFrontEnd extends AbstractCompiler {
 
+  private final FragmentLoaderCreator fragmentLoaderCreator;
   private final RebindPermutationOracle rebindPermOracle;
 
+  /**
+   * Construct a WebModeCompilerFrontEnd. The reason a
+   * {@link FragmentLoaderCreator} needs to be passed in is that it uses
+   * generator infrastructure, and therefore needs access to more parts of the
+   * compiler than WebModeCompilerFrontEnd currently has.
+   */
   public WebModeCompilerFrontEnd(CompilationState compilationState,
-      RebindPermutationOracle rebindPermOracle) {
+      RebindPermutationOracle rebindPermOracle,
+      FragmentLoaderCreator fragmentLoaderCreator) {
     super(compilationState, false);
     this.rebindPermOracle = rebindPermOracle;
+    this.fragmentLoaderCreator = fragmentLoaderCreator;
   }
 
   public CompilationUnitDeclaration[] getCompilationUnitDeclarations(
@@ -90,6 +100,7 @@
   /**
    * Pull in types referenced only via JSNI.
    */
+  @Override
   protected String[] doFindAdditionalTypesUsingJsni(TreeLogger logger,
       CompilationUnitDeclaration cud) {
     FindJsniRefVisitor v = new FindJsniRefVisitor();
@@ -109,20 +120,19 @@
   /**
    * Pull in types implicitly referenced through rebind answers.
    */
+  @Override
   protected String[] doFindAdditionalTypesUsingRebinds(TreeLogger logger,
       CompilationUnitDeclaration cud) {
     Set<String> dependentTypeNames = new HashSet<String>();
 
     // Find all the deferred binding request types.
-    //
     FindDeferredBindingSitesVisitor v = new FindDeferredBindingSitesVisitor();
     cud.traverse(v, cud.scope);
-    Map<String, DeferredBindingSite> requestedTypes = v.getSites();
+    Map<String, MessageSendSite> requestedTypes = v.getSites();
 
     // For each, ask the host for every possible deferred binding answer.
-    //
     for (String reqType : requestedTypes.keySet()) {
-      DeferredBindingSite site = requestedTypes.get(reqType);
+      MessageSendSite site = requestedTypes.get(reqType);
 
       try {
         String[] resultTypes = rebindPermOracle.getAllPossibleRebindAnswers(
@@ -134,7 +144,6 @@
           // This causes the compiler to find the additional type, possibly
           // winding its back to ask for the compilation unit from the source
           // oracle.
-          //
           ReferenceBinding type = resolvePossiblyNestedType(typeName);
 
           // Sanity check rebind results.
@@ -180,6 +189,27 @@
             "Failed to resolve '" + reqType + "' via deferred binding");
       }
     }
+
+    /*
+     * Create a a fragment loader for each GWT.runAsync call. They must be
+     * created now, rather than in ReplaceRunAsyncs, because all generated
+     * classes need to be created before GenerateJavaAST. Note that the loaders
+     * created are not yet associated with the specific sites. The present task
+     * is only to make sure that enough loaders exist. The real association
+     * between loaders and runAsync sites will be made in ReplaceRunAsyncs.
+     */
+    for (MessageSendSite site : v.getRunAsyncSites()) {
+      FragmentLoaderCreator loaderCreator = fragmentLoaderCreator;
+      String resultType;
+      try {
+        resultType = loaderCreator.create(logger);
+        dependentTypeNames.add(resultType);
+      } catch (UnableToCompleteException e) {
+        FindDeferredBindingSitesVisitor.reportRebindProblem(site,
+            "Failed to create a runAsync fragment loader");
+      }
+    }
+
     return dependentTypeNames.toArray(Empty.STRINGS);
   }
 }
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 0448a3c..15d18c1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -25,6 +25,7 @@
 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JExpression;
+import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JGwtCreate;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodBody;
@@ -39,13 +40,17 @@
 import com.google.gwt.dev.jjs.impl.BuildTypeMap;
 import com.google.gwt.dev.jjs.impl.CastNormalizer;
 import com.google.gwt.dev.jjs.impl.CatchBlockNormalizer;
+import com.google.gwt.dev.jjs.impl.CodeSplitter;
 import com.google.gwt.dev.jjs.impl.DeadCodeElimination;
 import com.google.gwt.dev.jjs.impl.EqualityNormalizer;
 import com.google.gwt.dev.jjs.impl.Finalizer;
 import com.google.gwt.dev.jjs.impl.FixAssignmentToUnbox;
+import com.google.gwt.dev.jjs.impl.FragmentLoaderCreator;
 import com.google.gwt.dev.jjs.impl.GenerateJavaAST;
 import com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST;
+import com.google.gwt.dev.jjs.impl.JavaAndJavaScript;
 import com.google.gwt.dev.jjs.impl.JavaScriptObjectNormalizer;
+import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
 import com.google.gwt.dev.jjs.impl.JsoDevirtualizer;
 import com.google.gwt.dev.jjs.impl.LongCastNormalizer;
 import com.google.gwt.dev.jjs.impl.LongEmulationNormalizer;
@@ -56,6 +61,7 @@
 import com.google.gwt.dev.jjs.impl.Pruner;
 import com.google.gwt.dev.jjs.impl.RecordRebinds;
 import com.google.gwt.dev.jjs.impl.ReplaceRebinds;
+import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs;
 import com.google.gwt.dev.jjs.impl.ResolveRebinds;
 import com.google.gwt.dev.jjs.impl.TypeMap;
 import com.google.gwt.dev.jjs.impl.TypeTightener;
@@ -70,7 +76,9 @@
 import com.google.gwt.dev.js.JsSymbolResolver;
 import com.google.gwt.dev.js.JsUnusedFunctionRemover;
 import com.google.gwt.dev.js.JsVerboseNamer;
+import com.google.gwt.dev.js.ast.JsName;
 import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsStatement;
 import com.google.gwt.dev.util.DefaultTextOutput;
 import com.google.gwt.dev.util.PerfLogger;
 import com.google.gwt.dev.util.Util;
@@ -81,6 +89,7 @@
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -105,9 +114,16 @@
    * @throws UnableToCompleteException if an error other than
    *           {@link OutOfMemoryError} occurs
    */
-  public static String compilePermutation(TreeLogger logger,
+  public static String[] compilePermutation(TreeLogger logger,
       UnifiedAst unifiedAst, Map<String, String> rebindAnswers)
       throws UnableToCompleteException {
+    return compilePermutationToJavaAndJavaScript(logger, unifiedAst,
+        rebindAnswers).jscode;
+  }
+
+  public static JavaAndJavaScript compilePermutationToJavaAndJavaScript(
+      TreeLogger logger, UnifiedAst unifiedAst,
+      Map<String, String> rebindAnswers) throws UnableToCompleteException {
     try {
       if (JProgram.isTracingEnabled()) {
         System.out.println("------------------------------------------------------------");
@@ -143,10 +159,8 @@
 
       // (7) Generate a JavaScript code DOM from the Java type declarations
       jprogram.typeOracle.recomputeClinits();
-      GenerateJavaScriptAST.exec(jprogram, jsProgram, options.getOutput());
-
-      // Allow GC.
-      jprogram = null;
+      final JavaToJavaScriptMap map = GenerateJavaScriptAST.exec(jprogram,
+          jsProgram, options.getOutput());
 
       // (8) Normalize the JS AST.
       // Fix invalid constructs created during JS AST gen.
@@ -173,23 +187,33 @@
       }
 
       // (10) Obfuscate
+      final Map<JsName, String> stringLiteralMap;
       switch (options.getOutput()) {
         case OBFUSCATED:
-          JsStringInterner.exec(jsProgram);
+          stringLiteralMap = JsStringInterner.exec(jsProgram);
           JsObfuscateNamer.exec(jsProgram);
           break;
         case PRETTY:
           // We don't intern strings in pretty mode to improve readability
+          stringLiteralMap = new HashMap<JsName, String>();
           JsPrettyNamer.exec(jsProgram);
           break;
         case DETAILED:
-          JsStringInterner.exec(jsProgram);
+          stringLiteralMap = JsStringInterner.exec(jsProgram);
           JsVerboseNamer.exec(jsProgram);
           break;
         default:
           throw new InternalCompilerException("Unknown output mode");
       }
 
+      JavaToJavaScriptMap postStringInterningMap = addStringLiteralMap(map,
+          stringLiteralMap);
+
+      // (10.5) Split up the program into fragments
+      if (options.isAggressivelyOptimize()) {
+        CodeSplitter.exec(jprogram, jsProgram, postStringInterningMap);
+      }
+
       // (11) Perform any post-obfuscation normalizations.
 
       // Work around an IE7 bug,
@@ -197,11 +221,18 @@
       JsIEBlockSizeVisitor.exec(jsProgram);
 
       // (12) Generate the final output text.
-      DefaultTextOutput out = new DefaultTextOutput(
-          options.getOutput().shouldMinimize());
-      JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out);
-      v.accept(jsProgram);
-      return out.toString();
+      String[] js = new String[jsProgram.getFragmentCount()];
+      for (int i = 0; i < js.length; i++) {
+
+        DefaultTextOutput out = new DefaultTextOutput(
+            options.getOutput().shouldMinimize());
+        JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out);
+        v.accept(jsProgram.getFragmentBlock(i));
+        js[i] = out.toString();
+      }
+
+      return new JavaAndJavaScript(jprogram, jsProgram, js,
+          postStringInterningMap);
     } catch (Throwable e) {
       throw logAndTranslateException(logger, e);
     }
@@ -239,6 +270,7 @@
     }
     allEntryPoints.addAll(JProgram.CODEGEN_TYPES_SET);
     allEntryPoints.addAll(JProgram.INDEX_TYPES_SET);
+    allEntryPoints.add(FragmentLoaderCreator.ASYNC_FRAGMENT_LOADER);
 
     // Compile the source and get the compiler so we can get the parse tree
     //
@@ -307,6 +339,11 @@
       // Replace GWT.create calls with JGwtCreate nodes.
       ReplaceRebinds.exec(logger, jprogram, rpo);
 
+      // Fix up GWT.runAsync()
+      if (options.isAggressivelyOptimize()) {
+        ReplaceRunAsyncs.exec(logger, jprogram);
+      }
+
       // Resolve entry points, rebinding non-static entry points.
       findEntryPoints(logger, rpo, declEntryPts, jprogram);
 
@@ -386,6 +423,40 @@
     } while (didChange);
   }
 
+  private static JavaToJavaScriptMap addStringLiteralMap(
+      final JavaToJavaScriptMap map, final Map<JsName, String> stringLiteralMap) {
+    JavaToJavaScriptMap postStringInterningMap = new JavaToJavaScriptMap() {
+      public JsName nameForMethod(JMethod method) {
+        return map.nameForMethod(method);
+      }
+
+      public JsName nameForType(JReferenceType type) {
+        return map.nameForType(type);
+      }
+
+      public JField nameToField(JsName name) {
+        return map.nameToField(name);
+      }
+
+      public JMethod nameToMethod(JsName name) {
+        return map.nameToMethod(name);
+      }
+
+      public String stringLiteralForName(JsName name) {
+        return stringLiteralMap.get(name);
+      }
+
+      public JReferenceType typeForStatement(JsStatement stat) {
+        return map.typeForStatement(stat);
+      }
+
+      public JMethod vtableInitToMethod(JsStatement stat) {
+        return map.vtableInitToMethod(stat);
+      }
+    };
+    return postStringInterningMap;
+  }
+
   private static void checkForErrors(TreeLogger logger,
       CompilationUnitDeclaration[] cuds, boolean itemizeErrors)
       throws UnableToCompleteException {
@@ -640,7 +711,8 @@
         isStatsAvailableMethod);
     JMethodCall onModuleStartCall = new JMethodCall(program, sourceInfo, null,
         onModuleStartMethod);
-    onModuleStartCall.getArgs().add(program.getLiteralString(sourceInfo, mainClassName));
+    onModuleStartCall.getArgs().add(
+        program.getLiteralString(sourceInfo, mainClassName));
 
     JBinaryOperation amp = new JBinaryOperation(program, sourceInfo,
         program.getTypePrimitiveBoolean(), JBinaryOperator.AND, availableCall,
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 d8d528c..0ef2689 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
@@ -67,6 +67,8 @@
           "com.google.gwt.core.client.GWT",
           "com.google.gwt.core.client.JavaScriptObject",
           "com.google.gwt.lang.ClassLiteralHolder",
+          "com.google.gwt.core.client.RunAsyncCallback",
+          "com.google.gwt.core.client.AsyncFragmentLoader",
           "com.google.gwt.lang.EntryMethodHolder",}));
 
   static final Map<String, Set<String>> traceMethods = new HashMap<String, Set<String>>();
@@ -150,7 +152,13 @@
 
   public final List<JClassType> codeGenTypes = new ArrayList<JClassType>();
 
-  public final List<JMethod> entryMethods = new ArrayList<JMethod>();
+  /**
+   * There is a list containing the main entry methods as well as the entry methods for
+   * each split point.  The main entry methods are at entry 0 of this list.  Split
+   * points are numbered sequentially from 1, and the entry methods for split point
+   * <em>i</em> are at entry <em>i</em> of this list.
+   */
+  public final List<List<JMethod>> entryMethods = new ArrayList<List<JMethod>>();
 
   public final Map<String, HasEnclosingType> jsniMap = new HashMap<String, HasEnclosingType>();
 
@@ -271,9 +279,17 @@
   }
 
   public void addEntryMethod(JMethod entryPoint) {
+    addEntryMethod(entryPoint, 0);
+  }
+
+  public void addEntryMethod(JMethod entryPoint, int fragmentNumber) {
     assert entryPoint.isStatic();
-    if (!entryMethods.contains(entryPoint)) {
-      entryMethods.add(entryPoint);
+    while (fragmentNumber >= entryMethods.size()) {
+      entryMethods.add(new ArrayList<JMethod>());
+    }
+    List<JMethod> methods = entryMethods.get(fragmentNumber);
+    if (!methods.contains(entryPoint)) {
+      methods.add(entryPoint);
     }
   }
 
@@ -406,12 +422,11 @@
   public JMethod createMethod(SourceInfo info, char[] name,
       JReferenceType enclosingType, JType returnType, boolean isAbstract,
       boolean isStatic, boolean isFinal, boolean isPrivate, boolean isNative) {
-    assert (name != null);
+    String sname = String.valueOf(name);
+    assert (sname != null);
     assert (enclosingType != null);
     assert (returnType != null);
     assert (!isAbstract || !isNative);
-
-    String sname = String.valueOf(name);
     JMethod x = new JMethod(this, info, sname, enclosingType, returnType,
         isAbstract, isStatic, isFinal, isPrivate);
     if (isNative) {
@@ -492,14 +507,30 @@
     return allArrayTypes;
   }
 
+  public List<JMethod> getAllEntryMethods() {
+    List<JMethod> allEntryMethods = new ArrayList<JMethod>();
+    for (List<JMethod> entries : entryMethods) {
+      allEntryMethods.addAll(entries);
+    }
+    return allEntryMethods;
+  }
+
   public List<JReferenceType> getDeclaredTypes() {
     return allTypes;
   }
 
+  public int getEntryCount(int fragment) {
+    return entryMethods.get(fragment).size();
+  }
+
   public JThisRef getExprThisRef(SourceInfo info, JClassType enclosingType) {
     return new JThisRef(this, info, enclosingType);
   }
 
+  public int getFragmentCount() {
+    return entryMethods.size();
+  }
+
   public JReferenceType getFromTypeMap(String qualifiedBinaryOrSourceName) {
     String srcTypeName = qualifiedBinaryOrSourceName.replace('$', '.');
     return typeNameMap.get(srcTypeName);
@@ -842,9 +873,9 @@
   }
 
   /**
-   * If <code>method</code> is a static impl method, returns the instance method
-   * that <code>method</code> is the implementation of. Otherwise, returns
-   * <code>null</code>.
+   * If <code>method</code> is a static impl method, returns the instance
+   * method that <code>method</code> is the implementation of. Otherwise,
+   * returns <code>null</code>.
    */
   public JMethod staticImplFor(JMethod method) {
     return staticToInstanceMap.get(method);
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 b7774a2..8bfddfa 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
@@ -138,6 +138,35 @@
   }
 
   /**
+   * Determine whether a type is instantiated, given an assumed list of
+   * instantiated types.
+   * 
+   * @param type any type
+   * @param instantiatedTypes a set of types assumed to be instantiated. If
+   *          <code>null</code>, then there are no assumptions about which
+   *          types are instantiated.
+   * @return whether the type is instantiated
+   */
+  private static boolean isInstantiatedType(JReferenceType type,
+      Set<JReferenceType> instantiatedTypes) {
+    if (instantiatedTypes == null) {
+      return true;
+    }
+
+    if (type instanceof JNullType) {
+      return true;
+    }
+
+    if (type instanceof JArrayType) {
+      JArrayType arrayType = (JArrayType) type;
+      if (arrayType.getLeafType() instanceof JNullType) {
+        return true;
+      }
+    }
+    return instantiatedTypes.contains(type);
+  }
+
+  /**
    * Compare two methods based on name and original argument types
    * {@link JMethod#getOriginalParamTypes()}. Note that nothing special is done
    * here regarding methods with type parameters in their argument lists. The
@@ -389,14 +418,19 @@
     return getOrCreate(superInterfaceMap, type).contains(qType);
   }
 
+  public Set<JMethod> getAllOverrides(JMethod method) {
+    return getAllOverrides(method, instantiatedTypes);
+  }
+
   /**
    * References to any methods which this method implementation might override
    * or implement in any instantiable class.
    */
-  public Set<JMethod> getAllOverrides(JMethod method) {
+  public Set<JMethod> getAllOverrides(JMethod method,
+      Set<JReferenceType> instantiatedTypes) {
     Set<JMethod> results = new HashSet<JMethod>();
     getAllRealOverrides(method, results);
-    getAllVirtualOverrides(method, results);
+    getAllVirtualOverrides(method, instantiatedTypes, results);
     return results;
   }
 
@@ -419,7 +453,7 @@
    */
   public Set<JMethod> getAllVirtualOverrides(JMethod method) {
     Set<JMethod> results = new HashSet<JMethod>();
-    getAllVirtualOverrides(method, results);
+    getAllVirtualOverrides(method, instantiatedTypes, results);
     return results;
   }
 
@@ -436,22 +470,7 @@
   }
 
   public boolean isInstantiatedType(JReferenceType type) {
-    if (instantiatedTypes == null) {
-      // The instantiated types have not yet been computed.
-      return true;
-    }
-
-    if (type instanceof JNullType) {
-      return true;
-    }
-
-    if (type instanceof JArrayType) {
-      JArrayType arrayType = (JArrayType) type;
-      if (arrayType.getLeafType() instanceof JNullType) {
-        return true;
-      }
-    }
-    return instantiatedTypes.contains(type);
+    return isInstantiatedType(type, instantiatedTypes);
   }
 
   /**
@@ -637,19 +656,19 @@
     }
   }
 
-  private void getAllRealOverrides(JMethod method, Set<JMethod> results) {
+  private void getAllRealOverrides(JMethod method,
+      Set<JMethod> results) {
     for (JMethod possibleOverride : method.overrides) {
-      // if (instantiatedTypes.contains(possibleOverride.getEnclosingType())) {
       results.add(possibleOverride);
-      // }
     }
   }
 
-  private void getAllVirtualOverrides(JMethod method, Set<JMethod> results) {
+  private void getAllVirtualOverrides(JMethod method,
+      Set<JReferenceType> instantiatedTypes, Set<JMethod> results) {
     Map<JClassType, Set<JMethod>> overrideMap = getOrCreateMap(virtualUpRefMap,
         method);
     for (JClassType classType : overrideMap.keySet()) {
-      if (isInstantiatedType(classType)) {
+      if (isInstantiatedType(classType, instantiatedTypes)) {
         Set<JMethod> set = overrideMap.get(classType);
         results.addAll(set);
       }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
index 3001dd7..a485615 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
@@ -44,6 +44,13 @@
     return null;
   }
 
+  public JExpression getInitializer() {
+    if (declStmt != null) {
+      return declStmt.getInitializer();
+    }
+    return null;
+  }
+
   public String getName() {
     return name;
   }
@@ -68,11 +75,4 @@
     type = newType;
   }
 
-  protected JExpression getInitializer() {
-    if (declStmt != null) {
-      return declStmt.getInitializer();
-    }
-    return null;
-  }
-
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodBody.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodBody.java
index ccc613f..7095710 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodBody.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodBody.java
@@ -20,10 +20,16 @@
 import com.google.gwt.dev.jjs.ast.JAbstractMethodBody;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.js.ast.JsContext;
+import com.google.gwt.dev.js.ast.JsExpression;
 import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsStringLiteral;
+import com.google.gwt.dev.js.ast.JsVisitor;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Represents a the body of a method. Can be Java or JSNI.
@@ -31,8 +37,8 @@
 public class JsniMethodBody extends JAbstractMethodBody {
 
   public final List<JsniFieldRef> jsniFieldRefs = new ArrayList<JsniFieldRef>();
-
   public final List<JsniMethodRef> jsniMethodRefs = new ArrayList<JsniMethodRef>();
+  private final Set<String> stringLiterals = new HashSet<String>();
 
   private JsFunction jsFunction = null;
 
@@ -45,6 +51,11 @@
     return jsFunction;
   }
 
+  public Set<String> getUsedStrings() {
+    return stringLiterals;
+  }
+
+  @Override
   public boolean isNative() {
     return true;
   }
@@ -52,6 +63,13 @@
   public void setFunc(JsFunction jsFunction) {
     assert (this.jsFunction == null);
     this.jsFunction = jsFunction;
+    class RecordStrings extends JsVisitor {
+      @Override
+      public void endVisit(JsStringLiteral lit, JsContext<JsExpression> ctx) {
+        stringLiterals.add(lit.getValue());
+      }
+    }
+    (new RecordStrings()).accept(jsFunction);
   }
 
   public void traverse(JVisitor visitor, Context ctx) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
new file mode 100644
index 0000000..725804b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
@@ -0,0 +1,444 @@
+/*
+ * 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.jjs.impl;
+
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JClassLiteral;
+import com.google.gwt.dev.jjs.ast.JExpression;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JNode;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JStringLiteral;
+import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.jjs.impl.FragmentExtractor.LivenessPredicate;
+import com.google.gwt.dev.jjs.impl.FragmentExtractor.NothingAlivePredicate;
+import com.google.gwt.dev.jjs.impl.FragmentExtractor.StatementLogger;
+import com.google.gwt.dev.js.ast.JsBlock;
+import com.google.gwt.dev.js.ast.JsExprStmt;
+import com.google.gwt.dev.js.ast.JsExpression;
+import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsStatement;
+import com.google.gwt.dev.util.PerfLogger;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+
+/**
+ * <p>
+ * Divides the code in a {@link JsProgram} into multiple fragments. The initial
+ * fragment is sufficient to run all of the program's functionality except for
+ * anything called in a callback supplied to
+ * {@link com.google.gwt.core.client.GWT#runAsync(com.google.gwt.core.client.RunAsyncCallback) GWT.runAsync()}.
+ * The remaining code should be downloadable via
+ * {@link com.google.gwt.core.client.AsyncFragmentLoader#inject(int)}.
+ * </p>
+ * 
+ * <p>
+ * The precise way the program is fragmented is an implementation detail that is
+ * subject to change. Whenever the fragment strategy changes,
+ * <code>AsyncFragmentLoader</code> must be updated in tandem. That said, the
+ * current fragmentation strategy is to create one fragment for each call to
+ * <code>runAsync()</code>. Each such fragment holds the code that is
+ * exclusively needed by that particular call to <code>runAsync()</code>. Any
+ * code needed by two or more calls to <code>runAsync()</code> is placed in
+ * the initial fragment.
+ * </p>
+ */
+public class CodeSplitter {
+  /**
+   * A statement logger that immediately prints out everything live that it
+   * sees.
+   */
+  public class EchoStatementLogger implements StatementLogger {
+    public void logStatement(JsStatement stat, boolean isIncluded) {
+      if (isIncluded) {
+        if (stat instanceof JsExprStmt) {
+          JsExpression expr = ((JsExprStmt) stat).getExpression();
+          if (expr instanceof JsFunction) {
+            JsFunction func = (JsFunction) expr;
+            if (func.getName() != null) {
+              JMethod method = map.nameToMethod(func.getName());
+              if (method != null && method.getEnclosingType() != null) {
+                System.out.println(method.getEnclosingType().getName() + "."
+                    + JProgram.getJsniSig(method));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * A map from program atoms to the fragment they should be placed in.
+   */
+  private static class FragmentMap {
+    public Map<JField, Integer> fields = new HashMap<JField, Integer>();
+    public Map<JMethod, Integer> methods = new HashMap<JMethod, Integer>();
+    public Map<String, Integer> strings = new HashMap<String, Integer>();
+    public Map<JReferenceType, Integer> types = new HashMap<JReferenceType, Integer>();
+  }
+
+  /**
+   * A liveness predicate that is based on a fragment map. See
+   * {@link #mapFragments()}. Note that all non-zero fragments are assumed to
+   * load after fragment 0, and so everything in fragment 0 is always live.
+   */
+  private static class FragmentMapLivenessPredicate implements
+      LivenessPredicate {
+    private final int fragment;
+    private final FragmentMap fragmentMap;
+
+    public FragmentMapLivenessPredicate(FragmentMap fragmentMap, int fragment) {
+      this.fragmentMap = fragmentMap;
+      this.fragment = fragment;
+    }
+
+    public boolean isLive(JField field) {
+      return checkMap(fragmentMap.fields, field);
+    }
+
+    public boolean isLive(JMethod method) {
+      return checkMap(fragmentMap.methods, method);
+    }
+
+    public boolean isLive(JReferenceType type) {
+      return checkMap(fragmentMap.types, type);
+    }
+
+    public boolean isLive(String literal) {
+      return checkMap(fragmentMap.strings, literal);
+    }
+
+    public boolean miscellaneousStatementsAreLive() {
+      return true;
+    }
+
+    private <T> boolean checkMap(Map<T, Integer> map, T x) {
+      Integer entryForX = map.get(x);
+      if (entryForX == null) {
+        // unrecognized items are always live
+        return true;
+      } else {
+        return (fragment == entryForX) || (entryForX == 0);
+      }
+    }
+  }
+
+  /**
+   * A Java property that causes the fragment map to be logged.
+   * 
+   * TODO(spoon) save the logging data to an auxiliary compiler output and, if
+   * the logging is not too slow, always enable it.
+   */
+  private static String PROP_LOG_FRAGMENT_MAP = "gwt.jjs.logFragmentMap";
+
+  public static void exec(JProgram jprogram, JsProgram jsprogram,
+      JavaToJavaScriptMap map) {
+    new CodeSplitter(jprogram, jsprogram, map).execImpl();
+  }
+
+  private static <T> int getOrZero(Map<T, Integer> map, T key) {
+    Integer value = map.get(key);
+    return (value == null) ? 0 : value;
+  }
+
+  private static <T> void updateMap(int entry, Map<T, Integer> map,
+      Set<?> liveWithoutEntry, Iterable<T> all) {
+    for (T each : all) {
+      if (!liveWithoutEntry.contains(each)) {
+        /*
+         * Note that it is fine to overwrite a preexisting entry in the map. If
+         * an atom is dead until split point i has been reached, and is also
+         * dead until entry j has been reached, then it is dead until both have
+         * been reached. Thus, it can be downloaded along with either i's or j's
+         * code.
+         */
+        map.put(each, entry);
+      }
+    }
+  }
+
+  private final Map<JField, JClassLiteral> fieldToLiteralOfClass;
+  private JProgram jprogram;
+  private JsProgram jsprogram;
+  private final boolean logging;
+  private JavaToJavaScriptMap map;
+  private final int numEntries;
+
+  private CodeSplitter(JProgram jprogram, JsProgram jsprogram,
+      JavaToJavaScriptMap map) {
+    this.jprogram = jprogram;
+    this.jsprogram = jsprogram;
+    this.map = map;
+    numEntries = jprogram.entryMethods.size();
+    logging = Boolean.getBoolean(PROP_LOG_FRAGMENT_MAP);
+    fieldToLiteralOfClass = FragmentExtractor.buildFieldToClassLiteralMap(jprogram);
+  }
+
+  /**
+   * For each split point other than the initial one (0), compute a CFA that
+   * traces every other split point.
+   */
+  private List<ControlFlowAnalyzer> computeAllButOneCfas() {
+    // Reusing initiallyLive for each entry gives a significant speedup
+    ControlFlowAnalyzer initiallyLive = new ControlFlowAnalyzer(jprogram);
+    traverseEntry(initiallyLive, 0);
+    initiallyLive.finishTraversal();
+
+    List<ControlFlowAnalyzer> allButOnes = new ArrayList<ControlFlowAnalyzer>(
+        numEntries - 1);
+
+    for (int entry = 1; entry < numEntries; entry++) {
+      ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(initiallyLive);
+      traverseAllButEntry(cfa, entry);
+      cfa.finishTraversal();
+      allButOnes.add(cfa);
+    }
+
+    return allButOnes;
+  }
+
+  /**
+   * Compute a CFA that covers the entire live code of the program.
+   */
+  private ControlFlowAnalyzer computeCompleteCfa() {
+    ControlFlowAnalyzer everything = new ControlFlowAnalyzer(jprogram);
+    for (int entry = 0; entry < numEntries; entry++) {
+      traverseEntry(everything, entry);
+    }
+    everything.finishTraversal();
+    return everything;
+  }
+
+  private void execImpl() {
+    if (numEntries == 1) {
+      // Don't do anything if there is no call to runAsync
+      return;
+    }
+
+    PerfLogger.start("CodeSplitter");
+
+    // Map code to the fragments
+    FragmentMap fragmentMap = mapFragments();
+
+    List<List<JsStatement>> fragmentStats = new ArrayList<List<JsStatement>>(
+        numEntries);
+
+    // Save the extractor for reuse
+    FragmentExtractor fragmentExtractor = new FragmentExtractor(jprogram,
+        jsprogram, map);
+
+    // Extract the code for each fragment, according to fragmentMap
+    for (int i = 0; i < numEntries; i++) {
+      LivenessPredicate pred = new FragmentMapLivenessPredicate(fragmentMap, i);
+
+      LivenessPredicate alreadyLoaded;
+      if (i == 0) {
+        alreadyLoaded = new NothingAlivePredicate();
+      } else {
+        alreadyLoaded = new FragmentMapLivenessPredicate(fragmentMap, 0);
+      }
+
+      if (logging) {
+        System.out.println();
+        System.out.println("==== Fragment " + i + " ====");
+        fragmentExtractor.setStatementLogger(new EchoStatementLogger());
+      }
+
+      List<JsStatement> entryStats = fragmentExtractor.extractStatements(pred,
+          alreadyLoaded);
+
+      if (i > 0) {
+        /*
+         * The fragment mapper drops all calls to entry methods. Add them back.
+         */
+        fragmentExtractor.addCallsToEntryMethods(i, entryStats);
+      }
+
+      fragmentStats.add(entryStats);
+    }
+
+    // Install the new statements in the program fragments
+    jsprogram.setFragmentCount(numEntries);
+    for (int i = 0; i < fragmentStats.size(); i++) {
+      JsBlock fragBlock = jsprogram.getFragmentBlock(i);
+      fragBlock.getStatements().clear();
+      fragBlock.getStatements().addAll(fragmentStats.get(i));
+    }
+
+    PerfLogger.end();
+  }
+
+  /**
+   * Mostly it is okay to load code before it is needed. However, there are some
+   * exceptions, where merely loading a code atom requires that some other atom
+   * has also been loaded. To address such situations, move the load-time
+   * dependencies to fragment 0, so they are sure to be available.
+   */
+  private void fixUpLoadOrderDependencies(FragmentMap fragmentMap) {
+    fixUpLoadOrderDependenciesForTypes(fragmentMap);
+    fixUpLoadOrderDependenciesForClassLiterals(fragmentMap);
+  }
+
+  /**
+   * A class literal cannot be loaded until its associate strings are. Make sure
+   * that the strings are available for all class literals at the time they are
+   * loaded.
+   */
+  private void fixUpLoadOrderDependenciesForClassLiterals(
+      FragmentMap fragmentMap) {
+    for (JField field : fragmentMap.fields.keySet()) {
+      JClassLiteral classLit = fieldToLiteralOfClass.get(field);
+      if (classLit != null) {
+        int classLitFrag = fragmentMap.fields.get(field);
+        for (String string : stringsIn(field.getInitializer())) {
+          int stringFrag = getOrZero(fragmentMap.strings, string);
+          if (stringFrag != classLitFrag && stringFrag != 0) {
+            fragmentMap.strings.put(string, 0);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * The setup code for a class cannot be loaded before the setup code for its
+   * superclass.
+   */
+  private void fixUpLoadOrderDependenciesForTypes(FragmentMap fragmentMap) {
+    Queue<JReferenceType> typesToCheck = new ArrayBlockingQueue<JReferenceType>(
+        jprogram.getDeclaredTypes().size());
+    typesToCheck.addAll(jprogram.getDeclaredTypes());
+    while (!typesToCheck.isEmpty()) {
+      JReferenceType type = typesToCheck.remove();
+      if (type.extnds != null) {
+        int typeFrag = getOrZero(fragmentMap.types, type);
+        int supertypeFrag = getOrZero(fragmentMap.types, type.extnds);
+        if (typeFrag != supertypeFrag && supertypeFrag != 0) {
+          fragmentMap.types.put(type.extnds, 0);
+          typesToCheck.add(type.extnds);
+        }
+      }
+    }
+  }
+
+  /**
+   * Map code to fragments. Do this by trying to find code atoms that are only
+   * needed by a single split point. Such code can be moved to the exclusively
+   * live fragment associated with that split point.
+   */
+  private void mapExclusiveAtoms(FragmentMap fragmentMap) {
+    List<ControlFlowAnalyzer> allButOnes = computeAllButOneCfas();
+
+    ControlFlowAnalyzer everything = computeCompleteCfa();
+
+    Set<JField> allFields = new HashSet<JField>();
+    Set<JMethod> allMethods = new HashSet<JMethod>();
+
+    for (JNode node : everything.getLiveFieldsAndMethods()) {
+      if (node instanceof JField) {
+        allFields.add((JField) node);
+      }
+      if (node instanceof JMethod) {
+        allMethods.add((JMethod) node);
+      }
+    }
+
+    for (int entry = 1; entry < numEntries; entry++) {
+      ControlFlowAnalyzer allButOne = allButOnes.get(entry - 1);
+      updateMap(entry, fragmentMap.fields, allButOne.getLiveFieldsAndMethods(),
+          allFields);
+      updateMap(entry, fragmentMap.methods,
+          allButOne.getLiveFieldsAndMethods(), allMethods);
+      updateMap(entry, fragmentMap.strings, allButOne.getLiveStrings(),
+          everything.getLiveStrings());
+      updateMap(entry, fragmentMap.types, allButOne.getInstantiatedTypes(),
+          everything.getInstantiatedTypes());
+    }
+  }
+
+  /**
+   * Map each program atom to a fragment. Atoms are mapped to a non-zero
+   * fragment whenever they are known not to be needed whenever that fragment's
+   * split point has not been reached. Any atoms that cannot be so mapped are
+   * left in fragment zero.
+   */
+  private FragmentMap mapFragments() {
+    FragmentMap fragmentMap = new FragmentMap();
+
+    mapExclusiveAtoms(fragmentMap);
+    fixUpLoadOrderDependencies(fragmentMap);
+
+    return fragmentMap;
+  }
+
+  /**
+   * Traverse <code>exp</code> and find all string literals within it.
+   */
+  private Set<String> stringsIn(JExpression exp) {
+    final Set<String> strings = new HashSet<String>();
+    class StringFinder extends JVisitor {
+      @Override
+      public void endVisit(JStringLiteral stringLiteral, Context ctx) {
+        strings.add(stringLiteral.getValue());
+      }
+    }
+    (new StringFinder()).accept(exp);
+    return strings;
+  }
+
+  /**
+   * Traverse all code in the program except for that reachable only via
+   * fragment <code>frag</code>. This does not call
+   * {@link ControlFlowAnalyzer#finishTraversal()}.
+   */
+  private void traverseAllButEntry(ControlFlowAnalyzer cfa, int entry) {
+    for (int otherEntry = 0; otherEntry < numEntries; otherEntry++) {
+      if (otherEntry != entry) {
+        traverseEntry(cfa, otherEntry);
+      }
+    }
+  }
+
+  /**
+   * Traverse all code in the program that is reachable via fragment
+   * <code>frag</code>. This does not call
+   * {@link ControlFlowAnalyzer#finishTraversal()}.
+   */
+  private void traverseEntry(ControlFlowAnalyzer cfa, int entry) {
+    for (JMethod entryMethod : jprogram.entryMethods.get(entry)) {
+      cfa.traverseFrom(entryMethod);
+    }
+    if (entry == 0) {
+      /*
+       * Include class literal factories for simplicity. It is possible to move
+       * them out, if they are only needed by one fragment, but they are tiny,
+       * so it does not look like it is worth the complexity in the compiler.
+       */
+      cfa.traverseFromClassLiteralFactories();
+    }
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
new file mode 100644
index 0000000..8831aec
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
@@ -0,0 +1,680 @@
+/*
+ * 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.jjs.impl;
+
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
+import com.google.gwt.dev.jjs.ast.JArrayType;
+import com.google.gwt.dev.jjs.ast.JBinaryOperation;
+import com.google.gwt.dev.jjs.ast.JBinaryOperator;
+import com.google.gwt.dev.jjs.ast.JCastOperation;
+import com.google.gwt.dev.jjs.ast.JClassLiteral;
+import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
+import com.google.gwt.dev.jjs.ast.JExpression;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JFieldRef;
+import com.google.gwt.dev.jjs.ast.JInterfaceType;
+import com.google.gwt.dev.jjs.ast.JLocal;
+import com.google.gwt.dev.jjs.ast.JLocalRef;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JNewArray;
+import com.google.gwt.dev.jjs.ast.JNewInstance;
+import com.google.gwt.dev.jjs.ast.JNode;
+import com.google.gwt.dev.jjs.ast.JParameter;
+import com.google.gwt.dev.jjs.ast.JParameterRef;
+import com.google.gwt.dev.jjs.ast.JPrimitiveType;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JStringLiteral;
+import com.google.gwt.dev.jjs.ast.JType;
+import com.google.gwt.dev.jjs.ast.JVariable;
+import com.google.gwt.dev.jjs.ast.JVariableRef;
+import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
+import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
+import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
+import com.google.gwt.dev.js.ast.JsContext;
+import com.google.gwt.dev.js.ast.JsExpression;
+import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsName;
+import com.google.gwt.dev.js.ast.JsNameRef;
+import com.google.gwt.dev.js.ast.JsVisitor;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class finds out what code in a program is live based on starting
+ * execution at a specified location. Note that the client must call
+ * {@link #finishTraversal()} after the other traversal methods have been
+ * called, or the results will be incomplete.
+ */
+public class ControlFlowAnalyzer {
+
+  /**
+   * Marks as "referenced" any types, methods, and fields that are reachable.
+   * Also marks as "instantiable" any the classes and interfaces that can
+   * possibly be instantiated.
+   * 
+   * TODO(later): make RescueVisitor use less stack?
+   */
+  private class RescueVisitor extends JVisitor {
+    @Override
+    public boolean visit(JArrayType type, Context ctx) {
+      assert (referencedTypes.contains(type));
+      boolean isInstantiated = instantiatedTypes.contains(type);
+
+      JType leafType = type.getLeafType();
+      int dims = type.getDims();
+
+      // Rescue my super array type
+      if (leafType instanceof JReferenceType) {
+        JReferenceType rLeafType = (JReferenceType) leafType;
+        if (rLeafType.extnds != null) {
+          JArrayType superArray = program.getTypeArray(rLeafType.extnds, dims);
+          rescue(superArray, true, isInstantiated);
+        }
+
+        for (int i = 0; i < rLeafType.implments.size(); ++i) {
+          JInterfaceType intfType = rLeafType.implments.get(i);
+          JArrayType intfArray = program.getTypeArray(intfType, dims);
+          rescue(intfArray, true, isInstantiated);
+        }
+      }
+
+      // Rescue the base Array type
+      rescue(program.getIndexedType("Array"), true, isInstantiated);
+      return false;
+    }
+
+    @Override
+    public boolean visit(JBinaryOperation x, Context ctx) {
+      // special string concat handling
+      if ((x.getOp() == JBinaryOperator.ADD || x.getOp() == JBinaryOperator.ASG_ADD)
+          && x.getType() == program.getTypeJavaLangString()) {
+        rescueByConcat(x.getLhs().getType());
+        rescueByConcat(x.getRhs().getType());
+      } else if (x.getOp() == JBinaryOperator.ASG) {
+        // Don't rescue variables that are merely assigned to and never read
+        boolean doSkip = false;
+        JExpression lhs = x.getLhs();
+        if (lhs.hasSideEffects() || isVolatileField(lhs)) {
+          // If the lhs has side effects, skipping it would lose the side
+          // effect.
+          // If the lhs is volatile, also keep it. This behavior provides a
+          // useful
+          // idiom for test cases to prevent code from being pruned.
+        } else if (lhs instanceof JLocalRef) {
+          // locals are ok to skip
+          doSkip = true;
+        } else if (lhs instanceof JParameterRef) {
+          // parameters are ok to skip
+          doSkip = true;
+        } else if (lhs instanceof JFieldRef) {
+          // fields must rescue the qualifier
+          doSkip = true;
+          JFieldRef fieldRef = (JFieldRef) lhs;
+          JExpression instance = fieldRef.getInstance();
+          if (instance != null) {
+            accept(instance);
+          }
+        }
+
+        if (doSkip) {
+          accept(x.getRhs());
+          return false;
+        }
+      }
+      return true;
+    }
+
+    @Override
+    public boolean visit(JCastOperation x, Context ctx) {
+      // Rescue any JavaScriptObject type that is the target of a cast.
+      JType targetType = x.getCastType();
+      if (program.isJavaScriptObject(targetType)) {
+        rescue((JReferenceType) targetType, true, true);
+      }
+      return true;
+    }
+
+    @Override
+    public boolean visit(JClassLiteral x, Context ctx) {
+      /*
+       * Rescue just slightly less than what would normally be rescued for a
+       * field reference to the literal's field. Rescue the field itself, and
+       * its initializer, but do NOT rescue the whole enclosing class. That
+       * would pull in the clinit of that class, which has initializers for all
+       * the class literals, which in turn have all of the strings of all of the
+       * class names.
+       */
+      JField field = x.getField();
+      rescue(field);
+      accept(field.getInitializer());
+      referencedTypes.add(field.getEnclosingType());
+      liveFieldsAndMethods.add(field.getEnclosingType().methods.get(0));
+      return true;
+    }
+
+    @Override
+    public boolean visit(JClassType type, Context ctx) {
+      assert (referencedTypes.contains(type));
+      boolean isInstantiated = instantiatedTypes.contains(type);
+
+      // Rescue my super type
+      rescue(type.extnds, true, isInstantiated);
+
+      // Rescue my clinit (it won't ever be explicitly referenced
+      rescue(type.methods.get(0));
+
+      // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
+      // However, if I'm instantiated, let's mark them as instantiated.
+      for (int i = 0; i < type.implments.size(); ++i) {
+        JInterfaceType intfType = type.implments.get(i);
+        rescue(intfType, false, isInstantiated);
+      }
+
+      return false;
+    }
+
+    @Override
+    public boolean visit(JDeclarationStatement x, Context ctx) {
+      /*
+       * A declaration by itself doesn't rescue a local (even if it has an
+       * initializer). Writes don't count, only reads.
+       */
+      if (x.getInitializer() != null) {
+        accept(x.getInitializer());
+      }
+
+      // If the lhs is a field ref, we have to visit its qualifier.
+      JVariableRef variableRef = x.getVariableRef();
+      if (variableRef instanceof JFieldRef) {
+        JFieldRef fieldRef = (JFieldRef) variableRef;
+        JExpression instance = fieldRef.getInstance();
+        if (instance != null) {
+          accept(instance);
+        }
+      }
+      return false;
+    }
+
+    @Override
+    public boolean visit(JFieldRef ref, Context ctx) {
+      JField target = ref.getField();
+
+      // JLS 12.4.1: references to static, non-final, or
+      // non-compile-time-constant fields rescue the enclosing class.
+      // JDT already folds in compile-time constants as literals, so we must
+      // rescue the enclosing types for any static fields that make it here.
+      if (target.isStatic()) {
+        rescue(target.getEnclosingType(), true, false);
+      }
+      rescue(target);
+      return true;
+    }
+
+    @Override
+    public boolean visit(JInterfaceType type, Context ctx) {
+      boolean isReferenced = referencedTypes.contains(type);
+      boolean isInstantiated = instantiatedTypes.contains(type);
+      assert (isReferenced || isInstantiated);
+
+      // Rescue my clinit (it won't ever be explicitly referenced
+      rescue(type.methods.get(0));
+
+      // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
+      // However, if I'm instantiated, let's mark them as instantiated.
+      if (isInstantiated) {
+        for (int i = 0; i < type.implments.size(); ++i) {
+          JInterfaceType intfType = type.implments.get(i);
+          rescue(intfType, false, true);
+        }
+      }
+
+      // visit any field initializers
+      for (int i = 0; i < type.fields.size(); ++i) {
+        JField it = type.fields.get(i);
+        accept(it);
+      }
+
+      return false;
+    }
+
+    @Override
+    public boolean visit(JLocalRef ref, Context ctx) {
+      JLocal target = ref.getLocal();
+      rescue(target);
+      return true;
+    }
+
+    @Override
+    public boolean visit(final JMethod x, Context ctx) {
+      JReferenceType enclosingType = x.getEnclosingType();
+      if (program.isJavaScriptObject(enclosingType)) {
+        // Calls to JavaScriptObject types rescue those types.
+        boolean instance = !x.isStatic() || program.isStaticImpl(x);
+        rescue(enclosingType, true, instance);
+      } else if (x.isStatic()) {
+        // JLS 12.4.1: references to static methods rescue the enclosing class
+        rescue(enclosingType, true, false);
+      }
+
+      if (x.isNative()) {
+        // Manually rescue native parameter references
+        final JsniMethodBody body = (JsniMethodBody) x.getBody();
+        final JsFunction func = body.getFunc();
+
+        new JsVisitor() {
+          @Override
+          public void endVisit(JsNameRef nameRef, JsContext<JsExpression> ctx) {
+            JsName ident = nameRef.getName();
+
+            if (ident != null) {
+              // If we're referencing a parameter, rescue the associated
+              // JParameter
+              int index = func.getParameters().indexOf(ident.getStaticRef());
+              if (index != -1) {
+                rescue(x.params.get(index));
+              }
+            }
+          }
+        }.accept(func);
+      }
+
+      return true;
+    }
+
+    @Override
+    public boolean visit(JMethodCall call, Context ctx) {
+      rescue(call.getTarget());
+      return true;
+    }
+
+    @Override
+    public boolean visit(JNewArray newArray, Context ctx) {
+      // rescue and instantiate the array type
+      JArrayType arrayType = newArray.getArrayType();
+      if (newArray.dims != null) {
+        // rescue my type and all the implicitly nested types (with fewer dims)
+        int nDims = arrayType.getDims();
+        JType leafType = arrayType.getLeafType();
+        assert (newArray.dims.size() == nDims);
+        for (int i = 0; i < nDims; ++i) {
+          if (newArray.dims.get(i) instanceof JAbsentArrayDimension) {
+            break;
+          }
+          rescue(program.getTypeArray(leafType, nDims - i), true, true);
+        }
+      } else {
+        // just rescue my own specific type
+        rescue(arrayType, true, true);
+      }
+      return true;
+    }
+
+    @Override
+    public boolean visit(JNewInstance newInstance, Context ctx) {
+      // rescue and instantiate the target class!
+      rescue(newInstance.getClassType(), true, true);
+      return true;
+    }
+
+    @Override
+    public boolean visit(JParameterRef x, Context ctx) {
+      // rescue the parameter for future pruning purposes
+      rescue(x.getParameter());
+      return true;
+    }
+
+    @Override
+    public boolean visit(JsniFieldRef x, Context ctx) {
+      /*
+       * SPECIAL: this could be an assignment that passes a value from
+       * JavaScript into Java.
+       */
+      if (x.isLvalue()) {
+        maybeRescueJavaScriptObjectPassingIntoJava(x.getField().getType());
+      }
+      // JsniFieldRef rescues as JFieldRef
+      return visit((JFieldRef) x, ctx);
+    }
+
+    @Override
+    public boolean visit(JsniMethodBody body, Context ctx) {
+      liveStrings.addAll(body.getUsedStrings());
+      return true;
+    }
+
+    @Override
+    public boolean visit(JsniMethodRef x, Context ctx) {
+      /*
+       * SPECIAL: each argument of the call passes a value from JavaScript into
+       * Java.
+       */
+      ArrayList<JParameter> params = x.getTarget().params;
+      for (int i = 0, c = params.size(); i < c; ++i) {
+        JParameter param = params.get(i);
+        maybeRescueJavaScriptObjectPassingIntoJava(param.getType());
+
+        /*
+         * Because we're not currently tracking methods through JSNI, we need to
+         * assume that it's not safe to prune parameters of a method referenced
+         * as such.
+         * 
+         * A better solution would be to perform basic escape analysis to ensure
+         * that the function reference never escapes, or at minimum, ensure that
+         * the method is immediately called after retrieving the method
+         * reference.
+         */
+        rescue(param);
+      }
+      // JsniMethodRef rescues as JMethodCall
+      return visit((JMethodCall) x, ctx);
+    }
+
+    @Override
+    public boolean visit(JStringLiteral literal, Context ctx) {
+      liveStrings.add(literal.getValue());
+
+      // rescue and instantiate java.lang.String
+      rescue(program.getTypeJavaLangString(), true, true);
+      return true;
+    }
+
+    private boolean isVolatileField(JExpression x) {
+      if (x instanceof JFieldRef) {
+        JFieldRef xFieldRef = (JFieldRef) x;
+        if (xFieldRef.getField().isVolatile()) {
+          return true;
+        }
+      }
+
+      return false;
+    }
+
+    /**
+     * Subclasses of JavaScriptObject are never instantiated directly. They are
+     * created "magically" when a JSNI method passes a reference to an existing
+     * JS object into Java code. If any point in the program can pass a value
+     * from JS into Java which could potentially be cast to JavaScriptObject, we
+     * must rescue JavaScriptObject.
+     * 
+     * @param type The type of the value passing from Java to JavaScript.
+     * @see com.google.gwt.core.client.JavaScriptObject
+     */
+    private void maybeRescueJavaScriptObjectPassingIntoJava(JType type) {
+      boolean doIt = false;
+      if (program.isJavaScriptObject(type)
+          || type == program.getTypeJavaLangString()) {
+        doIt = true;
+      } else if (type instanceof JArrayType) {
+        /*
+         * Hackish: in our own JRE we sometimes create "not quite baked" arrays
+         * in JavaScript for expediency.
+         */
+        JArrayType arrayType = (JArrayType) type;
+        JType elementType = arrayType.getElementType();
+        if (elementType instanceof JPrimitiveType
+            || elementType == program.getTypeJavaLangString()
+            || program.isJavaScriptObject(elementType)) {
+          doIt = true;
+        }
+      }
+      if (doIt) {
+        rescue((JReferenceType) type, true, true);
+      }
+    }
+
+    private boolean rescue(JMethod method) {
+      if (method != null) {
+        if (!liveFieldsAndMethods.contains(method)) {
+          liveFieldsAndMethods.add(method);
+          accept(method);
+          if (method.isNative()) {
+            /*
+             * SPECIAL: returning from this method passes a value from
+             * JavaScript into Java.
+             */
+            maybeRescueJavaScriptObjectPassingIntoJava(method.getType());
+          }
+          return true;
+        }
+      }
+      return false;
+    }
+
+    private void rescue(JReferenceType type, boolean isReferenced,
+        boolean isInstantiated) {
+      if (type != null) {
+
+        boolean doVisit = false;
+        if (isInstantiated && !instantiatedTypes.contains(type)) {
+          instantiatedTypes.add(type);
+          doVisit = true;
+        }
+
+        if (isReferenced && !referencedTypes.contains(type)) {
+          referencedTypes.add(type);
+          doVisit = true;
+        }
+
+        if (doVisit) {
+          accept(type);
+        }
+      }
+    }
+
+    private void rescue(JVariable var) {
+      if (var != null) {
+        liveFieldsAndMethods.add(var);
+      }
+    }
+
+    /**
+     * Handle special rescues needed implicitly to support concat.
+     */
+    private void rescueByConcat(JType type) {
+      JClassType stringType = program.getTypeJavaLangString();
+      JPrimitiveType charType = program.getTypePrimitiveChar();
+      if (type instanceof JReferenceType && type != stringType
+          && type != program.getTypeNull()) {
+        /*
+         * Any reference types (except String, which works by default) that take
+         * part in a concat must rescue java.lang.Object.toString().
+         * 
+         * TODO: can we narrow the focus by walking up the type hierarchy or
+         * doing explicit toString calls?
+         */
+        JMethod toStringMethod = program.getIndexedMethod("Object.toString");
+        rescue(toStringMethod);
+      } else if (type == charType) {
+        /*
+         * Characters must rescue String.valueOf(char)
+         */
+        if (stringValueOfChar == null) {
+          for (int i = 0; i < stringType.methods.size(); ++i) {
+            JMethod meth = stringType.methods.get(i);
+            if (meth.getName().equals("valueOf")) {
+              List<JType> params = meth.getOriginalParamTypes();
+              if (params.size() == 1) {
+                if (params.get(0) == charType) {
+                  stringValueOfChar = meth;
+                  break;
+                }
+              }
+            }
+          }
+          assert (stringValueOfChar != null);
+        }
+        rescue(stringValueOfChar);
+      }
+    }
+  }
+
+  /**
+   * Traverse methods that are reachable via virtual method calls. Specifically,
+   * traverse methods whose classes are instantiable and which override a method
+   * that is live.
+   */
+  private class UpRefVisitor extends JVisitor {
+
+    private boolean didRescue = false;
+
+    public boolean didRescue() {
+      return didRescue;
+    }
+
+    @Override
+    public boolean visit(JClassType x, Context ctx) {
+      return instantiatedTypes.contains(x);
+    }
+
+    @Override
+    public boolean visit(JMethod x, Context ctx) {
+      if (liveFieldsAndMethods.contains(x)) {
+        return false;
+      }
+
+      for (JMethod override : program.typeOracle.getAllOverrides(x)) {
+        if (liveFieldsAndMethods.contains(override)) {
+          rescuer.rescue(x);
+          didRescue = true;
+          return false;
+        }
+      }
+      return false;
+    }
+
+    @Override
+    public boolean visit(JProgram x, Context ctx) {
+      didRescue = false;
+      return true;
+    }
+  }
+
+  private Set<JReferenceType> instantiatedTypes = new HashSet<JReferenceType>();
+  private Set<JNode> liveFieldsAndMethods = new HashSet<JNode>();
+  private Set<String> liveStrings = new HashSet<String>();
+  private final JProgram program;
+  private Set<JReferenceType> referencedTypes = new HashSet<JReferenceType>();
+  private final RescueVisitor rescuer = new RescueVisitor();
+  private JMethod stringValueOfChar = null;
+  private final UpRefVisitor upRefer = new UpRefVisitor();
+
+  public ControlFlowAnalyzer(ControlFlowAnalyzer cfa) {
+    program = cfa.program;
+    instantiatedTypes = new HashSet<JReferenceType>(cfa.instantiatedTypes);
+    liveFieldsAndMethods = new HashSet<JNode>(cfa.liveFieldsAndMethods);
+    referencedTypes = new HashSet<JReferenceType>(cfa.referencedTypes);
+    stringValueOfChar = cfa.stringValueOfChar;
+    liveStrings = new HashSet<String>(cfa.liveStrings);
+  }
+
+  public ControlFlowAnalyzer(JProgram program) {
+    this.program = program;
+  }
+
+  /**
+   * Finish any remaining traversal that is needed. This must be called after
+   * calling any of the other traversal methods in order to get accurate
+   * results. It can also be called eagerly.
+   */
+  public void finishTraversal() {
+    do {
+      upRefer.accept(program);
+    } while (upRefer.didRescue());
+  }
+
+  /**
+   * Return the complete set of types that have been instantiated.
+   */
+  public Set<JReferenceType> getInstantiatedTypes() {
+    return instantiatedTypes;
+  }
+
+  /**
+   * Return all methods that could be executed, and all variables that could be
+   * read, based on the given entry points so far.
+   */
+  public Set<? extends JNode> getLiveFieldsAndMethods() {
+    return liveFieldsAndMethods;
+  }
+
+  public Set<String> getLiveStrings() {
+    return liveStrings;
+  }
+
+  /**
+   * Return the complete set of types that have been referenced.
+   */
+  public Set<? extends JReferenceType> getReferencedTypes() {
+    return referencedTypes;
+  }
+
+  /**
+   * Traverse all code executed by <code>expr</code>.
+   */
+  public void traverseFrom(JExpression expr) {
+    rescuer.accept(expr);
+  }
+
+  /**
+   * Assume <code>method</code> is live, and find out what else might execute.
+   */
+  public void traverseFrom(JMethod method) {
+    rescuer.rescue(method);
+  }
+
+  public void traverseFromReferenceTo(JReferenceType type) {
+    rescuer.rescue(type, true, false);
+  }
+
+  /**
+   * Trace all code needed by class literal constructor expressions except for
+   * the string literals they include. At the time of writing, these would
+   * include the factory methods for class literals.
+   */
+  public void traverseFromClassLiteralFactories() {
+    class ReplaceStringLiterals extends JModVisitor {
+      @Override
+      public void endVisit(JStringLiteral stringLiteral, Context ctx) {
+        ctx.replaceMe(program.getLiteralString(
+            stringLiteral.getSourceInfo().makeChild(ControlFlowAnalyzer.class, "remove string literals"),
+            ""));
+      }
+    }
+
+    final JModVisitor stringLiteralReplacer = new ReplaceStringLiterals();
+    final CloneExpressionVisitor cloner = new CloneExpressionVisitor(program);
+
+    class ClassLitTraverser extends JVisitor {
+      @Override
+      public void endVisit(JClassLiteral classLiteral, Context ctx) {
+        JExpression initializer = classLiteral.getField().getInitializer();
+        JExpression initializerWithoutStrings = stringLiteralReplacer.accept(cloner.cloneExpression(initializer));
+        rescuer.accept(initializerWithoutStrings);
+      }
+    }
+
+    (new ClassLitTraverser()).accept(program);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
new file mode 100644
index 0000000..204e90f
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
@@ -0,0 +1,516 @@
+/*
+ * 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.jjs.impl;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JClassLiteral;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.js.ast.JsBinaryOperation;
+import com.google.gwt.dev.js.ast.JsBinaryOperator;
+import com.google.gwt.dev.js.ast.JsEmpty;
+import com.google.gwt.dev.js.ast.JsExprStmt;
+import com.google.gwt.dev.js.ast.JsExpression;
+import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsInvocation;
+import com.google.gwt.dev.js.ast.JsName;
+import com.google.gwt.dev.js.ast.JsNameRef;
+import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsStatement;
+import com.google.gwt.dev.js.ast.JsVars;
+import com.google.gwt.dev.js.ast.JsVars.JsVar;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A class that extracts a fragment of code based on a supplied liveness
+ * condition.
+ */
+public class FragmentExtractor {
+  /**
+   * A {@link LivenessPredicate} that bases liveness on a single
+   * {@link ControlFlowAnalyzer}.
+   */
+  public static class CfaLivenessPredicate implements LivenessPredicate {
+    private final ControlFlowAnalyzer cfa;
+
+    public CfaLivenessPredicate(ControlFlowAnalyzer cfa) {
+      this.cfa = cfa;
+    }
+
+    public boolean isLive(JField field) {
+      return cfa.getLiveFieldsAndMethods().contains(field);
+    }
+
+    public boolean isLive(JMethod method) {
+      return cfa.getLiveFieldsAndMethods().contains(method);
+    }
+
+    public boolean isLive(JReferenceType type) {
+      return cfa.getInstantiatedTypes().contains(type);
+    }
+
+    public boolean isLive(String string) {
+      return cfa.getLiveStrings().contains(string);
+    }
+
+    public boolean miscellaneousStatementsAreLive() {
+      return true;
+    }
+  }
+
+  /**
+   * A predicate on whether statements and variables should be considered live.
+   */
+  public static interface LivenessPredicate {
+    boolean isLive(JField field);
+
+    boolean isLive(JMethod method);
+
+    boolean isLive(JReferenceType type);
+
+    boolean isLive(String literal);
+
+    /**
+     * Whether miscellelaneous statements should be considered live.
+     * Miscellaneous statements are any that the fragment extractor does not
+     * recognize as being in any particular category. This method should almost
+     * always return <code>true</code>, but does return <code>false</code>
+     * for {@link NothingAlivePredicate}.
+     */
+    boolean miscellaneousStatementsAreLive();
+  }
+
+  /**
+   * A {@link LivenessPredicate} where nothing is alive.
+   */
+  public static class NothingAlivePredicate implements LivenessPredicate {
+    public boolean isLive(JField field) {
+      return false;
+    }
+
+    public boolean isLive(JMethod method) {
+      return false;
+    }
+
+    public boolean isLive(JReferenceType type) {
+      return false;
+    }
+
+    public boolean isLive(String string) {
+      return false;
+    }
+
+    public boolean miscellaneousStatementsAreLive() {
+      return false;
+    }
+  }
+
+  /**
+   * A logger for statements that the fragment extractor encounters. Install one
+   * using
+   * {@link FragmentExtractor#setStatementLogger(com.google.gwt.fragserv.FragmentExtractor.StatementLogger)}.
+   */
+  public static interface StatementLogger {
+    void logStatement(JsStatement stat, boolean isIncluded);
+  }
+
+  private static class NullStatementLogger implements StatementLogger {
+    public void logStatement(JsStatement method, boolean isIncluded) {
+    }
+  }
+
+  public static Map<JField, JClassLiteral> buildFieldToClassLiteralMap(
+      JProgram jprogram) {
+    final Map<JField, JClassLiteral> map = new HashMap<JField, JClassLiteral>();
+    class BuildFieldToLiteralVisitor extends JVisitor {
+      @Override
+      public void endVisit(JClassLiteral lit, Context ctx) {
+        map.put(lit.getField(), lit);
+      }
+    }
+    (new BuildFieldToLiteralVisitor()).accept(jprogram);
+    return map;
+  }
+
+  private Set<JsName> entryMethodNames;
+
+  private Map<JField, JClassLiteral> fieldToLiteralOfClass;
+
+  private final JProgram jprogram;
+
+  private final JsProgram jsprogram;
+
+  private final JavaToJavaScriptMap map;
+
+  private StatementLogger statementLogger = new NullStatementLogger();
+
+  public FragmentExtractor(JavaAndJavaScript javaAndJavaScript) {
+    this(javaAndJavaScript.jprogram, javaAndJavaScript.jsprogram,
+        javaAndJavaScript.map);
+  }
+
+  public FragmentExtractor(JProgram jprogram, JsProgram jsprogram,
+      JavaToJavaScriptMap map) {
+    this.jprogram = jprogram;
+    this.jsprogram = jsprogram;
+    this.map = map;
+
+    fieldToLiteralOfClass = buildFieldToClassLiteralMap(jprogram);
+    buildEntryMethodSet();
+  }
+
+  /**
+   * Add direct calls to the entry methods of the specified entry number.
+   */
+  public void addCallsToEntryMethods(int entry, List<JsStatement> stats) {
+    for (JMethod entryMethod : jprogram.entryMethods.get(entry)) {
+      JsName name = map.nameForMethod(entryMethod);
+      assert name != null;
+      SourceInfo sourceInfo = jsprogram.getSourceInfo().makeChild(
+          FragmentExtractor.class, "call to entry function " + entry);
+      JsInvocation call = new JsInvocation(sourceInfo);
+      call.setQualifier(name.makeRef(sourceInfo));
+      stats.add(call.makeStmt());
+    }
+  }
+
+  /**
+   * Assume that all code described by <code>alreadyLoadedPredicate</code> has
+   * been downloaded. Extract enough JavaScript statements that the code
+   * described by <code>livenessPredicate</code> can also run. The caller
+   * should ensure that <code>livenessPredicate</code> includes strictly more
+   * live code than <code>alreadyLoadedPredicate</code>.
+   */
+  public List<JsStatement> extractStatements(
+      LivenessPredicate livenessPredicate,
+      LivenessPredicate alreadyLoadedPredicate) {
+    List<JsStatement> extractedStats = new ArrayList<JsStatement>();
+
+    /**
+     * The type whose vtables can currently be installed.
+     */
+    JReferenceType currentVtableType = null;
+
+    for (int frag = 0; frag < jsprogram.getFragmentCount(); frag++) {
+      List<JsStatement> stats = jsprogram.getFragmentBlock(frag).getStatements();
+      for (JsStatement stat : stats) {
+        if (isEntryCall(stat)) {
+          continue;
+        }
+
+        boolean keepIt;
+
+        if (containsInternedLiterals(stat)) {
+          stat = removeSomeInternedLiterals((JsVars) stat, livenessPredicate,
+              alreadyLoadedPredicate);
+          keepIt = !(stat instanceof JsEmpty);
+        } else {
+          keepIt = isLive(stat, livenessPredicate)
+              && !isLive(stat, alreadyLoadedPredicate);
+        }
+
+        statementLogger.logStatement(stat, keepIt);
+
+        if (keepIt) {
+          if (vtableTypeAssigned(stat) != null) {
+            currentVtableType = vtableTypeAssigned(stat);
+          }
+          JReferenceType vtableType = vtableTypeNeeded(stat);
+          if (vtableType != null && vtableType != currentVtableType) {
+            extractedStats.add(vtableStatFor(vtableType));
+            currentVtableType = vtableType;
+          }
+          extractedStats.add(stat);
+        }
+      }
+    }
+
+    return extractedStats;
+  }
+
+  public void setStatementLogger(StatementLogger logger) {
+    statementLogger = logger;
+  }
+
+  private void buildEntryMethodSet() {
+    entryMethodNames = new HashSet<JsName>();
+    for (JMethod entryMethod : jprogram.getAllEntryMethods()) {
+      JsName name = map.nameForMethod(entryMethod);
+      assert name != null;
+      entryMethodNames.add(name);
+    }
+  }
+
+  /**
+   * Check whether this statement is a <code>JsVars</code> that contains
+   * interned literals. If it does, then
+   * {@link #removeSomeInternedLiterals(JsVars, LivenessPredicate, LivenessPredicate)}
+   * is sensible for this statement and should be used instead of
+   * {@link #isLive(JsStatement, com.google.gwt.fragserv.FragmentExtractor.LivenessPredicate)}.
+   */
+  private boolean containsInternedLiterals(JsStatement stat) {
+    if (stat instanceof JsVars) {
+      for (JsVar var : (JsVars) stat) {
+        String lit = map.stringLiteralForName(var.getName());
+        if (lit != null) {
+          // It's an intern variable for a string literal
+          return true;
+        }
+
+        JField field = map.nameToField(var.getName());
+        if (field != null && fieldToLiteralOfClass.containsKey(field)) {
+          // It's an intern variable for a class literal
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Check whether the statement invokes an entry method. Detect JavaScript code
+   * of the form foo() where foo is a the JavaScript function corresponding to
+   * an entry method.
+   */
+  private boolean isEntryCall(JsStatement stat) {
+    if (stat instanceof JsExprStmt) {
+      JsExpression expr = ((JsExprStmt) stat).getExpression();
+      if (expr instanceof JsInvocation) {
+        JsInvocation inv = (JsInvocation) expr;
+        if (inv.getArguments().isEmpty()
+            && (inv.getQualifier() instanceof JsNameRef)) {
+          JsNameRef calleeRef = (JsNameRef) inv.getQualifier();
+          if (calleeRef.getQualifier() == null) {
+            return entryMethodNames.contains(calleeRef.getName());
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  private boolean isLive(JsStatement stat, LivenessPredicate livenessPredicate) {
+    JReferenceType type = map.typeForStatement(stat);
+    if (type != null) {
+      // This is part of the code only needed once a type is instantiable
+      return livenessPredicate.isLive(type);
+    }
+
+    JMethod meth = methodFor(stat);
+    if (meth == null) {
+      meth = map.vtableInitToMethod(stat);
+    }
+
+    if (meth != null) {
+      /*
+       * This statement either defines a method or installs it in a vtable.
+       */
+      if (!livenessPredicate.isLive(meth)) {
+        // The method is not live. Skip it.
+        return false;
+      }
+      // The method is live. Check that its enclosing type is instantiable.
+      // TODO(spoon): this check should not be needed once the CFA is updated
+      return meth.isStatic()
+          || livenessPredicate.isLive(meth.getEnclosingType());
+    }
+
+    return livenessPredicate.miscellaneousStatementsAreLive();
+  }
+
+  /**
+   * Check whether a variable is needed. If the variable is an intern variable,
+   * then return whether the interned value is live. Otherwise, assume the
+   * variable is needed and return <code>true</code>.
+   * 
+   * Whenever this method is updated, also look at
+   * {@link #containsInternedLiterals(JsStatement)}.
+   */
+  private boolean isLive(JsVar var, LivenessPredicate livenessPredicate) {
+    String lit = map.stringLiteralForName(var.getName());
+    if (lit != null) {
+      // It's an intern variable for a string literal
+      return livenessPredicate.isLive(lit);
+    }
+
+    JField field = map.nameToField(var.getName());
+    if (field != null && fieldToLiteralOfClass.containsKey(field)) {
+      // It's an intern variable for a class literal
+      return livenessPredicate.isLive(field);
+    }
+
+    // It's not an intern variable at all
+    return livenessPredicate.miscellaneousStatementsAreLive();
+  }
+
+  /**
+   * Return the Java method corresponding to <code>stat</code>, or
+   * <code>null</code> if there isn't one. It recognizes JavaScript of the
+   * form <code>function foo(...) { ...}</code>, where <code>foo</code> is
+   * the name of the JavaScript translation of a Java method.
+   */
+  private JMethod methodFor(JsStatement stat) {
+    if (stat instanceof JsExprStmt) {
+      JsExpression exp = ((JsExprStmt) stat).getExpression();
+      if (exp instanceof JsFunction) {
+        JsFunction func = (JsFunction) exp;
+        if (func.getName() != null) {
+          return map.nameToMethod(func.getName());
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * If stat is a {@link JsVars} that initializes a bunch of intern vars, return
+   * a modified statement that skips any vars are needed by
+   * <code>currentLivenessPredicate</code> but not by
+   * <code>alreadyLoadedPredicate</code>.
+   */
+  private JsStatement removeSomeInternedLiterals(JsVars stat,
+      LivenessPredicate currentLivenessPredicate,
+      LivenessPredicate alreadyLoadedPredicate) {
+    JsVars newVars = new JsVars(stat.getSourceInfo().makeChild(
+        FragmentExtractor.class, "subsetting of interned values"));
+
+    for (JsVar var : stat) {
+      if (isLive(var, currentLivenessPredicate)
+          && !isLive(var, alreadyLoadedPredicate)) {
+        newVars.add(var);
+      }
+    }
+
+    if (newVars.getNumVars() == stat.getNumVars()) {
+      // no change
+      return stat;
+    }
+
+    if (newVars.iterator().hasNext()) {
+      /*
+       * The new variables are non-empty; return them.
+       */
+      return newVars;
+    } else {
+      /*
+       * An empty JsVars seems possibly surprising; return a true empty
+       * statement instead.
+       */
+      return jsprogram.getEmptyStmt();
+    }
+  }
+
+  /**
+   * Compute a statement that can be used to set up for installing instance
+   * methods into a vtable. It will be of the form
+   * <code>_ = foo.prototype</code>, where <code>foo</code> is the
+   * constructor function for <code>vtableType</code>.
+   */
+  private JsStatement vtableStatFor(JReferenceType vtableType) {
+    JsNameRef prototypeField = new JsNameRef(
+        jsprogram.createSourceInfoSynthetic(FragmentExtractor.class,
+            "prototype field"), "prototype");
+    JsExpression constructorRef;
+    SourceInfo sourceInfoVtableSetup = jsprogram.createSourceInfoSynthetic(
+        FragmentExtractor.class, "vtable setup");
+    if (vtableType == jprogram.getTypeJavaLangString()) {
+      // The methods of java.lang.String are put onto JavaScript's String
+      // prototype
+      SourceInfo sourceInfoConstructorRef = jsprogram.createSourceInfoSynthetic(
+          FragmentExtractor.class, "String constructor");
+      constructorRef = new JsNameRef(sourceInfoConstructorRef, "String");
+    } else {
+      constructorRef = map.nameForType(vtableType).makeRef(
+          sourceInfoVtableSetup);
+    }
+    prototypeField.setQualifier(constructorRef);
+    SourceInfo underlineSourceInfo = jsprogram.createSourceInfoSynthetic(
+        FragmentExtractor.class, "global _ field");
+    return (new JsBinaryOperation(sourceInfoVtableSetup, JsBinaryOperator.ASG,
+        jsprogram.getScope().declareName("_").makeRef(underlineSourceInfo),
+        prototypeField)).makeStmt();
+  }
+
+  /**
+   * If <code>state</code> is of the form <code>_ = Foo.prototype = exp</code>,
+   * then return <code>Foo</code>. Otherwise return <code>null</code>.
+   * 
+   * TODO(spoon): get this information via source info on the statement
+   */
+  private JReferenceType vtableTypeAssigned(JsStatement stat) {
+    if (!(stat instanceof JsExprStmt)) {
+      return null;
+    }
+    JsExprStmt expr = (JsExprStmt) stat;
+    if (!(expr.getExpression() instanceof JsBinaryOperation)) {
+      return null;
+    }
+    JsBinaryOperation binExpr = (JsBinaryOperation) expr.getExpression();
+    if (binExpr.getOperator() != JsBinaryOperator.ASG) {
+      return null;
+    }
+    if (!(binExpr.getArg1() instanceof JsNameRef)) {
+      return null;
+    }
+    JsNameRef lhs = (JsNameRef) binExpr.getArg1();
+    if (lhs.getQualifier() != null) {
+      return null;
+    }
+    if (lhs.getName() == null) {
+      return null;
+    }
+    if (!lhs.getName().getShortIdent().equals("_")) {
+      return null;
+    }
+    if (!(binExpr.getArg2() instanceof JsBinaryOperation)) {
+      return null;
+    }
+    JsBinaryOperation binExprRhs = (JsBinaryOperation) binExpr.getArg2();
+    if (binExprRhs.getOperator() != JsBinaryOperator.ASG) {
+      return null;
+    }
+    if (!(binExprRhs.getArg1() instanceof JsNameRef)) {
+      return null;
+    }
+    JsNameRef middleNameRef = (JsNameRef) binExprRhs.getArg1();
+    if (!middleNameRef.getName().getShortIdent().equals("prototype")) {
+      return null;
+    }
+
+    return map.typeForStatement(stat);
+  }
+
+  private JReferenceType vtableTypeNeeded(JsStatement stat) {
+    JMethod meth = map.vtableInitToMethod(stat);
+    if (meth != null) {
+      if (!meth.isStatic()) {
+        return meth.getEnclosingType();
+      }
+    }
+    return null;
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java
new file mode 100644
index 0000000..cfb0e54
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java
@@ -0,0 +1,298 @@
+/*
+ * 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.jjs.impl;
+
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.PropertyOracle;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.dev.cfg.BindingProperty;
+import com.google.gwt.dev.cfg.ConfigurationProperty;
+import com.google.gwt.dev.cfg.PublicOracle;
+import com.google.gwt.dev.cfg.StaticPropertyOracle;
+import com.google.gwt.dev.javac.CompilationState;
+import com.google.gwt.dev.shell.StandardGeneratorContext;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Generates code for loading an island. The pattern of generated classes is
+ * more complicated than otherwise necessary so that the loader code is
+ * precisely handled by TypeTightener and LivenessAnalyzer.
+ * 
+ * TODO(spoon) Remove this generator by making LivenessAnalyzer know about
+ * runAsync and how it works.
+ */
+public class FragmentLoaderCreator {
+  public static final String ASYNC_FRAGMENT_LOADER = "com.google.gwt.core.client.AsyncFragmentLoader";
+  public static final String ASYNC_LOADER_CLASS_PREFIX = "AsyncLoader";
+  public static final String ASYNC_LOADER_PACKAGE = "com.google.gwt.lang.asyncloaders";
+  public static final String RUN_ASYNC_CALLBACK = "com.google.gwt.core.client.RunAsyncCallback";
+  private static final String PROP_RUN_ASYNC_NEVER_RUNS = "gwt.jjs.runAsyncNeverRuns";
+
+  private final ArtifactSet artifactSet;
+  private final CompilationState compilationState;
+  private int entryNumber;
+  private final File genDir;
+  private final File outDir;
+  private final PublicOracle publicOracle;
+
+  /**
+   * Construct a FragmentLoaderCreator. The reason it needs so many parameters
+   * is that it uses generator infrastructure.
+   */
+  public FragmentLoaderCreator(CompilationState compilationState,
+      PublicOracle publicOracle, File genDir, File moduleOutDir,
+      ArtifactSet artifactSet) {
+    this.compilationState = compilationState;
+    this.publicOracle = publicOracle;
+    this.genDir = genDir;
+    this.outDir = moduleOutDir;
+    this.artifactSet = artifactSet;
+  }
+
+  public String create(TreeLogger logger) throws UnableToCompleteException {
+    chooseEntryNumber();
+    StandardGeneratorContext context = makeGeneratorContext();
+
+    PrintWriter loaderWriter = getSourceWriterForLoader(logger, context);
+    if (loaderWriter == null) {
+      logger.log(TreeLogger.ERROR, "Failed to create island loader named "
+          + getLoaderQualifiedName());
+      throw new UnableToCompleteException();
+    }
+
+    generateLoaderFields(loaderWriter);
+    generateOnErrorMethod(loaderWriter);
+    generateOnLoadMethod(loaderWriter);
+    generateRunAsyncMethod(loaderWriter);
+    generateRunCallbacksMethod(loaderWriter);
+    generateRunCallbackOnFailuresMethod(loaderWriter);
+
+    loaderWriter.println("}");
+    loaderWriter.close();
+    context.commit(logger, loaderWriter);
+
+    writeCallbackListClass(logger, context);
+    writeLoaderSuperclass(logger, context);
+
+    context.finish(logger);
+
+    return getLoaderQualifiedName();
+  }
+
+  /**
+   * Pick the lowest-numbered entry number that has not yet had loaders
+   * generated.
+   */
+  private void chooseEntryNumber() {
+    entryNumber = 1;
+    while (compilationState.getTypeOracle().findType(getLoaderQualifiedName()) != null) {
+      entryNumber++;
+    }
+  }
+
+  private void generateLoaderFields(PrintWriter srcWriter) {
+    srcWriter.println("private static boolean loaded = false;");
+    srcWriter.println("private static boolean loading = false;");
+    srcWriter.println("private static " + getLoaderSuperclassSimpleName()
+        + " instance = new " + getLoaderSuperclassSimpleName() + "();");
+    srcWriter.println("private static " + getCallbackListSimpleName()
+        + " callbacks = null;");
+  }
+
+  private void generateOnErrorMethod(PrintWriter srcWriter) {
+    srcWriter.println("public static void onError(Throwable e) {");
+    srcWriter.println("loading = false;");
+    srcWriter.println("runCallbackOnFailures(e);");
+    srcWriter.println("}");
+  }
+
+  private void generateOnLoadMethod(PrintWriter srcWriter) {
+    srcWriter.println("public static void onLoad() {");
+    srcWriter.println(ASYNC_FRAGMENT_LOADER + ".logEventProgress(\"download"
+        + entryNumber + "\", \"end\");");
+    srcWriter.println("loaded = true;");
+    srcWriter.println("instance = new " + getLoaderSimpleName() + "();");
+    srcWriter.println(ASYNC_FRAGMENT_LOADER + ".fragmentHasLoaded("
+        + entryNumber + ");");
+
+    srcWriter.println("instance.runCallbacks();");
+
+    srcWriter.println("}");
+  }
+
+  /**
+   * Generate the <code>runAsync</code> method. Calls to
+   * <code>GWT.runAsync</code> are replaced by calls to this method.
+   */
+  private void generateRunAsyncMethod(PrintWriter srcWriter) {
+    srcWriter.println("public static void runAsync(RunAsyncCallback callback) {");
+    srcWriter.println(getCallbackListSimpleName() + " newCallbackList = new "
+        + getCallbackListSimpleName() + "();");
+    srcWriter.println("newCallbackList.callback = callback;");
+    srcWriter.println("newCallbackList.next = callbacks;");
+    srcWriter.println("callbacks = newCallbackList;");
+    srcWriter.println("if (loaded) {");
+    srcWriter.println("instance.runCallbacks();");
+    srcWriter.println("return;");
+    srcWriter.println("}");
+    srcWriter.println("if (!loading) {");
+    srcWriter.println("loading = true;");
+    srcWriter.println("AsyncFragmentLoader.inject(" + entryNumber + ");");
+    srcWriter.println("}");
+    srcWriter.println("}");
+  }
+
+  private void generateRunCallbackOnFailuresMethod(PrintWriter srcWriter) {
+    srcWriter.println("private static void runCallbackOnFailures(Throwable e) {");
+    // TODO(spoon): this runs the callbacks in reverse order
+    srcWriter.println(getCallbackListSimpleName() + " callback = callbacks;");
+    srcWriter.println("callbacks = null;");
+    srcWriter.println("while (callback != null) {");
+    srcWriter.println("callback.callback.onFailure(e);");
+    srcWriter.println("callback = callback.next;");
+    srcWriter.println("}");
+    srcWriter.println("}");
+  }
+
+  private void generateRunCallbacksMethod(PrintWriter srcWriter) {
+    srcWriter.println("public void runCallbacks() {");
+    // TODO(spoon): this runs the callbacks in reverse order
+    // TODO(spoon): this runs the callbacks immediately; deferred would be
+    // better
+    srcWriter.println(getCallbackListSimpleName() + " callback = callbacks;");
+    srcWriter.println("callbacks = null;");
+    if (!Boolean.getBoolean(PROP_RUN_ASYNC_NEVER_RUNS)) {
+      srcWriter.println("while (callback != null) {");
+      srcWriter.println("callback.callback.onSuccess();");
+      srcWriter.println("callback = callback.next;");
+      srcWriter.println("}");
+    }
+    srcWriter.println("}");
+  }
+
+  private String getCallbackListQualifiedName() {
+    return ASYNC_LOADER_PACKAGE + "__Callback";
+  }
+
+  private String getCallbackListSimpleName() {
+    return getLoaderSimpleName() + "__Callback";
+  }
+
+  private String getLoaderQualifiedName() {
+    return ASYNC_LOADER_PACKAGE + "." + getLoaderSimpleName();
+  }
+
+  private String getLoaderSimpleName() {
+    return ASYNC_LOADER_CLASS_PREFIX + entryNumber;
+  }
+
+  private String getLoaderSuperclassQualifiedName() {
+    return ASYNC_LOADER_PACKAGE + getLoaderSuperclassSimpleName();
+  }
+
+  private String getLoaderSuperclassSimpleName() {
+    return getLoaderSimpleName() + "__Super";
+  }
+
+  private String getPackage() {
+    return ASYNC_LOADER_PACKAGE;
+  }
+
+  private PrintWriter getSourceWriterForLoader(TreeLogger logger,
+      GeneratorContext ctx) {
+    PrintWriter printWriter = ctx.tryCreate(logger, getPackage(),
+        getLoaderSimpleName());
+    if (printWriter == null) {
+      return null;
+    }
+
+    printWriter.println("package " + getPackage() + ";");
+    String[] imports = new String[] {
+        RUN_ASYNC_CALLBACK, List.class.getCanonicalName(),
+        ArrayList.class.getCanonicalName(), ASYNC_FRAGMENT_LOADER};
+    for (String imp : imports) {
+      printWriter.println("import " + imp + ";");
+    }
+
+    printWriter.println("public class " + getLoaderSimpleName() + " extends "
+        + getLoaderSuperclassSimpleName() + " {");
+
+    return printWriter;
+  }
+
+  private StandardGeneratorContext makeGeneratorContext() {
+    // An empty property oracle is fine, because fragment loaders aren't
+    // affected by properties anyway
+    PropertyOracle propOracle = new StaticPropertyOracle(
+        new BindingProperty[0], new String[0], new ConfigurationProperty[0]);
+    StandardGeneratorContext context = new StandardGeneratorContext(
+        compilationState, propOracle, publicOracle, genDir, outDir, artifactSet);
+    return context;
+  }
+
+  private void writeCallbackListClass(TreeLogger logger, GeneratorContext ctx)
+      throws UnableToCompleteException {
+    PrintWriter printWriter = ctx.tryCreate(logger, getPackage(),
+        getCallbackListSimpleName());
+    if (printWriter == null) {
+      logger.log(TreeLogger.ERROR, "Could not create type: "
+          + getCallbackListQualifiedName());
+      throw new UnableToCompleteException();
+    }
+
+    printWriter.println("package " + getPackage() + ";");
+    printWriter.println("public class " + getCallbackListSimpleName() + "{");
+    printWriter.println(RUN_ASYNC_CALLBACK + " callback;");
+    printWriter.println(getCallbackListSimpleName() + " next;");
+    printWriter.println("}");
+
+    printWriter.close();
+    ctx.commit(logger, printWriter);
+  }
+
+  /**
+   * Create a stand-in superclass of the actual loader. This is used to keep the
+   * liveness analyzer from thinking the real <code>runCallbacks()</code>
+   * method is available until <code>onLoad</code> has been called and the
+   * real loader instantiated. A little work on TypeTightener could prevent the
+   * need for this class.
+   */
+  private void writeLoaderSuperclass(TreeLogger logger, GeneratorContext ctx)
+      throws UnableToCompleteException {
+    PrintWriter printWriter = ctx.tryCreate(logger, getPackage(),
+        getLoaderSuperclassSimpleName());
+    if (printWriter == null) {
+      logger.log(TreeLogger.ERROR, "Could not create type: "
+          + getLoaderSuperclassQualifiedName());
+      throw new UnableToCompleteException();
+    }
+
+    printWriter.println("package " + getPackage() + ";");
+    printWriter.println("public class " + getLoaderSuperclassSimpleName()
+        + " {");
+    printWriter.println("public void runCallbacks() { }");
+    printWriter.println("}");
+
+    printWriter.close();
+    ctx.commit(logger, printWriter);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index f80d5d3..8486fbd 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -320,8 +320,8 @@
         SourceInfo sourceInfo = x.getSourceInfo().makeChild(
             CreateNamesAndScopesVisitor.class, "Translated JS function");
         jsFunction = new JsFunction(sourceInfo, topScope, globalName, true);
-        methodBodyMap.put(x.getBody(), jsFunction);
       }
+      methodBodyMap.put(x.getBody(), jsFunction);
       jsFunction.getSourceInfo().addCorrelation(Correlation.by(jsFunction));
       push(jsFunction.getScope());
       return true;
@@ -375,22 +375,22 @@
 
     private JMethod currentMethod = null;
 
-    private final JsName globalTemp = topScope.declareName("_");
-
-    private final JsName prototype = objectScope.declareName("prototype");
-
     /**
      * The JavaScript functions corresponding to the entry methods of the
-     * program ({@link JProgram#entryMethods}).
+     * program ({@link JProgram#getAllEntryMethods()}).
      */
     private JsFunction[] entryFunctions;
 
     /**
-     * A reverse index for {@link JProgram#entryMethods}. Each entry method is
-     * mapped to its integer index.
+     * A reverse index for the entry methods of the program ({@link JProgram#getAllEntryMethods()}).
+     * Each entry method is mapped to its integer index.
      */
     private Map<JMethod, Integer> entryMethodToIndex;
 
+    private final JsName globalTemp = topScope.declareName("_");
+
+    private final JsName prototype = objectScope.declareName("prototype");
+
     {
       globalTemp.setObfuscatable(false);
       prototype.setObfuscatable(false);
@@ -541,10 +541,12 @@
           vars.add((JsVar) node);
         } else {
           assert (node instanceof JsStatement);
-          JsStatement stmt = (JsStatement) jsFields.get(i);
+          JsStatement stmt = (JsStatement) node;
           globalStmts.add(stmt);
+          typeForStatMap.put(stmt, x);
         }
       }
+
       if (!vars.isEmpty()) {
         globalStmts.add(vars);
       }
@@ -992,17 +994,16 @@
     public void endVisit(JProgram x, Context ctx) {
       List<JsStatement> globalStmts = jsProgram.getGlobalBlock().getStatements();
 
-      // types don't push
-
       // Generate entry methods
-      generateGwtOnLoad(entryFunctions, globalStmts);
+      generateGwtOnLoad(Arrays.asList(entryFunctions).subList(0,
+          x.getEntryCount(0)), globalStmts);
       generateNullFunc(globalStmts);
 
       // Add a few things onto the beginning.
 
       // Reserve the "_" identifier.
-      JsVars vars = new JsVars(x.getSourceInfo());
-      vars.add(new JsVar(x.getSourceInfo(), globalTemp));
+      JsVars vars = new JsVars(jsProgram.getSourceInfo());
+      vars.add(new JsVar(jsProgram.getSourceInfo(), globalTemp));
       globalStmts.add(0, vars);
 
       // Long lits must go at the top, they can be constant field initializers.
@@ -1011,12 +1012,28 @@
       // Class objects, but only if there are any.
       if (x.getDeclaredTypes().contains(x.getTypeClassLiteralHolder())) {
         // TODO: perhaps they could be constant field initializers also?
-        vars = new JsVars(x.getSourceInfo());
+        vars = new JsVars(jsProgram.getSourceInfo());
         generateClassLiterals(vars);
         if (!vars.isEmpty()) {
           globalStmts.add(vars);
         }
       }
+
+      /*
+       * Add calls to all split points other than the initial ones. That way, if
+       * the code splitter does not run, the resulting code will still function.
+       */
+      List<JsFunction> nonInitialEntries = Arrays.asList(entryFunctions).subList(
+          x.getEntryCount(0), entryFunctions.length);
+      for (JsFunction func : nonInitialEntries) {
+        if (func != null) {
+          SourceInfo sourceInfo = jsProgram.getSourceInfo().makeChild(GenerateJavaScriptAST.class,
+              "call to entry non-initial entry function");
+          JsInvocation call = new JsInvocation(sourceInfo);
+          call.setQualifier(func.getName().makeRef(sourceInfo));
+          globalStmts.add(call.makeStmt());
+        }
+      }
     }
 
     @Override
@@ -1140,10 +1157,15 @@
 
     @Override
     public boolean visit(JProgram x, Context ctx) {
-      entryFunctions = new JsFunction[x.entryMethods.size()];
+      /*
+       * Arrange for entryFunctions to be filled in as functions are visited.
+       * See their Javadoc comments for more details.
+       */
+      List<JMethod> entryMethods = x.getAllEntryMethods();
+      entryFunctions = new JsFunction[entryMethods.size()];
       entryMethodToIndex = new IdentityHashMap<JMethod, Integer>();
-      for (int i = 0; i < x.entryMethods.size(); i++) {
-        entryMethodToIndex.put(x.entryMethods.get(i), i);
+      for (int i = 0; i < entryMethods.size(); i++) {
+        entryMethodToIndex.put(entryMethods.get(i), i);
       }
       return true;
     }
@@ -1290,7 +1312,7 @@
       generateTypeId(x, globalStmts);
     }
 
-    private void generateGwtOnLoad(JsFunction[] entryFuncs,
+    private void generateGwtOnLoad(List<JsFunction> entryFuncs,
         List<JsStatement> globalStmts) {
       /**
        * <pre>
@@ -1397,7 +1419,9 @@
         sourceInfo.addCorrelation(Correlation.by(seedFunc));
         JsBlock body = new JsBlock(sourceInfo);
         seedFunc.setBody(body);
-        globalStmts.add(seedFunc.makeStmt());
+        JsExprStmt seedFuncStmt = seedFunc.makeStmt();
+        globalStmts.add(seedFuncStmt);
+        typeForStatMap.put(seedFuncStmt, x);
 
         // setup prototype, assign to temp
         // _ = com_example_foo_Foo.prototype = new com_example_foo_FooSuper();
@@ -1420,7 +1444,9 @@
         JsExpression protoAsg = createAssignment(lhs, rhs);
         JsExpression tmpAsg = createAssignment(globalTemp.makeRef(sourceInfo),
             protoAsg);
-        globalStmts.add(tmpAsg.makeStmt());
+        JsExprStmt tmpAsgStmt = tmpAsg.makeStmt();
+        globalStmts.add(tmpAsgStmt);
+        typeForStatMap.put(tmpAsgStmt, x);
       } else {
         /*
          * MAGIC: java.lang.String is implemented as a JavaScript String
@@ -1431,7 +1457,9 @@
             sourceInfo));
         JsExpression tmpAsg = createAssignment(globalTemp.makeRef(sourceInfo),
             rhs);
-        globalStmts.add(tmpAsg.makeStmt());
+        JsExprStmt tmpAsgStmt = tmpAsg.makeStmt();
+        globalStmts.add(tmpAsgStmt);
+        typeForStatMap.put(tmpAsgStmt, x);
       }
     }
 
@@ -1463,7 +1491,9 @@
 
         // asg
         JsExpression asg = createAssignment(lhs, rhs);
-        globalStmts.add(new JsExprStmt(sourceInfo, asg));
+        JsExprStmt stmt = asg.makeStmt();
+        globalStmts.add(stmt);
+        typeForStatMap.put(stmt, program.getTypeJavaLangObject());
       }
     }
 
@@ -1482,7 +1512,9 @@
         fieldRef.setQualifier(globalTemp.makeRef(sourceInfo));
         JsNumberLiteral typeIdLit = jsProgram.getNumberLiteral(typeId);
         JsExpression asg = createAssignment(fieldRef, typeIdLit);
-        globalStmts.add(new JsExprStmt(sourceInfo, asg));
+        JsExprStmt asgStmt = asg.makeStmt();
+        globalStmts.add(asgStmt);
+        typeForStatMap.put(asgStmt, x);
       }
     }
 
@@ -1499,7 +1531,9 @@
       fieldRef.setQualifier(globalTemp.makeRef(sourceInfo));
       JsExpression asg = createAssignment(fieldRef,
           nullMethodName.makeRef(sourceInfo));
-      globalStmts.add(new JsExprStmt(sourceInfo, asg));
+      JsExprStmt stmt = asg.makeStmt();
+      globalStmts.add(stmt);
+      typeForStatMap.put(stmt, program.getTypeJavaLangObject());
     }
 
     private JsExpression generateTypeTable() {
@@ -1524,7 +1558,9 @@
           lhs.setQualifier(globalTemp.makeRef(sourceInfo));
           JsNameRef rhs = names.get(method).makeRef(sourceInfo);
           JsExpression asg = createAssignment(lhs, rhs);
-          globalStmts.add(new JsExprStmt(x.getSourceInfo(), asg));
+          JsExprStmt asgStat = new JsExprStmt(x.getSourceInfo(), asg);
+          globalStmts.add(asgStat);
+          vtableInitForMethodMap.put(asgStat, method);
         }
       }
     }
@@ -1700,16 +1736,18 @@
 
     @Override
     public void endVisit(JProgram x, Context ctx) {
-      Collections.sort(x.entryMethods, hasNameSort);
+      for (List<JMethod> methods : x.entryMethods) {
+        Collections.sort(methods, hasNameSort);
+      }
       Collections.sort(x.getDeclaredTypes(), hasNameSort);
     }
   }
 
-  public static void exec(JProgram program, JsProgram jsProgram,
+  public static JavaToJavaScriptMap exec(JProgram program, JsProgram jsProgram,
       JsOutputOption output) {
     GenerateJavaScriptAST generateJavaScriptAST = new GenerateJavaScriptAST(
         program, jsProgram, output);
-    generateJavaScriptAST.execImpl();
+    return generateJavaScriptAST.execImpl();
   }
 
   private final Map<JBlock, JsCatch> catchMap = new IdentityHashMap<JBlock, JsCatch>();
@@ -1721,6 +1759,7 @@
    * clinit).
    */
   private Set<JMethod> crossClassTargets = new HashSet<JMethod>();
+
   /**
    * Contains JsNames for all interface methods. A special scope is needed so
    * that independent classes will obfuscate their interface implementation
@@ -1734,7 +1773,6 @@
    * Sorted to avoid nondeterministic iteration.
    */
   private final Map<Long, JsName> longLits = new TreeMap<Long, JsName>();
-
   private final Map<JsName, JsExpression> longObjects = new IdentityHashMap<JsName, JsExpression>();
   private final Map<JAbstractMethodBody, JsFunction> methodBodyMap = new IdentityHashMap<JAbstractMethodBody, JsFunction>();
   private final Map<HasName, JsName> names = new IdentityHashMap<HasName, JsName>();
@@ -1770,8 +1808,13 @@
    * Contains JsNames for all globals, such as static fields and methods.
    */
   private final JsScope topScope;
+
+  private final Map<JsStatement, JReferenceType> typeForStatMap = new HashMap<JsStatement, JReferenceType>();
+
   private final JTypeOracle typeOracle;
 
+  private final Map<JsStatement, JMethod> vtableInitForMethodMap = new HashMap<JsStatement, JMethod>();
+
   private GenerateJavaScriptAST(JProgram program, JsProgram jsProgram,
       JsOutputOption output) {
     this.program = program;
@@ -1899,7 +1942,7 @@
     throw new InternalCompilerException("Unknown output mode");
   }
 
-  private void execImpl() {
+  private JavaToJavaScriptMap execImpl() {
     SortVisitor sorter = new SortVisitor();
     sorter.accept(program);
     RecordCrossClassCalls recorder = new RecordCrossClassCalls();
@@ -1908,6 +1951,61 @@
     creator.accept(program);
     GenerateJavaScriptVisitor generator = new GenerateJavaScriptVisitor();
     generator.accept(program);
-  }
+    final Map<JsName, JMethod> nameToMethodMap = new HashMap<JsName, JMethod>();
+    for (JAbstractMethodBody body : methodBodyMap.keySet()) {
+      nameToMethodMap.put(methodBodyMap.get(body).getName(), body.getMethod());
+    }
+    final HashMap<JsName, JField> nameToFieldMap = new HashMap<JsName, JField>();
+    final HashMap<JsName, JReferenceType> constructorNameToTypeMap = new HashMap<JsName, JReferenceType>();
+    for (JReferenceType type : program.getDeclaredTypes()) {
+      JsName typeName = names.get(type);
+      if (typeName != null) {
+        constructorNameToTypeMap.put(typeName, type);
+      }
+      for (JField field : type.fields) {
+        if (field.isStatic()) {
+          JsName fieldName = names.get(field);
+          if (fieldName != null) {
+            nameToFieldMap.put(fieldName, field);
+          }
+        }
+      }
+    }
 
+    // TODO(spoon): Instead of gathering the information here, get it via
+    // SourceInfo
+    return new JavaToJavaScriptMap() {
+      public JsName nameForMethod(JMethod method) {
+        return names.get(method);
+      }
+
+      public JsName nameForType(JReferenceType type) {
+        return names.get(type);
+      }
+
+      public JField nameToField(JsName name) {
+        return nameToFieldMap.get(name);
+      }
+
+      public JMethod nameToMethod(JsName name) {
+        return nameToMethodMap.get(name);
+      }
+
+      public String stringLiteralForName(JsName var) {
+        /*
+         * This method will be supplied non-trivially elsewhere.
+         * GenerateJavaScriptAST doesn't have the information to implement it.
+         */
+        return null;
+      }
+
+      public JReferenceType typeForStatement(JsStatement stat) {
+        return typeForStatMap.get(stat);
+      }
+
+      public JMethod vtableInitToMethod(JsStatement stat) {
+        return vtableInitForMethodMap.get(stat);
+      }
+    };
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaAndJavaScript.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaAndJavaScript.java
new file mode 100644
index 0000000..7526b14
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaAndJavaScript.java
@@ -0,0 +1,36 @@
+/*
+ * 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.jjs.impl;
+
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.js.ast.JsProgram;
+
+/**
+ * A Java program, a JavaScript program, and a map between them.
+ */
+public class JavaAndJavaScript {
+  public final JProgram jprogram;
+  public final JsProgram jsprogram;
+  public final String[] jscode;
+  public final JavaToJavaScriptMap map;
+
+  public JavaAndJavaScript(JProgram jprogram, JsProgram jsprogram, String[] jscode, JavaToJavaScriptMap map) {
+    this.jprogram = jprogram;
+    this.jsprogram = jsprogram;
+    this.jscode = jscode;
+    this.map = map;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
new file mode 100644
index 0000000..7b21d28
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
@@ -0,0 +1,67 @@
+/*
+ * 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.jjs.impl;
+
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.js.ast.JsName;
+import com.google.gwt.dev.js.ast.JsStatement;
+
+/**
+ * A map between chunks of JavaScript to chunks of Java.
+ */
+public interface JavaToJavaScriptMap {
+  /**
+   * Return the JavaScript name corresponding to a Java type.
+   */
+  JsName nameForType(JReferenceType type);
+  
+  /**
+   * Return the JavaScript name corresponding to a Java method.
+   */
+  JsName nameForMethod(JMethod method);
+
+  /**
+   * If <code>name</code> is the name of a <code>var<code> that corresponds to a Java
+   * static field, then return that field. Otherwise, return null.
+   */
+  JField nameToField(JsName name);
+
+  /**
+   * If <code>name</code> is the name of a function that corresponds to a Java
+   * method, then return that method. Otherwise, return null.
+   */
+  JMethod nameToMethod(JsName name);
+
+  /**
+   * If <code>var</code> is the name of the variable used to hold an interned
+   * string literal, then return the string it interns. Otherwise, return null.
+   */
+  String stringLiteralForName(JsName var);
+  
+  /**
+   * If <code>stat</code> is used to set up the definition of some class,
+   * return that class. Otherwise, return null.
+   */
+  JReferenceType typeForStatement(JsStatement stat);
+  
+  /**
+   * If <code>stat</code> is used to set up a vtable entry for a method,
+   * then return that method.  Otherwise return null.
+   */
+  JMethod vtableInitToMethod(JsStatement stat);
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
index ce55dad..4b327ef 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
@@ -19,12 +19,8 @@
 import com.google.gwt.dev.jjs.ast.CanBeStatic;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.HasEnclosingType;
-import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
-import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
-import com.google.gwt.dev.jjs.ast.JCastOperation;
-import com.google.gwt.dev.jjs.ast.JClassLiteral;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JExpression;
@@ -32,40 +28,25 @@
 import com.google.gwt.dev.jjs.ast.JFieldRef;
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JLocal;
-import com.google.gwt.dev.jjs.ast.JLocalRef;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodBody;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JModVisitor;
-import com.google.gwt.dev.jjs.ast.JNewArray;
-import com.google.gwt.dev.jjs.ast.JNewInstance;
 import com.google.gwt.dev.jjs.ast.JNode;
 import com.google.gwt.dev.jjs.ast.JParameter;
-import com.google.gwt.dev.jjs.ast.JParameterRef;
-import com.google.gwt.dev.jjs.ast.JPrimitiveType;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
-import com.google.gwt.dev.jjs.ast.JStringLiteral;
 import com.google.gwt.dev.jjs.ast.JType;
-import com.google.gwt.dev.jjs.ast.JVariable;
 import com.google.gwt.dev.jjs.ast.JVariableRef;
-import com.google.gwt.dev.jjs.ast.JVisitor;
 import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
 import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
 import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
 import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
-import com.google.gwt.dev.js.ast.JsContext;
-import com.google.gwt.dev.js.ast.JsExpression;
 import com.google.gwt.dev.js.ast.JsFunction;
-import com.google.gwt.dev.js.ast.JsName;
-import com.google.gwt.dev.js.ast.JsNameRef;
-import com.google.gwt.dev.js.ast.JsVisitor;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -94,6 +75,14 @@
    * return type of methods declared to return a globally uninstantiable type.
    */
   private class CleanupRefsVisitor extends JModVisitor {
+    private final Map<JMethod, ArrayList<JParameter>> methodToOriginalParamsMap;
+    private final Set<? extends JNode> referencedNonTypes;
+
+    public CleanupRefsVisitor(Set<? extends JNode> referencedNodes,
+        Map<JMethod, ArrayList<JParameter>> methodToOriginalParamsMap) {
+      this.referencedNonTypes = referencedNodes;
+      this.methodToOriginalParamsMap = methodToOriginalParamsMap;
+    }
 
     @Override
     public void endVisit(JBinaryOperation x, Context ctx) {
@@ -249,14 +238,27 @@
    * unreferenced methods and fields from their containing classes.
    */
   private class PruneVisitor extends JModVisitor {
-
     private boolean didChange = false;
+    private final Map<JMethod, ArrayList<JParameter>> methodToOriginalParamsMap = new HashMap<JMethod, ArrayList<JParameter>>();
+    private final Set<? extends JNode> referencedNonTypes;
+
+    private final Set<? extends JReferenceType> referencedTypes;
+
+    public PruneVisitor(Set<? extends JReferenceType> referencedTypes,
+        Set<? extends JNode> referencedNodes) {
+      this.referencedTypes = referencedTypes;
+      this.referencedNonTypes = referencedNodes;
+    }
 
     @Override
     public boolean didChange() {
       return didChange;
     }
 
+    public Map<JMethod, ArrayList<JParameter>> getMethodToOriginalParamsMap() {
+      return methodToOriginalParamsMap;
+    }
+
     @Override
     public boolean visit(JClassType type, Context ctx) {
 
@@ -386,7 +388,7 @@
 
     @Override
     public boolean visit(JProgram program, Context ctx) {
-      for (JMethod method : program.entryMethods) {
+      for (JMethod method : program.getAllEntryMethods()) {
         accept(method);
       }
       for (Iterator<JReferenceType> it = program.getDeclaredTypes().iterator(); it.hasNext();) {
@@ -433,526 +435,12 @@
     }
   }
 
-  /**
-   * Marks as "referenced" any types, methods, and fields that are reachable.
-   * Also marks as "instantiable" any the classes and interfaces that can
-   * possibly be instantiated.
-   * 
-   * TODO(later): make RescueVisitor use less stack?
-   */
-  private class RescueVisitor extends JVisitor {
-
-    private final Set<JReferenceType> instantiatedTypes = new HashSet<JReferenceType>();
-
-    public void commitInstantiatedTypes() {
-      program.typeOracle.setInstantiatedTypes(instantiatedTypes);
-    }
-
-    @Override
-    public boolean visit(JArrayType type, Context ctx) {
-      assert (referencedTypes.contains(type));
-      boolean isInstantiated = instantiatedTypes.contains(type);
-
-      JType leafType = type.getLeafType();
-      int dims = type.getDims();
-
-      // Rescue my super array type
-      if (leafType instanceof JReferenceType) {
-        JReferenceType rLeafType = (JReferenceType) leafType;
-        if (rLeafType.extnds != null) {
-          JArrayType superArray = program.getTypeArray(rLeafType.extnds, dims);
-          rescue(superArray, true, isInstantiated);
-        }
-
-        for (int i = 0; i < rLeafType.implments.size(); ++i) {
-          JInterfaceType intfType = rLeafType.implments.get(i);
-          JArrayType intfArray = program.getTypeArray(intfType, dims);
-          rescue(intfArray, true, isInstantiated);
-        }
-      }
-
-      // Rescue the base Array type
-      rescue(program.getIndexedType("Array"), true, isInstantiated);
-      return false;
-    }
-
-    @Override
-    public boolean visit(JBinaryOperation x, Context ctx) {
-      // special string concat handling
-      if ((x.getOp() == JBinaryOperator.ADD || x.getOp() == JBinaryOperator.ASG_ADD)
-          && x.getType() == program.getTypeJavaLangString()) {
-        rescueByConcat(x.getLhs().getType());
-        rescueByConcat(x.getRhs().getType());
-      } else if (x.getOp() == JBinaryOperator.ASG) {
-        // Don't rescue variables that are merely assigned to and never read
-        boolean doSkip = false;
-        JExpression lhs = x.getLhs();
-        if (lhs.hasSideEffects() || isVolatileField(lhs)) {
-          // If the lhs has side effects, skipping it would lose the side effect.
-          // If the lhs is volatile, also keep it.  This behavior provides a useful
-          // idiom for test cases to prevent code from being pruned.
-        } else if (lhs instanceof JLocalRef) {
-          // locals are ok to skip
-          doSkip = true;
-        } else if (lhs instanceof JParameterRef) {
-          // parameters are ok to skip
-          doSkip = true;
-        } else if (lhs instanceof JFieldRef) {
-          // fields must rescue the qualifier
-          doSkip = true;
-          JFieldRef fieldRef = (JFieldRef) lhs;
-          JExpression instance = fieldRef.getInstance();
-          if (instance != null) {
-            accept(instance);
-          }
-        }
-
-        if (doSkip) {
-          accept(x.getRhs());
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    public boolean visit(JCastOperation x, Context ctx) {
-      // Rescue any JavaScriptObject type that is the target of a cast.
-      JType targetType = x.getCastType();
-      if (program.isJavaScriptObject(targetType)) {
-        rescue((JReferenceType) targetType, true, true);
-      }
-      return true;
-    }
-
-    @Override
-    public boolean visit(JClassType type, Context ctx) {
-      assert (referencedTypes.contains(type));
-      boolean isInstantiated = instantiatedTypes.contains(type);
-
-      /*
-       * SPECIAL: Some classes contain methods used by code generation later.
-       * Unless those transforms have already been performed, we must rescue all
-       * contained methods for later user.
-       */
-      if (saveCodeGenTypes && program.codeGenTypes.contains(type)) {
-        for (int i = 0; i < type.methods.size(); ++i) {
-          JMethod it = type.methods.get(i);
-          rescue(it);
-        }
-      }
-
-      // Rescue my super type
-      rescue(type.extnds, true, isInstantiated);
-
-      // Rescue my clinit (it won't ever be explicitly referenced
-      rescue(type.methods.get(0));
-
-      // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
-      // However, if I'm instantiated, let's mark them as instantiated.
-      for (int i = 0; i < type.implments.size(); ++i) {
-        JInterfaceType intfType = type.implments.get(i);
-        rescue(intfType, false, isInstantiated);
-      }
-
-      return false;
-    }
-
-    @Override
-    public boolean visit(JDeclarationStatement x, Context ctx) {
-      /*
-       * A declaration by itself doesn't rescue a local (even if it has an
-       * initializer). Writes don't count, only reads.
-       */
-      if (x.getInitializer() != null) {
-        accept(x.getInitializer());
-      }
-
-      // If the lhs is a field ref, we have to visit its qualifier.
-      JVariableRef variableRef = x.getVariableRef();
-      if (variableRef instanceof JFieldRef) {
-        JFieldRef fieldRef = (JFieldRef) variableRef;
-        JExpression instance = fieldRef.getInstance();
-        if (instance != null) {
-          accept(instance);
-        }
-      }
-      return false;
-    }
-
-    @Override
-    public boolean visit(JClassLiteral x, Context ctx) {
-      // Works just like JFieldRef to a static field.
-      JField field = x.getField();
-      rescue(field.getEnclosingType(), true, false);
-      rescue(field);
-      return true;
-    }
-
-    @Override
-    public boolean visit(JFieldRef ref, Context ctx) {
-      JField target = ref.getField();
-
-      // JLS 12.4.1: references to static, non-final, or
-      // non-compile-time-constant fields rescue the enclosing class.
-      // JDT already folds in compile-time constants as literals, so we must
-      // rescue the enclosing types for any static fields that make it here.
-      if (target.isStatic()) {
-        rescue(target.getEnclosingType(), true, false);
-      }
-      rescue(target);
-      return true;
-    }
-
-    @Override
-    public boolean visit(JInterfaceType type, Context ctx) {
-      boolean isReferenced = referencedTypes.contains(type);
-      boolean isInstantiated = instantiatedTypes.contains(type);
-      assert (isReferenced || isInstantiated);
-
-      // Rescue my clinit (it won't ever be explicitly referenced
-      rescue(type.methods.get(0));
-
-      // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
-      // However, if I'm instantiated, let's mark them as instantiated.
-      if (isInstantiated) {
-        for (int i = 0; i < type.implments.size(); ++i) {
-          JInterfaceType intfType = type.implments.get(i);
-          rescue(intfType, false, true);
-        }
-      }
-
-      // visit any field initializers
-      for (int i = 0; i < type.fields.size(); ++i) {
-        JField it = type.fields.get(i);
-        accept(it);
-      }
-
-      return false;
-    }
-
-    @Override
-    public boolean visit(JLocalRef ref, Context ctx) {
-      JLocal target = ref.getLocal();
-      rescue(target);
-      return true;
-    }
-
-    @Override
-    public boolean visit(final JMethod x, Context ctx) {
-      JReferenceType enclosingType = x.getEnclosingType();
-      if (program.isJavaScriptObject(enclosingType)) {
-        // Calls to JavaScriptObject types rescue those types.
-        boolean instance = !x.isStatic() || program.isStaticImpl(x);
-        rescue(enclosingType, true, instance);
-      }
-
-      if (x.isStatic()) {
-        // JLS 12.4.1: references to static methods rescue the enclosing class
-        rescue(enclosingType, true, false);
-      }
-
-      if (x.isNative()) {
-        // Manually rescue native parameter references
-        final JsniMethodBody body = (JsniMethodBody) x.getBody();
-        final JsFunction func = body.getFunc();
-
-        new JsVisitor() {
-          @Override
-          public void endVisit(JsNameRef nameRef, JsContext<JsExpression> ctx) {
-            JsName ident = nameRef.getName();
-
-            if (ident != null) {
-              // If we're referencing a parameter, rescue the associated
-              // JParameter
-              int index = func.getParameters().indexOf(ident.getStaticRef());
-              if (index != -1) {
-                rescue(x.params.get(index));
-              }
-            }
-          }
-        }.accept(func);
-      }
-
-      return true;
-    }
-
-    @Override
-    public boolean visit(JMethodCall call, Context ctx) {
-      rescue(call.getTarget());
-      return true;
-    }
-
-    @Override
-    public boolean visit(JNewArray newArray, Context ctx) {
-      // rescue and instantiate the array type
-      JArrayType arrayType = newArray.getArrayType();
-      if (newArray.dims != null) {
-        // rescue my type and all the implicitly nested types (with fewer dims)
-        int nDims = arrayType.getDims();
-        JType leafType = arrayType.getLeafType();
-        assert (newArray.dims.size() == nDims);
-        for (int i = 0; i < nDims; ++i) {
-          if (newArray.dims.get(i) instanceof JAbsentArrayDimension) {
-            break;
-          }
-          rescue(program.getTypeArray(leafType, nDims - i), true, true);
-        }
-      } else {
-        // just rescue my own specific type
-        rescue(arrayType, true, true);
-      }
-      return true;
-    }
-
-    @Override
-    public boolean visit(JNewInstance newInstance, Context ctx) {
-      // rescue and instantiate the target class!
-      rescue(newInstance.getClassType(), true, true);
-      return true;
-    }
-
-    @Override
-    public boolean visit(JParameterRef x, Context ctx) {
-      // rescue the parameter for future pruning purposes
-      rescue(x.getParameter());
-      return true;
-    }
-
-    @Override
-    public boolean visit(JsniFieldRef x, Context ctx) {
-      /*
-       * SPECIAL: this could be an assignment that passes a value from
-       * JavaScript into Java.
-       */
-      if (x.isLvalue()) {
-        maybeRescueJavaScriptObjectPassingIntoJava(x.getField().getType());
-      }
-      // JsniFieldRef rescues as JFieldRef
-      return visit((JFieldRef) x, ctx);
-    }
-
-    @Override
-    public boolean visit(JsniMethodRef x, Context ctx) {
-      /*
-       * SPECIAL: each argument of the call passes a value from JavaScript into
-       * Java.
-       */
-      ArrayList<JParameter> params = x.getTarget().params;
-      for (int i = 0, c = params.size(); i < c; ++i) {
-        JParameter param = params.get(i);
-        maybeRescueJavaScriptObjectPassingIntoJava(param.getType());
-
-        /*
-         * Because we're not currently tracking methods through JSNI, we need to
-         * assume that it's not safe to prune parameters of a method referenced
-         * as such.
-         * 
-         * A better solution would be to perform basic escape analysis to ensure
-         * that the function reference never escapes, or at minimum, ensure that
-         * the method is immediately called after retrieving the method
-         * reference.
-         */
-        rescue(param);
-      }
-      // JsniMethodRef rescues as JMethodCall
-      return visit((JMethodCall) x, ctx);
-    }
-
-    @Override
-    public boolean visit(JStringLiteral literal, Context ctx) {
-      // rescue and instantiate java.lang.String
-      rescue(program.getTypeJavaLangString(), true, true);
-      return true;
-    }
-
-    private boolean isVolatileField(JExpression x) {
-      if (x instanceof JFieldRef) {
-        JFieldRef xFieldRef = (JFieldRef) x;
-        if (xFieldRef.getField().isVolatile()) {
-          return true;
-        }
-      }
-
-      return false;
-    }
-
-    /**
-     * Subclasses of JavaScriptObject are never instantiated directly. They are
-     * created "magically" when a JSNI method passes a reference to an existing
-     * JS object into Java code. If any point in the program can pass a value
-     * from JS into Java which could potentially be cast to JavaScriptObject, we
-     * must rescue JavaScriptObject.
-     * 
-     * @param type The type of the value passing from Java to JavaScript.
-     * @see com.google.gwt.core.client.JavaScriptObject
-     */
-    private void maybeRescueJavaScriptObjectPassingIntoJava(JType type) {
-      boolean doIt = false;
-      if (program.isJavaScriptObject(type)
-          || type == program.getTypeJavaLangString()) {
-        doIt = true;
-      } else if (type instanceof JArrayType) {
-        /*
-         * Hackish: in our own JRE we sometimes create "not quite baked" arrays
-         * in JavaScript for expediency.
-         */
-        JArrayType arrayType = (JArrayType) type;
-        JType elementType = arrayType.getElementType();
-        if (elementType instanceof JPrimitiveType
-            || elementType == program.getTypeJavaLangString()
-            || program.isJavaScriptObject(elementType)) {
-          doIt = true;
-        }
-      }
-      if (doIt) {
-        rescue((JReferenceType) type, true, true);
-      }
-    }
-
-    private boolean rescue(JMethod method) {
-      if (method != null) {
-        if (!referencedNonTypes.contains(method)) {
-          referencedNonTypes.add(method);
-          accept(method);
-          if (method.isNative()) {
-            /*
-             * SPECIAL: returning from this method passes a value from
-             * JavaScript into Java.
-             */
-            maybeRescueJavaScriptObjectPassingIntoJava(method.getType());
-          }
-          return true;
-        }
-      }
-      return false;
-    }
-
-    private void rescue(JReferenceType type, boolean isReferenced,
-        boolean isInstantiated) {
-      if (type != null) {
-
-        boolean doVisit = false;
-        if (isInstantiated && !instantiatedTypes.contains(type)) {
-          instantiatedTypes.add(type);
-          doVisit = true;
-        }
-
-        if (isReferenced && !referencedTypes.contains(type)) {
-          referencedTypes.add(type);
-          doVisit = true;
-        }
-
-        if (doVisit) {
-          accept(type);
-        }
-      }
-    }
-
-    private void rescue(JVariable var) {
-      if (var != null) {
-        if (!referencedNonTypes.contains(var)) {
-          referencedNonTypes.add(var);
-        }
-      }
-    }
-
-    /**
-     * Handle special rescues needed implicitly to support concat.
-     */
-    private void rescueByConcat(JType type) {
-      JClassType stringType = program.getTypeJavaLangString();
-      JPrimitiveType charType = program.getTypePrimitiveChar();
-      if (type instanceof JReferenceType && type != stringType
-          && type != program.getTypeNull()) {
-        /*
-         * Any reference types (except String, which works by default) that take
-         * part in a concat must rescue java.lang.Object.toString().
-         * 
-         * TODO: can we narrow the focus by walking up the type hierarchy or
-         * doing explicit toString calls?
-         */
-        JMethod toStringMethod = program.getIndexedMethod("Object.toString");
-        rescue(toStringMethod);
-      } else if (type == charType) {
-        /*
-         * Characters must rescue String.valueOf(char)
-         */
-        if (stringValueOfChar == null) {
-          for (int i = 0; i < stringType.methods.size(); ++i) {
-            JMethod meth = stringType.methods.get(i);
-            if (meth.getName().equals("valueOf")) {
-              List<JType> params = meth.getOriginalParamTypes();
-              if (params.size() == 1) {
-                if (params.get(0) == charType) {
-                  stringValueOfChar = meth;
-                  break;
-                }
-              }
-            }
-          }
-          assert (stringValueOfChar != null);
-        }
-        rescue(stringValueOfChar);
-      }
-    }
-  }
-
-  /**
-   * A method that isn't called directly can still be needed, if it overrides or
-   * implements any methods that are called.
-   */
-  private class UpRefVisitor extends JVisitor {
-
-    private boolean didRescue = false;
-    private final RescueVisitor rescuer;
-
-    public UpRefVisitor(RescueVisitor rescuer) {
-      this.rescuer = rescuer;
-    }
-
-    public boolean didRescue() {
-      return didRescue;
-    }
-
-    @Override
-    public boolean visit(JMethod x, Context ctx) {
-      if (referencedNonTypes.contains(x)) {
-        return false;
-      }
-
-      for (JMethod override : program.typeOracle.getAllOverrides(x)) {
-        if (referencedNonTypes.contains(override)) {
-          rescuer.rescue(x);
-          didRescue = true;
-          return false;
-        }
-      }
-      return false;
-    }
-
-    @Override
-    public boolean visit(JClassType x, Context ctx) {
-      return rescuer.instantiatedTypes.contains(x);
-    }
-
-    @Override
-    public boolean visit(JProgram x, Context ctx) {
-      didRescue = false;
-      return true;
-    }
-  }
-
   public static boolean exec(JProgram program, boolean noSpecialTypes) {
     return new Pruner(program, noSpecialTypes).execImpl();
   }
 
-  private final Map<JMethod, ArrayList<JParameter>> methodToOriginalParamsMap = new HashMap<JMethod, ArrayList<JParameter>>();
   private final JProgram program;
-  private final Set<JNode> referencedNonTypes = new HashSet<JNode>();
-  private final Set<JReferenceType> referencedTypes = new HashSet<JReferenceType>();
   private final boolean saveCodeGenTypes;
-  private JMethod stringValueOfChar = null;
 
   private Pruner(JProgram program, boolean saveCodeGenTypes) {
     this.program = program;
@@ -962,35 +450,52 @@
   private boolean execImpl() {
     boolean madeChanges = false;
     while (true) {
-      RescueVisitor rescuer = new RescueVisitor();
-      for (JReferenceType type : program.codeGenTypes) {
-        rescuer.rescue(type, true, saveCodeGenTypes);
+      ControlFlowAnalyzer livenessAnalyzer = new ControlFlowAnalyzer(program);
+      if (saveCodeGenTypes) {
+        /*
+         * SPECIAL: Some classes contain methods used by code generation later.
+         * Unless those transforms have already been performed, we must rescue
+         * all contained methods for later user.
+         */
+        traverseFromCodeGenTypes(livenessAnalyzer);
       }
-      for (JMethod method : program.entryMethods) {
-        rescuer.rescue(method);
+      for (JMethod method : program.getAllEntryMethods()) {
+        livenessAnalyzer.traverseFrom(method);
       }
+      livenessAnalyzer.finishTraversal();
 
-      UpRefVisitor upRefer = new UpRefVisitor(rescuer);
-      do {
-        rescuer.commitInstantiatedTypes();
-        upRefer.accept(program);
-      } while (upRefer.didRescue());
+      program.typeOracle.setInstantiatedTypes(livenessAnalyzer.getInstantiatedTypes());
 
-      PruneVisitor pruner = new PruneVisitor();
+      PruneVisitor pruner = new PruneVisitor(
+          livenessAnalyzer.getReferencedTypes(),
+          livenessAnalyzer.getLiveFieldsAndMethods());
       pruner.accept(program);
       if (!pruner.didChange()) {
         break;
       }
 
-      CleanupRefsVisitor cleaner = new CleanupRefsVisitor();
-      cleaner.accept(program);
+      CleanupRefsVisitor cleaner = new CleanupRefsVisitor(
+          livenessAnalyzer.getLiveFieldsAndMethods(),
+          pruner.getMethodToOriginalParamsMap());
+      cleaner.accept(program.getDeclaredTypes());
 
-      referencedTypes.clear();
-      referencedNonTypes.clear();
-      methodToOriginalParamsMap.clear();
       madeChanges = true;
     }
     return madeChanges;
   }
 
+  /**
+   * Traverse from all methods in the program's code-gen types. See
+   * {@link JProgram#CODEGEN_TYPES_SET}.
+   */
+  private void traverseFromCodeGenTypes(ControlFlowAnalyzer livenessAnalyzer) {
+    for (JReferenceType type : program.codeGenTypes) {
+      livenessAnalyzer.traverseFromReferenceTo(type);
+      for (int i = 0; i < type.methods.size(); ++i) {
+        JMethod method = type.methods.get(i);
+        livenessAnalyzer.traverseFrom(method);
+      }
+    }
+  }
+
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
new file mode 100644
index 0000000..f9dfa3c
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
@@ -0,0 +1,126 @@
+/*
+ * 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.jjs.impl;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JExpression;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JType;
+
+/**
+ * Replaces calls to
+ * {@link com.google.gwt.core.client.GWT#runAsync(com.google.gwt.core.client.RunAsyncCallback)}"
+ * by calls to a fragment loader.
+ */
+public class ReplaceRunAsyncs {
+  private class AsyncCreateVisitor extends JModVisitor {
+    private JMethod currentMethod;
+    private int entryCount = 1;
+
+    @Override
+    public void endVisit(JMethodCall x, Context ctx) {
+      JMethod method = x.getTarget();
+      if (method == program.getIndexedMethod("GWT.runAsync")) {
+        assert (x.getArgs().size() == 1);
+        JExpression asyncCallback = x.getArgs().get(0);
+
+        int entryNumber = entryCount++;
+        logger.log(TreeLogger.TRACE, "Using island loader #" + entryNumber
+            + " in method " + currentMethod);
+
+        JClassType loader = getFragmentLoader(entryNumber);
+        JMethod loadMethod = getRunAsyncMethod(loader);
+        assert loadMethod != null;
+
+        JMethodCall methodCall = new JMethodCall(program, x.getSourceInfo(),
+            null, loadMethod);
+        methodCall.getArgs().add(asyncCallback);
+
+        program.addEntryMethod(getOnLoadMethod(loader), entryNumber);
+
+        ctx.replaceMe(methodCall);
+      }
+    }
+
+    @Override
+    public boolean visit(JMethod x, Context ctx) {
+      currentMethod = x;
+      return true;
+    }
+  }
+
+  public static int exec(TreeLogger logger, JProgram program) {
+    return new ReplaceRunAsyncs(logger, program).execImpl();
+  }
+
+  private final TreeLogger logger;
+  private JProgram program;
+
+  private ReplaceRunAsyncs(TreeLogger logger, JProgram program) {
+    this.logger = logger.branch(TreeLogger.TRACE,
+        "Replacing GWT.runAsync with island loader calls");
+    this.program = program;
+  }
+
+  private int execImpl() {
+    AsyncCreateVisitor visitor = new AsyncCreateVisitor();
+    visitor.accept(program);
+    return visitor.entryCount;
+  }
+
+  private JClassType getFragmentLoader(int fragmentNumber) {
+    String fragmentLoaderClassName = FragmentLoaderCreator.ASYNC_LOADER_PACKAGE
+        + "." + FragmentLoaderCreator.ASYNC_LOADER_CLASS_PREFIX
+        + fragmentNumber;
+    JType result = program.getFromTypeMap(fragmentLoaderClassName);
+    assert (result != null);
+    assert (result instanceof JClassType);
+    return (JClassType) result;
+  }
+
+  private JMethod getOnLoadMethod(JClassType loaderType) {
+    assert loaderType != null;
+    assert loaderType.methods != null;
+    for (JMethod method : loaderType.methods) {
+      if (method.getName().equals("onLoad")) {
+        assert (method.isStatic());
+        assert (method.params.size() == 0);
+        return method;
+      }
+    }
+    assert false;
+    return null;
+  }
+
+  private JMethod getRunAsyncMethod(JClassType loaderType) {
+    assert loaderType != null;
+    assert loaderType.methods != null;
+    for (JMethod method : loaderType.methods) {
+      if (method.getName().equals("runAsync")) {
+        assert (method.isStatic());
+        assert (method.params.size() == 1);
+        assert (method.params.get(0).getType().getName().equals(FragmentLoaderCreator.RUN_ASYNC_CALLBACK));
+        return method;
+      }
+    }
+    return null;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitor.java
index ad1a780..4a15d4a 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitor.java
@@ -18,6 +18,7 @@
 import com.google.gwt.dev.js.ast.JsBlock;
 import com.google.gwt.dev.js.ast.JsContext;
 import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsProgramFragment;
 import com.google.gwt.dev.js.ast.JsStatement;
 import com.google.gwt.dev.util.TextOutput;
 
@@ -30,11 +31,19 @@
     super(out);
   }
 
+  @Override
   public boolean visit(JsProgram x, JsContext<JsProgram> ctx) {
     // Descend naturally.
     return true;
   }
 
+  @Override
+  public boolean visit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+    // Descend naturally.
+    return true;
+  }
+
+  @Override
   public boolean visit(JsBlock x, JsContext<JsStatement> ctx) {
     printJsBlockOptionalTruncate(x, false);
     return false;
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java b/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
index f7f33e7..09336f3 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
@@ -34,6 +34,7 @@
 import com.google.gwt.dev.js.ast.JsNullLiteral;
 import com.google.gwt.dev.js.ast.JsPrefixOperation;
 import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsProgramFragment;
 import com.google.gwt.dev.js.ast.JsStatement;
 import com.google.gwt.dev.js.ast.JsUnaryOperator;
 import com.google.gwt.dev.js.ast.JsValueLiteral;
@@ -103,6 +104,11 @@
     }
 
     @Override
+    public void endVisit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+      scopeStack.pop();
+    }
+
+    @Override
     public boolean visit(JsBlock x, JsContext<JsStatement> ctx) {
       if (x == scopeStack.peek()) {
         ListIterator<JsStatement> itr = x.getStatements().listIterator();
@@ -159,6 +165,12 @@
       scopeStack.push(x.getGlobalBlock());
       return true;
     }
+
+    @Override
+    public boolean visit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+      scopeStack.push(x.getGlobalBlock());
+      return true;
+    }
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java b/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java
index 9bbbc72..4c10370 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java
@@ -32,6 +32,7 @@
 import com.google.gwt.dev.js.ast.JsVars.JsVar;
 
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.TreeMap;
@@ -162,19 +163,7 @@
     StringVisitor v = new StringVisitor(scope);
     v.accept(block);
 
-    if (v.toCreate.size() > 0) {
-      // Create the pool of variable names.
-      JsVars vars = new JsVars(program.createSourceInfoSynthetic(
-          JsStringInterner.class, "Interned string pool"));
-      SourceInfo sourceInfo = program.createSourceInfoSynthetic(
-          JsStringInterner.class, "Interned string assignment");
-      for (Map.Entry<JsStringLiteral, JsName> entry : v.toCreate.entrySet()) {
-        JsVar var = new JsVar(sourceInfo, entry.getValue());
-        var.setInitExpr(entry.getKey());
-        vars.add(var);
-      }
-      block.getStatements().add(0, vars);
-    }
+    createVars(program, block, v.toCreate);
 
     return v.didChange();
   }
@@ -186,10 +175,39 @@
    * global block.
    * 
    * @param program the JsProgram
-   * @return <code>true</code> if any changes were made to the program
+   * @return a map from each created JsName to the string it is a literal for.
    */
-  public static boolean exec(JsProgram program) {
-    return exec(program, program.getGlobalBlock(), program.getScope());
+  public static Map<JsName, String> exec(JsProgram program) {
+    StringVisitor v = new StringVisitor(program.getScope());
+    for (int i = 0; i < program.getFragmentCount(); i++) {
+      v.accept(program.getFragmentBlock(i));
+    }
+
+    Map<JsName, String> map = new HashMap<JsName, String>();
+    for (JsStringLiteral stringLit : v.toCreate.keySet()) {
+      map.put(v.toCreate.get(stringLit), stringLit.getValue());
+    }
+
+    createVars(program, program.getGlobalBlock(), v.toCreate);
+
+    return map;
+  }
+
+  private static void createVars(JsProgram program, JsBlock block,
+      SortedMap<JsStringLiteral, JsName> toCreate) {
+    if (toCreate.size() > 0) {
+      // Create the pool of variable names.
+      JsVars vars = new JsVars(program.createSourceInfoSynthetic(
+          JsStringInterner.class, "Interned string pool"));
+      SourceInfo sourceInfo = program.createSourceInfoSynthetic(
+          JsStringInterner.class, "Interned string assignment");
+      for (Map.Entry<JsStringLiteral, JsName> entry : toCreate.entrySet()) {
+        JsVar var = new JsVar(sourceInfo, entry.getValue());
+        var.setInitExpr(entry.getKey());
+        vars.add(var);
+      }
+      block.getStatements().add(0, vars);
+    }
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
index 13fb936..f2ce3e9 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
@@ -51,6 +51,7 @@
 import com.google.gwt.dev.js.ast.JsPostfixOperation;
 import com.google.gwt.dev.js.ast.JsPrefixOperation;
 import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsProgramFragment;
 import com.google.gwt.dev.js.ast.JsPropertyInitializer;
 import com.google.gwt.dev.js.ast.JsRegExp;
 import com.google.gwt.dev.js.ast.JsReturn;
@@ -658,6 +659,12 @@
   }
 
   @Override
+  public boolean visit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+    p.print("<JsProgramFragment>");
+    return false;
+  }
+
+  @Override
   public boolean visit(JsPropertyInitializer x,
       JsContext<JsPropertyInitializer> ctx) {
     // Since there are separators, we actually print the property init
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java b/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java
index 68e4988..e6b6b9e 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java
@@ -31,10 +31,12 @@
   private final JsEmpty emptyStmt = new JsEmpty(createSourceInfoSynthetic(
       JsProgram.class, "Empty statement"));
 
+  private boolean enableSourceInfoDescendants;
+
   private final JsBooleanLiteral falseLiteral = new JsBooleanLiteral(
       createSourceInfoSynthetic(JsProgram.class, "false literal"), false);
 
-  private final JsGlobalBlock globalBlock;
+  private JsProgramFragment[] fragments;
 
   private final JsNullLiteral nullLiteral = new JsNullLiteral(
       createSourceInfoSynthetic(JsProgram.class, "null literal"));
@@ -52,8 +54,6 @@
   private final JsBooleanLiteral trueLiteral = new JsBooleanLiteral(
       createSourceInfoSynthetic(JsProgram.class, "true literal"), true);
 
-  private boolean enableSourceInfoDescendants;
-
   /**
    * Constructs a JavaScript program object.
    */
@@ -61,10 +61,9 @@
     super(SourceInfoJs.INTRINSIC.makeChild(JsProgram.class,
         "JavaScript program"));
     rootScope = new JsRootScope(this);
-    globalBlock = new JsGlobalBlock(createSourceInfoSynthetic(JsProgram.class,
-        "global block"));
     topScope = new JsScope(rootScope, "Global");
     objectScope = new JsScope(rootScope, "Object");
+    setFragmentCount(1);
   }
 
   public SourceInfo createSourceInfo(int lineNumber, String location) {
@@ -102,11 +101,22 @@
     return falseLiteral;
   }
 
+  public JsBlock getFragmentBlock(int fragment) {
+    if (fragment < 0 || fragment >= fragments.length) {
+      throw new IllegalArgumentException("Invalid fragment: " + fragment);
+    }
+    return fragments[fragment].getGlobalBlock();
+  }
+
+  public int getFragmentCount() {
+    return this.fragments.length;
+  }
+
   /**
    * Gets the one and only global block.
    */
   public JsBlock getGlobalBlock() {
-    return globalBlock;
+    return getFragmentBlock(0);
   }
 
   public JsNullLiteral getNullLiteral() {
@@ -144,6 +154,11 @@
     return topScope;
   }
 
+  /**
+   * Note: This is currently assumed not to be called after
+   * GenerateJavaScriptAST has finished. If it ever is, then GWT.runAsync needs
+   * to be updated to account for such string literals.
+   */
   public JsStringLiteral getStringLiteral(SourceInfo sourceInfo, String value) {
     JsStringLiteral lit = stringLiteralMap.get(value);
     if (lit == null) {
@@ -174,9 +189,19 @@
     enableSourceInfoDescendants = enable;
   }
 
+  public void setFragmentCount(int fragments) {
+    this.fragments = new JsProgramFragment[fragments];
+    for (int i = 0; i < fragments; i++) {
+      this.fragments[i] = new JsProgramFragment(createSourceInfoSynthetic(
+          JsProgram.class, "fragment " + i));
+    }
+  }
+
   public void traverse(JsVisitor v, JsContext<JsProgram> ctx) {
     if (v.visit(this, ctx)) {
-      v.accept(globalBlock);
+      for (JsProgramFragment fragment : fragments) {
+        v.accept(fragment);
+      }
     }
     v.endVisit(this, ctx);
   }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsProgramFragment.java b/dev/core/src/com/google/gwt/dev/js/ast/JsProgramFragment.java
new file mode 100644
index 0000000..2b685aa
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsProgramFragment.java
@@ -0,0 +1,43 @@
+/*
+ * 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.js.ast;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+/**
+ * One independently loadable fragment of a {@link JsProgram}.
+ */
+public class JsProgramFragment extends JsNode<JsProgramFragment> {
+  private final JsGlobalBlock globalBlock;
+
+  public JsProgramFragment(SourceInfo sourceInfo) {
+    super(sourceInfo);
+    this.globalBlock = new JsGlobalBlock(sourceInfo.makeChild(
+        JsProgramFragment.class, "global block for a fragment"));
+  }
+
+  public JsBlock getGlobalBlock() {
+    return globalBlock;
+  }
+
+  public void traverse(JsVisitor v, JsContext<JsProgramFragment> ctx) {
+    if (v.visit(this, ctx)) {
+      v.accept(globalBlock);
+    }
+    v.endVisit(this, ctx);
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsVars.java b/dev/core/src/com/google/gwt/dev/js/ast/JsVars.java
index 86e1e41..5b37e06 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsVars.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsVars.java
@@ -72,6 +72,10 @@
     vars.add(var);
   }
 
+  public int getNumVars() {
+    return vars.size();
+  }
+
   public boolean isEmpty() {
     return vars.isEmpty();
   }
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
index ffaf3dd..e3151f5 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
@@ -91,6 +91,8 @@
   };
 
   public final <T extends JsVisitable> T accept(T node) {
+    // The following cast to T is needed for javac 1.5.0_13
+    // as shipped on OS X
     return (T) doAccept(node);
   }
 
@@ -201,6 +203,9 @@
   public void endVisit(JsProgram x, JsContext<JsProgram> ctx) {
   }
 
+  public void endVisit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+  }
+
   public void endVisit(JsPropertyInitializer x,
       JsContext<JsPropertyInitializer> ctx) {
   }
@@ -355,6 +360,10 @@
     return true;
   }
 
+  public boolean visit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+    return true;
+  }
+
   public boolean visit(JsPropertyInitializer x,
       JsContext<JsPropertyInitializer> ctx) {
     return true;
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
index b04fcf4..88dc709 100644
--- a/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
+++ b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
@@ -27,6 +27,7 @@
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.security.AccessControlException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -165,6 +166,9 @@
               classPath.add(entry);
             }
             continue;
+          } catch (AccessControlException e) {
+            logger.log(TreeLogger.DEBUG, "Skipping URL due to access restrictions: " + url);
+            continue;
           } catch (URISyntaxException e) {
             caught = e;
           } catch (IOException e) {
diff --git a/dev/core/src/com/google/gwt/dev/util/Util.java b/dev/core/src/com/google/gwt/dev/util/Util.java
index aabd864..31f6972 100644
--- a/dev/core/src/com/google/gwt/dev/util/Util.java
+++ b/dev/core/src/com/google/gwt/dev/util/Util.java
@@ -56,6 +56,7 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
+import java.nio.ByteBuffer;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
@@ -132,6 +133,16 @@
    *         formatted for use as a file name
    */
   public static String computeStrongName(byte[] content) {
+    return computeStrongName(new byte[][] {content});
+  }
+
+  /**
+   * Computes the MD5 hash of the specified byte arrays.
+   * 
+   * @return a big fat string encoding of the MD5 for the content, suitably
+   *         formatted for use as a file name
+   */
+  public static String computeStrongName(byte[][] contents) {
     MessageDigest md5;
     try {
       md5 = MessageDigest.getInstance("MD5");
@@ -139,7 +150,23 @@
       throw new RuntimeException("Error initializing MD5", e);
     }
 
-    md5.update(content);
+    /*
+     * Include the lengths of the contents components in the hash, so that the
+     * hashed sequence of bytes is in a one-to-one correspondence with the
+     * possible arguments to this method.
+     */
+    ByteBuffer b = ByteBuffer.allocate((contents.length + 1) * 4);
+    b.putInt(contents.length);
+    for (int i = 0; i < contents.length; i++) {
+      b.putInt(contents[i].length);
+    }
+    b.flip();
+    md5.update(b);
+
+    // Now hash the actual contents of the arrays
+    for (int i = 0; i < contents.length; i++) {
+      md5.update(contents[i]);
+    }
     return toHexString(md5.digest());
   }
 
@@ -296,6 +323,14 @@
     }
   }
 
+  public static byte[][] getBytes(String[] s) {
+    byte[][] bytes = new byte[s.length][];
+    for (int i = 0; i < s.length; i++) {
+      bytes[i] = getBytes(s[i]);
+    }
+    return bytes;
+  }
+
   /**
    * @param cls A class whose name you want.
    * @return The base name for the specified class.
@@ -543,6 +578,25 @@
     logger.log(TreeLogger.INFO, "Unable to dump source to disk", caught);
   }
 
+  public static byte[][] readFileAndSplit(File file) {
+    RandomAccessFile f = null;
+    try {
+      f = new RandomAccessFile(file, "r");
+      int length = f.readInt();
+      byte[][] results = new byte[length][];
+      for (int i = 0; i < length; i++) {
+        int nextLength = f.readInt();
+        results[i] = new byte[nextLength];
+        f.read(results[i]);
+      }
+      return results;
+    } catch (IOException e) {
+      return null;
+    } finally {
+      Utility.close(f);
+    }
+  }
+
   public static byte[] readFileAsBytes(File file) {
     FileInputStream fileInputStream = null;
     try {
@@ -877,6 +931,14 @@
     return toArray(String.class, coll);
   }
 
+  public static String[] toStrings(byte[][] bytes) {
+    String[] strings = new String[bytes.length];
+    for (int i = 0; i < bytes.length; i++) {
+      strings[i] = toString(bytes[i]);
+    }
+    return strings;
+  }
+
   public static URL toURL(File f) {
     try {
       return f.toURI().toURL();
@@ -1094,6 +1156,31 @@
   }
 
   /**
+   * Write all of the supplied bytes to the file, in a way that they can be read
+   * back by {@link #readFileAndSplit(File).
+   */
+  public static boolean writeStringsAsFile(TreeLogger branch,
+      File makePermFilename, String[] js) {
+    RandomAccessFile f = null;
+    try {
+      makePermFilename.delete();
+      makePermFilename.getParentFile().mkdirs();
+      f = new RandomAccessFile(makePermFilename, "rwd");
+      f.writeInt(js.length);
+      for (String s : js) {
+        byte[] b = getBytes(s);
+        f.writeInt(b.length);
+        f.write(b);
+      }
+      return true;
+    } catch (IOException e) {
+      return false;
+    } finally {
+      Utility.close(f);
+    }
+  }
+
+  /**
    * Reads the specified number of bytes from the {@link InputStream}.
    * 
    * @param byteLength number of bytes to read
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/ContentWidget.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/ContentWidget.java
index 3f422b6..f89e59b 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/ContentWidget.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/ContentWidget.java
@@ -24,6 +24,8 @@
 import com.google.gwt.i18n.client.HasDirection;
 import com.google.gwt.i18n.client.LocaleInfo;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.DeckPanel;
 import com.google.gwt.user.client.ui.HTML;
@@ -80,6 +82,11 @@
   private static String loadingImage;
 
   /**
+   * The tab bar of options.
+   */
+  protected TabBar tabBar = null;
+
+  /**
    * An instance of the constants.
    */
   private CwConstants constants;
@@ -116,11 +123,6 @@
   private HTML styleWidget = null;
 
   /**
-   * The tab bar of options.
-   */
-  private TabBar tabBar = null;
-
-  /**
    * Constructor.
    * 
    * @param constants the constants
@@ -223,12 +225,21 @@
         add(styleWidget, constants.contentWidgetStyle());
       }
 
-      // Initialize the widget and add it to the page
-      Widget widget = onInitialize();
-      if (widget != null) {
-        vPanel.add(widget);
-      }
-      onInitializeComplete();
+      asyncOnInitialize(new AsyncCallback<Widget>() {
+
+        public void onFailure(Throwable caught) {
+          Window.alert("exception: " + caught);
+        }
+
+        public void onSuccess(Widget result) {
+          // Initialize the widget and add it to the page
+          Widget widget = result;
+          if (widget != null) {
+            vPanel.add(widget);
+          }
+          onInitializeComplete();
+        }
+      });
     }
   }
 
@@ -304,6 +315,8 @@
     tabBar.selectTab(index);
   }
 
+  protected abstract void asyncOnInitialize(final AsyncCallback<Widget> callback);
+
   @Override
   protected void onLoad() {
     // Initialize this widget if we haven't already
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsExample.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsExample.java
index 13602aa..6db6ae0 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsExample.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsExample.java
@@ -16,12 +16,14 @@
 package com.google.gwt.sample.showcase.client.content.i18n;
 
 import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseConstants;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseRaw;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.ClickListener;
 import com.google.gwt.user.client.ui.FlexTable;
 import com.google.gwt.user.client.ui.HTML;
@@ -37,7 +39,7 @@
 /**
  * Example file.
  */
-@ShowcaseRaw({"ExampleConstants.java", "ExampleConstants.properties"})
+@ShowcaseRaw( {"ExampleConstants.java", "ExampleConstants.properties"})
 public class CwConstantsExample extends ContentWidget {
   /**
    * The constants used in this Content Widget.
@@ -189,6 +191,20 @@
     }
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Add a tab to this example to show the messages interface.
    */
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsWithLookupExample.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsWithLookupExample.java
index 82d69ce..38e55db 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsWithLookupExample.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsWithLookupExample.java
@@ -16,12 +16,14 @@
 package com.google.gwt.sample.showcase.client.content.i18n;
 
 import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseConstants;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseRaw;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.ClickListener;
 import com.google.gwt.user.client.ui.FlexTable;
 import com.google.gwt.user.client.ui.HTML;
@@ -37,7 +39,7 @@
 /**
  * Example file.
  */
-@ShowcaseRaw({"ColorConstants.java", "ColorConstants.properties"})
+@ShowcaseRaw( {"ColorConstants.java", "ColorConstants.properties"})
 public class CwConstantsWithLookupExample extends ContentWidget {
   /**
    * The constants used in this Content Widget.
@@ -211,6 +213,20 @@
     }
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Add a tab to this example to show the messages interface.
    */
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDateTimeFormat.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDateTimeFormat.java
index db1e90a..7789094 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDateTimeFormat.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDateTimeFormat.java
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.sample.showcase.client.content.i18n;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.i18n.client.DateTimeFormat;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.ChangeListener;
 import com.google.gwt.user.client.ui.Grid;
 import com.google.gwt.user.client.ui.HasVerticalAlignment;
@@ -178,6 +181,20 @@
     return layout;
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Show an error message. Pass in null to clear the error message.
    * 
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDictionaryExample.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDictionaryExample.java
index a6d78fe..16a02bf 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDictionaryExample.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDictionaryExample.java
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.sample.showcase.client.content.i18n;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.i18n.client.Dictionary;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.FlexTable;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.VerticalPanel;
@@ -117,4 +120,18 @@
     // Return the layout Widget
     return layout;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwMessagesExample.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwMessagesExample.java
index ff5183c..11e771b 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwMessagesExample.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwMessagesExample.java
@@ -16,12 +16,14 @@
 package com.google.gwt.sample.showcase.client.content.i18n;
 
 import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseConstants;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseRaw;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.ClickListener;
 import com.google.gwt.user.client.ui.FlexTable;
 import com.google.gwt.user.client.ui.HTML;
@@ -243,6 +245,20 @@
     }
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Add a tab to this example to show the {@link ErrorMessages} source code.
    */
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwNumberFormat.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwNumberFormat.java
index ad91de0..32ac953 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwNumberFormat.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwNumberFormat.java
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.sample.showcase.client.content.i18n;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.i18n.client.NumberFormat;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.ChangeListener;
 import com.google.gwt.user.client.ui.Grid;
 import com.google.gwt.user.client.ui.KeyboardListenerAdapter;
@@ -172,6 +175,20 @@
     return layout;
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Show an error message. Pass in null to clear the error message.
    * 
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwListBox.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwListBox.java
index e9715a5..2a98d82 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwListBox.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwListBox.java
@@ -15,11 +15,14 @@
  */
 package com.google.gwt.sample.showcase.client.content.lists;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.ChangeListener;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.HorizontalPanel;
@@ -131,6 +134,20 @@
     return hPanel;
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Display the options for a given category in the list box.
    * 
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwMenuBar.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwMenuBar.java
index 34c48f8..c022dbc 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwMenuBar.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwMenuBar.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.sample.showcase.client.content.lists;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
@@ -22,6 +24,7 @@
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
 import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.MenuBar;
 import com.google.gwt.user.client.ui.MenuItem;
 import com.google.gwt.user.client.ui.Widget;
@@ -29,7 +32,8 @@
 /**
  * Example file.
  */
-@ShowcaseStyle({".gwt-MenuBar", ".gwt-MenuBarPopup", "html>body .gwt-MenuBarPopup",
+@ShowcaseStyle( {
+    ".gwt-MenuBar", ".gwt-MenuBarPopup", "html>body .gwt-MenuBarPopup",
     "* html .gwt-MenuBarPopup"})
 public class CwMenuBar extends ContentWidget {
   /**
@@ -161,4 +165,18 @@
     menu.ensureDebugId("cwMenuBar");
     return menu;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwStackPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwStackPanel.java
index 1b8cc3b..cbe4706 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwStackPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwStackPanel.java
@@ -16,11 +16,13 @@
 package com.google.gwt.sample.showcase.client.content.lists;
 
 import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.AbstractImagePrototype;
 import com.google.gwt.user.client.ui.CheckBox;
 import com.google.gwt.user.client.ui.ClickListener;
@@ -38,7 +40,8 @@
 /**
  * Example file.
  */
-@ShowcaseStyle({".gwt-DecoratedStackPanel", "html>body .gwt-DecoratedStackPanel",
+@ShowcaseStyle( {
+    ".gwt-DecoratedStackPanel", "html>body .gwt-DecoratedStackPanel",
     "* html .gwt-DecoratedStackPanel", ".cw-StackPanelHeader"})
 public class CwStackPanel extends ContentWidget {
   /**
@@ -164,6 +167,20 @@
     return stackPanel;
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Create the list of Contacts.
    * 
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwSuggestBox.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwSuggestBox.java
index 49a850f..1d720be 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwSuggestBox.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwSuggestBox.java
@@ -15,11 +15,14 @@
  */
 package com.google.gwt.sample.showcase.client.content.lists;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
 import com.google.gwt.user.client.ui.SuggestBox;
@@ -29,7 +32,8 @@
 /**
  * Example file.
  */
-@ShowcaseStyle({".gwt-SuggestBox", ".gwt-SuggestBoxPopup",
+@ShowcaseStyle({
+    ".gwt-SuggestBox", ".gwt-SuggestBoxPopup",
     "html>body .gwt-SuggestBoxPopup", "* html .gwt-SuggestBoxPopup"})
 public class CwSuggestBox extends ContentWidget {
   /**
@@ -96,4 +100,18 @@
     // Return the panel
     return suggestPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwTree.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwTree.java
index 942a881..62f480b 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwTree.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwTree.java
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.sample.showcase.client.content.lists;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
 import com.google.gwt.user.client.Random;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.DecoratorPanel;
 import com.google.gwt.user.client.ui.Grid;
 import com.google.gwt.user.client.ui.HasVerticalAlignment;
@@ -150,6 +153,20 @@
     return grid;
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Add a new section of music created by a specific composer.
    * 
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwAnimation.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwAnimation.java
index 8e56c1f..409a587 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwAnimation.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwAnimation.java
@@ -16,11 +16,14 @@
 package com.google.gwt.sample.showcase.client.content.other;
 
 import com.google.gwt.animation.client.Animation;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.Showcase;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.AbsolutePanel;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.ClickListener;
@@ -238,6 +241,20 @@
     return mainLayout;
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Create an options panel that allows users to select a widget and reposition
    * it.
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwCookies.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwCookies.java
index 2cd0cc1..b660bb8 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwCookies.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwCookies.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.sample.showcase.client.content.other;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
@@ -23,6 +25,7 @@
 import com.google.gwt.user.client.Cookies;
 import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.ChangeListener;
 import com.google.gwt.user.client.ui.ClickListener;
@@ -110,12 +113,12 @@
   public String getName() {
     return constants.cwCookiesName();
   }
-  
+
   @Override
   public boolean hasStyle() {
     return false;
   }
-  
+
   /**
    * Initialize this example.
    */
@@ -175,7 +178,8 @@
     deleteCookieButton.addClickListener(new ClickListener() {
       public void onClick(Widget sender) {
         int selectedIndex = existingCookiesBox.getSelectedIndex();
-        if (selectedIndex > -1 && selectedIndex < existingCookiesBox.getItemCount()) {
+        if (selectedIndex > -1
+            && selectedIndex < existingCookiesBox.getItemCount()) {
           String cookieName = existingCookiesBox.getValue(selectedIndex);
           Cookies.removeCookie(cookieName);
           existingCookiesBox.removeItem(selectedIndex);
@@ -189,6 +193,20 @@
     return mainLayout;
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Refresh the list of existing cookies.
    * 
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwFrame.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwFrame.java
index b46826f..b6dbcb3 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwFrame.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwFrame.java
@@ -16,10 +16,12 @@
 package com.google.gwt.sample.showcase.client.content.other;
 
 import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.ClickListener;
 import com.google.gwt.user.client.ui.Frame;
@@ -71,7 +73,7 @@
   public String getName() {
     return constants.cwFrameName();
   }
-  
+
   @Override
   public boolean hasStyle() {
     return false;
@@ -121,4 +123,18 @@
     vPanel.add(frame);
     return vPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwAbsolutePanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwAbsolutePanel.java
index 26f55e4..3342df2 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwAbsolutePanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwAbsolutePanel.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.sample.showcase.client.content.panels;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.Showcase;
@@ -22,6 +24,7 @@
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.DeferredCommand;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.AbsolutePanel;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.ChangeListener;
@@ -190,6 +193,20 @@
     });
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Create an options panel that allows users to select a widget and reposition
    * it.
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDecoratorPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDecoratorPanel.java
index efcdaf1..71bb6ee 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDecoratorPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDecoratorPanel.java
@@ -15,11 +15,14 @@
  */
 package com.google.gwt.sample.showcase.client.content.panels;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.DecoratorPanel;
 import com.google.gwt.user.client.ui.FlexTable;
 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
@@ -30,7 +33,9 @@
 /**
  * Example file.
  */
-@ShowcaseStyle({".gwt-DecoratorPanel", "html>body .gwt-DecoratorPanel", "* html .gwt-DecoratorPanel"})
+@ShowcaseStyle({
+    ".gwt-DecoratorPanel", "html>body .gwt-DecoratorPanel",
+    "* html .gwt-DecoratorPanel"})
 public class CwDecoratorPanel extends ContentWidget {
   /**
    * The constants used in this Content Widget.
@@ -108,4 +113,18 @@
     decPanel.setWidget(layout);
     return decPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDisclosurePanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDisclosurePanel.java
index 0f89957..4250a27 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDisclosurePanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDisclosurePanel.java
@@ -15,11 +15,14 @@
  */
 package com.google.gwt.sample.showcase.client.content.panels;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.DecoratorPanel;
 import com.google.gwt.user.client.ui.DisclosurePanel;
 import com.google.gwt.user.client.ui.FlexTable;
@@ -103,6 +106,20 @@
     return vPanel;
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Create a form that contains undisclosed advanced options.
    */
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDockPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDockPanel.java
index b2f5ba1..60677f0 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDockPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDockPanel.java
@@ -15,11 +15,14 @@
  */
 package com.google.gwt.sample.showcase.client.content.panels;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.DockPanel;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.ScrollPanel;
@@ -112,4 +115,18 @@
     dock.ensureDebugId("cwDockPanel");
     return dock;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwFlowPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwFlowPanel.java
index 84b17fa..dd19fa5 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwFlowPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwFlowPanel.java
@@ -15,11 +15,14 @@
  */
 package com.google.gwt.sample.showcase.client.content.panels;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.CheckBox;
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.Widget;
@@ -88,4 +91,18 @@
     // Return the content
     return flowPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalPanel.java
index c6abae3..2f49674 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalPanel.java
@@ -15,10 +15,13 @@
  */
 package com.google.gwt.sample.showcase.client.content.panels;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.HorizontalPanel;
 import com.google.gwt.user.client.ui.Widget;
@@ -90,4 +93,18 @@
     hPanel.ensureDebugId("cwHorizontalPanel");
     return hPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalSplitPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalSplitPanel.java
index 1fc379b..b989453 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalSplitPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalSplitPanel.java
@@ -15,11 +15,14 @@
  */
 package com.google.gwt.sample.showcase.client.content.panels;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.DecoratorPanel;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.HorizontalSplitPanel;
@@ -96,4 +99,18 @@
     // Return the content
     return decPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java
index 356e88b..5c96d24 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.sample.showcase.client.content.panels;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.Showcase;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.DecoratedTabPanel;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.VerticalPanel;
@@ -29,7 +32,8 @@
 /**
  * Example file.
  */
-@ShowcaseStyle({".gwt-DecoratedTabBar", "html>body .gwt-DecoratedTabBar",
+@ShowcaseStyle({
+    ".gwt-DecoratedTabBar", "html>body .gwt-DecoratedTabBar",
     "* html .gwt-DecoratedTabBar", ".gwt-TabPanel"})
 public class CwTabPanel extends ContentWidget {
   /**
@@ -105,4 +109,18 @@
     tabPanel.ensureDebugId("cwTabPanel");
     return tabPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalPanel.java
index dd9ea5f..0ecd7db 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalPanel.java
@@ -15,10 +15,13 @@
  */
 package com.google.gwt.sample.showcase.client.content.panels;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.VerticalPanel;
 import com.google.gwt.user.client.ui.Widget;
@@ -90,4 +93,18 @@
     vPanel.ensureDebugId("cwVerticalPanel");
     return vPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalSplitPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalSplitPanel.java
index d7d0198..0f60296 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalSplitPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalSplitPanel.java
@@ -15,11 +15,14 @@
  */
 package com.google.gwt.sample.showcase.client.content.panels;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.DecoratorPanel;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.VerticalSplitPanel;
@@ -96,4 +99,18 @@
     // Return the content
     return decPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwBasicPopup.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwBasicPopup.java
index f9d0df9..91e5c5c 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwBasicPopup.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwBasicPopup.java
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.sample.showcase.client.content.popups;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.Showcase;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.ClickListener;
 import com.google.gwt.user.client.ui.DecoratedPopupPanel;
@@ -140,4 +143,18 @@
     // Return the panel
     return vPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwDialogBox.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwDialogBox.java
index 581d88a..9d06e55 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwDialogBox.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwDialogBox.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.sample.showcase.client.content.popups;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.i18n.client.LocaleInfo;
 import com.google.gwt.sample.showcase.client.ContentWidget;
@@ -22,6 +24,7 @@
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.ClickListener;
 import com.google.gwt.user.client.ui.DialogBox;
@@ -35,8 +38,9 @@
 /**
  * Example file.
  */
-@ShowcaseStyle({".gwt-DialogBox", "html>body .gwt-DialogBox",
-    "* html .gwt-DialogBox", ".cw-DialogBox"})
+@ShowcaseStyle({
+    ".gwt-DialogBox", "html>body .gwt-DialogBox", "* html .gwt-DialogBox",
+    ".cw-DialogBox"})
 public class CwDialogBox extends ContentWidget {
   /**
    * The constants used in this Content Widget.
@@ -129,6 +133,20 @@
     return vPanel;
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Create the dialog box for this example.
    * 
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwFlexTable.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwFlexTable.java
index ed969a1..1e41a7c 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwFlexTable.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwFlexTable.java
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.sample.showcase.client.content.tables;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.Showcase;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.ClickListener;
 import com.google.gwt.user.client.ui.FlexTable;
@@ -127,6 +130,20 @@
     return flexTable;
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Add a row to the flex table.
    */
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwGrid.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwGrid.java
index 85df366..2f0d1d8 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwGrid.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwGrid.java
@@ -15,11 +15,14 @@
  */
 package com.google.gwt.sample.showcase.client.content.tables;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.Showcase;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Grid;
 import com.google.gwt.user.client.ui.Widget;
 
@@ -91,4 +94,18 @@
     grid.ensureDebugId("cwGrid");
     return grid;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwBasicText.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwBasicText.java
index dd58750..f2f9d3d 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwBasicText.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwBasicText.java
@@ -15,11 +15,14 @@
  */
 package com.google.gwt.sample.showcase.client.content.text;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.ClickListener;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.HorizontalPanel;
@@ -126,6 +129,20 @@
     return vpanel;
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Create a TextBox example that includes the text box and an optional
    * listener that updates a Label with the currently selected text.
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwRichText.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwRichText.java
index f582285..2e15e28 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwRichText.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwRichText.java
@@ -15,11 +15,14 @@
  */
 package com.google.gwt.sample.showcase.client.content.text;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Grid;
 import com.google.gwt.user.client.ui.RichTextArea;
 import com.google.gwt.user.client.ui.Widget;
@@ -27,7 +30,8 @@
 /**
  * Example file.
  */
-@ShowcaseStyle({".gwt-RichTextArea", ".hasRichTextToolbar", ".gwt-RichTextToolbar",
+@ShowcaseStyle({
+    ".gwt-RichTextArea", ".hasRichTextToolbar", ".gwt-RichTextToolbar",
     ".cw-RichText"})
 public class CwRichText extends ContentWidget {
   /**
@@ -88,4 +92,18 @@
     grid.setWidget(1, 0, area);
     return grid;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwBasicButton.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwBasicButton.java
index 8e38535..e90f6ef 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwBasicButton.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwBasicButton.java
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.sample.showcase.client.content.widgets;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
 import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.ClickListener;
 import com.google.gwt.user.client.ui.HorizontalPanel;
@@ -103,4 +106,18 @@
     // Return the panel
     return hPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCheckBox.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCheckBox.java
index ac3813f..1322790 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCheckBox.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCheckBox.java
@@ -20,6 +20,7 @@
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.CheckBox;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.VerticalPanel;
@@ -101,4 +102,12 @@
     // Return the panel of checkboxes
     return vPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    /*
+     * CheckBox is the first demo loaded, so go ahead and load it synchronously.
+     */
+    callback.onSuccess(onInitialize());
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCustomButton.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCustomButton.java
index 60035ec..472d5de 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCustomButton.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCustomButton.java
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.sample.showcase.client.content.widgets;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.Showcase;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.HorizontalPanel;
 import com.google.gwt.user.client.ui.PushButton;
@@ -122,4 +125,18 @@
     // Return the panel
     return vpanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwFileUpload.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwFileUpload.java
index cbf855a..6760125 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwFileUpload.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwFileUpload.java
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.sample.showcase.client.content.widgets;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
 import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.ClickListener;
 import com.google.gwt.user.client.ui.FileUpload;
@@ -113,4 +116,18 @@
     // Return the layout panel
     return vPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwHyperlink.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwHyperlink.java
index 5999895..2a9abfe 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwHyperlink.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwHyperlink.java
@@ -15,12 +15,15 @@
  */
 package com.google.gwt.sample.showcase.client.content.widgets;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseConstants;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.Hyperlink;
 import com.google.gwt.user.client.ui.VerticalPanel;
@@ -96,6 +99,20 @@
     return vPanel;
   }
 
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
+
   /**
    * Get a {@link Hyperlink} to a section based on the name of the
    * {@link ContentWidget} example.
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwRadioButton.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwRadioButton.java
index a96d33c..dd5f1b8 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwRadioButton.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwRadioButton.java
@@ -15,11 +15,14 @@
  */
 package com.google.gwt.sample.showcase.client.content.widgets;
 
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
 import com.google.gwt.i18n.client.Constants;
 import com.google.gwt.sample.showcase.client.ContentWidget;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
 import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.RadioButton;
 import com.google.gwt.user.client.ui.VerticalPanel;
@@ -115,4 +118,18 @@
 
     return vPanel;
   }
+
+  @Override
+  protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+    GWT.runAsync(new RunAsyncCallback() {
+
+      public void onFailure(Throwable caught) {
+        callback.onFailure(caught);
+      }
+
+      public void onSuccess() {
+        callback.onSuccess(onInitialize());
+      }
+    });
+  }
 }
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/generator/ShowcaseGenerator.java b/samples/showcase/src/com/google/gwt/sample/showcase/generator/ShowcaseGenerator.java
index 3d6bb38..2d5b079 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/generator/ShowcaseGenerator.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/generator/ShowcaseGenerator.java
@@ -71,7 +71,7 @@
       String typeName) throws UnableToCompleteException {
     this.logger = logger;
     this.context = context;
-    this.classLoader = getClass().getClassLoader();
+    this.classLoader = Thread.currentThread().getContextClassLoader();
 
     // Only generate files on the first permutation
     if (!isFirstPass()) {
diff --git a/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java b/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java
new file mode 100644
index 0000000..448472e
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java
@@ -0,0 +1,91 @@
+/*
+ * 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.core.client;
+
+/**
+ * <p>
+ * Low-level support to download an extra fragment of code. This should not be
+ * invoked directly by user code.
+ * </p>
+ * 
+ * <p>
+ * Different linkers have different requirements about how the code is
+ * downloaded and installed. Thus, when it is time to actually download the
+ * code, this class defers to a JavaScript function named
+ * <code>__gwtStartLoadingFragment</code>. Linkers must arrange that a
+ * suitable <code>__gwtStartLoadingFragment</code> function is in scope.
+ */
+public class AsyncFragmentLoader {
+  /**
+   * Inform the loader that the code for an entry point has now finished
+   * loading.
+   * 
+   * @param entry The entry whose code fragment is now loaded.
+   */
+  public static void fragmentHasLoaded(int entry) {
+    // There is nothing to do with the current fragmentation strategy
+  }
+
+  /**
+   * Loads the specified fragment asynchronously.
+   * 
+   * @param fragment the fragment to load
+   */
+  public static void inject(int fragment) {
+    logEventProgress("download" + fragment, "begin");
+    startLoadingFragment(fragment);
+  }
+
+  /**
+   * Logs an event with the GWT lightweight metrics framework.
+   */
+  public static void logEventProgress(String eventGroup, String type) {
+    @SuppressWarnings("unused")
+    boolean toss = isStatsAvailable()
+        && stats(createStatsEvent(eventGroup, type));
+  }
+
+  /**
+   * Create an event object suitable for submitting to the lightweight metrics
+   * framework.
+   */
+  private static native JavaScriptObject createStatsEvent(String eventGroup,
+      String type) /*-{
+    return {
+      moduleName: @com.google.gwt.core.client.GWT::getModuleName()(), 
+      subSystem: 'runAsync',
+      evtGroup: eventGroup,
+      millis: (new Date()).getTime(),
+      type: type
+    };
+  }-*/;
+
+  private static native boolean isStatsAvailable() /*-{
+    return !!$stats;
+  }-*/;
+
+  private static native void startLoadingFragment(int fragment) /*-{
+    __gwtStartLoadingFragment(fragment);
+  }-*/;
+
+  /**
+   * Always use this as {@link isStatsAvailable} &amp;&amp;
+   * {@link #stats(JavaScriptObject)}.
+   */
+  private static native boolean stats(JavaScriptObject data) /*-{
+    return $stats(data);
+  }-*/;
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/core/client/GWT.java b/user/src/com/google/gwt/core/client/GWT.java
index 9aa0335..1184d9e 100644
--- a/user/src/com/google/gwt/core/client/GWT.java
+++ b/user/src/com/google/gwt/core/client/GWT.java
@@ -184,11 +184,13 @@
   }
 
   /**
-   * Run the supplied callback. The system is allowed to delay before running
-   * the callback while additional code is downloaded, but that feature is not
-   * yet implemented. Currently, the callback runs immediately.
+   * Run the specified callback once the necessary code for it has been loaded.
    */
   public static void runAsync(RunAsyncCallback callback) {
+    /*
+     * By default, just call the callback. This allows using <code>runAsync</code>
+     * in code that might or might not run in a web browser.
+     */
     callback.onSuccess();
   }