Author: [email protected]
Date: Mon May 11 07:59:01 2009
New Revision: 5330

Added:
     
branches/snapshot-2009.04.28-r5307/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java
       - copied, changed from r5295,  
/trunk/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java
Removed:
     
branches/snapshot-2009.04.28-r5307/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
Modified:
     
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
     
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/Link.java
     
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
     
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
     
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
     
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
     
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java

Log:
Reverting the code-splitting changes (r5296 and r5293) from the snapshot,
due to a critical bug in them.  Commands:

svn merge -r 5296:5295 https://google-web-toolkit.googlecode.com/svn/trunk .
svn merge -r 5293:5292 https://google-web-toolkit.googlecode.com/svn/trunk .
    (had a minor conflict in CodeSplitter.java)



Modified:  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
==============================================================================
---  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
    
(original)
+++  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
    
Mon May 11 07:59:01 2009
@@ -35,7 +35,7 @@
     * 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}. See {...@link  
com.google.gwt.core.client.impl.AsyncFragmentLoader
+   * GWT.runAsync}. See {...@link  
com.google.gwt.core.client.AsyncFragmentLoader
     * AsyncFragmentLoader} for details on the necessary linker support for
     * runAsync.
     */

Modified:  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/Link.java
==============================================================================
---  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/Link.java    
 
(original)
+++  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/Link.java    
 
Mon May 11 07:59:01 2009
@@ -248,23 +248,62 @@
    }

    /**
-   * Logs the total script size for this permutation, as calculated by
-   * {...@link CodeSplitter#totalScriptSize(int[])}.
+   * <p>
+   * Computes and logs the "maximum total script size" for this  
permutation. The
+   * total script size for one sequence of split points reached is the sum  
of
+   * the scripts that are downloaded for that sequence. The maximum total  
script
+   * size is the maximum such size for all possible sequences of split  
points.
+   * </p>
     */
    private static void logScriptSize(TreeLogger logger, int permId,
        StandardCompilationResult compilation) {
+    /*
+     * The total script size is fully determined by the first split point  
that
+     * is reached; the order that the remaining are reached doesn't  
matter. To
+     * find the maximum, divide the sum into two parts: first add the  
initial
+     * and exclusive fragments, and then calculate the adjustment that  
should be
+     * applied depending on which split point comes first. Choose among  
these
+     * adjustments the one that is largest.
+     */
+
      String[] javaScript = compilation.getJavaScript();
+    int numSplitPoints =  
CodeSplitter.numSplitPointsForFragments(javaScript.length);
+    int maxTotalSize;

-    int[] jsLengths = new int[javaScript.length];
-    for (int i = 0; i < javaScript.length; i++) {
-      jsLengths[i] = javaScript[i].length();
-    }
+    if (numSplitPoints == 0) {
+      maxTotalSize = javaScript[0].length();
+    } else {
+      // Add up the initial and exclusive fragments
+      maxTotalSize = javaScript[0].length();
+      for (int sp = 1; sp <= numSplitPoints; sp++) {
+        int excl = CodeSplitter.getExclusiveFragmentNumber(sp,  
numSplitPoints);
+        maxTotalSize += javaScript[excl].length();
+      }
+
+      // Find the largest adjustment for any split point
+      boolean first = true;
+      int adjustment = 0;

-    int totalSize = CodeSplitter.totalScriptSize(jsLengths);
+      for (int sp = 1; sp <= numSplitPoints; sp++) {
+        int excl = CodeSplitter.getExclusiveFragmentNumber(sp,  
numSplitPoints);
+        int base = CodeSplitter.getBaseFragmentNumber(sp, numSplitPoints);
+        int leftovers = CodeSplitter.getLeftoversFragmentNumber(sp,
+            numSplitPoints);
+        int thisAdjustment = javaScript[base].length()
+            + javaScript[leftovers].length() - javaScript[excl].length();
+        if (first || (thisAdjustment > adjustment)) {
+          adjustment = thisAdjustment;
+        }
+        first = false;
+      }
+
+      maxTotalSize += adjustment;
+    }

      logger.log(TreeLogger.TRACE, "Permutation " + permId + " (strong name "
          + compilation.getStrongName() + ") has an initial download size  
of "
-        + javaScript[0].length() + " and total script size of " +  
totalSize);
+        + javaScript[0].length() + " and max total script size of "
+        + maxTotalSize);
    }

    private final LinkOptionsImpl options;

Modified:  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
==============================================================================
---  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
     
(original)
+++  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
     
Mon May 11 07:59:01 2009
@@ -117,7 +117,6 @@
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.HashSet;
-import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
@@ -188,12 +187,6 @@

        // (4) Optimize the normalized Java AST for each permutation.
        optimize(options, jprogram);
-
-      // (4.5) Choose an initial load order sequence for runAsync's
-      LinkedHashSet<Integer> initialLoadSequence = new  
LinkedHashSet<Integer>();
-      if (options.isAggressivelyOptimize() && options.isRunAsyncEnabled())  
{
-        initialLoadSequence = CodeSplitter.pickInitialLoadSequence(logger,  
jprogram);
-      }

        // (5) "Normalize" the high-level Java tree into a lower-level tree  
more
        // suited for JavaScript code generation. Don't go reordering these
@@ -267,8 +260,7 @@

        // (10.5) Split up the program into fragments
        if (options.isAggressivelyOptimize() && options.isRunAsyncEnabled())  
{
-        CodeSplitter.exec(logger, jprogram, jsProgram,  
postStringInterningMap,
-            initialLoadSequence);
+        CodeSplitter.exec(logger, jprogram, jsProgram,  
postStringInterningMap);
        }

        // (11) Perform any post-obfuscation normalizations.

Modified:  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
==============================================================================
---  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
         
(original)
+++  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
         
Mon May 11 07:59:01 2009
@@ -71,7 +71,7 @@
            "com.google.gwt.core.client.JavaScriptObject",
            "com.google.gwt.lang.ClassLiteralHolder",
            "com.google.gwt.core.client.RunAsyncCallback",
-          "com.google.gwt.core.client.impl.AsyncFragmentLoader",
+          "com.google.gwt.core.client.AsyncFragmentLoader",
            "com.google.gwt.lang.EntryMethodHolder",}));

    static final Map<String, Set<String>> traceMethods = new HashMap<String,  
Set<String>>();

Modified:  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
==============================================================================
---  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
    
