A few runAsync-related code size improvements.

1) Removes the clinit, AsyncLoader__Supers, and loading fields from generated AsyncLoaders.  PRETTY mode Showcase's initial fragment drops from 500k to 480k.  Probably less impressive in OBF.  A tiny improvement within the split fragments, too.

2) Removes the last couple uses of JRE collections from AsyncFragmentLoader in favor of arrays.

3) Removes a missed logEventProgress Integer -> int conversion that was begun in an earlier commit.

http://gwt-code-reviews.appspot.com/159811
Review by: spoon


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7732 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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
index adc86db..da27c6d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java
@@ -26,7 +26,6 @@
 import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor;
 
 import java.io.PrintWriter;
-import java.util.List;
 
 /**
  * Generates code for loading an island. The pattern of generated classes is
@@ -40,12 +39,12 @@
   public static final String ASYNC_FRAGMENT_LOADER = "com.google.gwt.core.client.impl.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 BROWSER_LOADER = "AsyncFragmentLoader.BROWSER_LOADER";
   public static final String LOADER_METHOD_RUN_ASYNC = "runAsync";
   public static final String RUN_ASYNC_CALLBACK = "com.google.gwt.core.client.RunAsyncCallback";
   private static final String GWT_CLASS = FindDeferredBindingSitesVisitor.MAGIC_CLASS;
   private static final String PROP_RUN_ASYNC_NEVER_RUNS = "gwt.jjs.runAsyncNeverRuns";
-  private static final String UNCAUGHT_EXCEPTION_HANDLER_CLASS = GWT_CLASS
-      + ".UncaughtExceptionHandler";
+  private static final String UNCAUGHT_EXCEPTION_HANDLER_CLASS = "GWT.UncaughtExceptionHandler";
 
   private final StandardGeneratorContext context;
   private int entryNumber = 0;
@@ -75,7 +74,6 @@
     }
 
     generateLoaderFields(loaderWriter);
-    generateOnErrorMethod(loaderWriter);
     generateOnLoadMethod(loaderWriter);
     generateRunAsyncMethod(loaderWriter);
     generateRunCallbacksMethod(loaderWriter);
@@ -86,22 +84,11 @@
     context.commit(logger, loaderWriter);
 
     writeCallbackListClass(logger, context);
-    writeLoaderSuperclass(logger, context);
 
     return getLoaderQualifiedName();
   }
 
   private void generateLoaderFields(PrintWriter srcWriter) {
-    srcWriter.println("// Whether the code for this entry point has loaded");
-    srcWriter.println("private static boolean loaded = false;");
-
-    srcWriter.println("// Whether the code for this entry point is currently loading");
-    srcWriter.println("private static boolean loading = false;");
-
-    srcWriter.println("// A callback caller for this entry point");
-    srcWriter.println("private static " + getLoaderSuperclassSimpleName()
-        + " instance = new " + getLoaderSuperclassSimpleName() + "();");
-
     srcWriter.println("// Callbacks that are pending");
     srcWriter.println("private static " + getCallbackListSimpleName()
         + " callbacksHead = null;");
@@ -109,27 +96,23 @@
     srcWriter.println("// The tail of the callbacks list");
     srcWriter.println("private static " + getCallbackListSimpleName()
         + " callbacksTail = null;");
-  }
 
-  private void generateOnErrorMethod(PrintWriter srcWriter) {
-    srcWriter.println("public static void onError(Throwable e) {");
-    srcWriter.println("loading = false;");
-    srcWriter.println("runCallbackOnFailures(e);");
-    srcWriter.println("}");
+    srcWriter.println("// A callback caller for this entry point");
+    srcWriter.println("private static " + getLoaderSimpleName()
+        + " instance = null;");
   }
 
   private void generateOnLoadMethod(PrintWriter srcWriter) {
     srcWriter.println("public static void onLoad() {");
-    srcWriter.println("loaded = true;");
     srcWriter.println("instance = new " + getLoaderSimpleName() + "();");
-    srcWriter.println(ASYNC_FRAGMENT_LOADER + ".BROWSER_LOADER.fragmentHasLoaded("
-        + entryNumber + ");");
+    srcWriter.println(BROWSER_LOADER + ".fragmentHasLoaded(" + entryNumber
+        + ");");
 
-    srcWriter.println(ASYNC_FRAGMENT_LOADER
-        + ".BROWSER_LOADER.logEventProgress(\"runCallbacks" + entryNumber + "\", \"begin\");");
+    srcWriter.println(BROWSER_LOADER + ".logEventProgress(\"runCallbacks"
+        + entryNumber + "\", \"begin\");");
     srcWriter.println("instance.runCallbacks();");
-    srcWriter.println(ASYNC_FRAGMENT_LOADER
-        + ".BROWSER_LOADER.logEventProgress(\"runCallbacks" + entryNumber + "\", \"end\");");
+    srcWriter.println(BROWSER_LOADER + ".logEventProgress(\"runCallbacks"
+        + entryNumber + "\", \"end\");");
 
     srcWriter.println("}");
   }
@@ -154,16 +137,15 @@
     srcWriter.println("  callbacksHead = newCallback;");
     srcWriter.println("}");
 
-    srcWriter.println("if (loaded) {");
-    srcWriter.println("instance.runCallbacks();");
-    srcWriter.println("return;");
+    srcWriter.println("if (instance != null) {");
+    srcWriter.println("  instance.runCallbacks();");
+    srcWriter.println("  return;");
     srcWriter.println("}");
-    srcWriter.println("if (!loading) {");
-    srcWriter.println("loading = true;");
-    srcWriter.println("AsyncFragmentLoader.BROWSER_LOADER.inject(" + entryNumber + ",");
+    srcWriter.println("if (!" + BROWSER_LOADER + ".isLoading(" + entryNumber
+        + ")) {");
+    srcWriter.println("  " + BROWSER_LOADER + ".inject(" + entryNumber + ",");
     srcWriter.println("  new AsyncFragmentLoader.LoadErrorHandler() {");
     srcWriter.println("    public void loadFailed(Throwable reason) {");
-    srcWriter.println("      loading = false;");
     srcWriter.println("      runCallbackOnFailures(reason);");
     srcWriter.println("    }");
     srcWriter.println("  });");
@@ -174,8 +156,8 @@
   private void generateRunCallbackOnFailuresMethod(PrintWriter srcWriter) {
     srcWriter.println("private static void runCallbackOnFailures(Throwable e) {");
     srcWriter.println("while (callbacksHead != null) {");
-    srcWriter.println("callbacksHead.callback.onFailure(e);");
-    srcWriter.println("callbacksHead = callbacksHead.next;");
+    srcWriter.println("  callbacksHead.callback.onFailure(e);");
+    srcWriter.println("  callbacksHead = callbacksHead.next;");
     srcWriter.println("}");
     srcWriter.println("callbacksTail = null;");
     srcWriter.println("}");
@@ -187,8 +169,7 @@
     srcWriter.println("while (callbacksHead != null) {");
 
     srcWriter.println("  " + UNCAUGHT_EXCEPTION_HANDLER_CLASS + " handler = "
-        + FindDeferredBindingSitesVisitor.MAGIC_CLASS
-        + ".getUncaughtExceptionHandler();");
+        + "GWT.getUncaughtExceptionHandler();");
 
     srcWriter.println("  " + getCallbackListSimpleName()
         + " next = callbacksHead;");
@@ -231,14 +212,6 @@
     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;
   }
@@ -253,15 +226,12 @@
 
     printWriter.println("package " + getPackage() + ";");
     String[] imports = new String[] {
-        RUN_ASYNC_CALLBACK, List.class.getCanonicalName(),
-        ASYNC_FRAGMENT_LOADER};
+        GWT_CLASS, RUN_ASYNC_CALLBACK, ASYNC_FRAGMENT_LOADER};
     for (String imp : imports) {
       printWriter.println("import " + imp + ";");
     }
 
-    printWriter.println("public class " + getLoaderSimpleName() + " extends "
-        + getLoaderSuperclassSimpleName() + " {");
-
+    printWriter.println("public class " + getLoaderSimpleName() + " {");
     return printWriter;
   }
 
@@ -284,31 +254,4 @@
     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/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java b/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
index 8df3955..89119dc 100644
--- a/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
+++ b/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
@@ -18,10 +18,6 @@
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JavaScriptObject;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
 /**
  * <p>
  * Low-level support to download an extra fragment of code. This should not be
@@ -187,7 +183,8 @@
        * Make a local list of the handlers to run, in case one of them calls
        * another runAsync
        */
