Author: [email protected]
Date: Mon Jun 15 17:20:03 2009
New Revision: 5560
Added:
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JsniRefLookup.java
(contents, props changed)
Modified:
trunk/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java
trunk/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java
trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
trunk/user/src/com/google/gwt/core/CompilerParameters.gwt.xml
trunk/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
Log:
Allow specifying an initial load sequence for runAsync calls
in an application's gwt.xml file. If the specified sequence
matches the actual order the runAsync's are reached at
run time, then the load time is improved because the leftovers
fragment can be loaded later than otherwise. If the load
order is incorrect, the program still runs, but it
will likely be slowed down.
Review by: bobv
Modified:
trunk/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java
==============================================================================
---
trunk/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java
(original)
+++
trunk/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java
Mon Jun 15 17:20:03 2009
@@ -16,8 +16,11 @@
package com.google.gwt.core.ext.soyc.impl;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs.RunAsyncReplacement;
import com.google.gwt.dev.util.HtmlTextOutput;
+import com.google.gwt.dev.util.collect.HashMap;
import com.google.gwt.util.tools.Utility;
import java.io.OutputStream;
@@ -30,18 +33,13 @@
* Records split points to a file for SOYC reports.
*/
public class SplitPointRecorder {
-
/**
* Used to record (runAsync) split points of a program.
- *
- * @param jprogram
- * @param out
- * @param logger
*/
public static void recordSplitPoints(JProgram jprogram, OutputStream out,
TreeLogger logger) {
- logger = logger.branch(TreeLogger.INFO,
+ logger = logger.branch(TreeLogger.TRACE,
"Creating Split Point Map file for SOYC");
try {
@@ -59,19 +57,22 @@
htmlOut.indentIn();
htmlOut.indentIn();
- Map<Integer, String> splitPointMap = jprogram.getSplitPointMap();
+ Map<Integer, String> splitPointMap = splitPointNames(jprogram);
if (splitPointMap.size() > 0) {
curLine = "<splitpoints>";
htmlOut.printRaw(curLine);
htmlOut.newline();
htmlOut.indentIn();
htmlOut.indentIn();
- for (Map.Entry<Integer, String> entry : splitPointMap.entrySet()) {
- Integer splitPointCount = entry.getKey();
- curLine = "<splitpoint id=\"" + splitPointCount + "\"
location=\""
- + entry.getValue() + "\"/>";
+ for (int sp = 1; sp <= splitPointMap.size(); sp++) {
+ String location = splitPointMap.get(sp);
+ assert location != null;
+ curLine = "<splitpoint id=\"" + sp + "\" location=\"" + location
+ + "\"/>";
htmlOut.printRaw(curLine);
htmlOut.newline();
+ logger.log(TreeLogger.TRACE, "Assigning split point #" + sp
+ + " in method " + location);
}
htmlOut.indentOut();
htmlOut.indentOut();
@@ -94,6 +95,33 @@
} catch (Throwable e) {
logger.log(TreeLogger.ERROR, "Could not open dependency file.", e);
}
+ }
+
+ private static String fullMethodDescription(JMethod method) {
+ return (method.getEnclosingType().getName() + "." +
JProgram.getJsniSig(method));
+ }
+
+ /**
+ * Choose human-readable names for the split points.
+ */
+ private static Map<Integer, String> splitPointNames(JProgram program) {
+ Map<Integer, String> names = new HashMap<Integer, String>();
+ Map<String, Integer> counts = new HashMap<String, Integer>();
+ for (RunAsyncReplacement replacement :
program.getRunAsyncReplacements().values()) {
+ int entryNumber = replacement.getNumber();
+ String methodDescription =
fullMethodDescription(replacement.getEnclosingMethod());
+ if (counts.containsKey(methodDescription)) {
+ counts.put(methodDescription, counts.get(methodDescription) + 1);
+ methodDescription += "#"
+ + Integer.toString(counts.get(methodDescription));
+ } else {
+ counts.put(methodDescription, 1);
+ }
+
+ names.put(entryNumber, methodDescription);
+ }
+
+ return names;
}
private SplitPointRecorder() {
Modified:
trunk/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java
(original)
+++ trunk/dev/core/src/com/google/gwt/core/linker/SoycReportLinker.java Mon
Jun 15 17:20:03 2009
@@ -40,8 +40,12 @@
ArtifactSet artifacts) throws UnableToCompleteException {
ArtifactSet results = new ArtifactSet(artifacts);
for (StandardCompilationAnalysis soycFiles :
artifacts.find(StandardCompilationAnalysis.class)) {
- results.add(soycFiles.getDepFile());
- results.add(soycFiles.getStoriesFile());
+ if (soycFiles.getDepFile() != null) {
+ results.add(soycFiles.getDepFile());
+ }
+ if (soycFiles.getStoriesFile() != null) {
+ results.add(soycFiles.getStoriesFile());
+ }
results.add(soycFiles.getSplitPointsFile());
}
return results;
Modified:
trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
Mon Jun 15 17:20:03 2009
@@ -120,7 +120,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;
@@ -128,8 +127,8 @@
import java.util.TreeSet;
/**
- * Compiles the Java <code>JProgram</code> representation into its
- * corresponding JavaScript source.
+ * Compiles the Java <code>JProgram</code> representation into its
corresponding
+ * JavaScript source.
*/
public class JavaToJavaScriptCompiler {
@@ -182,14 +181,16 @@
* {...@link #precompile(TreeLogger, WebModeCompilerFrontEnd,
String[], JJSOptions, boolean)}
* @param rebindAnswers the set of rebind answers to resolve all
outstanding
* rebind decisions
- * @param propertyOracles All property oracles corresponding to this
permutation.
+ * @param propertyOracles All property oracles corresponding to this
+ * permutation.
* @return the output JavaScript
* @throws UnableToCompleteException if an error other than
* {...@link OutOfMemoryError} occurs
*/
public static PermutationResult compilePermutation(TreeLogger logger,
UnifiedAst unifiedAst, Map<String, String> rebindAnswers,
- PropertyOracle[] propertyOracles, int permutationId) throws
UnableToCompleteException {
+ PropertyOracle[] propertyOracles, int permutationId)
+ throws UnableToCompleteException {
long permStart = System.currentTimeMillis();
try {
if (JProgram.isTracingEnabled()) {
@@ -214,13 +215,6 @@
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
// willy-nilly because there are some subtle interdependencies.
@@ -293,8 +287,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.
@@ -331,45 +324,8 @@
PermutationResult toReturn = new PermutationResultImpl(js,
makeSymbolMap(symbolTable), ranges);
- if (sourceInfoMaps != null) {
- long soycStart = System.currentTimeMillis();
- System.out.println("Computing SOYC output");
- // Free up memory.
- symbolTable = null;
-
- long start = System.currentTimeMillis();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- // get method dependencies
- StoryRecorder.recordStories(logger, baos, sourceInfoMaps, js);
- SoycArtifact stories = new SoycArtifact("stories" + permutationId
- + ".xml.gz", baos.toByteArray());
- // Free up memory.
- js = null;
- System.out.println("Record stories: "
- + (System.currentTimeMillis() - start) + " ms");
-
- start = System.currentTimeMillis();
- baos.reset();
- DependencyRecorder.recordDependencies(logger, baos, jprogram);
- SoycArtifact dependencies = new SoycArtifact("dependencies"
- + permutationId + ".xml.gz", baos.toByteArray());
- System.out.println("Record dependencies: "
- + (System.currentTimeMillis() - start) + " ms");
-
- start = System.currentTimeMillis();
- baos.reset();
- SplitPointRecorder.recordSplitPoints(jprogram, baos, logger);
- SoycArtifact splitPoints = new SoycArtifact("splitPoints"
- + permutationId + ".xml.gz", baos.toByteArray());
- System.out.println("Record split points: "
- + (System.currentTimeMillis() - start) + " ms");
-
- toReturn.getArtifacts().add(
- new StandardCompilationAnalysis(dependencies, stories,
splitPoints));
-
- System.out.println("Completed SOYC phase in "
- + (System.currentTimeMillis() - soycStart) + " ms");
- }
+ toReturn.getArtifacts().add(
+ makeSoycArtifact(logger, permutationId, jprogram, js,
sourceInfoMaps));
System.out.println("Permutation took "
+ (System.currentTimeMillis() - permStart) + " ms");
@@ -508,6 +464,8 @@
// Fix up GWT.runAsync()
if (options.isAggressivelyOptimize() && options.isRunAsyncEnabled())
{
ReplaceRunAsyncs.exec(logger, jprogram);
+ CodeSplitter.pickInitialLoadSequence(logger, jprogram,
+ module.getProperties());
}
// Resolve entry points, rebinding non-static entry points.
@@ -906,6 +864,43 @@
logger.log(TreeLogger.ERROR, "Unexpected internal compiler error",
e);
return new UnableToCompleteException();
}
+ }
+
+ private static StandardCompilationAnalysis makeSoycArtifact(
+ TreeLogger logger, int permutationId, JProgram jprogram, String[] js,
+ List<Map<Range, SourceInfo>> sourceInfoMaps) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ PerfLogger.start("Computing SOYC output");
+
+ PerfLogger.start("Record split points");
+ SplitPointRecorder.recordSplitPoints(jprogram, baos, logger);
+ SoycArtifact splitPoints = new SoycArtifact("splitPoints" +
permutationId
+ + ".xml.gz", baos.toByteArray());
+ PerfLogger.end();
+
+ SoycArtifact stories = null;
+ SoycArtifact dependencies = null;
+
+ if (sourceInfoMaps != null) {
+ PerfLogger.start("Record stories");
+ baos.reset();
+ StoryRecorder.recordStories(logger, baos, sourceInfoMaps, js);
+ stories = new SoycArtifact("stories" + permutationId + ".xml.gz",
+ baos.toByteArray());
+ PerfLogger.end();
+
+ PerfLogger.start("Record dependencies");
+ baos.reset();
+ DependencyRecorder.recordDependencies(logger, baos, jprogram);
+ dependencies = new SoycArtifact("dependencies" + permutationId
+ + ".xml.gz", baos.toByteArray());
+ PerfLogger.end();
+ }
+
+ PerfLogger.end();
+
+ return new StandardCompilationAnalysis(dependencies, stories,
splitPoints);
}
/**
Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java (original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java Mon Jun 15
17:20:03 2009
@@ -24,6 +24,10 @@
import com.google.gwt.dev.jjs.ast.js.JClassSeed;
import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
import com.google.gwt.dev.jjs.ast.js.JsonObject;
+import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs;
+import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs.RunAsyncReplacement;
+import com.google.gwt.dev.util.collect.Lists;
+import com.google.gwt.dev.util.collect.Maps;
import java.io.IOException;
import java.io.ObjectInputStream;
@@ -223,7 +227,12 @@
private Map<JReferenceType, Integer> queryIds;
- private Map<Integer, String> splitPointMap = new TreeMap<Integer,
String>();
+ /**
+ * Filled in by ReplaceRunAsync, once the numbers are known.
+ */
+ private Map<Integer, RunAsyncReplacement> runAsyncReplacements =
Maps.create();
+
+ private List<Integer> splitPointInitialSequence = Lists.create();
private final Map<JMethod, JMethod> staticToInstanceMap = new
IdentityHashMap<JMethod, JMethod>();
@@ -838,8 +847,12 @@
return integer.intValue();
}
- public Map<Integer, String> getSplitPointMap() {
- return splitPointMap;
+ public Map<Integer, RunAsyncReplacement> getRunAsyncReplacements() {
+ return runAsyncReplacements;
+ }
+
+ public List<Integer> getSplitPointInitialSequence() {
+ return splitPointInitialSequence;
}
public JMethod getStaticImpl(JMethod method) {
@@ -1032,8 +1045,14 @@
this.queryIds = queryIds;
}
- public void setSplitPointMap(Map<Integer, String> splitPointMap) {
- this.splitPointMap = splitPointMap;
+ public void setRunAsyncReplacements(Map<Integer, RunAsyncReplacement>
map) {
+ assert runAsyncReplacements.isEmpty();
+ runAsyncReplacements = map;
+ }
+
+ public void setSplitPointInitialSequence(List<Integer> list) {
+ assert splitPointInitialSequence.isEmpty();
+ splitPointInitialSequence.addAll(list);
}
/**
Modified: trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java Mon
Jun 15 17:20:03 2009
@@ -16,8 +16,14 @@
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.cfg.ConfigurationProperty;
+import com.google.gwt.dev.cfg.Properties;
+import com.google.gwt.dev.cfg.Property;
+import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.HasEnclosingType;
import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JExpression;
@@ -34,6 +40,7 @@
import com.google.gwt.dev.jjs.impl.FragmentExtractor.LivenessPredicate;
import com.google.gwt.dev.jjs.impl.FragmentExtractor.NothingAlivePredicate;
import com.google.gwt.dev.jjs.impl.FragmentExtractor.StatementLogger;
+import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs.RunAsyncReplacement;
import com.google.gwt.dev.js.ast.JsBlock;
import com.google.gwt.dev.js.ast.JsExprStmt;
import com.google.gwt.dev.js.ast.JsExpression;
@@ -42,6 +49,7 @@
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.js.ast.JsVars;
import com.google.gwt.dev.js.ast.JsVars.JsVar;
+import com.google.gwt.dev.util.JsniRef;
import com.google.gwt.dev.util.PerfLogger;
import com.google.gwt.dev.util.collect.HashMap;
import com.google.gwt.dev.util.collect.HashSet;
@@ -53,8 +61,6 @@
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* <p>
@@ -185,8 +191,7 @@
}
}
- private static final Pattern LOADER_CLASS_PATTERN =
Pattern.compile(FragmentLoaderCreator.ASYNC_LOADER_PACKAGE
- + "." + FragmentLoaderCreator.ASYNC_LOADER_CLASS_PREFIX
+ "([0-9]+)");
+ private static final String PROP_INITIAL_SEQUENCE
= "compiler.splitpoint.initial.sequence";
/**
* A Java property that causes the fragment map to be logged.
@@ -204,14 +209,13 @@
}
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 getExclusiveFragmentNumber(int splitPoint,
@@ -242,37 +246,47 @@
* other split points. As a side effect, modifies
* {...@link
com.google.gwt.core.client.impl.AsyncFragmentLoader#initialLoadSequence}
* in the program being compiled.
+ *
+ * @throws UnableToCompleteException If the module specifies a bad load
order
*/
- 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);
+ public static void pickInitialLoadSequence(TreeLogger logger,
+ JProgram program, Properties properties) throws
UnableToCompleteException {
+ TreeLogger branch = logger.branch(TreeLogger.TRACE,
+ "Looking up initial load sequence for split points");
+ Map<JMethod, List<Integer>> reversedRunAsyncMap =
reverse(program.getRunAsyncReplacements());
- ControlFlowAnalyzer cfa = computeInitiallyLive(program);
-
- while (true) {
- Set<Integer> nextSplitPoints = splitPointsReachable(cfa,
- splitPointToMethod, numSplitPoints);
- nextSplitPoints.removeAll(initialLoadSequence);
-
- if (nextSplitPoints.size() != 1) {
- break;
- }
+ LinkedHashSet<Integer> initialLoadSequence = new
LinkedHashSet<Integer>();
- int nextSplitPoint = nextSplitPoints.iterator().next();
- initialLoadSequence.add(nextSplitPoint);
- CodeSplitter.traverseEntry(program, cfa, nextSplitPoint);
+ ConfigurationProperty prop;
+ {
+ Property p = properties.find(PROP_INITIAL_SEQUENCE);
+ if (p == null) {
+ throw new InternalCompilerException(
+ "Could not find configuration property " +
PROP_INITIAL_SEQUENCE);
+ }
+ if (!(p instanceof ConfigurationProperty)) {
+ throw new InternalCompilerException(PROP_INITIAL_SEQUENCE
+ + " is not a configuration property");
}
+ prop = (ConfigurationProperty) p;
+ }
- logInitialLoadSequence(logger, initialLoadSequence);
- installInitialLoadSequenceField(program, initialLoadSequence);
+ for (String refString : prop.getValues()) {
+ int splitPoint = findSplitPoint(refString, program, branch,
+ reversedRunAsyncMap);
+ if (initialLoadSequence.contains(splitPoint)) {
+ branch.log(TreeLogger.ERROR, "Split point specified more than
once: "
+ + refString);
+ }
+ initialLoadSequence.add(splitPoint);
}
- return initialLoadSequence;
+ // TODO(spoon) create an artifact in the aux dir describing the
choice, so
+ // that SOYC can use it
+ logInitialLoadSequence(logger, initialLoadSequence);
+ installInitialLoadSequenceField(program, initialLoadSequence);
+ program.setSplitPointInitialSequence(new ArrayList<Integer>(
+ initialLoadSequence));
}
/**
@@ -324,30 +338,60 @@
}
/**
- * 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);
+ * Find a split point as designated in the {...@link #PROP_INITIAL_SEQUENCE}
+ * configuration property.
+ *
+ * TODO(spoon) accept a labeled runAsync call, once runAsyncs can be
labeled
+ */
+ private static int findSplitPoint(String refString, JProgram program,
+ TreeLogger branch, Map<JMethod, List<Integer>> reversedRunAsyncMap)
+ throws UnableToCompleteException {
+ if (refString.startsWith("@")) {
+ JsniRef jsniRef = JsniRef.parse(refString);
+ if (jsniRef == null) {
+ branch.log(TreeLogger.ERROR, "Badly formatted JSNI reference in "
+ + PROP_INITIAL_SEQUENCE + ": " + refString);
+ throw new UnableToCompleteException();
+ }
+ final String lookupErrorHolder[] = new String[1];
+ HasEnclosingType referent = JsniRefLookup.findJsniRefTarget(jsniRef,
+ program, new JsniRefLookup.ErrorReporter() {
+ public void reportError(String error) {
+ lookupErrorHolder[0] = error;
+ }
+ });
+ if (referent == null) {
+ TreeLogger resolveLogger = branch.branch(TreeLogger.ERROR,
+ "Could not resolve JSNI reference: " + jsniRef);
+ resolveLogger.log(TreeLogger.ERROR, lookupErrorHolder[0]);
+ throw new UnableToCompleteException();
+ }
+
+ if (!(referent instanceof JMethod)) {
+ branch.log(TreeLogger.ERROR, "Not a method: " + referent);
+ throw new UnableToCompleteException();
+ }
+
+ JMethod method = (JMethod) referent;
+ List<Integer> splitPoints = reversedRunAsyncMap.get(method);
+ if (splitPoints == null) {
+ branch.log(TreeLogger.ERROR,
+ "Method does not enclose a runAsync call: " + jsniRef);
+ throw new UnableToCompleteException();
}
+ if (splitPoints.size() != 1) {
+ branch.log(TreeLogger.ERROR,
+ "Method includes multiple runAsync calls, "
+ + "so it's ambiguous which one is meant: " + jsniRef);
+ throw new UnableToCompleteException();
+ }
+
+ return splitPoints.get(0);
}
- return splitPointToLoadMethod;
+
+ branch.log(TreeLogger.ERROR, "Unrecognized designation of a split
point: "
+ + refString);
+ throw new UnableToCompleteException();
}
private static String fullNameString(JField field) {
@@ -403,24 +447,22 @@
}
/**
- * 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
+ * Reverses a runAsync map, returning a map from methods to the split
point
+ * numbers invoked from within that method.
*/
- 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);
+ private static Map<JMethod, List<Integer>> reverse(
+ Map<Integer, RunAsyncReplacement> runAsyncMap) {
+ Map<JMethod, List<Integer>> revmap = new HashMap<JMethod,
List<Integer>>();
+ for (RunAsyncReplacement replacement : runAsyncMap.values()) {
+ JMethod method = replacement.getEnclosingMethod();
+ List<Integer> list = revmap.get(method);
+ if (list == null) {
+ list = new ArrayList<Integer>();
+ revmap.put(method, list);
}
+ list.add(replacement.getNumber());
}
-
- return nextSplitPoints;
+ return revmap;
}
/**
@@ -488,14 +530,14 @@
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;
+ this.initialLoadSequence = new LinkedHashSet<Integer>(
+ jprogram.getSplitPointInitialSequence());
numEntries = jprogram.entryMethods.size();
logging = Boolean.getBoolean(PROP_LOG_FRAGMENT_MAP);
Modified:
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java Mon
Jun 15 17:20:03 2009
@@ -32,7 +32,6 @@
import com.google.gwt.dev.jjs.ast.JCaseStatement;
import com.google.gwt.dev.jjs.ast.JCastOperation;
import com.google.gwt.dev.jjs.ast.JCharLiteral;
-import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JConditional;
import com.google.gwt.dev.jjs.ast.JContinueStatement;
@@ -193,8 +192,6 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Queue;
-import java.util.TreeSet;
/**
* This is the big kahuna where most of the nitty gritty of creating our
AST
@@ -2680,7 +2677,7 @@
}
}
- private HasEnclosingType parseJsniRef(SourceInfo info, String ident)
{
+ private HasEnclosingType findJsniRefTarget(final SourceInfo info,
String ident) {
JsniRef parsed = JsniRef.parse(ident);
if (parsed == null) {
reportJsniError(info, methodDecl,
@@ -2688,109 +2685,14 @@
return null;
}
- String className = parsed.className();
- JType type = null;
- if (!className.equals("null")) {
- type = program.getTypeFromJsniRef(className);
- if (type == null) {
- reportJsniError(info, methodDecl,
- "Unresolvable native reference to type '" + className
+ "'");
- return null;
- }
- }
-
- if (!parsed.isMethod()) {
- // look for a field
- String fieldName = parsed.memberName();
- if (type == null) {
- if (fieldName.equals("nullField")) {
- return program.getNullField();
- }
+ JProgram prog = program;
- } else if (fieldName.equals("class")) {
- JClassLiteral lit = program.getLiteralClass(type);
- return lit.getField();
-
- } else if (type instanceof JPrimitiveType) {
- reportJsniError(info, methodDecl,
- "May not refer to fields on primitive types");
- return null;
-
- } else if (type instanceof JArrayType) {
- reportJsniError(info, methodDecl,
- "May not refer to fields on array types");
- return null;
-
- } else {
- for (JField field : ((JDeclaredType) type).getFields()) {
- if (field.getName().equals(fieldName)) {
- return field;
+ return JsniRefLookup.findJsniRefTarget(parsed, prog,
+ new JsniRefLookup.ErrorReporter() {
+ public void reportError(String error) {
+ reportJsniError(info, methodDecl, error);
}
- }
- }
-
- reportJsniError(info, methodDecl,
- "Unresolvable native reference to field '" + fieldName
- + "' in type '" + className + "'");
- return null;
-
- } else if (type instanceof JPrimitiveType) {
- reportJsniError(info, methodDecl,
- "May not refer to methods on primitive types");
- return null;
-
- } else {
- // look for a method
- TreeSet<String> almostMatches = new TreeSet<String>();
- String methodName = parsed.memberName();
- String jsniSig = parsed.memberSignature();
- if (type == null) {
- if (jsniSig.equals("nullMethod()")) {
- return program.getNullMethod();
- }
- } else {
- Queue<JDeclaredType> workList = new
LinkedList<JDeclaredType>();
- workList.add((JDeclaredType) type);
- while (!workList.isEmpty()) {
- JDeclaredType cur = workList.poll();
- for (JMethod method : cur.getMethods()) {
- if (method.getName().equals(methodName)) {
- String sig = JProgram.getJsniSig(method);
- if (sig.equals(jsniSig)) {
- return method;
- } else if (sig.startsWith(jsniSig) &&
jsniSig.endsWith(")")) {
- return method;
- } else {
- almostMatches.add(sig);
- }
- }
- }
- if (cur.getSuperClass() != null) {
- workList.add(cur.getSuperClass());
- }
- workList.addAll(cur.getImplements());
- }
- }
-
- if (almostMatches.isEmpty()) {
- reportJsniError(info, methodDecl,
- "Unresolvable native reference to method '" + methodName
- + "' in type '" + className + "'");
- return null;
- } else {
- StringBuilder suggestList = new StringBuilder();
- String comma = "";
- for (String almost : almostMatches) {
- suggestList.append(comma + "'" + almost + "'");
- comma = ", ";
- }
- reportJsniError(info, methodDecl,
- "Unresolvable native reference to method '" + methodName
- + "' in type '" + className + "' (did you mean "
- + suggestList.toString() + "?)");
- return null;
- }
- }
+ });
}
private void processField(JsNameRef nameRef, SourceInfo info,
@@ -2887,7 +2789,7 @@
String ident = nameRef.getIdent();
HasEnclosingType node = program.jsniMap.get(ident);
if (node == null) {
- node = parseJsniRef(info, ident);
+ node = findJsniRefTarget(info, ident);
if (node == null) {
return; // already reported error
}
@@ -3004,5 +2906,4 @@
}
return false;
}
-
}
Added: trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JsniRefLookup.java
==============================================================================
--- (empty file)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JsniRefLookup.java Mon
Jun 15 17:20:03 2009
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.impl;
+
+import com.google.gwt.dev.jjs.ast.HasEnclosingType;
+import com.google.gwt.dev.jjs.ast.JArrayType;
+import com.google.gwt.dev.jjs.ast.JClassLiteral;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JPrimitiveType;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JType;
+import com.google.gwt.dev.util.JsniRef;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.TreeSet;
+
+/**
+ * A utility class that can look up a {...@link JsniRef} in a {...@link
JProgram}.
+ */
+public class JsniRefLookup {
+ public interface ErrorReporter {
+ void reportError(String error);
+ }
+
+ /**
+ * Look up a JSNI reference.
+ *
+ * @param ref The reference to look up
+ * @param program The program to look up the reference in
+ * @param errorReporter A callback used to indicate the reason for a
failed
+ * JSNI lookup
+ * @return The item referred to, or <code>null</code> if it could not be
+ * found. If the return value is <code>null</code>,
+ * <code>errorReporter</code> will have been invoked.
+ */
+ public static HasEnclosingType findJsniRefTarget(JsniRef ref,
+ JProgram program, JsniRefLookup.ErrorReporter errorReporter) {
+ String className = ref.className();
+ JType type = null;
+ if (!className.equals("null")) {
+ type = program.getTypeFromJsniRef(className);
+ if (type == null) {
+ errorReporter.reportError("Unresolvable native reference to type '"
+ + className + "'");
+ return null;
+ }
+ }
+
+ if (!ref.isMethod()) {
+ // look for a field
+ String fieldName = ref.memberName();
+ if (type == null) {
+ if (fieldName.equals("nullField")) {
+ return program.getNullField();
+ }
+
+ } else if (fieldName.equals("class")) {
+ JClassLiteral lit = program.getLiteralClass(type);
+ return lit.getField();
+
+ } else if (type instanceof JPrimitiveType) {
+ errorReporter.reportError("May not refer to fields on primitive
types");
+ return null;
+
+ } else if (type instanceof JArrayType) {
+ errorReporter.reportError("May not refer to fields on array
types");
+ return null;
+
+ } else {
+ for (JField field : ((JDeclaredType) type).getFields()) {
+ if (field.getName().equals(fieldName)) {
+ return field;
+ }
+ }
+ }
+
+ errorReporter.reportError("Unresolvable native reference to field '"
+ + fieldName + "' in type '" + className + "'");
+ return null;
+
+ } else if (type instanceof JPrimitiveType) {
+ errorReporter.reportError("May not refer to methods on primitive
types");
+ return null;
+
+ } else {
+ // look for a method
+ TreeSet<String> almostMatches = new TreeSet<String>();
+ String methodName = ref.memberName();
+ String jsniSig = ref.memberSignature();
+ if (type == null) {
+ if (jsniSig.equals("nullMethod()")) {
+ return program.getNullMethod();
+ }
+ } else {
+ Queue<JDeclaredType> workList = new LinkedList<JDeclaredType>();
+ workList.add((JDeclaredType) type);
+ while (!workList.isEmpty()) {
+ JDeclaredType cur = workList.poll();
+ for (JMethod method : cur.getMethods()) {
+ if (method.getName().equals(methodName)) {
+ String sig = JProgram.getJsniSig(method);
+ if (sig.equals(jsniSig)) {
+ return method;
+ } else if (sig.startsWith(jsniSig) && jsniSig.endsWith(")"))
{
+ return method;
+ } else {
+ almostMatches.add(sig);
+ }
+ }
+ }
+ if (cur.getSuperClass() != null) {
+ workList.add(cur.getSuperClass());
+ }
+ workList.addAll(cur.getImplements());
+ }
+ }
+
+ if (almostMatches.isEmpty()) {
+ errorReporter.reportError("Unresolvable native reference to
method '"
+ + methodName + "' in type '" + className + "'");
+ return null;
+ } else {
+ StringBuilder suggestList = new StringBuilder();
+ String comma = "";
+ for (String almost : almostMatches) {
+ suggestList.append(comma + "'" + almost + "'");
+ comma = ", ";
+ }
+ errorReporter.reportError("Unresolvable native reference to
method '"
+ + methodName + "' in type '" + className + "' (did you mean "
+ + suggestList.toString() + "?)");
+ return null;
+ }
+ }
+ }
+
+}
Modified:
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
Mon Jun 15 17:20:03 2009
@@ -26,20 +26,61 @@
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JType;
+import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
-import java.util.TreeMap;
/**
* Replaces calls to
- * {...@link
com.google.gwt.core.client.GWT#runAsync(com.google.gwt.core.client.RunAsyncCallback)}"
+ * {...@link
com.google.gwt.core.client.GWT#runAsync(com.google.gwt.core.client.RunAsyncCallback)}
* by calls to a fragment loader.
*/
public class ReplaceRunAsyncs {
+ /**
+ * Information about the replacement of one runAsync call by a call to a
+ * generated code-loading method.
+ */
+ public static class RunAsyncReplacement implements Serializable {
+ private final int number;
+ private final JMethod enclosingMethod;
+ private final JMethod loadMethod;
+
+ RunAsyncReplacement(int number, JMethod enclosingMethod, JMethod
loadMethod) {
+ this.number = number;
+ this.enclosingMethod = enclosingMethod;
+ this.loadMethod = loadMethod;
+ }
+
+ @Override
+ public String toString() {
+ return "#" + number + ": " + enclosingMethod.toString();
+ }
+
+ /**
+ * The index of this runAsync, numbered from 1 to n.
+ */
+ public int getNumber() {
+ return number;
+ }
+
+ /**
+ * Can be null if the enclosing method cannot be designated with a JSNI
+ * reference.
+ */
+ public JMethod getEnclosingMethod() {
+ return enclosingMethod;
+ }
+
+ /**
+ * The load method to request loading the code for this method.
+ */
+ public JMethod getLoadMethod() {
+ return loadMethod;
+ }
+ }
+
private class AsyncCreateVisitor extends JModVisitor {
private JMethod currentMethod;
- private Map<Integer, String> splitPointMap = new TreeMap<Integer,
String>();
- private Map<String, Integer> methodCount = new HashMap<String,
Integer>();
private int entryCount = 1;
@Override
@@ -50,23 +91,11 @@
JExpression asyncCallback = x.getArgs().get(0);
int entryNumber = entryCount++;
- logger.log(TreeLogger.DEBUG, "Assigning split point #" +
entryNumber
- + " in method " + fullMethodDescription(currentMethod));
-
- String methodDescription = fullMethodDescription(currentMethod);
- if (methodCount.containsKey(methodDescription)) {
- methodCount.put(methodDescription,
- methodCount.get(methodDescription) + 1);
- methodDescription += "#"
- + Integer.toString(methodCount.get(methodDescription));
- } else {
- methodCount.put(methodDescription, 1);
- }
- splitPointMap.put(entryNumber, methodDescription);
-
JClassType loader = getFragmentLoader(entryNumber);
JMethod loadMethod = getRunAsyncMethod(loader);
assert loadMethod != null;
+ runAsyncReplacements.put(entryNumber, new RunAsyncReplacement(
+ entryNumber, currentMethod, loadMethod));
JMethodCall methodCall = new JMethodCall(x.getSourceInfo(), null,
loadMethod);
@@ -85,30 +114,24 @@
}
}
- public static int exec(TreeLogger logger, JProgram program) {
- return new ReplaceRunAsyncs(logger, program).execImpl();
- }
-
- private static String fullMethodDescription(JMethod method) {
- return (method.getEnclosingType().getName() + "." +
JProgram.getJsniSig(method));
+ public static void exec(TreeLogger logger, JProgram program) {
+ logger.log(TreeLogger.TRACE,
+ "Replacing GWT.runAsync with island loader calls");
+ new ReplaceRunAsyncs(program).execImpl();
}
- private final TreeLogger logger;
private JProgram program;
+ private Map<Integer, RunAsyncReplacement> runAsyncReplacements = new
HashMap<Integer, RunAsyncReplacement>();
- private ReplaceRunAsyncs(TreeLogger logger, JProgram program) {
- this.logger = logger.branch(TreeLogger.TRACE,
- "Replacing GWT.runAsync with island loader calls");
+ private ReplaceRunAsyncs(JProgram program) {
this.program = program;
}
- private int execImpl() {
+ private void execImpl() {
AsyncCreateVisitor visitor = new AsyncCreateVisitor();
visitor.accept(program);
setNumEntriesInAsyncFragmentLoader(visitor.entryCount);
- program.setSplitPointMap(visitor.splitPointMap);
-
- return visitor.entryCount;
+ program.setRunAsyncReplacements(runAsyncReplacements);
}
private JClassType getFragmentLoader(int fragmentNumber) {
Modified: trunk/user/src/com/google/gwt/core/CompilerParameters.gwt.xml
==============================================================================
--- trunk/user/src/com/google/gwt/core/CompilerParameters.gwt.xml
(original)
+++ trunk/user/src/com/google/gwt/core/CompilerParameters.gwt.xml Mon Jun
15 17:20:03 2009
@@ -12,13 +12,23 @@
<!-- implied. License for the specific language governing permissions
and -->
<!-- limitations under the
License. -->
-<!-- Compiler parameters that can be overridden for test cases . -->
+<!-- Compiler parameters that can be overridden . -->
<!--
-->
<module>
<!--
- This is the maximum number of variables in any var statement GWT will
emit. This avoids a bug in
- some browsers including the initial beta of Safari 4. See Issue 3455.
If it is set to -1, then
- there is no limit.
+ A user-specified initial load sequence for the runAsync calls. Each
entry should specify the
+ surrounding method immediately enclosing the call, using a full JSNI
reference.
+ -->
+ <define-configuration-property
name='compiler.splitpoint.initial.sequence'
+ is-multi-valued='true' />
+
+ <!-- From here down, the properties are unsupported and are only
available for test cases -->
+
+ <!--
+ This is the maximum number of variables in any var statement GWT
+ will emit. This avoids a bug in some browsers including
+ the initial beta of Safari 4. See Issue 3455. If it is set to -1,
+ then there is no limit.
-->
<define-configuration-property name='compiler.max.vars.per.var'
is-multi-valued='false' />
Modified:
trunk/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
==============================================================================
--- trunk/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
(original)
+++ trunk/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
Mon Jun 15 17:20:03 2009
@@ -414,6 +414,17 @@
}
remainingInitialFragments.push(leftoversFragment());
}
+
+ if (initialFragmentErrorHandlers.isEmpty()
+ && waitingForInitialFragmentsErrorHandlers.isEmpty()
+ && remainingInitialFragments.length() > 1) {
+ /*
+ * No further requests are pending, and more than the leftovers
fragment
+ * is left outstanding. Stop loading stuff for now.
+ */
+ initialFragmentsLoading = false;
+ return;
+ }
if (remainingInitialFragments.length() > 0) {
// start loading the next initial fragment
--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---