(original)
+++  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
    
Mon May 11 07:59:01 2009
@@ -16,16 +16,13 @@
  package com.google.gwt.dev.jjs.impl;

  import com.google.gwt.core.ext.TreeLogger;
-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.JDeclaredType;
  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.JNewArray;
  import com.google.gwt.dev.jjs.ast.JNode;
-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;
@@ -43,18 +40,15 @@
  import com.google.gwt.dev.js.ast.JsVars;
  import com.google.gwt.dev.js.ast.JsVars.JsVar;
  import com.google.gwt.dev.util.PerfLogger;
-import com.google.gwt.dev.util.collect.HashMap;
-import com.google.gwt.dev.util.collect.HashSet;

  import java.util.ArrayList;
-import java.util.LinkedHashSet;
+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;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;

  /**
   * <p>
@@ -63,37 +57,35 @@
   * 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.impl.AsyncFragmentLoader#inject(int)}.
+ * {...@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 an initial fragment, a  
leftovers
- * fragment, and one fragment per split point. Additionally, the splitter
- * computes an initial load sequence. All runAsync calls in the initial  
load
- * sequence are reached before any call not in the sequence. Further, any  
call
- * in the sequence is reached before any call later in the sequence.
+ * current fragmentation strategy is to create an initial fragment and then
+ * three more fragments for each split point. For each split point, there  
is:
   * </p>
   *
- * <p>
- * The fragment for a split point contains different things depending on  
whether
- * it is in the initial load sequence or not. If it's in the initial load
- * sequence, then the fragment includes the code newly live once that split
- * point is crossed, that wasn't already live for the set of split points
- * earlier in the sequence. For a split point not in the initial load  
sequence,
- * the fragment contains only code exclusive to that split point, that is,  
code
- * that cannot be reached except via that split point. All other code goes  
into
- * the leftovers fragment.
- * </p>
+ * <ul>
+ * <li>a secondary base fragment, which is downloaded if this split point  
is the
+ * first one reached. It contains enough code to continue running as soon  
as it
+ * downloads.
+ * <li>an exclusively live fragment, which is downloaded if this split  
point is
+ * reached but is not the first one. It includes only that code that is
+ * exclusively needed by this split point.
+ * <li>a leftovers fragment, which includes all code that is in none of:  
the
+ * initial download, any exclusive fragment, or the secondary base  
fragment for
+ * this split point.
+ * </ul>
   */
  public class CodeSplitter {
    /**
     * A statement logger that immediately prints out everything live that it
     * sees.
     */
-  private class EchoStatementLogger implements StatementLogger {
+  public class EchoStatementLogger implements StatementLogger {
      public void logStatement(JsStatement stat, boolean isIncluded) {
        if (isIncluded) {
          if (stat instanceof JsExprStmt) {
@@ -127,13 +119,10 @@
    }

    /**
-   * A map from program atoms to the split point, if any, that they are
-   * exclusive to. Atoms not exclusive to any split point are either  
mapped to 0
-   * or left out of the map entirely. Note that the map is incomplete; any  
entry
-   * not included has not been proven to be exclusive. Also, note that the
-   * initial load sequence is assumed to already be loaded.
+   * A map from program atoms to the fragment they should be placed in. An  
entry
+   * of 0 means it did not go into any fragment in particular.
     */
-  private static class ExclusivityMap {
+  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>();
@@ -141,15 +130,16 @@
    }

    /**
-   * A liveness predicate that is based on an exclusivity map.
+   * 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 ExclusivityMapLivenessPredicate implements
+  private static class FragmentMapLivenessPredicate implements
        LivenessPredicate {
      private final int fragment;
-    private final ExclusivityMap fragmentMap;
+    private final FragmentMap fragmentMap;

-    public ExclusivityMapLivenessPredicate(ExclusivityMap fragmentMap,
-        int fragment) {
+    public FragmentMapLivenessPredicate(FragmentMap fragmentMap, int  
fragment) {
        this.fragmentMap = fragmentMap;
        this.fragment = fragment;
      }
@@ -185,11 +175,46 @@
      }
    }

-  private static final Pattern LOADER_CLASS_PATTERN =  
Pattern.compile(FragmentLoaderCreator.ASYNC_LOADER_PACKAGE
-      + "." + FragmentLoaderCreator.ASYNC_LOADER_CLASS_PREFIX  
+ "([0-9]+)");
+  /**
+   * A liveness predicate that checks two separate underlying predicates.
+   */
+  private static class UnionLivenessPredicate implements LivenessPredicate  
{
+    private final LivenessPredicate pred1;
+    private final LivenessPredicate pred2;
+
+    public UnionLivenessPredicate(LivenessPredicate pred1,
+        LivenessPredicate pred2) {
+      this.pred1 = pred1;
+      this.pred2 = pred2;
+    }
+
+    public boolean isLive(JField field) {
+      return pred1.isLive(field) || pred2.isLive(field);
+    }
+
+    public boolean isLive(JMethod method) {
+      return pred1.isLive(method) || pred2.isLive(method);
+    }
+
+    public boolean isLive(JReferenceType type) {
+      return pred1.isLive(type) || pred2.isLive(type);
+    }
+
+    public boolean isLive(String literal) {
+      return pred1.isLive(literal) || pred2.isLive(literal);
+    }
+
+    public boolean miscellaneousStatementsAreLive() {
+      return pred1.miscellaneousStatementsAreLive()
+          || pred2.miscellaneousStatementsAreLive();
+    }
+  }

    /**
     * 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";

@@ -204,14 +229,17 @@
    }

    public static void exec(TreeLogger logger, JProgram jprogram,
-      JsProgram jsprogram, JavaToJavaScriptMap map,
-      LinkedHashSet<Integer> initialLoadSequence) {
+      JsProgram jsprogram, JavaToJavaScriptMap map) {
      if (jprogram.entryMethods.size() == 1) {
        // Don't do anything if there is no call to runAsync
        return;
      }

-    new CodeSplitter(logger, jprogram, jsprogram, map,  
initialLoadSequence).execImpl();
+    new CodeSplitter(logger, jprogram, jsprogram, map).execImpl();
+  }
+
+  public static int getBaseFragmentNumber(int sp, int numSplitPoints) {
+    return numSplitPoints + 2 * sp - 1;
    }

    public static int getExclusiveFragmentNumber(int splitPoint,
@@ -219,95 +247,17 @@
      return splitPoint;
    }

-  public static int getLeftoversFragmentNumber(int numSplitPoints) {
-    return numSplitPoints + 1;
+  public static int getLeftoversFragmentNumber(int splitPoint,
+      int numSplitPoints) {
+    return numSplitPoints + 2 * splitPoint;
    }

    /**
     * Infer the number of split points for a given number of code fragments.
     */
    public static int numSplitPointsForFragments(int codeFragments) {
-    assert (codeFragments != 2);
-
-    if (codeFragments == 1) {
-      return 0;
-    }
-
-    return codeFragments - 2;
-  }
-
-  /**
-   * Choose an initial load sequence of split points for the specified  
program.
-   * Do so by identifying split points whose code always load first,  
before any
-   * other split points. As a side effect, modifies
-   * {...@link  
com.google.gwt.core.client.impl.AsyncFragmentLoader#initialLoadSequence}
-   * in the program being compiled.
-   */
-  public static LinkedHashSet<Integer> pickInitialLoadSequence(
-      TreeLogger logger, JProgram program) {
-    LinkedHashSet<Integer> initialLoadSequence = new  
LinkedHashSet<Integer>();
-    int numSplitPoints = program.entryMethods.size() - 1;
-
-    if (numSplitPoints != 0) {
-      Map<Integer, JMethod> splitPointToMethod =  
findRunAsyncMethods(program);
-      assert (splitPointToMethod.size() == numSplitPoints);
-
-      ControlFlowAnalyzer cfa = computeInitiallyLive(program);
-
-      while (true) {
-        Set<Integer> nextSplitPoints = splitPointsReachable(cfa,
-            splitPointToMethod, numSplitPoints);
-        nextSplitPoints.removeAll(initialLoadSequence);
-
-        if (nextSplitPoints.size() != 1) {
-          break;
-        }
-
-        int nextSplitPoint = nextSplitPoints.iterator().next();
-        initialLoadSequence.add(nextSplitPoint);
-        CodeSplitter.traverseEntry(program, cfa, nextSplitPoint);
-      }
-
-      logInitialLoadSequence(logger, initialLoadSequence);
-      installInitialLoadSequenceField(program, initialLoadSequence);
-    }
-
-    return initialLoadSequence;
-  }
-
-  /**
-   * <p>
-   * Computes the "maximum total script size" for one permutation. The  
total
-   * script size for one sequence of split points reached is the sum of the
-   * scripts that are downloaded for that sequence. The maximum total  
script
-   * size is the maximum such size for all possible sequences of split  
points.
-   * </p>
-   *
-   * @param jsLengths The lengths of the fragments for the compilation of  
one
-   *          permutation
-   */
-  public static int totalScriptSize(int[] jsLengths) {
-    /*
-     * The total script size is currently simple: it's the sum of all the
-     * individual script files.
-     */
-
-    int maxTotalSize;
-    int numSplitPoints = numSplitPointsForFragments(jsLengths.length);
-    if (numSplitPoints == 0) {
-      maxTotalSize = jsLengths[0];
-    } else {
-      // Add up the initial and exclusive fragments
-      maxTotalSize = jsLengths[0];
-      for (int sp = 1; sp <= numSplitPoints; sp++) {
-        int excl = getExclusiveFragmentNumber(sp, numSplitPoints);
-        maxTotalSize += jsLengths[excl];
-      }
-
-      // Add the leftovers
-      maxTotalSize +=  
jsLengths[getLeftoversFragmentNumber(numSplitPoints)];
-    }
-    return maxTotalSize;
+    assert (((codeFragments - 1) % 3) == 0);
+    return (codeFragments - 1) / 3;
    }

    private static Map<JField, JClassLiteral> buildFieldToClassLiteralMap(
@@ -323,33 +273,6 @@
      return map;
    }

-  /**
-   * Maps each split point number to its corresponding generated
-   * <code>runAsync</code> method. If that method has been discarded, then  
map
-   * the split point number to <code>null</code>.
-   */
-  private static Map<Integer, JMethod> findRunAsyncMethods(JProgram  
program)
-      throws NumberFormatException {
-    Map<Integer, JMethod> splitPointToLoadMethod = new HashMap<Integer,  
JMethod>();
-    // These methods aren't indexed, so scan the whole program
-
-    for (JDeclaredType type : program.getDeclaredTypes()) {
-      Matcher matcher = LOADER_CLASS_PATTERN.matcher(type.getName());
-      if (matcher.matches()) {
-        int sp = Integer.parseInt(matcher.group(1));
-        JMethod loadMethod = null;
-        for (JMethod meth : type.getMethods()) {
-          if (meth.getName().equals(
-              FragmentLoaderCreator.LOADER_METHOD_RUN_ASYNC)) {
-            loadMethod = meth;
-          }
-        }
-        splitPointToLoadMethod.put(sp, loadMethod);
-      }
-    }
-    return splitPointToLoadMethod;
-  }
-
    private static String fullNameString(JField field) {
      return field.getEnclosingType().getName() + "." + field.getName();
    }
@@ -364,65 +287,6 @@
      return (value == null) ? 0 : value;
    }

-  private static void installInitialLoadSequenceField(JProgram program,
-      LinkedHashSet<Integer> initialLoadSequence) {
-    JField initLoadSeqField =  
program.getIndexedField("AsyncFragmentLoader.initialLoadSequence");
-    SourceInfo info =  
program.createSourceInfoSynthetic(ReplaceRunAsyncs.class,
-        "array with initial load sequence");
-    List<JExpression> intExprs = new ArrayList<JExpression>();
-    for (int sp : initialLoadSequence) {
-      intExprs.add(program.getLiteralInt(sp));
-    }
-    /*
-     * Note: the following field is known to have a manually installed
-     * initializer, of new int[0].
-     */
-    initLoadSeqField.getDeclarationStatement().initializer =  
JNewArray.createInitializers(
-        program, info, program.getTypeArray(JPrimitiveType.INT, 1),  
intExprs);
-  }
-
-  private static void logInitialLoadSequence(TreeLogger logger,
-      LinkedHashSet<Integer> initialLoadSequence) {
-    StringBuffer message = new StringBuffer();
-    message.append("Initial load sequence of split points: ");
-    if (initialLoadSequence.isEmpty()) {
-      message.append("(none)");
-    } else {
-      boolean first = true;
-      for (int sp : initialLoadSequence) {
-        if (first) {
-          first = false;
-        } else {
-          message.append(", ");
-        }
-        message.append(sp);
-      }
-    }
-
-    logger.log(TreeLogger.TRACE, message.toString());
-  }
-
-  /**
-   * Find all split points reachable in the specified ControlFlowAnalyzer.
-   *
-   * @param cfa the control-flow analyzer to search
-   * @param splitPointToMethod a map from split points to methods,  
computed with
-   *          {...@link #findRunAsyncMethods(JProgram)}.
-   * @param numSplitPoints the number of split points in the program
-   */
-  private static Set<Integer> splitPointsReachable(ControlFlowAnalyzer cfa,
-      Map<Integer, JMethod> splitPointToMethod, int numSplitPoints) {
-    Set<Integer> nextSplitPoints = new HashSet<Integer>();
-
-    for (int sp = 1; sp <= numSplitPoints; sp++) {
-      if  
(cfa.getLiveFieldsAndMethods().contains(splitPointToMethod.get(sp))) {
-        nextSplitPoints.add(sp);
-      }
-    }
-
-    return nextSplitPoints;
-  }
-
    /**
     * Traverse all code in the program that is reachable via split point
     * <code>splitPoint</code>.
@@ -467,7 +331,6 @@

    private final Map<JField, JClassLiteral> fieldToLiteralOfClass;
    private final FragmentExtractor fragmentExtractor;
-  private final LinkedHashSet<Integer> initialLoadSequence;

    /**
     * Code that is initially live when the program first downloads.
@@ -475,12 +338,6 @@
    private final ControlFlowAnalyzer initiallyLive;
    private JProgram jprogram;
    private JsProgram jsprogram;
-
-  /**
-   * Computed during {...@link #execImpl()}, so that intermediate steps of it  
can
-   * be used as they are created.
-   */
-  private ControlFlowAnalyzer liveAfterInitialSequence;
    private final TreeLogger logger;
    private final boolean logging;
    private JavaToJavaScriptMap map;
@@ -488,14 +345,12 @@
    private final int numEntries;

    private CodeSplitter(TreeLogger logger, JProgram jprogram,
-      JsProgram jsprogram, JavaToJavaScriptMap map,
-      LinkedHashSet<Integer> initialLoadSequence) {
+      JsProgram jsprogram, JavaToJavaScriptMap map) {
      this.logger = logger.branch(TreeLogger.TRACE,
          "Splitting JavaScript for incremental download");
      this.jprogram = jprogram;
      this.jsprogram = jsprogram;
      this.map = map;
-    this.initialLoadSequence = initialLoadSequence;

      numEntries = jprogram.entryMethods.size();
      logging = Boolean.getBoolean(PROP_LOG_FRAGMENT_MAP);
@@ -508,21 +363,18 @@
    }

    /**
-   * Create a new fragment and add it to the table of fragments.
+   * Create a new fragment and add it to the list.
     *
-   * @param splitPoint The split point to associate this code with
     * @param alreadyLoaded The code that should be assumed to have already  
been
     *          loaded
-   * @param liveNow The code that is assumed live once this fragment loads;
-   *          anything in here but not in <code>alreadyLoaded</code> will  
be
-   *          included in the created fragment
-   * @param stmtsToAppend Additional statements to append to the end of  
the new
+   * @param liveNow The code that needs to be live once this fragment loads
+   * @param statsToAppend Additional statements to append to the end of  
the new
     *          fragment
     * @param fragmentStats The list of fragments to append to
     */
-  private void addFragment(int splitPoint, LivenessPredicate alreadyLoaded,
-      LivenessPredicate liveNow, List<JsStatement> stmtsToAppend,
-      Map<Integer, List<JsStatement>> fragmentStats) {
+  private void addFragment(LivenessPredicate alreadyLoaded,
+      LivenessPredicate liveNow, List<JsStatement> statsToAppend,
+      List<List<JsStatement>> fragmentStats) {
      if (logging) {
        System.out.println();
        System.out.println("==== Fragment " + fragmentStats.size() + "  
====");
@@ -530,26 +382,20 @@
      }
      List<JsStatement> stats = fragmentExtractor.extractStatements(liveNow,
          alreadyLoaded);
-    stats.addAll(stmtsToAppend);
-    fragmentStats.put(splitPoint, stats);
+    stats.addAll(statsToAppend);
+    fragmentStats.add(stats);
    }

    /**
-   * For each split point other than those in the initial load sequence,  
compute
-   * a CFA that traces every other split point. For those that are in the
-   * initial load sequence, add a <code>null</code> to the list.
+   * For each split point other than the initial one (0), compute a CFA  
that
+   * traces every other split point.
     */
    private List<ControlFlowAnalyzer> computeAllButOneCfas() {
      List<ControlFlowAnalyzer> allButOnes = new  
ArrayList<ControlFlowAnalyzer>(
          numEntries - 1);

      for (int entry = 1; entry < numEntries; entry++) {
-      if (isInitial(entry)) {
-        allButOnes.add(null);
-        continue;
-      }
-      ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(
-          liveAfterInitialSequence);
+      ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(initiallyLive);
        traverseAllButEntry(cfa, entry);
        // Traverse leftoversFragmentHasLoaded, because it should not
        // go into any of the exclusive fragments.
@@ -572,23 +418,13 @@
      return everything;
    }

-  /**
-   * Map each program atom as exclusive to some split point, whenever  
possible.
-   * Also fixes up load order problems that could result from splitting  
code
-   * based on this assumption.
-   */
-  private ExclusivityMap determineExclusivity() {
-    ExclusivityMap fragmentMap = new ExclusivityMap();
-
-    mapExclusiveAtoms(fragmentMap);
-    fixUpLoadOrderDependencies(fragmentMap);
-
-    return fragmentMap;
-  }
-
    private void execImpl() {
      PerfLogger.start("CodeSplitter");
-    Map<Integer, List<JsStatement>> fragmentStats = new HashMap<Integer,  
List<JsStatement>>();
+
+    FragmentMap fragmentMap = mapFragments();
+
+    List<List<JsStatement>> fragmentStats = new  
ArrayList<List<JsStatement>>(
+        3 * numEntries - 2);

      {
        /*
@@ -598,59 +434,46 @@
        LivenessPredicate alreadyLoaded = new NothingAlivePredicate();
        LivenessPredicate liveNow = new CfaLivenessPredicate(initiallyLive);
        List<JsStatement> noStats = new ArrayList<JsStatement>();
-      addFragment(0, alreadyLoaded, liveNow, noStats, fragmentStats);
+      addFragment(alreadyLoaded, liveNow, noStats, fragmentStats);
      }

      /*
-     * Compute the base fragments, for split points in the initial load
-     * sequence.
-     */
-    liveAfterInitialSequence = new ControlFlowAnalyzer(initiallyLive);
-    for (int sp : initialLoadSequence) {
-      LivenessPredicate alreadyLoaded = new CfaLivenessPredicate(
-          liveAfterInitialSequence);
-
-      ControlFlowAnalyzer liveAfterSp = new ControlFlowAnalyzer(
-          liveAfterInitialSequence);
-      traverseEntry(liveAfterSp, sp);
-      LivenessPredicate liveNow = new CfaLivenessPredicate(liveAfterSp);
-
-      List<JsStatement> statsToAppend =  
fragmentExtractor.createCallsToEntryMethods(sp);
-
-      addFragment(sp, alreadyLoaded, liveNow, statsToAppend,  
fragmentStats);
-
-      liveAfterInitialSequence = liveAfterSp;
-    }
-
-    ExclusivityMap fragmentMap = determineExclusivity();
-
-    /*
       * Compute the exclusively live fragments. Each includes everything
       * exclusively live after entry point i.
       */
      for (int i = 1; i < numEntries; i++) {
-      if (isInitial(i)) {
-        continue;
-      }
-      LivenessPredicate alreadyLoaded = new  
ExclusivityMapLivenessPredicate(
+      LivenessPredicate alreadyLoaded = new FragmentMapLivenessPredicate(
            fragmentMap, 0);
-      LivenessPredicate liveNow = new ExclusivityMapLivenessPredicate(
-          fragmentMap, i);
+      LivenessPredicate liveNow = new  
FragmentMapLivenessPredicate(fragmentMap,
+          i);
        List<JsStatement> statsToAppend =  
fragmentExtractor.createCallsToEntryMethods(i);
-      addFragment(i, alreadyLoaded, liveNow, statsToAppend, fragmentStats);
+      addFragment(alreadyLoaded, liveNow, statsToAppend, fragmentStats);
      }

      /*
-     * Compute the leftovers fragment.
+     * Add secondary base fragments and their associated leftover  
fragments.
       */
-    {
-      LivenessPredicate alreadyLoaded = new CfaLivenessPredicate(
-          liveAfterInitialSequence);
-      LivenessPredicate liveNow = new ExclusivityMapLivenessPredicate(
+    for (int base = 1; base < numEntries; base++) {
+      ControlFlowAnalyzer baseCfa = new ControlFlowAnalyzer(initiallyLive);
+      traverseEntry(baseCfa, base);
+      LivenessPredicate baseLive = new CfaLivenessPredicate(baseCfa);
+
+      // secondary base
+      List<JsStatement> baseStatsToAppend =  
fragmentExtractor.createCallsToEntryMethods(base);
+      addFragment(new CfaLivenessPredicate(initiallyLive), baseLive,
+          baseStatsToAppend, fragmentStats);
+
+      // leftovers
+      LivenessPredicate globalLeftoversLive = new  
FragmentMapLivenessPredicate(
            fragmentMap, 0);
+      LivenessPredicate associatedExclusives = new  
FragmentMapLivenessPredicate(
+          fragmentMap, base);
+      // Be sure to add in anything in the exclusives for base that is
+      // not in its secondary base.
+      LivenessPredicate leftoversLive = new UnionLivenessPredicate(
+          globalLeftoversLive, associatedExclusives);
        List<JsStatement> statsToAppend =  
fragmentExtractor.createCallToLeftoversFragmentHasLoaded();
-      addFragment(numEntries, alreadyLoaded, liveNow, statsToAppend,
-          fragmentStats);
+      addFragment(baseLive, leftoversLive, statsToAppend, fragmentStats);
      }

      // now install the new statements in the program fragments
@@ -680,7 +503,7 @@
     * happen, so it seems better to keep the compiler simpler.
     * </p>
     */
-  private void fixUpLoadOrderDependencies(ExclusivityMap fragmentMap) {
+  private void fixUpLoadOrderDependencies(FragmentMap fragmentMap) {
      fixUpLoadOrderDependenciesForMethods(fragmentMap);
      fixUpLoadOrderDependenciesForTypes(fragmentMap);
      fixUpLoadOrderDependenciesForClassLiterals(fragmentMap);
@@ -688,7 +511,7 @@
    }

    private void fixUpLoadOrderDependenciesForClassLiterals(
-      ExclusivityMap fragmentMap) {
+      FragmentMap fragmentMap) {
      int numClassLitStrings = 0;
      int numFixups = 0;
      for (JField field : fragmentMap.fields.keySet()) {
@@ -712,7 +535,7 @@
    }

    private void fixUpLoadOrderDependenciesForFieldsInitializedToStrings(
-      ExclusivityMap fragmentMap) {
+      FragmentMap fragmentMap) {
      int numFixups = 0;
      int numFieldStrings = 0;

@@ -736,7 +559,7 @@
          + +numFieldStrings);
    }

-  private void fixUpLoadOrderDependenciesForMethods(ExclusivityMap  
fragmentMap) {
+  private void fixUpLoadOrderDependenciesForMethods(FragmentMap  
fragmentMap) {
      int numFixups = 0;

      for (JDeclaredType type : jprogram.getDeclaredTypes()) {
@@ -766,7 +589,7 @@
              + jprogram.getDeclaredTypes().size());
    }

-  private void fixUpLoadOrderDependenciesForTypes(ExclusivityMap  
fragmentMap) {
+  private void fixUpLoadOrderDependenciesForTypes(FragmentMap fragmentMap)  
{
      int numFixups = 0;
      Queue<JReferenceType> typesToCheck = new  
ArrayBlockingQueue<JReferenceType>(
          jprogram.getDeclaredTypes().size());
@@ -791,16 +614,12 @@
              + jprogram.getDeclaredTypes().size());
    }

-  private boolean isInitial(int entry) {
-    return initialLoadSequence.contains(entry);
-  }
-
    /**
-   * Map atoms to exclusive 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.
+   * 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(ExclusivityMap fragmentMap) {
+  private void mapExclusiveAtoms(FragmentMap fragmentMap) {
      List<ControlFlowAnalyzer> allButOnes = computeAllButOneCfas();

      ControlFlowAnalyzer everything = computeCompleteCfa();
@@ -819,9 +638,6 @@
      allFields.addAll(everything.getFieldsWritten());

      for (int entry = 1; entry < numEntries; entry++) {
-      if (isInitial(entry)) {
-        continue;
-      }
        ControlFlowAnalyzer allButOne = allButOnes.get(entry - 1);
        Set<JNode> allLiveNodes = union(allButOne.getLiveFieldsAndMethods(),
            allButOne.getFieldsWritten());
@@ -833,6 +649,21 @@
        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;
    }

    /**

Modified:  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
==============================================================================
---  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
       
(original)
+++  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
       
Mon May 11 07:59:01 2009
@@ -204,7 +204,7 @@

    /**
     * Create a call to
-   * {...@link  
com.google.gwt.core.client.impl.AsyncFragmentLoader#leftoversFragmentHasLoaded()}.
+   * {...@link  
com.google.gwt.lang.AsyncFragmentLoader#leftoversFragmentHasLoaded()}.
     */
    public List<JsStatement> createCallToLeftoversFragmentHasLoaded() {
      JMethod loadedMethod =  
jprogram.getIndexedMethod("AsyncFragmentLoader.leftoversFragmentHasLoaded");

Modified:  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java
==============================================================================
---  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java
   
(original)
+++  
branches/snapshot-2009.04.28-r5307/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java
   
Mon May 11 07:59:01 2009
@@ -41,10 +41,9 @@
   * runAsync and how it works.
   */
  public class FragmentLoaderCreator {
-  public static final String ASYNC_FRAGMENT_LOADER  
= "com.google.gwt.core.client.impl.AsyncFragmentLoader";
+  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 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";
@@ -161,8 +160,7 @@
     * <code>GWT.runAsync</code> are replaced by calls to this method.
     */
    private void generateRunAsyncMethod(PrintWriter srcWriter) {
-    srcWriter.println("public static void " + LOADER_METHOD_RUN_ASYNC
-        + "(RunAsyncCallback callback) {");
+    srcWriter.println("public static void runAsync(RunAsyncCallback  
callback) {");
      srcWriter.println(getCallbackListSimpleName() + " newCallback = new "
          + getCallbackListSimpleName() + "();");
      srcWriter.println("newCallback.callback = callback;");

Copied:  
branches/snapshot-2009.04.28-r5307/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java
  
(from r5295,  
/trunk/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java)
==============================================================================
--- /trunk/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java  
(original)
+++  
branches/snapshot-2009.04.28-r5307/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java
  
Mon May 11 07:59:01 2009
@@ -19,10 +19,8 @@
  import com.google.gwt.xhr.client.XMLHttpRequest;

  import java.util.ArrayList;
-import java.util.HashMap;
  import java.util.LinkedList;
  import java.util.List;
-import java.util.Map;
  import java.util.Queue;

  /**
@@ -37,8 +35,14 @@
   *
   * <ul>
   * <li>0 -- the <em>base</em> fragment, which is initially downloaded
- * <li>1..m -- fragments for each split point
- * <li>m+1 -- the <em>leftovers</em> fragment of code that goes nowhere  
else
+ * <li>1-m -- the <em>exclusively live</em> fragments, holding the code  
needed
+ * exclusively for each split point
+ * <li>m -- the <em>secondary base</em> fragment for entry point 1. It  
holds
+ * precisely that code needed if entry point 1 is the first one reached  
after
+ * the application downloads.
+ * <li>m+1 -- the <em>leftovers fragment</em> for entry point 1. It holds  
all
+ * code not in fragments 0-(m-1) nor in fragment m.
+ * <li>(m+2)..(3m) -- secondary bases and leftovers for entry points 2..m
   * </ul>
   *
   * <p>
@@ -77,12 +81,32 @@

      private static final String LEFTOVERS_DOWNLOAD = "leftoversDownload";

+    /**
+     * @param splitPoint
+     * @return
+     */
      private static String downloadGroup(int splitPoint) {
        return "download" + splitPoint;
      }
    }

    /**
+   * Handles a failure to download a base fragment.
+   */
+  private static class BaseDownloadFailed implements LoadErrorHandler {
+    private final LoadErrorHandler chainedHandler;
+
+    public BaseDownloadFailed(LoadErrorHandler chainedHandler) {
+      this.chainedHandler = chainedHandler;
+    }
+
+    public void loadFailed(Throwable reason) {
+      baseLoading = false;
+      chainedHandler.loadFailed(reason);
+    }
+  }
+
+  /**
     * An exception indicating than at HTTP download failed.
     */
    private static class HttpDownloadFailure extends RuntimeException {
@@ -99,37 +123,25 @@
    }

    /**
-   * Handles a failure to download a fragment in the initial sequence.
+   * Handles a failure to download a leftovers fragment.
     */
-  private static class InitialFragmentDownloadFailed implements
-      LoadErrorHandler {
+  private static class LeftoversDownloadFailed implements LoadErrorHandler  
{
      public void loadFailed(Throwable reason) {
-      initialFragmentsLoading = false;
-
-      // Cancel all pending downloads.
+      leftoversLoading = false;

        /*
-       * Make a local list of the handlers to run, in case one of them  
calls
-       * another runAsync
+       * Cancel all other pending downloads. If any exception is thrown  
while
+       * cancelling any of them, throw only the last one.
         */
-      List<LoadErrorHandler> handlersToRun = new  
ArrayList<LoadErrorHandler>();
+      RuntimeException lastException = null;

-      // add handlers that are waiting pending the initials download
-      assert waitingForInitialFragments.length() ==  
waitingForInitialFragmentsErrorHandlers.size();
-      while (waitingForInitialFragments.length() > 0) {
-         
handlersToRun.add(waitingForInitialFragmentsErrorHandlers.remove());
-        waitingForInitialFragments.shift();
-      }
+      assert waitingForLeftovers.size() ==  
waitingForLeftoversErrorHandlers.size();

-      // add handlers for pending initial fragment downloads
-      handlersToRun.addAll(initialFragmentErrorHandlers.values());
-      initialFragmentErrorHandlers.clear();
-
-      /*
-       * If an exception is thrown while canceling any of them, remember  
and
-       * throw the last one.
-       */
-      RuntimeException lastException = null;
+      // Copy the list in case a handler makes another runAsync call
+      List<LoadErrorHandler> handlersToRun = new  
ArrayList<LoadErrorHandler>(
+          waitingForLeftoversErrorHandlers);
+      waitingForLeftoversErrorHandlers.clear();
+      waitingForLeftovers.clear();

        for (LoadErrorHandler handler : handlersToRun) {
          try {
@@ -145,6 +157,16 @@
      }
    }

+  /**
+   * The first entry point reached after the program started.
+   */
+  private static int base = -1;
+
+  /**
+   * Whether the secondary base fragment is currently loading.
+   */
+  private static boolean baseLoading = false;
+
    private static final String HTTP_GET = "GET";

    /**
@@ -157,79 +179,64 @@
    private static final int HTTP_STATUS_OK = 200;

    /**
-   * Error handlers for failure to download an initial fragment.
-   *
-   * TODO(spoon) make it a lightweight integer map
+   * Whether the leftovers fragment has loaded yet.
     */
-  private static Map<Integer, LoadErrorHandler>  
initialFragmentErrorHandlers = new HashMap<Integer, LoadErrorHandler>();
+  private static boolean leftoversLoaded = false;

    /**
-   * Indicates that the next fragment in {...@link  
#remainingInitialFragments} is
-   * currently downloading.
+   * Whether the leftovers fragment is currently loading.
     */
-  private static boolean initialFragmentsLoading = false;
-
-  /**
-   * The sequence of fragments to load initially, before anything else can  
be
-   * loaded. This array will hold the initial sequence of bases followed  
by the
-   * leftovers fragment. It is filled in by
-   * {...@link com.google.gwt.dev.jjs.impl.CodeSplitter}.  It does *not*  
include
-   * the leftovers fragment, which must be loaded once all of these are  
finished.
-   */
-  private static int[] initialLoadSequence = new int[] { };
+  private static boolean leftoversLoading = false;

    /**
     * The total number of split points in the program, counting the initial  
entry
-   * as an honorary split point. This is changed to the correct value by
+   * as a split point. This is changed to the correct value by
     * {...@link com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs}.
     */
    private static int numEntries = 1;

    /**
-   * Base fragments that remain to be downloaded. It is lazily initialized  
in
-   * the first call to {...@link #startLoadingNextInitial()}.  It does include
-   * the leftovers fragment.
-   */
-  private static JsArrayInteger remainingInitialFragments = null;
-
-  /**
     * Split points that have been reached, but that cannot be downloaded  
until
-   * the initial fragments finish downloading.
+   * the leftovers fragment finishes downloading.
     */
-  private static JsArrayInteger waitingForInitialFragments =  
createJsArrayInteger();
+  private static Queue<Integer> waitingForLeftovers = new  
LinkedList<Integer>();

    /**
     * Error handlers for the above queue.
-   *
-   * TODO(spoon) change this to a lightweight JS collection
     */
-  private static Queue<LoadErrorHandler>  
waitingForInitialFragmentsErrorHandlers = new  
LinkedList<LoadErrorHandler>();
+  private static Queue<LoadErrorHandler> waitingForLeftoversErrorHandlers  
= new LinkedList<LoadErrorHandler>();

    /**
-   * Inform the loader that a fragment has now finished loading.
+   * 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 fragment) {
-    logFragmentLoaded(fragment);
-
-    if (isInitial(fragment)) {
-      assert (fragment == remainingInitialFragments.get(0));
-      remainingInitialFragments.shift();
-      initialFragmentErrorHandlers.remove(fragment);
+  public static void fragmentHasLoaded(int entry) {
+    int fragment = base >= 0 ? entry : baseFragmentNumber(entry);
+    logEventProgress(LwmLabels.downloadGroup(entry), LwmLabels.END,  
fragment,
+        null);
+
+    if (base < 0) {
+      // The base fragment has loaded
+      base = entry;
+      baseLoading = false;

-      startLoadingNextInitial();
+      // Go ahead and download the appropriate leftovers fragment
+      startLoadingLeftovers();
      }
    }

    /**
     * Loads the specified split point.
     *
-   * @param splitPoint the split point whose code needs to be loaded
+   * @param splitPoint the fragment to load
     */
    public static void inject(int splitPoint, LoadErrorHandler  
loadErrorHandler) {
-    if (haveInitialFragmentsLoaded()) {
+    if (leftoversLoaded) {
        /*
-       * The initial fragments has loaded. Immediately start loading the
-       * requested code.
+       * A base and a leftovers fragment have loaded. Load an exclusively  
live
+       * fragment.
         */
        logEventProgress(LwmLabels.downloadGroup(splitPoint),  
LwmLabels.BEGIN,
            splitPoint, null);
@@ -237,36 +244,46 @@
        return;
      }

-    if (isInitial(splitPoint)) {
+    if (baseLoading || leftoversLoading) {
        /*
-       * The loading of an initial fragment will happen via
-       * startLoadingNextInitial(), so don't start it here. Do, however,  
record
-       * the error handler.
+       * Wait until the leftovers fragment has loaded before loading this  
one.
         */
-      initialFragmentErrorHandlers.put(splitPoint, loadErrorHandler);
-    } else {
+      assert (waitingForLeftovers.size() ==  
waitingForLeftoversErrorHandlers.size());
+      waitingForLeftovers.add(splitPoint);
+      waitingForLeftoversErrorHandlers.add(loadErrorHandler);
+
        /*
-       * For a non-initial fragment, queue it for later loading, once the
-       * initial fragments have all been loaded.
+       * Also, restart the leftovers download if it previously failed.
         */
+      if (!leftoversLoading) {
+        startLoadingLeftovers();
+      }

-      assert (waitingForInitialFragments.length() ==  
waitingForInitialFragmentsErrorHandlers.size());
-      waitingForInitialFragments.push(splitPoint);
-      waitingForInitialFragmentsErrorHandlers.add(loadErrorHandler);
-    }
-
-    /*
-     * Start the initial downloads if they aren't running already.
-     */
-    if (!initialFragmentsLoading) {
-      startLoadingNextInitial();
+      return;
      }

-    return;
+    // Nothing has loaded or started to load. Treat this fragment as the  
base.
+    baseLoading = true;
+    logEventProgress(LwmLabels.downloadGroup(splitPoint), LwmLabels.BEGIN,
+        baseFragmentNumber(splitPoint), null);
+    startLoadingFragment(baseFragmentNumber(splitPoint),
+        new BaseDownloadFailed(loadErrorHandler));
    }
-
+
+  /**
+   * Inform the loader that the "leftovers" fragment has loaded.
+   */
    public static void leftoversFragmentHasLoaded() {
-    fragmentHasLoaded(leftoversFragment());
+    leftoversLoaded = true;
+    leftoversLoading = false;
+    logEventProgress(LwmLabels.LEFTOVERS_DOWNLOAD, LwmLabels.END,
+        leftoversFragmentNumber(), null);
+
+    assert (waitingForLeftovers.size() ==  
waitingForLeftoversErrorHandlers.size());
+    while (!waitingForLeftovers.isEmpty()) {
+      inject(waitingForLeftovers.remove(),
+          waitingForLeftoversErrorHandlers.remove());
+    }
    }

    /**
@@ -276,14 +293,18 @@
      logEventProgress(eventGroup, type, null, null);
    }

-  private static native JsArrayInteger createJsArrayInteger() /*-{
-    return [];
-  }-*/;
+  /**
+   * Compute the fragment number for the base fragment of
+   * <code>splitPoint</code>.
+   */
+  private static int baseFragmentNumber(int splitPoint) {
+    return numEntries + 2 * (splitPoint - 1);
+  }

    private static native JavaScriptObject createStatsEvent(String  
eventGroup,
        String type, Integer fragment, Integer size) /*-{
      var evt = {
-     moduleName: @com.google.gwt.core.client.GWT::getModuleName()(),
+      moduleName: @com.google.gwt.core.client.GWT::getModuleName()(),
        subSystem: 'runAsync',
        evtGroup: eventGroup,
        millis: (new Date()).getTime(),
@@ -298,12 +319,8 @@
      return evt;
    }-*/;

-  private static native void gwtInstallCode(String text) /*-{
-    __gwtInstallCode(text);
-  }-*/;
-
    /**
-   * Call the linker-supplied __gwtStartLoadingFragment function. It should
+   * Use the linker-supplied __gwtStartLoadingFragment function. It should
     * either start the download and return null or undefined, or it should  
return
     * a URL that should be downloaded to get the code. If it starts the  
download
     * itself, it can synchronously load it, e.g. from cache, if that makes  
sense.
@@ -312,32 +329,21 @@
      return __gwtStartLoadingFragment(fragment);
    }-*/;

-  /**
-   * Return whether all initial fragments have completed loading.
-   */
-  private static boolean haveInitialFragmentsLoaded() {
-    return remainingInitialFragments != null
-        && remainingInitialFragments.length() > 0;
-  }
-
-  private static boolean isInitial(int splitPoint) {
-    if (splitPoint == leftoversFragment()) {
-      return true;
-    }
-    for (int sp : initialLoadSequence) {
-      if (sp == splitPoint) {
-        return true;
-      }
-    }
-    return false;
-  }
+  private static native void installCode(String text) /*-{
+    __gwtInstallCode(text);
+  }-*/;

    private static native boolean isStatsAvailable() /*-{
      return !!$stats;
    }-*/;

-  private static int leftoversFragment() {
-    return numEntries;
+  /**
+   * Compute the leftovers fragment number. This method can only be called  
once
+   * <code>base</code> has been set.
+   */
+  private static int leftoversFragmentNumber() {
+    assert (base >= 0);
+    return numEntries + 2 * (base - 1) + 1;
    }

    /**
@@ -352,12 +358,6 @@
          && stats(createStatsEvent(eventGroup, type, fragment, size));
    }

-  private static void logFragmentLoaded(int fragment) {
-    String logGroup = (fragment == leftoversFragment())
-        ? LwmLabels.LEFTOVERS_DOWNLOAD : LwmLabels.downloadGroup(fragment);
-    logEventProgress(logGroup, LwmLabels.END, fragment, null);
-  }
-
    private static void startLoadingFragment(int fragment,
        final LoadErrorHandler loadErrorHandler) {
      String fragmentUrl = gwtStartLoadingFragment(fragment);
@@ -376,7 +376,7 @@
                  && xhr.getResponseText() != null
                  && xhr.getResponseText().length() != 0) {
                try {
-                gwtInstallCode(xhr.getResponseText());
+                installCode(xhr.getResponseText());
                } catch (RuntimeException e) {
                  loadErrorHandler.loadFailed(e);
                }
@@ -392,40 +392,12 @@
      }
    }

-  /**
-   * Start downloading the next fragment in the initial sequence, if there  
are
-   * any left.
-   */
-  private static void startLoadingNextInitial() {
-    if (remainingInitialFragments == null) {
-      // first call, so initialize remainingInitialFragments
-      remainingInitialFragments = createJsArrayInteger();
-      for (int sp : initialLoadSequence) {
-        remainingInitialFragments.push(sp);
-      }
-      remainingInitialFragments.push(leftoversFragment());
-    }
-
-    if (remainingInitialFragments.length() > 0) {
-      // start loading the next initial fragment
-      initialFragmentsLoading = true;
-      int nextSplitPoint = remainingInitialFragments.get(0);
-      logEventProgress(LwmLabels.downloadGroup(nextSplitPoint),  
LwmLabels.BEGIN,
-          nextSplitPoint, null);
-      startLoadingFragment(nextSplitPoint, new  
InitialFragmentDownloadFailed());
-      return;
-    }
-
-    // all initials are finished
-    initialFragmentsLoading = false;
-    assert (haveInitialFragmentsLoaded());
-
-    // start loading any pending fragments
-    assert (waitingForInitialFragments.length() ==  
waitingForInitialFragmentsErrorHandlers.size());
-    while (waitingForInitialFragments.length() > 0) {
-      startLoadingFragment(waitingForInitialFragments.shift(),
-          waitingForInitialFragmentsErrorHandlers.remove());
-    }
+  private static void startLoadingLeftovers() {
+    leftoversLoading = true;
+    logEventProgress(LwmLabels.LEFTOVERS_DOWNLOAD, LwmLabels.BEGIN,
+        leftoversFragmentNumber(), null);
+    startLoadingFragment(leftoversFragmentNumber(),
+        new LeftoversDownloadFailed());
    }

    /**

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to