-      List<LoadErrorHandler> handlersToRun = new ArrayList<LoadErrorHandler>();
+      LoadErrorHandler[] handlersToRun = pendingDownloadErrorHandlers;
+      pendingDownloadErrorHandlers = new LoadErrorHandler[numEntries + 1];
 
       /*
        * Call clear() here so that requestedExclusives makes all of its space
@@ -195,14 +192,6 @@
        */
       requestedExclusives.clear();
 
-      // add handlers for pending downloads
-      for (LoadErrorHandler handler : pendingDownloadErrorHandlers) {
-        if (handler != null) {
-          handlersToRun.add(handler);
-        }
-      }
-      pendingDownloadErrorHandlers.clear();
-
       fragmentLoading = -1;
 
       /*
@@ -212,10 +201,12 @@
       RuntimeException lastException = null;
 
       for (LoadErrorHandler handler : handlersToRun) {
-        try {
-          handler.loadFailed(reason);
-        } catch (RuntimeException e) {
-          lastException = e;
+        if (handler != null) {
+          try {
+            handler.loadFailed(reason);
+          } catch (RuntimeException e) {
+            lastException = e;
+          }
         }
       }
 
@@ -340,7 +331,7 @@
    * Externally provided handlers for all outstanding and queued download
    * requests.
    */
