Author: [EMAIL PROTECTED]
Date: Sat Oct 18 19:42:21 2008
New Revision: 3784
Modified:
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
changes/spoon/runAsync/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java
Log:
CodeSplitter now generates separate fragments for the
"base" fragment of initially needed code and the "leftovers"
fragment for all code that has no other particular fragment
to be placed in. Before this patch, both categories
of code were combined into the base fragment.
Modified:
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
==============================================================================
---
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
(original)
+++
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
Sat Oct 18 19:42:21 2008
@@ -43,6 +43,10 @@
}
return null;
}
+
+ public JDeclarationStatement getDeclarationStatement() {
+ return declStmt;
+ }
public JExpression getInitializer() {
if (declStmt != null) {
Modified:
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
==============================================================================
---
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
(original)
+++
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
Sat Oct 18 19:42:21 2008
@@ -25,6 +25,7 @@
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JStringLiteral;
import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.jjs.impl.FragmentExtractor.CfaLivenessPredicate;
import com.google.gwt.dev.jjs.impl.FragmentExtractor.LivenessPredicate;
import com.google.gwt.dev.jjs.impl.FragmentExtractor.NothingAlivePredicate;
import com.google.gwt.dev.jjs.impl.FragmentExtractor.StatementLogger;
@@ -76,7 +77,8 @@
}
/**
- * A map from program atoms to the fragment they should be placed in.
+ * 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 FragmentMap {
public Map<JField, Integer> fields = new HashMap<JField, Integer>();
@@ -149,6 +151,18 @@
return (value == null) ? 0 : value;
}
+ private static Set<String> stringsIn(JExpression exp) {
+ final Set<String> strings = new HashSet<String>();
+ class StringFinder extends JVisitor {
+ @Override
+ public void endVisit(JStringLiteral stringLiteral, Context ctx) {
+ strings.add(stringLiteral.getValue());
+ }
+ }
+ (new StringFinder()).accept(exp);
+ return strings;
+ }
+
private static <T> void updateMap(int entry, Map<T, Integer> map,
Set<?> liveWithoutEntry, Iterable<T> all) {
for (T each : all) {
@@ -223,23 +237,39 @@
PerfLogger.start("CodeSplitter");
FragmentMap fragmentMap = mapFragments();
+ ControlFlowAnalyzer initiallyLive = new ControlFlowAnalyzer(jprogram);
+ traverseEntry(initiallyLive, 0);
+ initiallyLive.finishTraversal();
List<List<JsStatement>> fragmentStats = new
ArrayList<List<JsStatement>>(
- numEntries);
+ numEntries + 1);
FragmentExtractor fragmentExtractor = new FragmentExtractor(jprogram,
jsprogram, map);
- if (logging) {
- }
- for (int i = 0; i < numEntries; i++) {
- LivenessPredicate pred = new
FragmentMapLivenessPredicate(fragmentMap, i);
-
- LivenessPredicate alreadyLoaded;
+ for (int i = 0; i <= numEntries; i++) {
+ LivenessPredicate alreadyLoaded, pred;
if (i == 0) {
+ /*
+ * The base fragment. It includes everything that is live when the
+ * program starts.
+ */
alreadyLoaded = new NothingAlivePredicate();
+ pred = new CfaLivenessPredicate(initiallyLive);
+ } else if (i == numEntries) {
+ /*
+ * The leftovers fragment. It includes everything mapped to 0
except for
+ * things that are initially live.
+ */
+ alreadyLoaded = new CfaLivenessPredicate(initiallyLive);
+ pred = new FragmentMapLivenessPredicate(fragmentMap, 0);
} else {
+ /*
+ * An exclusively live fragment. It includes everything
exclusively live
+ * after entry point i.
+ */
alreadyLoaded = new FragmentMapLivenessPredicate(fragmentMap, 0);
+ pred = new FragmentMapLivenessPredicate(fragmentMap, i);
}
if (logging) {
@@ -248,18 +278,20 @@
fragmentExtractor.setStatementLogger(new EchoStatementLogger());
}
- List<JsStatement> entryStats =
fragmentExtractor.extractStatements(pred,
+ List<JsStatement> stats = fragmentExtractor.extractStatements(pred,
alreadyLoaded);
- if (i > 0) {
- fragmentExtractor.addCallsToEntryMethods(i, entryStats);
+ if (i == numEntries) {
+ fragmentExtractor.addCallToLeftoversFragmentHasLoaded(stats);
+ } else if (i > 0) {
+ fragmentExtractor.addCallsToEntryMethods(i, stats);
}
- fragmentStats.add(entryStats);
+ fragmentStats.add(stats);
}
// now install the new statements in the program fragments
- jsprogram.setFragmentCount(numEntries);
+ jsprogram.setFragmentCount(numEntries + 1);
for (int i = 0; i < fragmentStats.size(); i++) {
JsBlock fragBlock = jsprogram.getFragmentBlock(i);
fragBlock.getStatements().clear();
@@ -354,18 +386,6 @@
return fragmentMap;
}
- private Set<String> stringsIn(JExpression exp) {
- final Set<String> strings = new HashSet<String>();
- class StringFinder extends JVisitor {
- @Override
- public void endVisit(JStringLiteral stringLiteral, Context ctx) {
- strings.add(stringLiteral.getValue());
- }
- }
- (new StringFinder()).accept(exp);
- return strings;
- }
-
/**
* Traverse all code in the program except for that reachable only via
* fragment <code>frag</code>. This does not call
@@ -384,15 +404,15 @@
* <code>frag</code>. This does not call
* [EMAIL PROTECTED] ControlFlowAnalyzer#finishTraversal()}.
*/
- private void traverseEntry(ControlFlowAnalyzer cfa, int entry) {
- for (JMethod entryMethod : jprogram.entryMethods.get(entry)) {
+ private void traverseEntry(ControlFlowAnalyzer cfa, int splitPoint) {
+ for (JMethod entryMethod : jprogram.entryMethods.get(splitPoint)) {
cfa.traverseFrom(entryMethod);
}
- if (entry == 0) {
+ if (splitPoint == 0) {
/*
* Include class literal factories for simplicity. It is possible to
move
* them out, if they are only needed by one fragment, but they are
tiny,
- * so it does not look like it is worth the complexity in the
compiler.
+ * so it does not seem worth the complexity in the compiler.
*/
cfa.traverseFromClassLiteralFactories();
}
Modified:
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
==============================================================================
---
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
(original)
+++
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
Sat Oct 18 19:42:21 2008
@@ -183,8 +183,8 @@
}
/**
- * Add direct calls to the entry methods of the specified entry
number.
- */
+ * Add direct calls to the entry methods of the specified entry number.
+ */
public void addCallsToEntryMethods(int entry, List<JsStatement> stats) {
for (JMethod entryMethod : jprogram.entryMethods.get(entry)) {
JsName name = map.nameForMethod(entryMethod);
@@ -197,6 +197,16 @@
}
}
+ public void addCallToLeftoversFragmentHasLoaded(List<JsStatement> stats)
{
+ JMethod loadedMethod =
jprogram.getIndexedMethod("AsyncFragmentLoader.leftoversFragmentHasLoaded");
+ JsName loadedMethodName = map.nameForMethod(loadedMethod);
+ SourceInfo sourceInfo = jsprogram.getSourceInfo().makeChild(
+ "call to leftoversFragmentHasLoaded ");
+ JsInvocation call = new JsInvocation(sourceInfo);
+ call.setQualifier(loadedMethodName.makeRef(sourceInfo));
+ stats.add(call.makeStmt());
+ }
+
/**
* Assume that all code described by <code>alreadyLoadedPredicate</code>
has
* been downloaded. Extract enough JavaScript statements that the code
@@ -259,6 +269,13 @@
entryMethodNames = new HashSet<JsName>();
for (JMethod entryMethod : jprogram.getAllEntryMethods()) {
JsName name = map.nameForMethod(entryMethod);
+ assert name != null;
+ entryMethodNames.add(name);
+ }
+
+ JMethod leftoverFragmentLoaded =
jprogram.getIndexedMethod("AsyncFragmentLoader.leftoverFragmentHasLoaded");
+ if (leftoverFragmentLoaded != null) {
+ JsName name = map.nameForMethod(leftoverFragmentLoaded);
assert name != null;
entryMethodNames.add(name);
}
Modified:
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
==============================================================================
---
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
(original)
+++
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
Sat Oct 18 19:42:21 2008
@@ -1022,9 +1022,19 @@
/*
* Add calls to all split points other than the initial ones. That
way, if
* the code splitter does not run, the resulting code will still
function.
+ * Likewise, add a call to
AsyncFragmentLoader.leftoversFragmentHasLoaded().
*/
List<JsFunction> nonInitialEntries =
Arrays.asList(entryFunctions).subList(
x.getEntryCount(0), entryFunctions.length);
+ if (!nonInitialEntries.isEmpty()) {
+ JMethod loadedMethod =
program.getIndexedMethod("AsyncFragmentLoader.leftoversFragmentHasLoaded");
+ JsName loadedMethodName = names.get(loadedMethod);
+ SourceInfo sourceInfo = jsProgram.getSourceInfo().makeChild(
+ "call to leftoversFragmentHasLoaded ");
+ JsInvocation call = new JsInvocation(sourceInfo);
+ call.setQualifier(loadedMethodName.makeRef(sourceInfo));
+ globalStmts.add(call.makeStmt());
+ }
for (JsFunction func : nonInitialEntries) {
if (func != null) {
SourceInfo sourceInfo = jsProgram.getSourceInfo().makeChild(
Modified:
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
==============================================================================
---
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
(original)
+++
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
Sat Oct 18 19:42:21 2008
@@ -462,6 +462,9 @@
for (JMethod method : program.getAllEntryMethods()) {
livenessAnalyzer.traverseFrom(method);
}
+ if (program.entryMethods.size() > 1) {
+
livenessAnalyzer.traverseFrom(program.getIndexedMethod("AsyncFragmentLoader.leftoversFragmentHasLoaded"));
+ }
livenessAnalyzer.finishTraversal();
program.typeOracle.setInstantiatedTypes(livenessAnalyzer.getInstantiatedTypes());
@@ -483,5 +486,4 @@
}
return madeChanges;
}
-
}
Modified:
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
==============================================================================
---
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
(original)
+++
changes/spoon/runAsync/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
Sat Oct 18 19:42:21 2008
@@ -19,6 +19,7 @@
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JExpression;
+import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JModVisitor;
@@ -83,6 +84,7 @@
private int execImpl() {
AsyncCreateVisitor visitor = new AsyncCreateVisitor();
visitor.accept(program);
+ setNumEntriesInAsyncFragmentLoader(visitor.entryCount);
return visitor.entryCount;
}
@@ -122,5 +124,10 @@
}
}
return null;
+ }
+
+ private void setNumEntriesInAsyncFragmentLoader(int entryCount) {
+ JField field =
program.getIndexedField("AsyncFragmentLoader.numEntries");
+ field.getDeclarationStatement().initializer =
program.getLiteralInt(entryCount);
}
}
Modified:
changes/spoon/runAsync/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java
==============================================================================
---
changes/spoon/runAsync/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java
(original)
+++
changes/spoon/runAsync/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java
Sat Oct 18 19:42:21 2008
@@ -18,12 +18,38 @@
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
+import java.util.LinkedList;
+import java.util.Queue;
+
/**
* Low-level support to download an extra fragment of code. This should
not be
* invoked directly by user code.
*/
public class AsyncFragmentLoader {
/**
+ * Whether the leftovers fragment has loaded yet.
+ */
+ private static boolean leftoversLoaded = false;
+
+ /**
+ * Whether the leftovers fragment is currently loading.
+ */
+ private static boolean leftoversLoading = false;
+
+ /**
+ * The total number of split points in the program, counting the initial
entry
+ * as a split point. This is changed to the correct value by
+ * [EMAIL PROTECTED] com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs}.
+ */
+ private static int numEntries = 1;
+
+ /**
+ * Split points that have been reached, but that cannot be downloaded
+ * until the leftovers fragment finishes downloading.
+ */
+ private static Queue<Integer> waitingForLeftovers = new
LinkedList<Integer>();
+
+ /**
* Inform the loader that the code for an entry point has now finished
* loading.
*
@@ -39,14 +65,26 @@
* @param fragment the fragment to load
*/
public static void inject(int fragment) {
- logEventProgress("download" + fragment, "begin");
+ if (leftoversLoaded) {
+ startDownloadingFragment(fragment);
+ } else {
+ waitingForLeftovers.add(fragment);
+ if (!leftoversLoading) {
+ leftoversLoading = true;
+ startDownloadingFragment(numEntries);
+ }
+ }
+ }
- Element head = getHeadElement();
- Element script = createScriptElement();
- DOM.setElementAttribute(script, "type", "text/javascript");
- String src = getFragmentName(fragment);
- DOM.setElementAttribute(script, "src", src);
- DOM.appendChild(head, script);
+ /**
+ * Inform the loader that the "leftovers" fragment has loaded.
+ */
+ public static void leftoversFragmentHasLoaded() {
+ leftoversLoaded = true;
+ leftoversLoading = false;
+ while (!waitingForLeftovers.isEmpty()) {
+ inject(waitingForLeftovers.remove());
+ }
}
/**
@@ -92,6 +130,17 @@
private static native boolean isStatsAvailable() /*-{
return !!$stats;
}-*/;
+
+ private static void startDownloadingFragment(int fragment) {
+ logEventProgress("download" + fragment, "begin");
+
+ Element head = getHeadElement();
+ Element script = createScriptElement();
+ DOM.setElementAttribute(script, "type", "text/javascript");
+ String src = getFragmentName(fragment);
+ DOM.setElementAttribute(script, "src", src);
+ DOM.appendChild(head, script);
+ }
/**
* Always use this as [EMAIL PROTECTED] isStatsAvailable} &&
--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---