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
-~----------~----~----~----~------~----~------~--~---