-  private ArrayList<LoadErrorHandler> pendingDownloadErrorHandlers = new ArrayList<LoadErrorHandler>();
+  private LoadErrorHandler[] pendingDownloadErrorHandlers;
 
   /**
    * Whether prefetching is currently enabled.
@@ -373,8 +364,10 @@
     this.initialLoadSequence = initialLoadSequence;
     this.loadingStrategy = loadingStrategy;
     this.logger = logger;
-    requestedExclusives = new BoundedIntQueue(numEntries + 1);
-    isLoaded = new boolean[numEntries + 1];
+    int numEntriesPlusOne = numEntries + 1;
+    requestedExclusives = new BoundedIntQueue(numEntriesPlusOne);
+    isLoaded = new boolean[numEntriesPlusOne];
+    pendingDownloadErrorHandlers = new LoadErrorHandler[numEntriesPlusOne];
   }
 
   /**
@@ -382,8 +375,8 @@
    */
   public void fragmentHasLoaded(int fragment) {
     logFragmentLoaded(fragment);
-    if (fragment < pendingDownloadErrorHandlers.size()) {
-      pendingDownloadErrorHandlers.set(fragment, null);
+    if (fragment < pendingDownloadErrorHandlers.length) {
+      pendingDownloadErrorHandlers[fragment] = null;
     }
 
     if (isInitial(fragment)) {
@@ -410,8 +403,7 @@
    * @param splitPoint the split point whose code needs to be loaded
    */
   public void inject(int splitPoint, LoadErrorHandler loadErrorHandler) {
-    setCapacity(pendingDownloadErrorHandlers, splitPoint + 1);
-    pendingDownloadErrorHandlers.set(splitPoint, loadErrorHandler);
+    pendingDownloadErrorHandlers[splitPoint] = loadErrorHandler;
     if (!isInitial(splitPoint)) {
       requestedExclusives.add(splitPoint);
     }
@@ -422,6 +414,10 @@
    return isLoaded[splitPoint];
   }
 
+  public boolean isLoading(int splitPoint) {
+    return pendingDownloadErrorHandlers[splitPoint] != null;
+  }
+
   public void leftoversFragmentHasLoaded() {
     fragmentHasLoaded(leftoversFragment());
   }
@@ -439,12 +435,12 @@
    * whenever there is nothing else to download. Each call to this method
    * overwrites the entire prefetch queue with the newly specified one.
    */
-  public void setPrefetchQueue(Collection<? extends Integer> splitPoints) {
+  public void setPrefetchQueue(int... runAsyncSplitPoints) {
     if (prefetchQueue == null) {
       prefetchQueue = new BoundedIntQueue(numEntries);
     }
     prefetchQueue.clear();
-    for (Integer sp : splitPoints) {
+    for (int sp : runAsyncSplitPoints) {
       prefetchQueue.add(sp);
     }
     startLoadingNextFragment();
@@ -472,8 +468,8 @@
     while (requestedExclusives.size() > 0
         && isLoaded[requestedExclusives.peek()]) {
       int offset = requestedExclusives.remove();
-      if (offset < pendingDownloadErrorHandlers.size()) {
-        pendingDownloadErrorHandlers.set(offset, null);
+      if (offset < pendingDownloadErrorHandlers.length) {
+        pendingDownloadErrorHandlers[offset] = null;
       }
     }
 
@@ -512,12 +508,12 @@
   }
 
   /**
-   * Return if the the ArrayList is empty.
-   * @param list the list to check if empty
+   * Returns <code>true</code> if array contains only <code>null</code>
+   * elements.
    */
-  private boolean isEmpty(ArrayList<?> list) {
-    for (int i = 0; i < list.size(); i++) {
-      if (list.get(i) != null) {
+  private boolean isEmpty(Object[] array) {
+    for (int i = 0; i < array.length; i++) {
+      if (array[i] != null) {
         return false;
       }
     }
@@ -549,8 +545,8 @@
    * <code>fragment</code> and <code>size</code> objects are allowed to be
    * <code>null</code>.
    */
-  private void logEventProgress(String eventGroup, String type,
-      Integer fragment, Integer size) {
+  private void logEventProgress(String eventGroup, String type, int fragment,
+      int size) {
     logger.logEventProgress(eventGroup, type, fragment, size);
   }
 
@@ -559,17 +555,6 @@
     logEventProgress(logGroup, LwmLabels.END, fragment, -1);
   }
 
-  /**
-   * Set capacity ArrayList list.
-   * @param list the list to add capacity to.
-   * @param size the new size to increase the capacity to.
-   */
-  private void setCapacity(ArrayList<?> list, int size) {
-    while (list.size() < size) {
-      list.add(null);
-    }
-  }
-
   private void startLoadingFragment(int fragment) {
     assert (fragmentLoading < 0);
     fragmentLoading = fragment;
diff --git a/user/src/com/google/gwt/core/client/prefetch/Prefetcher.java b/user/src/com/google/gwt/core/client/prefetch/Prefetcher.java
index ed17450..f91bb15 100644
--- a/user/src/com/google/gwt/core/client/prefetch/Prefetcher.java
+++ b/user/src/com/google/gwt/core/client/prefetch/Prefetcher.java
@@ -18,9 +18,7 @@
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.impl.AsyncFragmentLoader;
 
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 
 /**
  * This class allows requesting the download of resources before they are
@@ -37,14 +35,15 @@
       return;
     }
 
-    List<Integer> runAsyncSplitPoints = new ArrayList<Integer>();
-
+    // No range checking in web mode means we needn't precompute the size.
+    int[] runAsyncSplitPoints = new int[0];
+    int i = 0;
     for (PrefetchableResource resource : resources) {
       if (resource instanceof RunAsyncCode) {
         RunAsyncCode resourceRunAsync = (RunAsyncCode) resource;
         int splitPoint = resourceRunAsync.getSplitPoint();
         if (splitPoint >= 0) { // Skip placeholders, which have a -1 split point
-          runAsyncSplitPoints.add(splitPoint);
+          runAsyncSplitPoints[i++] = splitPoint;
         }
         continue;
       }
diff --git a/user/test/com/google/gwt/core/client/impl/AsyncFragmentLoaderTest.java b/user/test/com/google/gwt/core/client/impl/AsyncFragmentLoaderTest.java
index 83eeee4..b14c3c2 100644
--- a/user/test/com/google/gwt/core/client/impl/AsyncFragmentLoaderTest.java
+++ b/user/test/com/google/gwt/core/client/impl/AsyncFragmentLoaderTest.java
@@ -21,8 +21,6 @@
 
 import junit.framework.TestCase;
 
-import static java.util.Arrays.asList;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -454,7 +452,7 @@
         1, 2, 3}, reqs, progress);
     loader.startPrefetching();
     // request a prefetch of something in the initial load sequence
-    loader.setPrefetchQueue(asList(2));
+    loader.setPrefetchQueue(2);
     reqs.assertFragmentsRequested(1);
     progress.assertEvent("download1", BEGIN, 1);
 
@@ -468,7 +466,7 @@
     progress.assertEvent("download2", END, 2);
     progress.assertNoEvents();
     // request a prefetch of an exclusive
-    loader.setPrefetchQueue(asList(4));
+    loader.setPrefetchQueue(4);
     reqs.assertFragmentsRequested(3);
     progress.assertEvent("download3", BEGIN, 3);
 
@@ -487,7 +485,7 @@
     progress.assertEvent("download4", END, 4);
     progress.assertNoEvents();
     // request a prefetch, but check that an inject call takes priority
-    loader.setPrefetchQueue(asList(5,6));
+    loader.setPrefetchQueue(5, 6);
     reqs.assertFragmentsRequested(5);
     progress.assertEvent("download5", BEGIN, 5);
 
@@ -510,10 +508,10 @@
     progress.assertEvent("download6", END, 6);
     progress.assertNoEvents();
     // request prefetches, then request different prefetches
-    loader.setPrefetchQueue(asList(8,9));
+    loader.setPrefetchQueue(8, 9);
     reqs.assertFragmentsRequested(8);
     progress.assertEvent("download8", BEGIN, 8);
-    loader.setPrefetchQueue(asList(10));
+    loader.setPrefetchQueue(10);
     reqs.assertFragmentsRequested();
     progress.assertNoEvents();
 
@@ -527,7 +525,7 @@
     progress.assertEvent("download10", END, 10);
     progress.assertNoEvents();
     // request prefetches that have already been loaded
-    loader.setPrefetchQueue(asList(1, 3, 7, 10));
+    loader.setPrefetchQueue(1, 3, 7, 10);
     reqs.assertFragmentsRequested();
     progress.assertNoEvents();
   }
@@ -543,7 +541,7 @@
         1, 2, 3}, reqs, progress);
     loader.startPrefetching();
     // request a prefetch of something in the initial load sequence
-    loader.setPrefetchQueue(asList(3, 2, 1));
+    loader.setPrefetchQueue(3, 2, 1);
     reqs.assertFragmentsRequested(1);
     progress.assertEvent("download1", BEGIN, 1);
 
@@ -589,7 +587,7 @@
         new int[] {}, reqs, progress);
     loader.stopPrefetching();
     // Prefetch 1, but leave prefetching off
-    loader.setPrefetchQueue(asList(1));
+    loader.setPrefetchQueue(1);
     reqs.assertFragmentsRequested();
     progress.assertNoEvents();
 
@@ -646,7 +644,7 @@
     progress.assertEvent("download1", END, 1);
     progress.assertNoEvents();
     // Start prefetching a fragment
-    loader.setPrefetchQueue(asList(2));
+    loader.setPrefetchQueue(2);
     reqs.assertFragmentsRequested(2);
     progress.assertEvent("download2", BEGIN, 2);