Support GWT.runAsync by splitting the JavaScript code
into independently downloadable program fragments.
This commit is a merge from /changes/spoon/runAsync,
but with many small cleanups.
Review by: bobv
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3901 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java b/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
index 9760804..093f578 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
@@ -26,16 +26,22 @@
* result in identical JavaScript.
*/
public abstract class CompilationResult extends Artifact<CompilationResult> {
-
protected CompilationResult(Class<? extends Linker> linkerType) {
super(linkerType);
}
/**
- * Returns the JavaScript compilation. The exact form and function of the
- * JavaScript should be considered opaque.
+ * Returns the JavaScript compilation. The first element of the array contains
+ * 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}.
+ * The linker should provide a function named
+ * <code>__gwtStartLoadingFragment</code> that can takes an integer as argument
+ * and loads that specified code segment. To see how this function is used,
+ * see
+ * {@link com.google.gwt.core.client.AsyncFragmentLoader AsyncFragmentLoader}.
*/
- public abstract String getJavaScript();
+ public abstract String[] getJavaScript();
/**
* Provides values for {@link SelectionProperty} instances that are not
@@ -45,9 +51,19 @@
*/
public abstract SortedSet<SortedMap<SelectionProperty, String>> getPropertyMap();
+ /**
+ * Return a string that uniquely identifies this compilation result. Typically
+ * this is a cryptographic hash of the compiled data.
+ */
+ public abstract String getStrongName();
+
@Override
public final int hashCode() {
- return getJavaScript().hashCode();
+ int hash = 17;
+ for (String js : getJavaScript()) {
+ hash = hash * 37 + js.hashCode();
+ }
+ return hash;
}
@Override
@@ -69,7 +85,18 @@
@Override
protected final int compareToComparableArtifact(CompilationResult o) {
- return getJavaScript().compareTo(o.getJavaScript());
+ String[] js = getJavaScript();
+ String[] otherJs = o.getJavaScript();
+ if (js.length != otherJs.length) {
+ return js.length - otherJs.length;
+ }
+ for (int i = 0; i < js.length; i++) {
+ int diff = js[i].compareTo(otherJs[i]);
+ if (diff != 0) {
+ return diff;
+ }
+ }
+ return 0;
}
@Override
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeLinker.java
index ea3cf05..d3e8f22 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/HostedModeLinker.java
@@ -44,6 +44,7 @@
return super.generateSelectionScript(logger, context, artifacts);
}
+ @Override
public String getDescription() {
return "Hosted Mode";
}
@@ -61,8 +62,8 @@
}
@Override
- protected String getModulePrefix(TreeLogger logger, LinkerContext context)
- throws UnableToCompleteException {
+ protected String getModulePrefix(TreeLogger logger, LinkerContext context,
+ String strongName) throws UnableToCompleteException {
return unsupported(logger);
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
index 71d941b..3d78cb9 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
@@ -31,6 +31,8 @@
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
@@ -48,6 +50,11 @@
*/
/**
+ * The extension added to demand-loaded fragment files.
+ */
+ protected static final String FRAGMENT_EXTENSION = ".cache.js";
+
+ /**
* Determines whether or not the URL is relative.
*
* @param src the test url
@@ -84,7 +91,7 @@
}
}
- private final Map<CompilationResult, String> compilationPartialPaths = new IdentityHashMap<CompilationResult, String>();
+ private final Map<CompilationResult, String> compilationStrongNames = new IdentityHashMap<CompilationResult, String>();
@Override
public ArtifactSet link(TreeLogger logger, LinkerContext context,
@@ -92,24 +99,33 @@
ArtifactSet toReturn = new ArtifactSet(artifacts);
for (CompilationResult compilation : toReturn.find(CompilationResult.class)) {
- toReturn.add(doEmitCompilation(logger, context, compilation));
+ toReturn.addAll(doEmitCompilation(logger, context, compilation));
}
toReturn.add(emitSelectionScript(logger, context, artifacts));
return toReturn;
}
- protected EmittedArtifact doEmitCompilation(TreeLogger logger,
+ protected Collection<EmittedArtifact> doEmitCompilation(TreeLogger logger,
LinkerContext context, CompilationResult result)
throws UnableToCompleteException {
- StringBuffer b = new StringBuffer();
- b.append(getModulePrefix(logger, context));
- b.append(result.getJavaScript());
- b.append(getModuleSuffix(logger, context));
- EmittedArtifact toReturn = emitWithStrongName(logger,
- Util.getBytes(b.toString()), "", getCompilationExtension(logger,
- context));
- compilationPartialPaths.put(result, toReturn.getPartialPath());
+ String[] js = result.getJavaScript();
+ byte[][] bytes = new byte[js.length][];
+ bytes[0] = generatePrimaryFragment(logger, context, js[0], result.getStrongName());
+ for (int i = 1; i < js.length; i++) {
+ bytes[i] = Util.getBytes(js[i]);
+ }
+
+ Collection<EmittedArtifact> toReturn = new ArrayList<EmittedArtifact>();
+ toReturn.add(emitBytes(logger, bytes[0], result.getStrongName()
+ + getCompilationExtension(logger, context)));
+ for (int i = 1; i < js.length; i++) {
+ toReturn.add(emitBytes(logger, bytes[i], result.getStrongName() + "-" + i
+ + FRAGMENT_EXTENSION));
+ }
+
+ compilationStrongNames.put(result, result.getStrongName());
+
return toReturn;
}
@@ -231,9 +247,8 @@
// Just one distinct compilation; no need to evaluate properties
Iterator<CompilationResult> iter = compilations.iterator();
CompilationResult result = iter.next();
- text.append("strongName = '" + compilationPartialPaths.get(result)
+ text.append("strongName = '" + compilationStrongNames.get(result)
+ "';");
-
} else {
for (CompilationResult r : compilations) {
for (Map<SelectionProperty, String> propertyMap : r.getPropertyMap()) {
@@ -252,7 +267,7 @@
}
text.append("'" + propertyMap.get(p) + "'");
}
- text.append("], '").append(compilationPartialPaths.get(r)).append(
+ text.append("], '").append(compilationStrongNames.get(r)).append(
"');\n");
}
}
@@ -316,16 +331,25 @@
* @return the partial path, or <code>null</code> if the CompilationResult
* has not been emitted.
*/
- protected String getCompilationPartialPath(CompilationResult result) {
- return compilationPartialPaths.get(result);
+ protected String getCompilationStrongName(CompilationResult result) {
+ return compilationStrongNames.get(result);
}
protected abstract String getModulePrefix(TreeLogger logger,
- LinkerContext context) throws UnableToCompleteException;
+ LinkerContext context, String strongName) throws UnableToCompleteException;
protected abstract String getModuleSuffix(TreeLogger logger,
LinkerContext context) throws UnableToCompleteException;
protected abstract String getSelectionScriptTemplate(TreeLogger logger,
LinkerContext context) throws UnableToCompleteException;
+
+ private byte[] generatePrimaryFragment(TreeLogger logger,
+ LinkerContext context, String js, String strongName) throws UnableToCompleteException {
+ StringBuffer b = new StringBuffer();
+ b.append(getModulePrefix(logger, context, strongName));
+ b.append(js);
+ b.append(getModuleSuffix(logger, context));
+ return Util.getBytes(b.toString());
+ }
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
index e8cdb0d..2b4f0f8 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
@@ -69,14 +69,16 @@
public static final Comparator<SortedMap<SelectionProperty, String>> MAP_COMPARATOR = new MapComparator();
private final File cacheFile;
- private transient SoftReference<String> js;
+ private transient SoftReference<String[]> js;
+
private final SortedSet<SortedMap<SelectionProperty, String>> propertyValues = new TreeSet<SortedMap<SelectionProperty, String>>(
MAP_COMPARATOR);
private final String strongName;
- public StandardCompilationResult(String js, String strongName, File cacheFile) {
+ public StandardCompilationResult(String[] js, String strongName,
+ File cacheFile) {
super(StandardLinkerContext.class);
- this.js = new SoftReference<String>(js);
+ this.js = new SoftReference<String[]>(js);
this.strongName = strongName;
this.cacheFile = cacheFile;
}
@@ -92,26 +94,31 @@
propertyValues.add(Collections.unmodifiableSortedMap(map));
}
- public String getJavaScript() {
- String toReturn = null;
+ @Override
+ public String[] getJavaScript() {
+ String[] toReturn = null;
if (js != null) {
toReturn = js.get();
}
+
if (toReturn == null) {
- toReturn = Util.readFileAsString(cacheFile);
- if (toReturn == null) {
+ byte[][] bytes = Util.readFileAndSplit(cacheFile);
+ if (bytes == null) {
throw new RuntimeException("Unexpectedly unable to read JS file '"
+ cacheFile.getAbsolutePath() + "'");
}
- js = new SoftReference<String>(toReturn);
+ toReturn = Util.toStrings(bytes);
+ js = new SoftReference<String[]>(toReturn);
}
return toReturn;
}
+ @Override
public SortedSet<SortedMap<SelectionProperty, String>> getPropertyMap() {
return Collections.unmodifiableSortedSet(propertyValues);
}
+ @Override
public String getStrongName() {
return strongName;
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
index fc6e785..bb2a27d 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
@@ -224,16 +224,18 @@
*/
public StandardCompilationResult getCompilation(TreeLogger logger, File jsFile)
throws UnableToCompleteException {
- byte[] bytes = Util.readFileAsBytes(jsFile);
- if (bytes == null) {
+ byte[][] results = Util.readFileAndSplit(jsFile);
+
+ if (results == null) {
logger.log(TreeLogger.ERROR, "Unable to read file '"
+ jsFile.getAbsolutePath() + "'");
throw new UnableToCompleteException();
}
- String strongName = Util.computeStrongName(bytes);
+
+ String strongName = Util.computeStrongName(results);
StandardCompilationResult result = resultsByStrongName.get(strongName);
if (result == null) {
- result = new StandardCompilationResult(Util.toString(bytes), strongName,
+ result = new StandardCompilationResult(Util.toStrings(results), strongName,
jsFile);
resultsByStrongName.put(result.getStrongName(), result);
artifacts.add(result);
diff --git a/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java b/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
index 89517d8..ab19c33 100644
--- a/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
@@ -35,7 +35,7 @@
*/
@LinkerOrder(Order.PRIMARY)
public class IFrameLinker extends SelectionScriptLinker {
-
+ @Override
public String getDescription() {
return "Standard";
}
@@ -65,13 +65,13 @@
}
@Override
- protected String getModulePrefix(TreeLogger logger, LinkerContext context) {
+ protected String getModulePrefix(TreeLogger logger, LinkerContext context,
+ String strongName) {
DefaultTextOutput out = new DefaultTextOutput(context.isOutputCompact());
out.print("<html>");
out.newlineOpt();
// Setup the well-known variables.
- //
out.print("<head><script>");
out.newlineOpt();
out.print("var $gwt_version = \"" + About.GWT_VERSION_NUM + "\";");
@@ -82,6 +82,17 @@
out.newlineOpt();
out.print("var $moduleName, $moduleBase;");
out.newlineOpt();
+ out.print("function __gwtStartLoadingFragment(frag) {");
+ out.newlineOpt();
+ out.indentIn();
+ out.print(" var script = $doc.createElement('script');");
+ out.newlineOpt();
+ out.print(" script.src = '" + strongName + "-' + frag + '" + FRAGMENT_EXTENSION + "';");
+ out.print(" document.getElementsByTagName('head').item(0).appendChild(script);");
+ out.indentOut();
+ out.newlineOpt();
+ out.print("};");
+ out.newlineOpt();
out.print("var $stats = $wnd.__gwtStatsEvent ? function(a) {return $wnd.__gwtStatsEvent(a);} : null;");
out.newlineOpt();
out.print("$stats && $stats({moduleName:'" + context.getModuleName()
diff --git a/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js b/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js
index 1f35a26..7051125 100644
--- a/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js
+++ b/dev/core/src/com/google/gwt/core/linker/IFrameTemplate.js
@@ -295,7 +295,7 @@
millis:(new Date()).getTime(),
type: 'moduleRequested'
});
- iframe.contentWindow.location.replace(base + strongName);
+ iframe.contentWindow.location.replace(base + strongName + '.cache.html');
}
}
diff --git a/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java b/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java
index 50aa425..790c1cc 100644
--- a/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java
@@ -28,6 +28,7 @@
import com.google.gwt.dev.util.DefaultTextOutput;
import com.google.gwt.dev.util.Util;
+import java.util.Collection;
import java.util.Set;
/**
@@ -37,6 +38,7 @@
*/
@LinkerOrder(Order.PRIMARY)
public class SingleScriptLinker extends SelectionScriptLinker {
+ @Override
public String getDescription() {
return "Single Script";
}
@@ -52,6 +54,19 @@
}
@Override
+ protected Collection<EmittedArtifact> doEmitCompilation(TreeLogger logger,
+ LinkerContext context, CompilationResult result)
+ throws UnableToCompleteException {
+ if (result.getJavaScript().length != 1) {
+ logger.branch(TreeLogger.ERROR,
+ "The module must not have multiple fragments when using the "
+ + getDescription() + " Linker.", null);
+ throw new UnableToCompleteException();
+ }
+ return super.doEmitCompilation(logger, context, result);
+ }
+
+ @Override
protected EmittedArtifact emitSelectionScript(TreeLogger logger,
LinkerContext context, ArtifactSet artifacts)
throws UnableToCompleteException {
@@ -92,7 +107,14 @@
}
CompilationResult result = results.iterator().next();
- out.print(result.getJavaScript());
+ String[] js = result.getJavaScript();
+ if (js.length != 1) {
+ logger = logger.branch(TreeLogger.ERROR,
+ "The module must not have multiple fragments when using the "
+ + getDescription() + " Linker.", null);
+ throw new UnableToCompleteException();
+ }
+ out.print(js[0]);
// Add a callback to the selection script
out.newlineOpt();
@@ -128,8 +150,8 @@
* {@link #doEmitCompilation(TreeLogger, LinkerContext, CompilationResult).
*/
@Override
- protected String getModulePrefix(TreeLogger logger, LinkerContext context)
- throws UnableToCompleteException {
+ protected String getModulePrefix(TreeLogger logger, LinkerContext context,
+ String strongName) throws UnableToCompleteException {
throw new UnableToCompleteException();
}
diff --git a/dev/core/src/com/google/gwt/core/linker/XSLinker.java b/dev/core/src/com/google/gwt/core/linker/XSLinker.java
index ed544c2..8d2d9fc 100644
--- a/dev/core/src/com/google/gwt/core/linker/XSLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/XSLinker.java
@@ -18,31 +18,48 @@
import com.google.gwt.core.ext.LinkerContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.CompilationResult;
+import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.LinkerOrder;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import com.google.gwt.core.ext.linker.impl.SelectionScriptLinker;
import com.google.gwt.dev.About;
import com.google.gwt.dev.util.DefaultTextOutput;
+import java.util.Collection;
+
/**
* Generates a cross-site compatible bootstrap sequence.
*/
@LinkerOrder(Order.PRIMARY)
public class XSLinker extends SelectionScriptLinker {
-
+ @Override
public String getDescription() {
return "Cross-Site";
}
@Override
+ protected Collection<EmittedArtifact> doEmitCompilation(TreeLogger logger,
+ LinkerContext context, CompilationResult result)
+ throws UnableToCompleteException {
+ if (result.getJavaScript().length != 1) {
+ logger.branch(TreeLogger.ERROR,
+ "The module must not have multiple fragments when using the "
+ + getDescription() + " Linker.", null);
+ throw new UnableToCompleteException();
+ }
+ return super.doEmitCompilation(logger, context, result);
+ }
+
+ @Override
protected String getCompilationExtension(TreeLogger logger,
LinkerContext context) throws UnableToCompleteException {
return ".cache.js";
}
@Override
- protected String getModulePrefix(TreeLogger logger, LinkerContext context)
- throws UnableToCompleteException {
+ protected String getModulePrefix(TreeLogger logger, LinkerContext context,
+ String strongName) throws UnableToCompleteException {
DefaultTextOutput out = new DefaultTextOutput(context.isOutputCompact());
out.print("(function(){");
diff --git a/dev/core/src/com/google/gwt/dev/CompilePerms.java b/dev/core/src/com/google/gwt/dev/CompilePerms.java
index 17b1042..6ead2f6 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePerms.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePerms.java
@@ -158,7 +158,7 @@
}
}
- public static String compile(TreeLogger logger, Permutation permutation,
+ public static String[] compile(TreeLogger logger, Permutation permutation,
UnifiedAst unifiedAst) {
try {
return JavaToJavaScriptCompiler.compilePermutation(logger, unifiedAst,
@@ -255,9 +255,9 @@
PermutationCompiler multiThread = new PermutationCompiler(branch,
unifiedAst, perms, permsToRun);
multiThread.go(new ResultsHandler() {
- public void addResult(Permutation permutation, int permNum, String js)
+ public void addResult(Permutation permutation, int permNum, String[] js)
throws UnableToCompleteException {
- Util.writeStringAsFile(branch, makePermFilename(
+ Util.writeStringsAsFile(branch, makePermFilename(
options.getCompilerWorkDir(), permNum), js);
}
});
diff --git a/dev/core/src/com/google/gwt/dev/PermutationCompiler.java b/dev/core/src/com/google/gwt/dev/PermutationCompiler.java
index f89b102..4922b76 100644
--- a/dev/core/src/com/google/gwt/dev/PermutationCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/PermutationCompiler.java
@@ -39,7 +39,7 @@
* Hands back results as they are finished.
*/
public interface ResultsHandler {
- void addResult(Permutation permutation, int permNum, String js)
+ void addResult(Permutation permutation, int permNum, String[] js)
throws UnableToCompleteException;
}
@@ -62,7 +62,7 @@
/**
* Represents the task of compiling a single permutation.
*/
- private static final class PermutationTask implements Callable<String> {
+ private static final class PermutationTask implements Callable<String[]> {
private static void logProperties(TreeLogger logger,
StaticPropertyOracle[] propOracles) {
for (StaticPropertyOracle propOracle : propOracles) {
@@ -92,7 +92,7 @@
this.permNum = permNum;
}
- public String call() throws Exception {
+ public String[] call() throws Exception {
PerfLogger.start("Permutation #" + permNum);
try {
TreeLogger branch = logger.branch(TreeLogger.TRACE, "Permutation #"
@@ -139,14 +139,14 @@
* A Result for a permutation that succeeded.
*/
private static final class SuccessResult extends Result {
- private final String js;
+ private final String[] js;
- public SuccessResult(Permutation perm, int permNum, String js) {
+ public SuccessResult(Permutation perm, int permNum, String[] result) {
super(perm, permNum);
- this.js = js;
+ this.js = result;
}
- public String getJs() {
+ public String[] getJs() {
return js;
}
}
@@ -201,7 +201,7 @@
boolean definitelyFinalThread = (threadCount.get() == 1);
try {
- String result = currentTask.call();
+ String[] result = currentTask.call();
results.add(new SuccessResult(currentTask.getPermutation(),
currentTask.getPermNum(), result));
} catch (OutOfMemoryError e) {
@@ -353,7 +353,7 @@
assert threadCount.get() == 0;
return;
} else if (result instanceof SuccessResult) {
- String js = ((SuccessResult) result).getJs();
+ String[] js = ((SuccessResult) result).getJs();
handler.addResult(result.getPermutation(), result.getPermNum(), js);
} else if (result instanceof FailedResult) {
FailedResult failedResult = (FailedResult) result;
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index 8465ac5..de667a7 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -30,11 +30,12 @@
import com.google.gwt.dev.jdt.RebindOracle;
import com.google.gwt.dev.jdt.RebindPermutationOracle;
import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd;
-import com.google.gwt.dev.jjs.UnifiedAst;
import com.google.gwt.dev.jjs.JJSOptions;
import com.google.gwt.dev.jjs.JJSOptionsImpl;
import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
import com.google.gwt.dev.jjs.JsOutputOption;
+import com.google.gwt.dev.jjs.UnifiedAst;
+import com.google.gwt.dev.jjs.impl.FragmentLoaderCreator;
import com.google.gwt.dev.shell.StandardRebindOracle;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.ArgHandlerDisableAggressiveOptimization;
@@ -258,9 +259,10 @@
DistillerRebindPermutationOracle rpo = new DistillerRebindPermutationOracle(
module, generatedArtifacts, new PropertyPermutations(
module.getProperties()), genDir, generatorResourcesDir);
-
+ FragmentLoaderCreator fragmentLoaderCreator = new FragmentLoaderCreator(
+ module.getCompilationState(), module, genDir, generatorResourcesDir, generatedArtifacts);
WebModeCompilerFrontEnd frontEnd = new WebModeCompilerFrontEnd(
- module.getCompilationState(), rpo);
+ module.getCompilationState(), rpo, fragmentLoaderCreator);
UnifiedAst unifiedAst = JavaToJavaScriptCompiler.precompile(logger,
frontEnd, declEntryPts, jjsOptions, rpo.getPermuationCount() == 1);
@@ -316,9 +318,10 @@
DistillerRebindPermutationOracle rpo = new DistillerRebindPermutationOracle(
module, generatorArtifacts, new PropertyPermutations(
module.getProperties()), genDir, generatorResourcesDir);
-
+ FragmentLoaderCreator fragmentLoaderCreator = new FragmentLoaderCreator(
+ module.getCompilationState(), module, genDir, generatorResourcesDir, generatorArtifacts);
WebModeCompilerFrontEnd frontEnd = new WebModeCompilerFrontEnd(
- module.getCompilationState(), rpo);
+ module.getCompilationState(), rpo, fragmentLoaderCreator);
JavaToJavaScriptCompiler.precompile(logger, frontEnd, declEntryPts,
jjsOptions, true);
return true;
diff --git a/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java b/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
index e9e1eab..e4baf2c 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
@@ -26,8 +26,10 @@
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -42,21 +44,22 @@
* Information about the site at which a rebind request was found, used to
* report problems.
*/
- public static class DeferredBindingSite {
+ public static class MessageSendSite {
public final MessageSend messageSend;
public final Scope scope;
- public DeferredBindingSite(MessageSend messageSend, Scope scope) {
+ public MessageSendSite(MessageSend messageSend, Scope scope) {
this.messageSend = messageSend;
this.scope = scope;
}
}
- public static final String REBIND_MAGIC_CLASS = "com.google.gwt.core.client.GWT";
+ public static final String MAGIC_CLASS = "com.google.gwt.core.client.GWT";
public static final String REBIND_MAGIC_METHOD = "create";
+ public static final String ASYNC_MAGIC_METHOD = "runAsync";
- public static void reportRebindProblem(DeferredBindingSite site,
+ public static void reportRebindProblem(MessageSendSite site,
String message) {
MessageSend messageSend = site.messageSend;
Scope scope = site.scope;
@@ -65,52 +68,77 @@
GWTProblem.recordInCud(messageSend, cud, message, null);
}
- private final Map<String, DeferredBindingSite> results = new HashMap<String, DeferredBindingSite>();
+ private final Map<String, MessageSendSite> results = new HashMap<String, MessageSendSite>();
+ private final List<MessageSendSite> runAsyncCalls = new ArrayList<MessageSendSite>();
+
+ @Override
public void endVisit(MessageSend messageSend, BlockScope scope) {
if (messageSend.binding == null) {
// Some sort of problem.
- //
return;
}
String methodName = String.valueOf(messageSend.selector);
- if (!methodName.equals(REBIND_MAGIC_METHOD)) {
- // Not the create() method.
- //
+ boolean rebindMagicMethod = methodName.equals(REBIND_MAGIC_METHOD);
+ boolean asyncMagicMethod = methodName.equals(ASYNC_MAGIC_METHOD);
+ if (!(rebindMagicMethod || asyncMagicMethod)) {
+ // Not the create() method or the runAsync() method.
return;
}
char[][] targetClass = messageSend.binding.declaringClass.compoundName;
String targetClassName = CharOperation.toString(targetClass);
- if (!targetClassName.equals(REBIND_MAGIC_CLASS)) {
+ if (!targetClassName.equals(MAGIC_CLASS)) {
// Not being called on the Rebind class.
return;
}
- DeferredBindingSite site = new DeferredBindingSite(messageSend, scope);
+ MessageSendSite site = new MessageSendSite(messageSend, scope);
Expression[] args = messageSend.arguments;
- if (args.length != 1) {
- reportRebindProblem(site, "GWT.create() should take exactly one argument");
+ if (rebindMagicMethod) {
+ if (args.length != 1) {
+ reportRebindProblem(site,
+ "GWT.create() should take exactly one argument");
+ return;
+ }
+
+ if (!(args[0] instanceof ClassLiteralAccess)) {
+ reportRebindProblem(site,
+ "Only class literals may be used as arguments to GWT.create()");
+ return;
+ }
+ } else {
+ assert asyncMagicMethod;
+ if (args.length != 1) {
+ reportRebindProblem(site,
+ "GWT.runAsync() should take exactly one argument");
+ return;
+ }
+ }
+
+ if (asyncMagicMethod) {
+ runAsyncCalls.add(new MessageSendSite(messageSend, scope));
return;
}
- Expression arg = args[0];
- if (!(arg instanceof ClassLiteralAccess)) {
- reportRebindProblem(site,
- "Only class literals may be used as arguments to GWT.create()");
- return;
- }
-
- ClassLiteralAccess cla = (ClassLiteralAccess) arg;
+ ClassLiteralAccess cla = (ClassLiteralAccess) args[0];
String typeName = String.valueOf(cla.targetType.readableName());
+
if (!results.containsKey(typeName)) {
results.put(typeName, site);
}
}
- public Map<String, DeferredBindingSite> getSites() {
+ /**
+ * Return the calls to GWT.runAsync() that were seen.
+ */
+ public List<MessageSendSite> getRunAsyncSites() {
+ return runAsyncCalls;
+ }
+
+ public Map<String, MessageSendSite> getSites() {
return Collections.unmodifiableMap(results);
}
}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java b/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
index 8566a6d..28185c6 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
@@ -20,7 +20,8 @@
import com.google.gwt.dev.javac.CompilationState;
import com.google.gwt.dev.javac.CompiledClass;
import com.google.gwt.dev.javac.JdtCompiler.CompilationUnitAdapter;
-import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor.DeferredBindingSite;
+import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor.MessageSendSite;
+import com.google.gwt.dev.jjs.impl.FragmentLoaderCreator;
import com.google.gwt.dev.util.Empty;
import com.google.gwt.dev.util.JsniRef;
import com.google.gwt.dev.util.Util;
@@ -41,12 +42,21 @@
*/
public class WebModeCompilerFrontEnd extends AbstractCompiler {
+ private final FragmentLoaderCreator fragmentLoaderCreator;
private final RebindPermutationOracle rebindPermOracle;
+ /**
+ * Construct a WebModeCompilerFrontEnd. The reason a
+ * {@link FragmentLoaderCreator} needs to be passed in is that it uses
+ * generator infrastructure, and therefore needs access to more parts of the
+ * compiler than WebModeCompilerFrontEnd currently has.
+ */
public WebModeCompilerFrontEnd(CompilationState compilationState,
- RebindPermutationOracle rebindPermOracle) {
+ RebindPermutationOracle rebindPermOracle,
+ FragmentLoaderCreator fragmentLoaderCreator) {
super(compilationState, false);
this.rebindPermOracle = rebindPermOracle;
+ this.fragmentLoaderCreator = fragmentLoaderCreator;
}
public CompilationUnitDeclaration[] getCompilationUnitDeclarations(
@@ -90,6 +100,7 @@
/**
* Pull in types referenced only via JSNI.
*/
+ @Override
protected String[] doFindAdditionalTypesUsingJsni(TreeLogger logger,
CompilationUnitDeclaration cud) {
FindJsniRefVisitor v = new FindJsniRefVisitor();
@@ -109,20 +120,19 @@
/**
* Pull in types implicitly referenced through rebind answers.
*/
+ @Override
protected String[] doFindAdditionalTypesUsingRebinds(TreeLogger logger,
CompilationUnitDeclaration cud) {
Set<String> dependentTypeNames = new HashSet<String>();
// Find all the deferred binding request types.
- //
FindDeferredBindingSitesVisitor v = new FindDeferredBindingSitesVisitor();
cud.traverse(v, cud.scope);
- Map<String, DeferredBindingSite> requestedTypes = v.getSites();
+ Map<String, MessageSendSite> requestedTypes = v.getSites();
// For each, ask the host for every possible deferred binding answer.
- //
for (String reqType : requestedTypes.keySet()) {
- DeferredBindingSite site = requestedTypes.get(reqType);
+ MessageSendSite site = requestedTypes.get(reqType);
try {
String[] resultTypes = rebindPermOracle.getAllPossibleRebindAnswers(
@@ -134,7 +144,6 @@
// This causes the compiler to find the additional type, possibly
// winding its back to ask for the compilation unit from the source
// oracle.
- //
ReferenceBinding type = resolvePossiblyNestedType(typeName);
// Sanity check rebind results.
@@ -180,6 +189,27 @@
"Failed to resolve '" + reqType + "' via deferred binding");
}
}
+
+ /*
+ * Create a a fragment loader for each GWT.runAsync call. They must be
+ * created now, rather than in ReplaceRunAsyncs, because all generated
+ * classes need to be created before GenerateJavaAST. Note that the loaders
+ * created are not yet associated with the specific sites. The present task
+ * is only to make sure that enough loaders exist. The real association
+ * between loaders and runAsync sites will be made in ReplaceRunAsyncs.
+ */
+ for (MessageSendSite site : v.getRunAsyncSites()) {
+ FragmentLoaderCreator loaderCreator = fragmentLoaderCreator;
+ String resultType;
+ try {
+ resultType = loaderCreator.create(logger);
+ dependentTypeNames.add(resultType);
+ } catch (UnableToCompleteException e) {
+ FindDeferredBindingSitesVisitor.reportRebindProblem(site,
+ "Failed to create a runAsync fragment loader");
+ }
+ }
+
return dependentTypeNames.toArray(Empty.STRINGS);
}
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index 0448a3c..15d18c1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -25,6 +25,7 @@
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
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.JGwtCreate;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodBody;
@@ -39,13 +40,17 @@
import com.google.gwt.dev.jjs.impl.BuildTypeMap;
import com.google.gwt.dev.jjs.impl.CastNormalizer;
import com.google.gwt.dev.jjs.impl.CatchBlockNormalizer;
+import com.google.gwt.dev.jjs.impl.CodeSplitter;
import com.google.gwt.dev.jjs.impl.DeadCodeElimination;
import com.google.gwt.dev.jjs.impl.EqualityNormalizer;
import com.google.gwt.dev.jjs.impl.Finalizer;
import com.google.gwt.dev.jjs.impl.FixAssignmentToUnbox;
+import com.google.gwt.dev.jjs.impl.FragmentLoaderCreator;
import com.google.gwt.dev.jjs.impl.GenerateJavaAST;
import com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST;
+import com.google.gwt.dev.jjs.impl.JavaAndJavaScript;
import com.google.gwt.dev.jjs.impl.JavaScriptObjectNormalizer;
+import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
import com.google.gwt.dev.jjs.impl.JsoDevirtualizer;
import com.google.gwt.dev.jjs.impl.LongCastNormalizer;
import com.google.gwt.dev.jjs.impl.LongEmulationNormalizer;
@@ -56,6 +61,7 @@
import com.google.gwt.dev.jjs.impl.Pruner;
import com.google.gwt.dev.jjs.impl.RecordRebinds;
import com.google.gwt.dev.jjs.impl.ReplaceRebinds;
+import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs;
import com.google.gwt.dev.jjs.impl.ResolveRebinds;
import com.google.gwt.dev.jjs.impl.TypeMap;
import com.google.gwt.dev.jjs.impl.TypeTightener;
@@ -70,7 +76,9 @@
import com.google.gwt.dev.js.JsSymbolResolver;
import com.google.gwt.dev.js.JsUnusedFunctionRemover;
import com.google.gwt.dev.js.JsVerboseNamer;
+import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.util.DefaultTextOutput;
import com.google.gwt.dev.util.PerfLogger;
import com.google.gwt.dev.util.Util;
@@ -81,6 +89,7 @@
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -105,9 +114,16 @@
* @throws UnableToCompleteException if an error other than
* {@link OutOfMemoryError} occurs
*/
- public static String compilePermutation(TreeLogger logger,
+ public static String[] compilePermutation(TreeLogger logger,
UnifiedAst unifiedAst, Map<String, String> rebindAnswers)
throws UnableToCompleteException {
+ return compilePermutationToJavaAndJavaScript(logger, unifiedAst,
+ rebindAnswers).jscode;
+ }
+
+ public static JavaAndJavaScript compilePermutationToJavaAndJavaScript(
+ TreeLogger logger, UnifiedAst unifiedAst,
+ Map<String, String> rebindAnswers) throws UnableToCompleteException {
try {
if (JProgram.isTracingEnabled()) {
System.out.println("------------------------------------------------------------");
@@ -143,10 +159,8 @@
// (7) Generate a JavaScript code DOM from the Java type declarations
jprogram.typeOracle.recomputeClinits();
- GenerateJavaScriptAST.exec(jprogram, jsProgram, options.getOutput());
-
- // Allow GC.
- jprogram = null;
+ final JavaToJavaScriptMap map = GenerateJavaScriptAST.exec(jprogram,
+ jsProgram, options.getOutput());
// (8) Normalize the JS AST.
// Fix invalid constructs created during JS AST gen.
@@ -173,23 +187,33 @@
}
// (10) Obfuscate
+ final Map<JsName, String> stringLiteralMap;
switch (options.getOutput()) {
case OBFUSCATED:
- JsStringInterner.exec(jsProgram);
+ stringLiteralMap = JsStringInterner.exec(jsProgram);
JsObfuscateNamer.exec(jsProgram);
break;
case PRETTY:
// We don't intern strings in pretty mode to improve readability
+ stringLiteralMap = new HashMap<JsName, String>();
JsPrettyNamer.exec(jsProgram);
break;
case DETAILED:
- JsStringInterner.exec(jsProgram);
+ stringLiteralMap = JsStringInterner.exec(jsProgram);
JsVerboseNamer.exec(jsProgram);
break;
default:
throw new InternalCompilerException("Unknown output mode");
}
+ JavaToJavaScriptMap postStringInterningMap = addStringLiteralMap(map,
+ stringLiteralMap);
+
+ // (10.5) Split up the program into fragments
+ if (options.isAggressivelyOptimize()) {
+ CodeSplitter.exec(jprogram, jsProgram, postStringInterningMap);
+ }
+
// (11) Perform any post-obfuscation normalizations.
// Work around an IE7 bug,
@@ -197,11 +221,18 @@
JsIEBlockSizeVisitor.exec(jsProgram);
// (12) Generate the final output text.
- DefaultTextOutput out = new DefaultTextOutput(
- options.getOutput().shouldMinimize());
- JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out);
- v.accept(jsProgram);
- return out.toString();
+ String[] js = new String[jsProgram.getFragmentCount()];
+ for (int i = 0; i < js.length; i++) {
+
+ DefaultTextOutput out = new DefaultTextOutput(
+ options.getOutput().shouldMinimize());
+ JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out);
+ v.accept(jsProgram.getFragmentBlock(i));
+ js[i] = out.toString();
+ }
+
+ return new JavaAndJavaScript(jprogram, jsProgram, js,
+ postStringInterningMap);
} catch (Throwable e) {
throw logAndTranslateException(logger, e);
}
@@ -239,6 +270,7 @@
}
allEntryPoints.addAll(JProgram.CODEGEN_TYPES_SET);
allEntryPoints.addAll(JProgram.INDEX_TYPES_SET);
+ allEntryPoints.add(FragmentLoaderCreator.ASYNC_FRAGMENT_LOADER);
// Compile the source and get the compiler so we can get the parse tree
//
@@ -307,6 +339,11 @@
// Replace GWT.create calls with JGwtCreate nodes.
ReplaceRebinds.exec(logger, jprogram, rpo);
+ // Fix up GWT.runAsync()
+ if (options.isAggressivelyOptimize()) {
+ ReplaceRunAsyncs.exec(logger, jprogram);
+ }
+
// Resolve entry points, rebinding non-static entry points.
findEntryPoints(logger, rpo, declEntryPts, jprogram);
@@ -386,6 +423,40 @@
} while (didChange);
}
+ private static JavaToJavaScriptMap addStringLiteralMap(
+ final JavaToJavaScriptMap map, final Map<JsName, String> stringLiteralMap) {
+ JavaToJavaScriptMap postStringInterningMap = new JavaToJavaScriptMap() {
+ public JsName nameForMethod(JMethod method) {
+ return map.nameForMethod(method);
+ }
+
+ public JsName nameForType(JReferenceType type) {
+ return map.nameForType(type);
+ }
+
+ public JField nameToField(JsName name) {
+ return map.nameToField(name);
+ }
+
+ public JMethod nameToMethod(JsName name) {
+ return map.nameToMethod(name);
+ }
+
+ public String stringLiteralForName(JsName name) {
+ return stringLiteralMap.get(name);
+ }
+
+ public JReferenceType typeForStatement(JsStatement stat) {
+ return map.typeForStatement(stat);
+ }
+
+ public JMethod vtableInitToMethod(JsStatement stat) {
+ return map.vtableInitToMethod(stat);
+ }
+ };
+ return postStringInterningMap;
+ }
+
private static void checkForErrors(TreeLogger logger,
CompilationUnitDeclaration[] cuds, boolean itemizeErrors)
throws UnableToCompleteException {
@@ -640,7 +711,8 @@
isStatsAvailableMethod);
JMethodCall onModuleStartCall = new JMethodCall(program, sourceInfo, null,
onModuleStartMethod);
- onModuleStartCall.getArgs().add(program.getLiteralString(sourceInfo, mainClassName));
+ onModuleStartCall.getArgs().add(
+ program.getLiteralString(sourceInfo, mainClassName));
JBinaryOperation amp = new JBinaryOperation(program, sourceInfo,
program.getTypePrimitiveBoolean(), JBinaryOperator.AND, availableCall,
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index d8d528c..0ef2689 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -67,6 +67,8 @@
"com.google.gwt.core.client.GWT",
"com.google.gwt.core.client.JavaScriptObject",
"com.google.gwt.lang.ClassLiteralHolder",
+ "com.google.gwt.core.client.RunAsyncCallback",
+ "com.google.gwt.core.client.AsyncFragmentLoader",
"com.google.gwt.lang.EntryMethodHolder",}));
static final Map<String, Set<String>> traceMethods = new HashMap<String, Set<String>>();
@@ -150,7 +152,13 @@
public final List<JClassType> codeGenTypes = new ArrayList<JClassType>();
- public final List<JMethod> entryMethods = new ArrayList<JMethod>();
+ /**
+ * There is a list containing the main entry methods as well as the entry methods for
+ * each split point. The main entry methods are at entry 0 of this list. Split
+ * points are numbered sequentially from 1, and the entry methods for split point
+ * <em>i</em> are at entry <em>i</em> of this list.
+ */
+ public final List<List<JMethod>> entryMethods = new ArrayList<List<JMethod>>();
public final Map<String, HasEnclosingType> jsniMap = new HashMap<String, HasEnclosingType>();
@@ -271,9 +279,17 @@
}
public void addEntryMethod(JMethod entryPoint) {
+ addEntryMethod(entryPoint, 0);
+ }
+
+ public void addEntryMethod(JMethod entryPoint, int fragmentNumber) {
assert entryPoint.isStatic();
- if (!entryMethods.contains(entryPoint)) {
- entryMethods.add(entryPoint);
+ while (fragmentNumber >= entryMethods.size()) {
+ entryMethods.add(new ArrayList<JMethod>());
+ }
+ List<JMethod> methods = entryMethods.get(fragmentNumber);
+ if (!methods.contains(entryPoint)) {
+ methods.add(entryPoint);
}
}
@@ -406,12 +422,11 @@
public JMethod createMethod(SourceInfo info, char[] name,
JReferenceType enclosingType, JType returnType, boolean isAbstract,
boolean isStatic, boolean isFinal, boolean isPrivate, boolean isNative) {
- assert (name != null);
+ String sname = String.valueOf(name);
+ assert (sname != null);
assert (enclosingType != null);
assert (returnType != null);
assert (!isAbstract || !isNative);
-
- String sname = String.valueOf(name);
JMethod x = new JMethod(this, info, sname, enclosingType, returnType,
isAbstract, isStatic, isFinal, isPrivate);
if (isNative) {
@@ -492,14 +507,30 @@
return allArrayTypes;
}
+ public List<JMethod> getAllEntryMethods() {
+ List<JMethod> allEntryMethods = new ArrayList<JMethod>();
+ for (List<JMethod> entries : entryMethods) {
+ allEntryMethods.addAll(entries);
+ }
+ return allEntryMethods;
+ }
+
public List<JReferenceType> getDeclaredTypes() {
return allTypes;
}
+ public int getEntryCount(int fragment) {
+ return entryMethods.get(fragment).size();
+ }
+
public JThisRef getExprThisRef(SourceInfo info, JClassType enclosingType) {
return new JThisRef(this, info, enclosingType);
}
+ public int getFragmentCount() {
+ return entryMethods.size();
+ }
+
public JReferenceType getFromTypeMap(String qualifiedBinaryOrSourceName) {
String srcTypeName = qualifiedBinaryOrSourceName.replace('$', '.');
return typeNameMap.get(srcTypeName);
@@ -842,9 +873,9 @@
}
/**
- * If <code>method</code> is a static impl method, returns the instance method
- * that <code>method</code> is the implementation of. Otherwise, returns
- * <code>null</code>.
+ * If <code>method</code> is a static impl method, returns the instance
+ * method that <code>method</code> is the implementation of. Otherwise,
+ * returns <code>null</code>.
*/
public JMethod staticImplFor(JMethod method) {
return staticToInstanceMap.get(method);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index b7774a2..8bfddfa 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -138,6 +138,35 @@
}
/**
+ * Determine whether a type is instantiated, given an assumed list of
+ * instantiated types.
+ *
+ * @param type any type
+ * @param instantiatedTypes a set of types assumed to be instantiated. If
+ * <code>null</code>, then there are no assumptions about which
+ * types are instantiated.
+ * @return whether the type is instantiated
+ */
+ private static boolean isInstantiatedType(JReferenceType type,
+ Set<JReferenceType> instantiatedTypes) {
+ if (instantiatedTypes == null) {
+ return true;
+ }
+
+ if (type instanceof JNullType) {
+ return true;
+ }
+
+ if (type instanceof JArrayType) {
+ JArrayType arrayType = (JArrayType) type;
+ if (arrayType.getLeafType() instanceof JNullType) {
+ return true;
+ }
+ }
+ return instantiatedTypes.contains(type);
+ }
+
+ /**
* Compare two methods based on name and original argument types
* {@link JMethod#getOriginalParamTypes()}. Note that nothing special is done
* here regarding methods with type parameters in their argument lists. The
@@ -389,14 +418,19 @@
return getOrCreate(superInterfaceMap, type).contains(qType);
}
+ public Set<JMethod> getAllOverrides(JMethod method) {
+ return getAllOverrides(method, instantiatedTypes);
+ }
+
/**
* References to any methods which this method implementation might override
* or implement in any instantiable class.
*/
- public Set<JMethod> getAllOverrides(JMethod method) {
+ public Set<JMethod> getAllOverrides(JMethod method,
+ Set<JReferenceType> instantiatedTypes) {
Set<JMethod> results = new HashSet<JMethod>();
getAllRealOverrides(method, results);
- getAllVirtualOverrides(method, results);
+ getAllVirtualOverrides(method, instantiatedTypes, results);
return results;
}
@@ -419,7 +453,7 @@
*/
public Set<JMethod> getAllVirtualOverrides(JMethod method) {
Set<JMethod> results = new HashSet<JMethod>();
- getAllVirtualOverrides(method, results);
+ getAllVirtualOverrides(method, instantiatedTypes, results);
return results;
}
@@ -436,22 +470,7 @@
}
public boolean isInstantiatedType(JReferenceType type) {
- if (instantiatedTypes == null) {
- // The instantiated types have not yet been computed.
- return true;
- }
-
- if (type instanceof JNullType) {
- return true;
- }
-
- if (type instanceof JArrayType) {
- JArrayType arrayType = (JArrayType) type;
- if (arrayType.getLeafType() instanceof JNullType) {
- return true;
- }
- }
- return instantiatedTypes.contains(type);
+ return isInstantiatedType(type, instantiatedTypes);
}
/**
@@ -637,19 +656,19 @@
}
}
- private void getAllRealOverrides(JMethod method, Set<JMethod> results) {
+ private void getAllRealOverrides(JMethod method,
+ Set<JMethod> results) {
for (JMethod possibleOverride : method.overrides) {
- // if (instantiatedTypes.contains(possibleOverride.getEnclosingType())) {
results.add(possibleOverride);
- // }
}
}
- private void getAllVirtualOverrides(JMethod method, Set<JMethod> results) {
+ private void getAllVirtualOverrides(JMethod method,
+ Set<JReferenceType> instantiatedTypes, Set<JMethod> results) {
Map<JClassType, Set<JMethod>> overrideMap = getOrCreateMap(virtualUpRefMap,
method);
for (JClassType classType : overrideMap.keySet()) {
- if (isInstantiatedType(classType)) {
+ if (isInstantiatedType(classType, instantiatedTypes)) {
Set<JMethod> set = overrideMap.get(classType);
results.addAll(set);
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
index 3001dd7..a485615 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
@@ -44,6 +44,13 @@
return null;
}
+ public JExpression getInitializer() {
+ if (declStmt != null) {
+ return declStmt.getInitializer();
+ }
+ return null;
+ }
+
public String getName() {
return name;
}
@@ -68,11 +75,4 @@
type = newType;
}
- protected JExpression getInitializer() {
- if (declStmt != null) {
- return declStmt.getInitializer();
- }
- return null;
- }
-
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodBody.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodBody.java
index ccc613f..7095710 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodBody.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodBody.java
@@ -20,10 +20,16 @@
import com.google.gwt.dev.jjs.ast.JAbstractMethodBody;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.js.ast.JsContext;
+import com.google.gwt.dev.js.ast.JsExpression;
import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsStringLiteral;
+import com.google.gwt.dev.js.ast.JsVisitor;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Represents a the body of a method. Can be Java or JSNI.
@@ -31,8 +37,8 @@
public class JsniMethodBody extends JAbstractMethodBody {
public final List<JsniFieldRef> jsniFieldRefs = new ArrayList<JsniFieldRef>();
-
public final List<JsniMethodRef> jsniMethodRefs = new ArrayList<JsniMethodRef>();
+ private final Set<String> stringLiterals = new HashSet<String>();
private JsFunction jsFunction = null;
@@ -45,6 +51,11 @@
return jsFunction;
}
+ public Set<String> getUsedStrings() {
+ return stringLiterals;
+ }
+
+ @Override
public boolean isNative() {
return true;
}
@@ -52,6 +63,13 @@
public void setFunc(JsFunction jsFunction) {
assert (this.jsFunction == null);
this.jsFunction = jsFunction;
+ class RecordStrings extends JsVisitor {
+ @Override
+ public void endVisit(JsStringLiteral lit, JsContext<JsExpression> ctx) {
+ stringLiterals.add(lit.getValue());
+ }
+ }
+ (new RecordStrings()).accept(jsFunction);
}
public void traverse(JVisitor visitor, Context ctx) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
new file mode 100644
index 0000000..725804b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright 2008 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.Context;
+import com.google.gwt.dev.jjs.ast.JClassLiteral;
+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.JNode;
+import com.google.gwt.dev.jjs.ast.JProgram;
+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.LivenessPredicate;
+import com.google.gwt.dev.jjs.impl.FragmentExtractor.NothingAlivePredicate;
+import com.google.gwt.dev.jjs.impl.FragmentExtractor.StatementLogger;
+import com.google.gwt.dev.js.ast.JsBlock;
+import com.google.gwt.dev.js.ast.JsExprStmt;
+import com.google.gwt.dev.js.ast.JsExpression;
+import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsStatement;
+import com.google.gwt.dev.util.PerfLogger;
+
+import java.util.ArrayList;
+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;
+
+/**
+ * <p>
+ * Divides the code in a {@link JsProgram} into multiple fragments. The initial
+ * fragment is sufficient to run all of the program's functionality except for
+ * 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.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 one fragment for each call to
+ * <code>runAsync()</code>. Each such fragment holds the code that is
+ * exclusively needed by that particular call to <code>runAsync()</code>. Any
+ * code needed by two or more calls to <code>runAsync()</code> is placed in
+ * the initial fragment.
+ * </p>
+ */
+public class CodeSplitter {
+ /**
+ * A statement logger that immediately prints out everything live that it
+ * sees.
+ */
+ public class EchoStatementLogger implements StatementLogger {
+ public void logStatement(JsStatement stat, boolean isIncluded) {
+ if (isIncluded) {
+ if (stat instanceof JsExprStmt) {
+ JsExpression expr = ((JsExprStmt) stat).getExpression();
+ if (expr instanceof JsFunction) {
+ JsFunction func = (JsFunction) expr;
+ if (func.getName() != null) {
+ JMethod method = map.nameToMethod(func.getName());
+ if (method != null && method.getEnclosingType() != null) {
+ System.out.println(method.getEnclosingType().getName() + "."
+ + JProgram.getJsniSig(method));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * A map from program atoms to the fragment they should be placed in.
+ */
+ 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>();
+ public Map<JReferenceType, Integer> types = new HashMap<JReferenceType, Integer>();
+ }
+
+ /**
+ * 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 FragmentMapLivenessPredicate implements
+ LivenessPredicate {
+ private final int fragment;
+ private final FragmentMap fragmentMap;
+
+ public FragmentMapLivenessPredicate(FragmentMap fragmentMap, int fragment) {
+ this.fragmentMap = fragmentMap;
+ this.fragment = fragment;
+ }
+
+ public boolean isLive(JField field) {
+ return checkMap(fragmentMap.fields, field);
+ }
+
+ public boolean isLive(JMethod method) {
+ return checkMap(fragmentMap.methods, method);
+ }
+
+ public boolean isLive(JReferenceType type) {
+ return checkMap(fragmentMap.types, type);
+ }
+
+ public boolean isLive(String literal) {
+ return checkMap(fragmentMap.strings, literal);
+ }
+
+ public boolean miscellaneousStatementsAreLive() {
+ return true;
+ }
+
+ private <T> boolean checkMap(Map<T, Integer> map, T x) {
+ Integer entryForX = map.get(x);
+ if (entryForX == null) {
+ // unrecognized items are always live
+ return true;
+ } else {
+ return (fragment == entryForX) || (entryForX == 0);
+ }
+ }
+ }
+
+ /**
+ * 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";
+
+ public static void exec(JProgram jprogram, JsProgram jsprogram,
+ JavaToJavaScriptMap map) {
+ new CodeSplitter(jprogram, jsprogram, map).execImpl();
+ }
+
+ private static <T> int getOrZero(Map<T, Integer> map, T key) {
+ Integer value = map.get(key);
+ return (value == null) ? 0 : value;
+ }
+
+ private static <T> void updateMap(int entry, Map<T, Integer> map,
+ Set<?> liveWithoutEntry, Iterable<T> all) {
+ for (T each : all) {
+ if (!liveWithoutEntry.contains(each)) {
+ /*
+ * Note that it is fine to overwrite a preexisting entry in the map. If
+ * an atom is dead until split point i has been reached, and is also
+ * dead until entry j has been reached, then it is dead until both have
+ * been reached. Thus, it can be downloaded along with either i's or j's
+ * code.
+ */
+ map.put(each, entry);
+ }
+ }
+ }
+
+ private final Map<JField, JClassLiteral> fieldToLiteralOfClass;
+ private JProgram jprogram;
+ private JsProgram jsprogram;
+ private final boolean logging;
+ private JavaToJavaScriptMap map;
+ private final int numEntries;
+
+ private CodeSplitter(JProgram jprogram, JsProgram jsprogram,
+ JavaToJavaScriptMap map) {
+ this.jprogram = jprogram;
+ this.jsprogram = jsprogram;
+ this.map = map;
+ numEntries = jprogram.entryMethods.size();
+ logging = Boolean.getBoolean(PROP_LOG_FRAGMENT_MAP);
+ fieldToLiteralOfClass = FragmentExtractor.buildFieldToClassLiteralMap(jprogram);
+ }
+
+ /**
+ * For each split point other than the initial one (0), compute a CFA that
+ * traces every other split point.
+ */
+ private List<ControlFlowAnalyzer> computeAllButOneCfas() {
+ // Reusing initiallyLive for each entry gives a significant speedup
+ ControlFlowAnalyzer initiallyLive = new ControlFlowAnalyzer(jprogram);
+ traverseEntry(initiallyLive, 0);
+ initiallyLive.finishTraversal();
+
+ List<ControlFlowAnalyzer> allButOnes = new ArrayList<ControlFlowAnalyzer>(
+ numEntries - 1);
+
+ for (int entry = 1; entry < numEntries; entry++) {
+ ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(initiallyLive);
+ traverseAllButEntry(cfa, entry);
+ cfa.finishTraversal();
+ allButOnes.add(cfa);
+ }
+
+ return allButOnes;
+ }
+
+ /**
+ * Compute a CFA that covers the entire live code of the program.
+ */
+ private ControlFlowAnalyzer computeCompleteCfa() {
+ ControlFlowAnalyzer everything = new ControlFlowAnalyzer(jprogram);
+ for (int entry = 0; entry < numEntries; entry++) {
+ traverseEntry(everything, entry);
+ }
+ everything.finishTraversal();
+ return everything;
+ }
+
+ private void execImpl() {
+ if (numEntries == 1) {
+ // Don't do anything if there is no call to runAsync
+ return;
+ }
+
+ PerfLogger.start("CodeSplitter");
+
+ // Map code to the fragments
+ FragmentMap fragmentMap = mapFragments();
+
+ List<List<JsStatement>> fragmentStats = new ArrayList<List<JsStatement>>(
+ numEntries);
+
+ // Save the extractor for reuse
+ FragmentExtractor fragmentExtractor = new FragmentExtractor(jprogram,
+ jsprogram, map);
+
+ // Extract the code for each fragment, according to fragmentMap
+ for (int i = 0; i < numEntries; i++) {
+ LivenessPredicate pred = new FragmentMapLivenessPredicate(fragmentMap, i);
+
+ LivenessPredicate alreadyLoaded;
+ if (i == 0) {
+ alreadyLoaded = new NothingAlivePredicate();
+ } else {
+ alreadyLoaded = new FragmentMapLivenessPredicate(fragmentMap, 0);
+ }
+
+ if (logging) {
+ System.out.println();
+ System.out.println("==== Fragment " + i + " ====");
+ fragmentExtractor.setStatementLogger(new EchoStatementLogger());
+ }
+
+ List<JsStatement> entryStats = fragmentExtractor.extractStatements(pred,
+ alreadyLoaded);
+
+ if (i > 0) {
+ /*
+ * The fragment mapper drops all calls to entry methods. Add them back.
+ */
+ fragmentExtractor.addCallsToEntryMethods(i, entryStats);
+ }
+
+ fragmentStats.add(entryStats);
+ }
+
+ // Install the new statements in the program fragments
+ jsprogram.setFragmentCount(numEntries);
+ for (int i = 0; i < fragmentStats.size(); i++) {
+ JsBlock fragBlock = jsprogram.getFragmentBlock(i);
+ fragBlock.getStatements().clear();
+ fragBlock.getStatements().addAll(fragmentStats.get(i));
+ }
+
+ PerfLogger.end();
+ }
+
+ /**
+ * Mostly it is okay to load code before it is needed. However, there are some
+ * exceptions, where merely loading a code atom requires that some other atom
+ * has also been loaded. To address such situations, move the load-time
+ * dependencies to fragment 0, so they are sure to be available.
+ */
+ private void fixUpLoadOrderDependencies(FragmentMap fragmentMap) {
+ fixUpLoadOrderDependenciesForTypes(fragmentMap);
+ fixUpLoadOrderDependenciesForClassLiterals(fragmentMap);
+ }
+
+ /**
+ * A class literal cannot be loaded until its associate strings are. Make sure
+ * that the strings are available for all class literals at the time they are
+ * loaded.
+ */
+ private void fixUpLoadOrderDependenciesForClassLiterals(
+ FragmentMap fragmentMap) {
+ for (JField field : fragmentMap.fields.keySet()) {
+ JClassLiteral classLit = fieldToLiteralOfClass.get(field);
+ if (classLit != null) {
+ int classLitFrag = fragmentMap.fields.get(field);
+ for (String string : stringsIn(field.getInitializer())) {
+ int stringFrag = getOrZero(fragmentMap.strings, string);
+ if (stringFrag != classLitFrag && stringFrag != 0) {
+ fragmentMap.strings.put(string, 0);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * The setup code for a class cannot be loaded before the setup code for its
+ * superclass.
+ */
+ private void fixUpLoadOrderDependenciesForTypes(FragmentMap fragmentMap) {
+ Queue<JReferenceType> typesToCheck = new ArrayBlockingQueue<JReferenceType>(
+ jprogram.getDeclaredTypes().size());
+ typesToCheck.addAll(jprogram.getDeclaredTypes());
+ while (!typesToCheck.isEmpty()) {
+ JReferenceType type = typesToCheck.remove();
+ if (type.extnds != null) {
+ int typeFrag = getOrZero(fragmentMap.types, type);
+ int supertypeFrag = getOrZero(fragmentMap.types, type.extnds);
+ if (typeFrag != supertypeFrag && supertypeFrag != 0) {
+ fragmentMap.types.put(type.extnds, 0);
+ typesToCheck.add(type.extnds);
+ }
+ }
+ }
+ }
+
+ /**
+ * 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(FragmentMap fragmentMap) {
+ List<ControlFlowAnalyzer> allButOnes = computeAllButOneCfas();
+
+ ControlFlowAnalyzer everything = computeCompleteCfa();
+
+ Set<JField> allFields = new HashSet<JField>();
+ Set<JMethod> allMethods = new HashSet<JMethod>();
+
+ for (JNode node : everything.getLiveFieldsAndMethods()) {
+ if (node instanceof JField) {
+ allFields.add((JField) node);
+ }
+ if (node instanceof JMethod) {
+ allMethods.add((JMethod) node);
+ }
+ }
+
+ for (int entry = 1; entry < numEntries; entry++) {
+ ControlFlowAnalyzer allButOne = allButOnes.get(entry - 1);
+ updateMap(entry, fragmentMap.fields, allButOne.getLiveFieldsAndMethods(),
+ allFields);
+ updateMap(entry, fragmentMap.methods,
+ allButOne.getLiveFieldsAndMethods(), allMethods);
+ updateMap(entry, fragmentMap.strings, allButOne.getLiveStrings(),
+ everything.getLiveStrings());
+ 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;
+ }
+
+ /**
+ * Traverse <code>exp</code> and find all string literals within it.
+ */
+ 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
+ * {@link ControlFlowAnalyzer#finishTraversal()}.
+ */
+ private void traverseAllButEntry(ControlFlowAnalyzer cfa, int entry) {
+ for (int otherEntry = 0; otherEntry < numEntries; otherEntry++) {
+ if (otherEntry != entry) {
+ traverseEntry(cfa, otherEntry);
+ }
+ }
+ }
+
+ /**
+ * Traverse all code in the program that is reachable via fragment
+ * <code>frag</code>. This does not call
+ * {@link ControlFlowAnalyzer#finishTraversal()}.
+ */
+ private void traverseEntry(ControlFlowAnalyzer cfa, int entry) {
+ for (JMethod entryMethod : jprogram.entryMethods.get(entry)) {
+ cfa.traverseFrom(entryMethod);
+ }
+ if (entry == 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.
+ */
+ cfa.traverseFromClassLiteralFactories();
+ }
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
new file mode 100644
index 0000000..8831aec
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
@@ -0,0 +1,680 @@
+/*
+ * Copyright 2008 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.Context;
+import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
+import com.google.gwt.dev.jjs.ast.JArrayType;
+import com.google.gwt.dev.jjs.ast.JBinaryOperation;
+import com.google.gwt.dev.jjs.ast.JBinaryOperator;
+import com.google.gwt.dev.jjs.ast.JCastOperation;
+import com.google.gwt.dev.jjs.ast.JClassLiteral;
+import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
+import com.google.gwt.dev.jjs.ast.JExpression;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JFieldRef;
+import com.google.gwt.dev.jjs.ast.JInterfaceType;
+import com.google.gwt.dev.jjs.ast.JLocal;
+import com.google.gwt.dev.jjs.ast.JLocalRef;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JNewArray;
+import com.google.gwt.dev.jjs.ast.JNewInstance;
+import com.google.gwt.dev.jjs.ast.JNode;
+import com.google.gwt.dev.jjs.ast.JParameter;
+import com.google.gwt.dev.jjs.ast.JParameterRef;
+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;
+import com.google.gwt.dev.jjs.ast.JType;
+import com.google.gwt.dev.jjs.ast.JVariable;
+import com.google.gwt.dev.jjs.ast.JVariableRef;
+import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
+import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
+import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
+import com.google.gwt.dev.js.ast.JsContext;
+import com.google.gwt.dev.js.ast.JsExpression;
+import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsName;
+import com.google.gwt.dev.js.ast.JsNameRef;
+import com.google.gwt.dev.js.ast.JsVisitor;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class finds out what code in a program is live based on starting
+ * execution at a specified location. Note that the client must call
+ * {@link #finishTraversal()} after the other traversal methods have been
+ * called, or the results will be incomplete.
+ */
+public class ControlFlowAnalyzer {
+
+ /**
+ * Marks as "referenced" any types, methods, and fields that are reachable.
+ * Also marks as "instantiable" any the classes and interfaces that can
+ * possibly be instantiated.
+ *
+ * TODO(later): make RescueVisitor use less stack?
+ */
+ private class RescueVisitor extends JVisitor {
+ @Override
+ public boolean visit(JArrayType type, Context ctx) {
+ assert (referencedTypes.contains(type));
+ boolean isInstantiated = instantiatedTypes.contains(type);
+
+ JType leafType = type.getLeafType();
+ int dims = type.getDims();
+
+ // Rescue my super array type
+ if (leafType instanceof JReferenceType) {
+ JReferenceType rLeafType = (JReferenceType) leafType;
+ if (rLeafType.extnds != null) {
+ JArrayType superArray = program.getTypeArray(rLeafType.extnds, dims);
+ rescue(superArray, true, isInstantiated);
+ }
+
+ for (int i = 0; i < rLeafType.implments.size(); ++i) {
+ JInterfaceType intfType = rLeafType.implments.get(i);
+ JArrayType intfArray = program.getTypeArray(intfType, dims);
+ rescue(intfArray, true, isInstantiated);
+ }
+ }
+
+ // Rescue the base Array type
+ rescue(program.getIndexedType("Array"), true, isInstantiated);
+ return false;
+ }
+
+ @Override
+ public boolean visit(JBinaryOperation x, Context ctx) {
+ // special string concat handling
+ if ((x.getOp() == JBinaryOperator.ADD || x.getOp() == JBinaryOperator.ASG_ADD)
+ && x.getType() == program.getTypeJavaLangString()) {
+ rescueByConcat(x.getLhs().getType());
+ rescueByConcat(x.getRhs().getType());
+ } else if (x.getOp() == JBinaryOperator.ASG) {
+ // Don't rescue variables that are merely assigned to and never read
+ boolean doSkip = false;
+ JExpression lhs = x.getLhs();
+ if (lhs.hasSideEffects() || isVolatileField(lhs)) {
+ // If the lhs has side effects, skipping it would lose the side
+ // effect.
+ // If the lhs is volatile, also keep it. This behavior provides a
+ // useful
+ // idiom for test cases to prevent code from being pruned.
+ } else if (lhs instanceof JLocalRef) {
+ // locals are ok to skip
+ doSkip = true;
+ } else if (lhs instanceof JParameterRef) {
+ // parameters are ok to skip
+ doSkip = true;
+ } else if (lhs instanceof JFieldRef) {
+ // fields must rescue the qualifier
+ doSkip = true;
+ JFieldRef fieldRef = (JFieldRef) lhs;
+ JExpression instance = fieldRef.getInstance();
+ if (instance != null) {
+ accept(instance);
+ }
+ }
+
+ if (doSkip) {
+ accept(x.getRhs());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean visit(JCastOperation x, Context ctx) {
+ // Rescue any JavaScriptObject type that is the target of a cast.
+ JType targetType = x.getCastType();
+ if (program.isJavaScriptObject(targetType)) {
+ rescue((JReferenceType) targetType, true, true);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean visit(JClassLiteral x, Context ctx) {
+ /*
+ * Rescue just slightly less than what would normally be rescued for a
+ * field reference to the literal's field. Rescue the field itself, and
+ * its initializer, but do NOT rescue the whole enclosing class. That
+ * would pull in the clinit of that class, which has initializers for all
+ * the class literals, which in turn have all of the strings of all of the
+ * class names.
+ */
+ JField field = x.getField();
+ rescue(field);
+ accept(field.getInitializer());
+ referencedTypes.add(field.getEnclosingType());
+ liveFieldsAndMethods.add(field.getEnclosingType().methods.get(0));
+ return true;
+ }
+
+ @Override
+ public boolean visit(JClassType type, Context ctx) {
+ assert (referencedTypes.contains(type));
+ boolean isInstantiated = instantiatedTypes.contains(type);
+
+ // Rescue my super type
+ rescue(type.extnds, true, isInstantiated);
+
+ // Rescue my clinit (it won't ever be explicitly referenced
+ rescue(type.methods.get(0));
+
+ // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
+ // However, if I'm instantiated, let's mark them as instantiated.
+ for (int i = 0; i < type.implments.size(); ++i) {
+ JInterfaceType intfType = type.implments.get(i);
+ rescue(intfType, false, isInstantiated);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean visit(JDeclarationStatement x, Context ctx) {
+ /*
+ * A declaration by itself doesn't rescue a local (even if it has an
+ * initializer). Writes don't count, only reads.
+ */
+ if (x.getInitializer() != null) {
+ accept(x.getInitializer());
+ }
+
+ // If the lhs is a field ref, we have to visit its qualifier.
+ JVariableRef variableRef = x.getVariableRef();
+ if (variableRef instanceof JFieldRef) {
+ JFieldRef fieldRef = (JFieldRef) variableRef;
+ JExpression instance = fieldRef.getInstance();
+ if (instance != null) {
+ accept(instance);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(JFieldRef ref, Context ctx) {
+ JField target = ref.getField();
+
+ // JLS 12.4.1: references to static, non-final, or
+ // non-compile-time-constant fields rescue the enclosing class.
+ // JDT already folds in compile-time constants as literals, so we must
+ // rescue the enclosing types for any static fields that make it here.
+ if (target.isStatic()) {
+ rescue(target.getEnclosingType(), true, false);
+ }
+ rescue(target);
+ return true;
+ }
+
+ @Override
+ public boolean visit(JInterfaceType type, Context ctx) {
+ boolean isReferenced = referencedTypes.contains(type);
+ boolean isInstantiated = instantiatedTypes.contains(type);
+ assert (isReferenced || isInstantiated);
+
+ // Rescue my clinit (it won't ever be explicitly referenced
+ rescue(type.methods.get(0));
+
+ // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
+ // However, if I'm instantiated, let's mark them as instantiated.
+ if (isInstantiated) {
+ for (int i = 0; i < type.implments.size(); ++i) {
+ JInterfaceType intfType = type.implments.get(i);
+ rescue(intfType, false, true);
+ }
+ }
+
+ // visit any field initializers
+ for (int i = 0; i < type.fields.size(); ++i) {
+ JField it = type.fields.get(i);
+ accept(it);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean visit(JLocalRef ref, Context ctx) {
+ JLocal target = ref.getLocal();
+ rescue(target);
+ return true;
+ }
+
+ @Override
+ public boolean visit(final JMethod x, Context ctx) {
+ JReferenceType enclosingType = x.getEnclosingType();
+ if (program.isJavaScriptObject(enclosingType)) {
+ // Calls to JavaScriptObject types rescue those types.
+ boolean instance = !x.isStatic() || program.isStaticImpl(x);
+ rescue(enclosingType, true, instance);
+ } else if (x.isStatic()) {
+ // JLS 12.4.1: references to static methods rescue the enclosing class
+ rescue(enclosingType, true, false);
+ }
+
+ if (x.isNative()) {
+ // Manually rescue native parameter references
+ final JsniMethodBody body = (JsniMethodBody) x.getBody();
+ final JsFunction func = body.getFunc();
+
+ new JsVisitor() {
+ @Override
+ public void endVisit(JsNameRef nameRef, JsContext<JsExpression> ctx) {
+ JsName ident = nameRef.getName();
+
+ if (ident != null) {
+ // If we're referencing a parameter, rescue the associated
+ // JParameter
+ int index = func.getParameters().indexOf(ident.getStaticRef());
+ if (index != -1) {
+ rescue(x.params.get(index));
+ }
+ }
+ }
+ }.accept(func);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean visit(JMethodCall call, Context ctx) {
+ rescue(call.getTarget());
+ return true;
+ }
+
+ @Override
+ public boolean visit(JNewArray newArray, Context ctx) {
+ // rescue and instantiate the array type
+ JArrayType arrayType = newArray.getArrayType();
+ if (newArray.dims != null) {
+ // rescue my type and all the implicitly nested types (with fewer dims)
+ int nDims = arrayType.getDims();
+ JType leafType = arrayType.getLeafType();
+ assert (newArray.dims.size() == nDims);
+ for (int i = 0; i < nDims; ++i) {
+ if (newArray.dims.get(i) instanceof JAbsentArrayDimension) {
+ break;
+ }
+ rescue(program.getTypeArray(leafType, nDims - i), true, true);
+ }
+ } else {
+ // just rescue my own specific type
+ rescue(arrayType, true, true);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean visit(JNewInstance newInstance, Context ctx) {
+ // rescue and instantiate the target class!
+ rescue(newInstance.getClassType(), true, true);
+ return true;
+ }
+
+ @Override
+ public boolean visit(JParameterRef x, Context ctx) {
+ // rescue the parameter for future pruning purposes
+ rescue(x.getParameter());
+ return true;
+ }
+
+ @Override
+ public boolean visit(JsniFieldRef x, Context ctx) {
+ /*
+ * SPECIAL: this could be an assignment that passes a value from
+ * JavaScript into Java.
+ */
+ if (x.isLvalue()) {
+ maybeRescueJavaScriptObjectPassingIntoJava(x.getField().getType());
+ }
+ // JsniFieldRef rescues as JFieldRef
+ return visit((JFieldRef) x, ctx);
+ }
+
+ @Override
+ public boolean visit(JsniMethodBody body, Context ctx) {
+ liveStrings.addAll(body.getUsedStrings());
+ return true;
+ }
+
+ @Override
+ public boolean visit(JsniMethodRef x, Context ctx) {
+ /*
+ * SPECIAL: each argument of the call passes a value from JavaScript into
+ * Java.
+ */
+ ArrayList<JParameter> params = x.getTarget().params;
+ for (int i = 0, c = params.size(); i < c; ++i) {
+ JParameter param = params.get(i);
+ maybeRescueJavaScriptObjectPassingIntoJava(param.getType());
+
+ /*
+ * Because we're not currently tracking methods through JSNI, we need to
+ * assume that it's not safe to prune parameters of a method referenced
+ * as such.
+ *
+ * A better solution would be to perform basic escape analysis to ensure
+ * that the function reference never escapes, or at minimum, ensure that
+ * the method is immediately called after retrieving the method
+ * reference.
+ */
+ rescue(param);
+ }
+ // JsniMethodRef rescues as JMethodCall
+ return visit((JMethodCall) x, ctx);
+ }
+
+ @Override
+ public boolean visit(JStringLiteral literal, Context ctx) {
+ liveStrings.add(literal.getValue());
+
+ // rescue and instantiate java.lang.String
+ rescue(program.getTypeJavaLangString(), true, true);
+ return true;
+ }
+
+ private boolean isVolatileField(JExpression x) {
+ if (x instanceof JFieldRef) {
+ JFieldRef xFieldRef = (JFieldRef) x;
+ if (xFieldRef.getField().isVolatile()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Subclasses of JavaScriptObject are never instantiated directly. They are
+ * created "magically" when a JSNI method passes a reference to an existing
+ * JS object into Java code. If any point in the program can pass a value
+ * from JS into Java which could potentially be cast to JavaScriptObject, we
+ * must rescue JavaScriptObject.
+ *
+ * @param type The type of the value passing from Java to JavaScript.
+ * @see com.google.gwt.core.client.JavaScriptObject
+ */
+ private void maybeRescueJavaScriptObjectPassingIntoJava(JType type) {
+ boolean doIt = false;
+ if (program.isJavaScriptObject(type)
+ || type == program.getTypeJavaLangString()) {
+ doIt = true;
+ } else if (type instanceof JArrayType) {
+ /*
+ * Hackish: in our own JRE we sometimes create "not quite baked" arrays
+ * in JavaScript for expediency.
+ */
+ JArrayType arrayType = (JArrayType) type;
+ JType elementType = arrayType.getElementType();
+ if (elementType instanceof JPrimitiveType
+ || elementType == program.getTypeJavaLangString()
+ || program.isJavaScriptObject(elementType)) {
+ doIt = true;
+ }
+ }
+ if (doIt) {
+ rescue((JReferenceType) type, true, true);
+ }
+ }
+
+ private boolean rescue(JMethod method) {
+ if (method != null) {
+ if (!liveFieldsAndMethods.contains(method)) {
+ liveFieldsAndMethods.add(method);
+ accept(method);
+ if (method.isNative()) {
+ /*
+ * SPECIAL: returning from this method passes a value from
+ * JavaScript into Java.
+ */
+ maybeRescueJavaScriptObjectPassingIntoJava(method.getType());
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void rescue(JReferenceType type, boolean isReferenced,
+ boolean isInstantiated) {
+ if (type != null) {
+
+ boolean doVisit = false;
+ if (isInstantiated && !instantiatedTypes.contains(type)) {
+ instantiatedTypes.add(type);
+ doVisit = true;
+ }
+
+ if (isReferenced && !referencedTypes.contains(type)) {
+ referencedTypes.add(type);
+ doVisit = true;
+ }
+
+ if (doVisit) {
+ accept(type);
+ }
+ }
+ }
+
+ private void rescue(JVariable var) {
+ if (var != null) {
+ liveFieldsAndMethods.add(var);
+ }
+ }
+
+ /**
+ * Handle special rescues needed implicitly to support concat.
+ */
+ private void rescueByConcat(JType type) {
+ JClassType stringType = program.getTypeJavaLangString();
+ JPrimitiveType charType = program.getTypePrimitiveChar();
+ if (type instanceof JReferenceType && type != stringType
+ && type != program.getTypeNull()) {
+ /*
+ * Any reference types (except String, which works by default) that take
+ * part in a concat must rescue java.lang.Object.toString().
+ *
+ * TODO: can we narrow the focus by walking up the type hierarchy or
+ * doing explicit toString calls?
+ */
+ JMethod toStringMethod = program.getIndexedMethod("Object.toString");
+ rescue(toStringMethod);
+ } else if (type == charType) {
+ /*
+ * Characters must rescue String.valueOf(char)
+ */
+ if (stringValueOfChar == null) {
+ for (int i = 0; i < stringType.methods.size(); ++i) {
+ JMethod meth = stringType.methods.get(i);
+ if (meth.getName().equals("valueOf")) {
+ List<JType> params = meth.getOriginalParamTypes();
+ if (params.size() == 1) {
+ if (params.get(0) == charType) {
+ stringValueOfChar = meth;
+ break;
+ }
+ }
+ }
+ }
+ assert (stringValueOfChar != null);
+ }
+ rescue(stringValueOfChar);
+ }
+ }
+ }
+
+ /**
+ * Traverse methods that are reachable via virtual method calls. Specifically,
+ * traverse methods whose classes are instantiable and which override a method
+ * that is live.
+ */
+ private class UpRefVisitor extends JVisitor {
+
+ private boolean didRescue = false;
+
+ public boolean didRescue() {
+ return didRescue;
+ }
+
+ @Override
+ public boolean visit(JClassType x, Context ctx) {
+ return instantiatedTypes.contains(x);
+ }
+
+ @Override
+ public boolean visit(JMethod x, Context ctx) {
+ if (liveFieldsAndMethods.contains(x)) {
+ return false;
+ }
+
+ for (JMethod override : program.typeOracle.getAllOverrides(x)) {
+ if (liveFieldsAndMethods.contains(override)) {
+ rescuer.rescue(x);
+ didRescue = true;
+ return false;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(JProgram x, Context ctx) {
+ didRescue = false;
+ return true;
+ }
+ }
+
+ private Set<JReferenceType> instantiatedTypes = new HashSet<JReferenceType>();
+ private Set<JNode> liveFieldsAndMethods = new HashSet<JNode>();
+ private Set<String> liveStrings = new HashSet<String>();
+ private final JProgram program;
+ private Set<JReferenceType> referencedTypes = new HashSet<JReferenceType>();
+ private final RescueVisitor rescuer = new RescueVisitor();
+ private JMethod stringValueOfChar = null;
+ private final UpRefVisitor upRefer = new UpRefVisitor();
+
+ public ControlFlowAnalyzer(ControlFlowAnalyzer cfa) {
+ program = cfa.program;
+ instantiatedTypes = new HashSet<JReferenceType>(cfa.instantiatedTypes);
+ liveFieldsAndMethods = new HashSet<JNode>(cfa.liveFieldsAndMethods);
+ referencedTypes = new HashSet<JReferenceType>(cfa.referencedTypes);
+ stringValueOfChar = cfa.stringValueOfChar;
+ liveStrings = new HashSet<String>(cfa.liveStrings);
+ }
+
+ public ControlFlowAnalyzer(JProgram program) {
+ this.program = program;
+ }
+
+ /**
+ * Finish any remaining traversal that is needed. This must be called after
+ * calling any of the other traversal methods in order to get accurate
+ * results. It can also be called eagerly.
+ */
+ public void finishTraversal() {
+ do {
+ upRefer.accept(program);
+ } while (upRefer.didRescue());
+ }
+
+ /**
+ * Return the complete set of types that have been instantiated.
+ */
+ public Set<JReferenceType> getInstantiatedTypes() {
+ return instantiatedTypes;
+ }
+
+ /**
+ * Return all methods that could be executed, and all variables that could be
+ * read, based on the given entry points so far.
+ */
+ public Set<? extends JNode> getLiveFieldsAndMethods() {
+ return liveFieldsAndMethods;
+ }
+
+ public Set<String> getLiveStrings() {
+ return liveStrings;
+ }
+
+ /**
+ * Return the complete set of types that have been referenced.
+ */
+ public Set<? extends JReferenceType> getReferencedTypes() {
+ return referencedTypes;
+ }
+
+ /**
+ * Traverse all code executed by <code>expr</code>.
+ */
+ public void traverseFrom(JExpression expr) {
+ rescuer.accept(expr);
+ }
+
+ /**
+ * Assume <code>method</code> is live, and find out what else might execute.
+ */
+ public void traverseFrom(JMethod method) {
+ rescuer.rescue(method);
+ }
+
+ public void traverseFromReferenceTo(JReferenceType type) {
+ rescuer.rescue(type, true, false);
+ }
+
+ /**
+ * Trace all code needed by class literal constructor expressions except for
+ * the string literals they include. At the time of writing, these would
+ * include the factory methods for class literals.
+ */
+ public void traverseFromClassLiteralFactories() {
+ class ReplaceStringLiterals extends JModVisitor {
+ @Override
+ public void endVisit(JStringLiteral stringLiteral, Context ctx) {
+ ctx.replaceMe(program.getLiteralString(
+ stringLiteral.getSourceInfo().makeChild(ControlFlowAnalyzer.class, "remove string literals"),
+ ""));
+ }
+ }
+
+ final JModVisitor stringLiteralReplacer = new ReplaceStringLiterals();
+ final CloneExpressionVisitor cloner = new CloneExpressionVisitor(program);
+
+ class ClassLitTraverser extends JVisitor {
+ @Override
+ public void endVisit(JClassLiteral classLiteral, Context ctx) {
+ JExpression initializer = classLiteral.getField().getInitializer();
+ JExpression initializerWithoutStrings = stringLiteralReplacer.accept(cloner.cloneExpression(initializer));
+ rescuer.accept(initializerWithoutStrings);
+ }
+ }
+
+ (new ClassLitTraverser()).accept(program);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
new file mode 100644
index 0000000..204e90f
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
@@ -0,0 +1,516 @@
+/*
+ * Copyright 2008 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.SourceInfo;
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JClassLiteral;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.js.ast.JsBinaryOperation;
+import com.google.gwt.dev.js.ast.JsBinaryOperator;
+import com.google.gwt.dev.js.ast.JsEmpty;
+import com.google.gwt.dev.js.ast.JsExprStmt;
+import com.google.gwt.dev.js.ast.JsExpression;
+import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsInvocation;
+import com.google.gwt.dev.js.ast.JsName;
+import com.google.gwt.dev.js.ast.JsNameRef;
+import com.google.gwt.dev.js.ast.JsProgram;
+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 java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A class that extracts a fragment of code based on a supplied liveness
+ * condition.
+ */
+public class FragmentExtractor {
+ /**
+ * A {@link LivenessPredicate} that bases liveness on a single
+ * {@link ControlFlowAnalyzer}.
+ */
+ public static class CfaLivenessPredicate implements LivenessPredicate {
+ private final ControlFlowAnalyzer cfa;
+
+ public CfaLivenessPredicate(ControlFlowAnalyzer cfa) {
+ this.cfa = cfa;
+ }
+
+ public boolean isLive(JField field) {
+ return cfa.getLiveFieldsAndMethods().contains(field);
+ }
+
+ public boolean isLive(JMethod method) {
+ return cfa.getLiveFieldsAndMethods().contains(method);
+ }
+
+ public boolean isLive(JReferenceType type) {
+ return cfa.getInstantiatedTypes().contains(type);
+ }
+
+ public boolean isLive(String string) {
+ return cfa.getLiveStrings().contains(string);
+ }
+
+ public boolean miscellaneousStatementsAreLive() {
+ return true;
+ }
+ }
+
+ /**
+ * A predicate on whether statements and variables should be considered live.
+ */
+ public static interface LivenessPredicate {
+ boolean isLive(JField field);
+
+ boolean isLive(JMethod method);
+
+ boolean isLive(JReferenceType type);
+
+ boolean isLive(String literal);
+
+ /**
+ * Whether miscellelaneous statements should be considered live.
+ * Miscellaneous statements are any that the fragment extractor does not
+ * recognize as being in any particular category. This method should almost
+ * always return <code>true</code>, but does return <code>false</code>
+ * for {@link NothingAlivePredicate}.
+ */
+ boolean miscellaneousStatementsAreLive();
+ }
+
+ /**
+ * A {@link LivenessPredicate} where nothing is alive.
+ */
+ public static class NothingAlivePredicate implements LivenessPredicate {
+ public boolean isLive(JField field) {
+ return false;
+ }
+
+ public boolean isLive(JMethod method) {
+ return false;
+ }
+
+ public boolean isLive(JReferenceType type) {
+ return false;
+ }
+
+ public boolean isLive(String string) {
+ return false;
+ }
+
+ public boolean miscellaneousStatementsAreLive() {
+ return false;
+ }
+ }
+
+ /**
+ * A logger for statements that the fragment extractor encounters. Install one
+ * using
+ * {@link FragmentExtractor#setStatementLogger(com.google.gwt.fragserv.FragmentExtractor.StatementLogger)}.
+ */
+ public static interface StatementLogger {
+ void logStatement(JsStatement stat, boolean isIncluded);
+ }
+
+ private static class NullStatementLogger implements StatementLogger {
+ public void logStatement(JsStatement method, boolean isIncluded) {
+ }
+ }
+
+ public static Map<JField, JClassLiteral> buildFieldToClassLiteralMap(
+ JProgram jprogram) {
+ final Map<JField, JClassLiteral> map = new HashMap<JField, JClassLiteral>();
+ class BuildFieldToLiteralVisitor extends JVisitor {
+ @Override
+ public void endVisit(JClassLiteral lit, Context ctx) {
+ map.put(lit.getField(), lit);
+ }
+ }
+ (new BuildFieldToLiteralVisitor()).accept(jprogram);
+ return map;
+ }
+
+ private Set<JsName> entryMethodNames;
+
+ private Map<JField, JClassLiteral> fieldToLiteralOfClass;
+
+ private final JProgram jprogram;
+
+ private final JsProgram jsprogram;
+
+ private final JavaToJavaScriptMap map;
+
+ private StatementLogger statementLogger = new NullStatementLogger();
+
+ public FragmentExtractor(JavaAndJavaScript javaAndJavaScript) {
+ this(javaAndJavaScript.jprogram, javaAndJavaScript.jsprogram,
+ javaAndJavaScript.map);
+ }
+
+ public FragmentExtractor(JProgram jprogram, JsProgram jsprogram,
+ JavaToJavaScriptMap map) {
+ this.jprogram = jprogram;
+ this.jsprogram = jsprogram;
+ this.map = map;
+
+ fieldToLiteralOfClass = buildFieldToClassLiteralMap(jprogram);
+ buildEntryMethodSet();
+ }
+
+ /**
+ * 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);
+ assert name != null;
+ SourceInfo sourceInfo = jsprogram.getSourceInfo().makeChild(
+ FragmentExtractor.class, "call to entry function " + entry);
+ JsInvocation call = new JsInvocation(sourceInfo);
+ call.setQualifier(name.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
+ * described by <code>livenessPredicate</code> can also run. The caller
+ * should ensure that <code>livenessPredicate</code> includes strictly more
+ * live code than <code>alreadyLoadedPredicate</code>.
+ */
+ public List<JsStatement> extractStatements(
+ LivenessPredicate livenessPredicate,
+ LivenessPredicate alreadyLoadedPredicate) {
+ List<JsStatement> extractedStats = new ArrayList<JsStatement>();
+
+ /**
+ * The type whose vtables can currently be installed.
+ */
+ JReferenceType currentVtableType = null;
+
+ for (int frag = 0; frag < jsprogram.getFragmentCount(); frag++) {
+ List<JsStatement> stats = jsprogram.getFragmentBlock(frag).getStatements();
+ for (JsStatement stat : stats) {
+ if (isEntryCall(stat)) {
+ continue;
+ }
+
+ boolean keepIt;
+
+ if (containsInternedLiterals(stat)) {
+ stat = removeSomeInternedLiterals((JsVars) stat, livenessPredicate,
+ alreadyLoadedPredicate);
+ keepIt = !(stat instanceof JsEmpty);
+ } else {
+ keepIt = isLive(stat, livenessPredicate)
+ && !isLive(stat, alreadyLoadedPredicate);
+ }
+
+ statementLogger.logStatement(stat, keepIt);
+
+ if (keepIt) {
+ if (vtableTypeAssigned(stat) != null) {
+ currentVtableType = vtableTypeAssigned(stat);
+ }
+ JReferenceType vtableType = vtableTypeNeeded(stat);
+ if (vtableType != null && vtableType != currentVtableType) {
+ extractedStats.add(vtableStatFor(vtableType));
+ currentVtableType = vtableType;
+ }
+ extractedStats.add(stat);
+ }
+ }
+ }
+
+ return extractedStats;
+ }
+
+ public void setStatementLogger(StatementLogger logger) {
+ statementLogger = logger;
+ }
+
+ private void buildEntryMethodSet() {
+ entryMethodNames = new HashSet<JsName>();
+ for (JMethod entryMethod : jprogram.getAllEntryMethods()) {
+ JsName name = map.nameForMethod(entryMethod);
+ assert name != null;
+ entryMethodNames.add(name);
+ }
+ }
+
+ /**
+ * Check whether this statement is a <code>JsVars</code> that contains
+ * interned literals. If it does, then
+ * {@link #removeSomeInternedLiterals(JsVars, LivenessPredicate, LivenessPredicate)}
+ * is sensible for this statement and should be used instead of
+ * {@link #isLive(JsStatement, com.google.gwt.fragserv.FragmentExtractor.LivenessPredicate)}.
+ */
+ private boolean containsInternedLiterals(JsStatement stat) {
+ if (stat instanceof JsVars) {
+ for (JsVar var : (JsVars) stat) {
+ String lit = map.stringLiteralForName(var.getName());
+ if (lit != null) {
+ // It's an intern variable for a string literal
+ return true;
+ }
+
+ JField field = map.nameToField(var.getName());
+ if (field != null && fieldToLiteralOfClass.containsKey(field)) {
+ // It's an intern variable for a class literal
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether the statement invokes an entry method. Detect JavaScript code
+ * of the form foo() where foo is a the JavaScript function corresponding to
+ * an entry method.
+ */
+ private boolean isEntryCall(JsStatement stat) {
+ if (stat instanceof JsExprStmt) {
+ JsExpression expr = ((JsExprStmt) stat).getExpression();
+ if (expr instanceof JsInvocation) {
+ JsInvocation inv = (JsInvocation) expr;
+ if (inv.getArguments().isEmpty()
+ && (inv.getQualifier() instanceof JsNameRef)) {
+ JsNameRef calleeRef = (JsNameRef) inv.getQualifier();
+ if (calleeRef.getQualifier() == null) {
+ return entryMethodNames.contains(calleeRef.getName());
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isLive(JsStatement stat, LivenessPredicate livenessPredicate) {
+ JReferenceType type = map.typeForStatement(stat);
+ if (type != null) {
+ // This is part of the code only needed once a type is instantiable
+ return livenessPredicate.isLive(type);
+ }
+
+ JMethod meth = methodFor(stat);
+ if (meth == null) {
+ meth = map.vtableInitToMethod(stat);
+ }
+
+ if (meth != null) {
+ /*
+ * This statement either defines a method or installs it in a vtable.
+ */
+ if (!livenessPredicate.isLive(meth)) {
+ // The method is not live. Skip it.
+ return false;
+ }
+ // The method is live. Check that its enclosing type is instantiable.
+ // TODO(spoon): this check should not be needed once the CFA is updated
+ return meth.isStatic()
+ || livenessPredicate.isLive(meth.getEnclosingType());
+ }
+
+ return livenessPredicate.miscellaneousStatementsAreLive();
+ }
+
+ /**
+ * Check whether a variable is needed. If the variable is an intern variable,
+ * then return whether the interned value is live. Otherwise, assume the
+ * variable is needed and return <code>true</code>.
+ *
+ * Whenever this method is updated, also look at
+ * {@link #containsInternedLiterals(JsStatement)}.
+ */
+ private boolean isLive(JsVar var, LivenessPredicate livenessPredicate) {
+ String lit = map.stringLiteralForName(var.getName());
+ if (lit != null) {
+ // It's an intern variable for a string literal
+ return livenessPredicate.isLive(lit);
+ }
+
+ JField field = map.nameToField(var.getName());
+ if (field != null && fieldToLiteralOfClass.containsKey(field)) {
+ // It's an intern variable for a class literal
+ return livenessPredicate.isLive(field);
+ }
+
+ // It's not an intern variable at all
+ return livenessPredicate.miscellaneousStatementsAreLive();
+ }
+
+ /**
+ * Return the Java method corresponding to <code>stat</code>, or
+ * <code>null</code> if there isn't one. It recognizes JavaScript of the
+ * form <code>function foo(...) { ...}</code>, where <code>foo</code> is
+ * the name of the JavaScript translation of a Java method.
+ */
+ private JMethod methodFor(JsStatement stat) {
+ if (stat instanceof JsExprStmt) {
+ JsExpression exp = ((JsExprStmt) stat).getExpression();
+ if (exp instanceof JsFunction) {
+ JsFunction func = (JsFunction) exp;
+ if (func.getName() != null) {
+ return map.nameToMethod(func.getName());
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * If stat is a {@link JsVars} that initializes a bunch of intern vars, return
+ * a modified statement that skips any vars are needed by
+ * <code>currentLivenessPredicate</code> but not by
+ * <code>alreadyLoadedPredicate</code>.
+ */
+ private JsStatement removeSomeInternedLiterals(JsVars stat,
+ LivenessPredicate currentLivenessPredicate,
+ LivenessPredicate alreadyLoadedPredicate) {
+ JsVars newVars = new JsVars(stat.getSourceInfo().makeChild(
+ FragmentExtractor.class, "subsetting of interned values"));
+
+ for (JsVar var : stat) {
+ if (isLive(var, currentLivenessPredicate)
+ && !isLive(var, alreadyLoadedPredicate)) {
+ newVars.add(var);
+ }
+ }
+
+ if (newVars.getNumVars() == stat.getNumVars()) {
+ // no change
+ return stat;
+ }
+
+ if (newVars.iterator().hasNext()) {
+ /*
+ * The new variables are non-empty; return them.
+ */
+ return newVars;
+ } else {
+ /*
+ * An empty JsVars seems possibly surprising; return a true empty
+ * statement instead.
+ */
+ return jsprogram.getEmptyStmt();
+ }
+ }
+
+ /**
+ * Compute a statement that can be used to set up for installing instance
+ * methods into a vtable. It will be of the form
+ * <code>_ = foo.prototype</code>, where <code>foo</code> is the
+ * constructor function for <code>vtableType</code>.
+ */
+ private JsStatement vtableStatFor(JReferenceType vtableType) {
+ JsNameRef prototypeField = new JsNameRef(
+ jsprogram.createSourceInfoSynthetic(FragmentExtractor.class,
+ "prototype field"), "prototype");
+ JsExpression constructorRef;
+ SourceInfo sourceInfoVtableSetup = jsprogram.createSourceInfoSynthetic(
+ FragmentExtractor.class, "vtable setup");
+ if (vtableType == jprogram.getTypeJavaLangString()) {
+ // The methods of java.lang.String are put onto JavaScript's String
+ // prototype
+ SourceInfo sourceInfoConstructorRef = jsprogram.createSourceInfoSynthetic(
+ FragmentExtractor.class, "String constructor");
+ constructorRef = new JsNameRef(sourceInfoConstructorRef, "String");
+ } else {
+ constructorRef = map.nameForType(vtableType).makeRef(
+ sourceInfoVtableSetup);
+ }
+ prototypeField.setQualifier(constructorRef);
+ SourceInfo underlineSourceInfo = jsprogram.createSourceInfoSynthetic(
+ FragmentExtractor.class, "global _ field");
+ return (new JsBinaryOperation(sourceInfoVtableSetup, JsBinaryOperator.ASG,
+ jsprogram.getScope().declareName("_").makeRef(underlineSourceInfo),
+ prototypeField)).makeStmt();
+ }
+
+ /**
+ * If <code>state</code> is of the form <code>_ = Foo.prototype = exp</code>,
+ * then return <code>Foo</code>. Otherwise return <code>null</code>.
+ *
+ * TODO(spoon): get this information via source info on the statement
+ */
+ private JReferenceType vtableTypeAssigned(JsStatement stat) {
+ if (!(stat instanceof JsExprStmt)) {
+ return null;
+ }
+ JsExprStmt expr = (JsExprStmt) stat;
+ if (!(expr.getExpression() instanceof JsBinaryOperation)) {
+ return null;
+ }
+ JsBinaryOperation binExpr = (JsBinaryOperation) expr.getExpression();
+ if (binExpr.getOperator() != JsBinaryOperator.ASG) {
+ return null;
+ }
+ if (!(binExpr.getArg1() instanceof JsNameRef)) {
+ return null;
+ }
+ JsNameRef lhs = (JsNameRef) binExpr.getArg1();
+ if (lhs.getQualifier() != null) {
+ return null;
+ }
+ if (lhs.getName() == null) {
+ return null;
+ }
+ if (!lhs.getName().getShortIdent().equals("_")) {
+ return null;
+ }
+ if (!(binExpr.getArg2() instanceof JsBinaryOperation)) {
+ return null;
+ }
+ JsBinaryOperation binExprRhs = (JsBinaryOperation) binExpr.getArg2();
+ if (binExprRhs.getOperator() != JsBinaryOperator.ASG) {
+ return null;
+ }
+ if (!(binExprRhs.getArg1() instanceof JsNameRef)) {
+ return null;
+ }
+ JsNameRef middleNameRef = (JsNameRef) binExprRhs.getArg1();
+ if (!middleNameRef.getName().getShortIdent().equals("prototype")) {
+ return null;
+ }
+
+ return map.typeForStatement(stat);
+ }
+
+ private JReferenceType vtableTypeNeeded(JsStatement stat) {
+ JMethod meth = map.vtableInitToMethod(stat);
+ if (meth != null) {
+ if (!meth.isStatic()) {
+ return meth.getEnclosingType();
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java
new file mode 100644
index 0000000..cfb0e54
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2008 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.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.PropertyOracle;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.dev.cfg.BindingProperty;
+import com.google.gwt.dev.cfg.ConfigurationProperty;
+import com.google.gwt.dev.cfg.PublicOracle;
+import com.google.gwt.dev.cfg.StaticPropertyOracle;
+import com.google.gwt.dev.javac.CompilationState;
+import com.google.gwt.dev.shell.StandardGeneratorContext;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Generates code for loading an island. The pattern of generated classes is
+ * more complicated than otherwise necessary so that the loader code is
+ * precisely handled by TypeTightener and LivenessAnalyzer.
+ *
+ * TODO(spoon) Remove this generator by making LivenessAnalyzer know about
+ * runAsync and how it works.
+ */
+public class FragmentLoaderCreator {
+ 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 RUN_ASYNC_CALLBACK = "com.google.gwt.core.client.RunAsyncCallback";
+ private static final String PROP_RUN_ASYNC_NEVER_RUNS = "gwt.jjs.runAsyncNeverRuns";
+
+ private final ArtifactSet artifactSet;
+ private final CompilationState compilationState;
+ private int entryNumber;
+ private final File genDir;
+ private final File outDir;
+ private final PublicOracle publicOracle;
+
+ /**
+ * Construct a FragmentLoaderCreator. The reason it needs so many parameters
+ * is that it uses generator infrastructure.
+ */
+ public FragmentLoaderCreator(CompilationState compilationState,
+ PublicOracle publicOracle, File genDir, File moduleOutDir,
+ ArtifactSet artifactSet) {
+ this.compilationState = compilationState;
+ this.publicOracle = publicOracle;
+ this.genDir = genDir;
+ this.outDir = moduleOutDir;
+ this.artifactSet = artifactSet;
+ }
+
+ public String create(TreeLogger logger) throws UnableToCompleteException {
+ chooseEntryNumber();
+ StandardGeneratorContext context = makeGeneratorContext();
+
+ PrintWriter loaderWriter = getSourceWriterForLoader(logger, context);
+ if (loaderWriter == null) {
+ logger.log(TreeLogger.ERROR, "Failed to create island loader named "
+ + getLoaderQualifiedName());
+ throw new UnableToCompleteException();
+ }
+
+ generateLoaderFields(loaderWriter);
+ generateOnErrorMethod(loaderWriter);
+ generateOnLoadMethod(loaderWriter);
+ generateRunAsyncMethod(loaderWriter);
+ generateRunCallbacksMethod(loaderWriter);
+ generateRunCallbackOnFailuresMethod(loaderWriter);
+
+ loaderWriter.println("}");
+ loaderWriter.close();
+ context.commit(logger, loaderWriter);
+
+ writeCallbackListClass(logger, context);
+ writeLoaderSuperclass(logger, context);
+
+ context.finish(logger);
+
+ return getLoaderQualifiedName();
+ }
+
+ /**
+ * Pick the lowest-numbered entry number that has not yet had loaders
+ * generated.
+ */
+ private void chooseEntryNumber() {
+ entryNumber = 1;
+ while (compilationState.getTypeOracle().findType(getLoaderQualifiedName()) != null) {
+ entryNumber++;
+ }
+ }
+
+ private void generateLoaderFields(PrintWriter srcWriter) {
+ srcWriter.println("private static boolean loaded = false;");
+ srcWriter.println("private static boolean loading = false;");
+ srcWriter.println("private static " + getLoaderSuperclassSimpleName()
+ + " instance = new " + getLoaderSuperclassSimpleName() + "();");
+ srcWriter.println("private static " + getCallbackListSimpleName()
+ + " callbacks = null;");
+ }
+
+ private void generateOnErrorMethod(PrintWriter srcWriter) {
+ srcWriter.println("public static void onError(Throwable e) {");
+ srcWriter.println("loading = false;");
+ srcWriter.println("runCallbackOnFailures(e);");
+ srcWriter.println("}");
+ }
+
+ private void generateOnLoadMethod(PrintWriter srcWriter) {
+ srcWriter.println("public static void onLoad() {");
+ srcWriter.println(ASYNC_FRAGMENT_LOADER + ".logEventProgress(\"download"
+ + entryNumber + "\", \"end\");");
+ srcWriter.println("loaded = true;");
+ srcWriter.println("instance = new " + getLoaderSimpleName() + "();");
+ srcWriter.println(ASYNC_FRAGMENT_LOADER + ".fragmentHasLoaded("
+ + entryNumber + ");");
+
+ srcWriter.println("instance.runCallbacks();");
+
+ srcWriter.println("}");
+ }
+
+ /**
+ * Generate the <code>runAsync</code> method. Calls to
+ * <code>GWT.runAsync</code> are replaced by calls to this method.
+ */
+ private void generateRunAsyncMethod(PrintWriter srcWriter) {
+ srcWriter.println("public static void runAsync(RunAsyncCallback callback) {");
+ srcWriter.println(getCallbackListSimpleName() + " newCallbackList = new "
+ + getCallbackListSimpleName() + "();");
+ srcWriter.println("newCallbackList.callback = callback;");
+ srcWriter.println("newCallbackList.next = callbacks;");
+ srcWriter.println("callbacks = newCallbackList;");
+ srcWriter.println("if (loaded) {");
+ srcWriter.println("instance.runCallbacks();");
+ srcWriter.println("return;");
+ srcWriter.println("}");
+ srcWriter.println("if (!loading) {");
+ srcWriter.println("loading = true;");
+ srcWriter.println("AsyncFragmentLoader.inject(" + entryNumber + ");");
+ srcWriter.println("}");
+ srcWriter.println("}");
+ }
+
+ private void generateRunCallbackOnFailuresMethod(PrintWriter srcWriter) {
+ srcWriter.println("private static void runCallbackOnFailures(Throwable e) {");
+ // TODO(spoon): this runs the callbacks in reverse order
+ srcWriter.println(getCallbackListSimpleName() + " callback = callbacks;");
+ srcWriter.println("callbacks = null;");
+ srcWriter.println("while (callback != null) {");
+ srcWriter.println("callback.callback.onFailure(e);");
+ srcWriter.println("callback = callback.next;");
+ srcWriter.println("}");
+ srcWriter.println("}");
+ }
+
+ private void generateRunCallbacksMethod(PrintWriter srcWriter) {
+ srcWriter.println("public void runCallbacks() {");
+ // TODO(spoon): this runs the callbacks in reverse order
+ // TODO(spoon): this runs the callbacks immediately; deferred would be
+ // better
+ srcWriter.println(getCallbackListSimpleName() + " callback = callbacks;");
+ srcWriter.println("callbacks = null;");
+ if (!Boolean.getBoolean(PROP_RUN_ASYNC_NEVER_RUNS)) {
+ srcWriter.println("while (callback != null) {");
+ srcWriter.println("callback.callback.onSuccess();");
+ srcWriter.println("callback = callback.next;");
+ srcWriter.println("}");
+ }
+ srcWriter.println("}");
+ }
+
+ private String getCallbackListQualifiedName() {
+ return ASYNC_LOADER_PACKAGE + "__Callback";
+ }
+
+ private String getCallbackListSimpleName() {
+ return getLoaderSimpleName() + "__Callback";
+ }
+
+ private String getLoaderQualifiedName() {
+ return ASYNC_LOADER_PACKAGE + "." + getLoaderSimpleName();
+ }
+
+ private String getLoaderSimpleName() {
+ return ASYNC_LOADER_CLASS_PREFIX + entryNumber;
+ }
+
+ private String getLoaderSuperclassQualifiedName() {
+ return ASYNC_LOADER_PACKAGE + getLoaderSuperclassSimpleName();
+ }
+
+ private String getLoaderSuperclassSimpleName() {
+ return getLoaderSimpleName() + "__Super";
+ }
+
+ private String getPackage() {
+ return ASYNC_LOADER_PACKAGE;
+ }
+
+ private PrintWriter getSourceWriterForLoader(TreeLogger logger,
+ GeneratorContext ctx) {
+ PrintWriter printWriter = ctx.tryCreate(logger, getPackage(),
+ getLoaderSimpleName());
+ if (printWriter == null) {
+ return null;
+ }
+
+ printWriter.println("package " + getPackage() + ";");
+ String[] imports = new String[] {
+ RUN_ASYNC_CALLBACK, List.class.getCanonicalName(),
+ ArrayList.class.getCanonicalName(), ASYNC_FRAGMENT_LOADER};
+ for (String imp : imports) {
+ printWriter.println("import " + imp + ";");
+ }
+
+ printWriter.println("public class " + getLoaderSimpleName() + " extends "
+ + getLoaderSuperclassSimpleName() + " {");
+
+ return printWriter;
+ }
+
+ private StandardGeneratorContext makeGeneratorContext() {
+ // An empty property oracle is fine, because fragment loaders aren't
+ // affected by properties anyway
+ PropertyOracle propOracle = new StaticPropertyOracle(
+ new BindingProperty[0], new String[0], new ConfigurationProperty[0]);
+ StandardGeneratorContext context = new StandardGeneratorContext(
+ compilationState, propOracle, publicOracle, genDir, outDir, artifactSet);
+ return context;
+ }
+
+ private void writeCallbackListClass(TreeLogger logger, GeneratorContext ctx)
+ throws UnableToCompleteException {
+ PrintWriter printWriter = ctx.tryCreate(logger, getPackage(),
+ getCallbackListSimpleName());
+ if (printWriter == null) {
+ logger.log(TreeLogger.ERROR, "Could not create type: "
+ + getCallbackListQualifiedName());
+ throw new UnableToCompleteException();
+ }
+
+ printWriter.println("package " + getPackage() + ";");
+ printWriter.println("public class " + getCallbackListSimpleName() + "{");
+ printWriter.println(RUN_ASYNC_CALLBACK + " callback;");
+ printWriter.println(getCallbackListSimpleName() + " next;");
+ printWriter.println("}");
+
+ printWriter.close();
+ ctx.commit(logger, printWriter);
+ }
+
+ /**
+ * Create a stand-in superclass of the actual loader. This is used to keep the
+ * liveness analyzer from thinking the real <code>runCallbacks()</code>
+ * method is available until <code>onLoad</code> has been called and the
+ * real loader instantiated. A little work on TypeTightener could prevent the
+ * need for this class.
+ */
+ private void writeLoaderSuperclass(TreeLogger logger, GeneratorContext ctx)
+ throws UnableToCompleteException {
+ PrintWriter printWriter = ctx.tryCreate(logger, getPackage(),
+ getLoaderSuperclassSimpleName());
+ if (printWriter == null) {
+ logger.log(TreeLogger.ERROR, "Could not create type: "
+ + getLoaderSuperclassQualifiedName());
+ throw new UnableToCompleteException();
+ }
+
+ printWriter.println("package " + getPackage() + ";");
+ printWriter.println("public class " + getLoaderSuperclassSimpleName()
+ + " {");
+ printWriter.println("public void runCallbacks() { }");
+ printWriter.println("}");
+
+ printWriter.close();
+ ctx.commit(logger, printWriter);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index f80d5d3..8486fbd 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -320,8 +320,8 @@
SourceInfo sourceInfo = x.getSourceInfo().makeChild(
CreateNamesAndScopesVisitor.class, "Translated JS function");
jsFunction = new JsFunction(sourceInfo, topScope, globalName, true);
- methodBodyMap.put(x.getBody(), jsFunction);
}
+ methodBodyMap.put(x.getBody(), jsFunction);
jsFunction.getSourceInfo().addCorrelation(Correlation.by(jsFunction));
push(jsFunction.getScope());
return true;
@@ -375,22 +375,22 @@
private JMethod currentMethod = null;
- private final JsName globalTemp = topScope.declareName("_");
-
- private final JsName prototype = objectScope.declareName("prototype");
-
/**
* The JavaScript functions corresponding to the entry methods of the
- * program ({@link JProgram#entryMethods}).
+ * program ({@link JProgram#getAllEntryMethods()}).
*/
private JsFunction[] entryFunctions;
/**
- * A reverse index for {@link JProgram#entryMethods}. Each entry method is
- * mapped to its integer index.
+ * A reverse index for the entry methods of the program ({@link JProgram#getAllEntryMethods()}).
+ * Each entry method is mapped to its integer index.
*/
private Map<JMethod, Integer> entryMethodToIndex;
+ private final JsName globalTemp = topScope.declareName("_");
+
+ private final JsName prototype = objectScope.declareName("prototype");
+
{
globalTemp.setObfuscatable(false);
prototype.setObfuscatable(false);
@@ -541,10 +541,12 @@
vars.add((JsVar) node);
} else {
assert (node instanceof JsStatement);
- JsStatement stmt = (JsStatement) jsFields.get(i);
+ JsStatement stmt = (JsStatement) node;
globalStmts.add(stmt);
+ typeForStatMap.put(stmt, x);
}
}
+
if (!vars.isEmpty()) {
globalStmts.add(vars);
}
@@ -992,17 +994,16 @@
public void endVisit(JProgram x, Context ctx) {
List<JsStatement> globalStmts = jsProgram.getGlobalBlock().getStatements();
- // types don't push
-
// Generate entry methods
- generateGwtOnLoad(entryFunctions, globalStmts);
+ generateGwtOnLoad(Arrays.asList(entryFunctions).subList(0,
+ x.getEntryCount(0)), globalStmts);
generateNullFunc(globalStmts);
// Add a few things onto the beginning.
// Reserve the "_" identifier.
- JsVars vars = new JsVars(x.getSourceInfo());
- vars.add(new JsVar(x.getSourceInfo(), globalTemp));
+ JsVars vars = new JsVars(jsProgram.getSourceInfo());
+ vars.add(new JsVar(jsProgram.getSourceInfo(), globalTemp));
globalStmts.add(0, vars);
// Long lits must go at the top, they can be constant field initializers.
@@ -1011,12 +1012,28 @@
// Class objects, but only if there are any.
if (x.getDeclaredTypes().contains(x.getTypeClassLiteralHolder())) {
// TODO: perhaps they could be constant field initializers also?
- vars = new JsVars(x.getSourceInfo());
+ vars = new JsVars(jsProgram.getSourceInfo());
generateClassLiterals(vars);
if (!vars.isEmpty()) {
globalStmts.add(vars);
}
}
+
+ /*
+ * 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.
+ */
+ List<JsFunction> nonInitialEntries = Arrays.asList(entryFunctions).subList(
+ x.getEntryCount(0), entryFunctions.length);
+ for (JsFunction func : nonInitialEntries) {
+ if (func != null) {
+ SourceInfo sourceInfo = jsProgram.getSourceInfo().makeChild(GenerateJavaScriptAST.class,
+ "call to entry non-initial entry function");
+ JsInvocation call = new JsInvocation(sourceInfo);
+ call.setQualifier(func.getName().makeRef(sourceInfo));
+ globalStmts.add(call.makeStmt());
+ }
+ }
}
@Override
@@ -1140,10 +1157,15 @@
@Override
public boolean visit(JProgram x, Context ctx) {
- entryFunctions = new JsFunction[x.entryMethods.size()];
+ /*
+ * Arrange for entryFunctions to be filled in as functions are visited.
+ * See their Javadoc comments for more details.
+ */
+ List<JMethod> entryMethods = x.getAllEntryMethods();
+ entryFunctions = new JsFunction[entryMethods.size()];
entryMethodToIndex = new IdentityHashMap<JMethod, Integer>();
- for (int i = 0; i < x.entryMethods.size(); i++) {
- entryMethodToIndex.put(x.entryMethods.get(i), i);
+ for (int i = 0; i < entryMethods.size(); i++) {
+ entryMethodToIndex.put(entryMethods.get(i), i);
}
return true;
}
@@ -1290,7 +1312,7 @@
generateTypeId(x, globalStmts);
}
- private void generateGwtOnLoad(JsFunction[] entryFuncs,
+ private void generateGwtOnLoad(List<JsFunction> entryFuncs,
List<JsStatement> globalStmts) {
/**
* <pre>
@@ -1397,7 +1419,9 @@
sourceInfo.addCorrelation(Correlation.by(seedFunc));
JsBlock body = new JsBlock(sourceInfo);
seedFunc.setBody(body);
- globalStmts.add(seedFunc.makeStmt());
+ JsExprStmt seedFuncStmt = seedFunc.makeStmt();
+ globalStmts.add(seedFuncStmt);
+ typeForStatMap.put(seedFuncStmt, x);
// setup prototype, assign to temp
// _ = com_example_foo_Foo.prototype = new com_example_foo_FooSuper();
@@ -1420,7 +1444,9 @@
JsExpression protoAsg = createAssignment(lhs, rhs);
JsExpression tmpAsg = createAssignment(globalTemp.makeRef(sourceInfo),
protoAsg);
- globalStmts.add(tmpAsg.makeStmt());
+ JsExprStmt tmpAsgStmt = tmpAsg.makeStmt();
+ globalStmts.add(tmpAsgStmt);
+ typeForStatMap.put(tmpAsgStmt, x);
} else {
/*
* MAGIC: java.lang.String is implemented as a JavaScript String
@@ -1431,7 +1457,9 @@
sourceInfo));
JsExpression tmpAsg = createAssignment(globalTemp.makeRef(sourceInfo),
rhs);
- globalStmts.add(tmpAsg.makeStmt());
+ JsExprStmt tmpAsgStmt = tmpAsg.makeStmt();
+ globalStmts.add(tmpAsgStmt);
+ typeForStatMap.put(tmpAsgStmt, x);
}
}
@@ -1463,7 +1491,9 @@
// asg
JsExpression asg = createAssignment(lhs, rhs);
- globalStmts.add(new JsExprStmt(sourceInfo, asg));
+ JsExprStmt stmt = asg.makeStmt();
+ globalStmts.add(stmt);
+ typeForStatMap.put(stmt, program.getTypeJavaLangObject());
}
}
@@ -1482,7 +1512,9 @@
fieldRef.setQualifier(globalTemp.makeRef(sourceInfo));
JsNumberLiteral typeIdLit = jsProgram.getNumberLiteral(typeId);
JsExpression asg = createAssignment(fieldRef, typeIdLit);
- globalStmts.add(new JsExprStmt(sourceInfo, asg));
+ JsExprStmt asgStmt = asg.makeStmt();
+ globalStmts.add(asgStmt);
+ typeForStatMap.put(asgStmt, x);
}
}
@@ -1499,7 +1531,9 @@
fieldRef.setQualifier(globalTemp.makeRef(sourceInfo));
JsExpression asg = createAssignment(fieldRef,
nullMethodName.makeRef(sourceInfo));
- globalStmts.add(new JsExprStmt(sourceInfo, asg));
+ JsExprStmt stmt = asg.makeStmt();
+ globalStmts.add(stmt);
+ typeForStatMap.put(stmt, program.getTypeJavaLangObject());
}
private JsExpression generateTypeTable() {
@@ -1524,7 +1558,9 @@
lhs.setQualifier(globalTemp.makeRef(sourceInfo));
JsNameRef rhs = names.get(method).makeRef(sourceInfo);
JsExpression asg = createAssignment(lhs, rhs);
- globalStmts.add(new JsExprStmt(x.getSourceInfo(), asg));
+ JsExprStmt asgStat = new JsExprStmt(x.getSourceInfo(), asg);
+ globalStmts.add(asgStat);
+ vtableInitForMethodMap.put(asgStat, method);
}
}
}
@@ -1700,16 +1736,18 @@
@Override
public void endVisit(JProgram x, Context ctx) {
- Collections.sort(x.entryMethods, hasNameSort);
+ for (List<JMethod> methods : x.entryMethods) {
+ Collections.sort(methods, hasNameSort);
+ }
Collections.sort(x.getDeclaredTypes(), hasNameSort);
}
}
- public static void exec(JProgram program, JsProgram jsProgram,
+ public static JavaToJavaScriptMap exec(JProgram program, JsProgram jsProgram,
JsOutputOption output) {
GenerateJavaScriptAST generateJavaScriptAST = new GenerateJavaScriptAST(
program, jsProgram, output);
- generateJavaScriptAST.execImpl();
+ return generateJavaScriptAST.execImpl();
}
private final Map<JBlock, JsCatch> catchMap = new IdentityHashMap<JBlock, JsCatch>();
@@ -1721,6 +1759,7 @@
* clinit).
*/
private Set<JMethod> crossClassTargets = new HashSet<JMethod>();
+
/**
* Contains JsNames for all interface methods. A special scope is needed so
* that independent classes will obfuscate their interface implementation
@@ -1734,7 +1773,6 @@
* Sorted to avoid nondeterministic iteration.
*/
private final Map<Long, JsName> longLits = new TreeMap<Long, JsName>();
-
private final Map<JsName, JsExpression> longObjects = new IdentityHashMap<JsName, JsExpression>();
private final Map<JAbstractMethodBody, JsFunction> methodBodyMap = new IdentityHashMap<JAbstractMethodBody, JsFunction>();
private final Map<HasName, JsName> names = new IdentityHashMap<HasName, JsName>();
@@ -1770,8 +1808,13 @@
* Contains JsNames for all globals, such as static fields and methods.
*/
private final JsScope topScope;
+
+ private final Map<JsStatement, JReferenceType> typeForStatMap = new HashMap<JsStatement, JReferenceType>();
+
private final JTypeOracle typeOracle;
+ private final Map<JsStatement, JMethod> vtableInitForMethodMap = new HashMap<JsStatement, JMethod>();
+
private GenerateJavaScriptAST(JProgram program, JsProgram jsProgram,
JsOutputOption output) {
this.program = program;
@@ -1899,7 +1942,7 @@
throw new InternalCompilerException("Unknown output mode");
}
- private void execImpl() {
+ private JavaToJavaScriptMap execImpl() {
SortVisitor sorter = new SortVisitor();
sorter.accept(program);
RecordCrossClassCalls recorder = new RecordCrossClassCalls();
@@ -1908,6 +1951,61 @@
creator.accept(program);
GenerateJavaScriptVisitor generator = new GenerateJavaScriptVisitor();
generator.accept(program);
- }
+ final Map<JsName, JMethod> nameToMethodMap = new HashMap<JsName, JMethod>();
+ for (JAbstractMethodBody body : methodBodyMap.keySet()) {
+ nameToMethodMap.put(methodBodyMap.get(body).getName(), body.getMethod());
+ }
+ final HashMap<JsName, JField> nameToFieldMap = new HashMap<JsName, JField>();
+ final HashMap<JsName, JReferenceType> constructorNameToTypeMap = new HashMap<JsName, JReferenceType>();
+ for (JReferenceType type : program.getDeclaredTypes()) {
+ JsName typeName = names.get(type);
+ if (typeName != null) {
+ constructorNameToTypeMap.put(typeName, type);
+ }
+ for (JField field : type.fields) {
+ if (field.isStatic()) {
+ JsName fieldName = names.get(field);
+ if (fieldName != null) {
+ nameToFieldMap.put(fieldName, field);
+ }
+ }
+ }
+ }
+ // TODO(spoon): Instead of gathering the information here, get it via
+ // SourceInfo
+ return new JavaToJavaScriptMap() {
+ public JsName nameForMethod(JMethod method) {
+ return names.get(method);
+ }
+
+ public JsName nameForType(JReferenceType type) {
+ return names.get(type);
+ }
+
+ public JField nameToField(JsName name) {
+ return nameToFieldMap.get(name);
+ }
+
+ public JMethod nameToMethod(JsName name) {
+ return nameToMethodMap.get(name);
+ }
+
+ public String stringLiteralForName(JsName var) {
+ /*
+ * This method will be supplied non-trivially elsewhere.
+ * GenerateJavaScriptAST doesn't have the information to implement it.
+ */
+ return null;
+ }
+
+ public JReferenceType typeForStatement(JsStatement stat) {
+ return typeForStatMap.get(stat);
+ }
+
+ public JMethod vtableInitToMethod(JsStatement stat) {
+ return vtableInitForMethodMap.get(stat);
+ }
+ };
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaAndJavaScript.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaAndJavaScript.java
new file mode 100644
index 0000000..7526b14
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaAndJavaScript.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2008 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.JProgram;
+import com.google.gwt.dev.js.ast.JsProgram;
+
+/**
+ * A Java program, a JavaScript program, and a map between them.
+ */
+public class JavaAndJavaScript {
+ public final JProgram jprogram;
+ public final JsProgram jsprogram;
+ public final String[] jscode;
+ public final JavaToJavaScriptMap map;
+
+ public JavaAndJavaScript(JProgram jprogram, JsProgram jsprogram, String[] jscode, JavaToJavaScriptMap map) {
+ this.jprogram = jprogram;
+ this.jsprogram = jsprogram;
+ this.jscode = jscode;
+ this.map = map;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
new file mode 100644
index 0000000..7b21d28
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2008 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.JField;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.js.ast.JsName;
+import com.google.gwt.dev.js.ast.JsStatement;
+
+/**
+ * A map between chunks of JavaScript to chunks of Java.
+ */
+public interface JavaToJavaScriptMap {
+ /**
+ * Return the JavaScript name corresponding to a Java type.
+ */
+ JsName nameForType(JReferenceType type);
+
+ /**
+ * Return the JavaScript name corresponding to a Java method.
+ */
+ JsName nameForMethod(JMethod method);
+
+ /**
+ * If <code>name</code> is the name of a <code>var<code> that corresponds to a Java
+ * static field, then return that field. Otherwise, return null.
+ */
+ JField nameToField(JsName name);
+
+ /**
+ * If <code>name</code> is the name of a function that corresponds to a Java
+ * method, then return that method. Otherwise, return null.
+ */
+ JMethod nameToMethod(JsName name);
+
+ /**
+ * If <code>var</code> is the name of the variable used to hold an interned
+ * string literal, then return the string it interns. Otherwise, return null.
+ */
+ String stringLiteralForName(JsName var);
+
+ /**
+ * If <code>stat</code> is used to set up the definition of some class,
+ * return that class. Otherwise, return null.
+ */
+ JReferenceType typeForStatement(JsStatement stat);
+
+ /**
+ * If <code>stat</code> is used to set up a vtable entry for a method,
+ * then return that method. Otherwise return null.
+ */
+ JMethod vtableInitToMethod(JsStatement stat);
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
index ce55dad..4b327ef 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
@@ -19,12 +19,8 @@
import com.google.gwt.dev.jjs.ast.CanBeStatic;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.HasEnclosingType;
-import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
-import com.google.gwt.dev.jjs.ast.JArrayType;
import com.google.gwt.dev.jjs.ast.JBinaryOperation;
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
-import com.google.gwt.dev.jjs.ast.JCastOperation;
-import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
import com.google.gwt.dev.jjs.ast.JExpression;
@@ -32,40 +28,25 @@
import com.google.gwt.dev.jjs.ast.JFieldRef;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JLocal;
-import com.google.gwt.dev.jjs.ast.JLocalRef;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JModVisitor;
-import com.google.gwt.dev.jjs.ast.JNewArray;
-import com.google.gwt.dev.jjs.ast.JNewInstance;
import com.google.gwt.dev.jjs.ast.JNode;
import com.google.gwt.dev.jjs.ast.JParameter;
-import com.google.gwt.dev.jjs.ast.JParameterRef;
-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;
import com.google.gwt.dev.jjs.ast.JType;
-import com.google.gwt.dev.jjs.ast.JVariable;
import com.google.gwt.dev.jjs.ast.JVariableRef;
-import com.google.gwt.dev.jjs.ast.JVisitor;
import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
-import com.google.gwt.dev.js.ast.JsContext;
-import com.google.gwt.dev.js.ast.JsExpression;
import com.google.gwt.dev.js.ast.JsFunction;
-import com.google.gwt.dev.js.ast.JsName;
-import com.google.gwt.dev.js.ast.JsNameRef;
-import com.google.gwt.dev.js.ast.JsVisitor;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -94,6 +75,14 @@
* return type of methods declared to return a globally uninstantiable type.
*/
private class CleanupRefsVisitor extends JModVisitor {
+ private final Map<JMethod, ArrayList<JParameter>> methodToOriginalParamsMap;
+ private final Set<? extends JNode> referencedNonTypes;
+
+ public CleanupRefsVisitor(Set<? extends JNode> referencedNodes,
+ Map<JMethod, ArrayList<JParameter>> methodToOriginalParamsMap) {
+ this.referencedNonTypes = referencedNodes;
+ this.methodToOriginalParamsMap = methodToOriginalParamsMap;
+ }
@Override
public void endVisit(JBinaryOperation x, Context ctx) {
@@ -249,14 +238,27 @@
* unreferenced methods and fields from their containing classes.
*/
private class PruneVisitor extends JModVisitor {
-
private boolean didChange = false;
+ private final Map<JMethod, ArrayList<JParameter>> methodToOriginalParamsMap = new HashMap<JMethod, ArrayList<JParameter>>();
+ private final Set<? extends JNode> referencedNonTypes;
+
+ private final Set<? extends JReferenceType> referencedTypes;
+
+ public PruneVisitor(Set<? extends JReferenceType> referencedTypes,
+ Set<? extends JNode> referencedNodes) {
+ this.referencedTypes = referencedTypes;
+ this.referencedNonTypes = referencedNodes;
+ }
@Override
public boolean didChange() {
return didChange;
}
+ public Map<JMethod, ArrayList<JParameter>> getMethodToOriginalParamsMap() {
+ return methodToOriginalParamsMap;
+ }
+
@Override
public boolean visit(JClassType type, Context ctx) {
@@ -386,7 +388,7 @@
@Override
public boolean visit(JProgram program, Context ctx) {
- for (JMethod method : program.entryMethods) {
+ for (JMethod method : program.getAllEntryMethods()) {
accept(method);
}
for (Iterator<JReferenceType> it = program.getDeclaredTypes().iterator(); it.hasNext();) {
@@ -433,526 +435,12 @@
}
}
- /**
- * Marks as "referenced" any types, methods, and fields that are reachable.
- * Also marks as "instantiable" any the classes and interfaces that can
- * possibly be instantiated.
- *
- * TODO(later): make RescueVisitor use less stack?
- */
- private class RescueVisitor extends JVisitor {
-
- private final Set<JReferenceType> instantiatedTypes = new HashSet<JReferenceType>();
-
- public void commitInstantiatedTypes() {
- program.typeOracle.setInstantiatedTypes(instantiatedTypes);
- }
-
- @Override
- public boolean visit(JArrayType type, Context ctx) {
- assert (referencedTypes.contains(type));
- boolean isInstantiated = instantiatedTypes.contains(type);
-
- JType leafType = type.getLeafType();
- int dims = type.getDims();
-
- // Rescue my super array type
- if (leafType instanceof JReferenceType) {
- JReferenceType rLeafType = (JReferenceType) leafType;
- if (rLeafType.extnds != null) {
- JArrayType superArray = program.getTypeArray(rLeafType.extnds, dims);
- rescue(superArray, true, isInstantiated);
- }
-
- for (int i = 0; i < rLeafType.implments.size(); ++i) {
- JInterfaceType intfType = rLeafType.implments.get(i);
- JArrayType intfArray = program.getTypeArray(intfType, dims);
- rescue(intfArray, true, isInstantiated);
- }
- }
-
- // Rescue the base Array type
- rescue(program.getIndexedType("Array"), true, isInstantiated);
- return false;
- }
-
- @Override
- public boolean visit(JBinaryOperation x, Context ctx) {
- // special string concat handling
- if ((x.getOp() == JBinaryOperator.ADD || x.getOp() == JBinaryOperator.ASG_ADD)
- && x.getType() == program.getTypeJavaLangString()) {
- rescueByConcat(x.getLhs().getType());
- rescueByConcat(x.getRhs().getType());
- } else if (x.getOp() == JBinaryOperator.ASG) {
- // Don't rescue variables that are merely assigned to and never read
- boolean doSkip = false;
- JExpression lhs = x.getLhs();
- if (lhs.hasSideEffects() || isVolatileField(lhs)) {
- // If the lhs has side effects, skipping it would lose the side effect.
- // If the lhs is volatile, also keep it. This behavior provides a useful
- // idiom for test cases to prevent code from being pruned.
- } else if (lhs instanceof JLocalRef) {
- // locals are ok to skip
- doSkip = true;
- } else if (lhs instanceof JParameterRef) {
- // parameters are ok to skip
- doSkip = true;
- } else if (lhs instanceof JFieldRef) {
- // fields must rescue the qualifier
- doSkip = true;
- JFieldRef fieldRef = (JFieldRef) lhs;
- JExpression instance = fieldRef.getInstance();
- if (instance != null) {
- accept(instance);
- }
- }
-
- if (doSkip) {
- accept(x.getRhs());
- return false;
- }
- }
- return true;
- }
-
- @Override
- public boolean visit(JCastOperation x, Context ctx) {
- // Rescue any JavaScriptObject type that is the target of a cast.
- JType targetType = x.getCastType();
- if (program.isJavaScriptObject(targetType)) {
- rescue((JReferenceType) targetType, true, true);
- }
- return true;
- }
-
- @Override
- public boolean visit(JClassType type, Context ctx) {
- assert (referencedTypes.contains(type));
- boolean isInstantiated = instantiatedTypes.contains(type);
-
- /*
- * SPECIAL: Some classes contain methods used by code generation later.
- * Unless those transforms have already been performed, we must rescue all
- * contained methods for later user.
- */
- if (saveCodeGenTypes && program.codeGenTypes.contains(type)) {
- for (int i = 0; i < type.methods.size(); ++i) {
- JMethod it = type.methods.get(i);
- rescue(it);
- }
- }
-
- // Rescue my super type
- rescue(type.extnds, true, isInstantiated);
-
- // Rescue my clinit (it won't ever be explicitly referenced
- rescue(type.methods.get(0));
-
- // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
- // However, if I'm instantiated, let's mark them as instantiated.
- for (int i = 0; i < type.implments.size(); ++i) {
- JInterfaceType intfType = type.implments.get(i);
- rescue(intfType, false, isInstantiated);
- }
-
- return false;
- }
-
- @Override
- public boolean visit(JDeclarationStatement x, Context ctx) {
- /*
- * A declaration by itself doesn't rescue a local (even if it has an
- * initializer). Writes don't count, only reads.
- */
- if (x.getInitializer() != null) {
- accept(x.getInitializer());
- }
-
- // If the lhs is a field ref, we have to visit its qualifier.
- JVariableRef variableRef = x.getVariableRef();
- if (variableRef instanceof JFieldRef) {
- JFieldRef fieldRef = (JFieldRef) variableRef;
- JExpression instance = fieldRef.getInstance();
- if (instance != null) {
- accept(instance);
- }
- }
- return false;
- }
-
- @Override
- public boolean visit(JClassLiteral x, Context ctx) {
- // Works just like JFieldRef to a static field.
- JField field = x.getField();
- rescue(field.getEnclosingType(), true, false);
- rescue(field);
- return true;
- }
-
- @Override
- public boolean visit(JFieldRef ref, Context ctx) {
- JField target = ref.getField();
-
- // JLS 12.4.1: references to static, non-final, or
- // non-compile-time-constant fields rescue the enclosing class.
- // JDT already folds in compile-time constants as literals, so we must
- // rescue the enclosing types for any static fields that make it here.
- if (target.isStatic()) {
- rescue(target.getEnclosingType(), true, false);
- }
- rescue(target);
- return true;
- }
-
- @Override
- public boolean visit(JInterfaceType type, Context ctx) {
- boolean isReferenced = referencedTypes.contains(type);
- boolean isInstantiated = instantiatedTypes.contains(type);
- assert (isReferenced || isInstantiated);
-
- // Rescue my clinit (it won't ever be explicitly referenced
- rescue(type.methods.get(0));
-
- // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
- // However, if I'm instantiated, let's mark them as instantiated.
- if (isInstantiated) {
- for (int i = 0; i < type.implments.size(); ++i) {
- JInterfaceType intfType = type.implments.get(i);
- rescue(intfType, false, true);
- }
- }
-
- // visit any field initializers
- for (int i = 0; i < type.fields.size(); ++i) {
- JField it = type.fields.get(i);
- accept(it);
- }
-
- return false;
- }
-
- @Override
- public boolean visit(JLocalRef ref, Context ctx) {
- JLocal target = ref.getLocal();
- rescue(target);
- return true;
- }
-
- @Override
- public boolean visit(final JMethod x, Context ctx) {
- JReferenceType enclosingType = x.getEnclosingType();
- if (program.isJavaScriptObject(enclosingType)) {
- // Calls to JavaScriptObject types rescue those types.
- boolean instance = !x.isStatic() || program.isStaticImpl(x);
- rescue(enclosingType, true, instance);
- }
-
- if (x.isStatic()) {
- // JLS 12.4.1: references to static methods rescue the enclosing class
- rescue(enclosingType, true, false);
- }
-
- if (x.isNative()) {
- // Manually rescue native parameter references
- final JsniMethodBody body = (JsniMethodBody) x.getBody();
- final JsFunction func = body.getFunc();
-
- new JsVisitor() {
- @Override
- public void endVisit(JsNameRef nameRef, JsContext<JsExpression> ctx) {
- JsName ident = nameRef.getName();
-
- if (ident != null) {
- // If we're referencing a parameter, rescue the associated
- // JParameter
- int index = func.getParameters().indexOf(ident.getStaticRef());
- if (index != -1) {
- rescue(x.params.get(index));
- }
- }
- }
- }.accept(func);
- }
-
- return true;
- }
-
- @Override
- public boolean visit(JMethodCall call, Context ctx) {
- rescue(call.getTarget());
- return true;
- }
-
- @Override
- public boolean visit(JNewArray newArray, Context ctx) {
- // rescue and instantiate the array type
- JArrayType arrayType = newArray.getArrayType();
- if (newArray.dims != null) {
- // rescue my type and all the implicitly nested types (with fewer dims)
- int nDims = arrayType.getDims();
- JType leafType = arrayType.getLeafType();
- assert (newArray.dims.size() == nDims);
- for (int i = 0; i < nDims; ++i) {
- if (newArray.dims.get(i) instanceof JAbsentArrayDimension) {
- break;
- }
- rescue(program.getTypeArray(leafType, nDims - i), true, true);
- }
- } else {
- // just rescue my own specific type
- rescue(arrayType, true, true);
- }
- return true;
- }
-
- @Override
- public boolean visit(JNewInstance newInstance, Context ctx) {
- // rescue and instantiate the target class!
- rescue(newInstance.getClassType(), true, true);
- return true;
- }
-
- @Override
- public boolean visit(JParameterRef x, Context ctx) {
- // rescue the parameter for future pruning purposes
- rescue(x.getParameter());
- return true;
- }
-
- @Override
- public boolean visit(JsniFieldRef x, Context ctx) {
- /*
- * SPECIAL: this could be an assignment that passes a value from
- * JavaScript into Java.
- */
- if (x.isLvalue()) {
- maybeRescueJavaScriptObjectPassingIntoJava(x.getField().getType());
- }
- // JsniFieldRef rescues as JFieldRef
- return visit((JFieldRef) x, ctx);
- }
-
- @Override
- public boolean visit(JsniMethodRef x, Context ctx) {
- /*
- * SPECIAL: each argument of the call passes a value from JavaScript into
- * Java.
- */
- ArrayList<JParameter> params = x.getTarget().params;
- for (int i = 0, c = params.size(); i < c; ++i) {
- JParameter param = params.get(i);
- maybeRescueJavaScriptObjectPassingIntoJava(param.getType());
-
- /*
- * Because we're not currently tracking methods through JSNI, we need to
- * assume that it's not safe to prune parameters of a method referenced
- * as such.
- *
- * A better solution would be to perform basic escape analysis to ensure
- * that the function reference never escapes, or at minimum, ensure that
- * the method is immediately called after retrieving the method
- * reference.
- */
- rescue(param);
- }
- // JsniMethodRef rescues as JMethodCall
- return visit((JMethodCall) x, ctx);
- }
-
- @Override
- public boolean visit(JStringLiteral literal, Context ctx) {
- // rescue and instantiate java.lang.String
- rescue(program.getTypeJavaLangString(), true, true);
- return true;
- }
-
- private boolean isVolatileField(JExpression x) {
- if (x instanceof JFieldRef) {
- JFieldRef xFieldRef = (JFieldRef) x;
- if (xFieldRef.getField().isVolatile()) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Subclasses of JavaScriptObject are never instantiated directly. They are
- * created "magically" when a JSNI method passes a reference to an existing
- * JS object into Java code. If any point in the program can pass a value
- * from JS into Java which could potentially be cast to JavaScriptObject, we
- * must rescue JavaScriptObject.
- *
- * @param type The type of the value passing from Java to JavaScript.
- * @see com.google.gwt.core.client.JavaScriptObject
- */
- private void maybeRescueJavaScriptObjectPassingIntoJava(JType type) {
- boolean doIt = false;
- if (program.isJavaScriptObject(type)
- || type == program.getTypeJavaLangString()) {
- doIt = true;
- } else if (type instanceof JArrayType) {
- /*
- * Hackish: in our own JRE we sometimes create "not quite baked" arrays
- * in JavaScript for expediency.
- */
- JArrayType arrayType = (JArrayType) type;
- JType elementType = arrayType.getElementType();
- if (elementType instanceof JPrimitiveType
- || elementType == program.getTypeJavaLangString()
- || program.isJavaScriptObject(elementType)) {
- doIt = true;
- }
- }
- if (doIt) {
- rescue((JReferenceType) type, true, true);
- }
- }
-
- private boolean rescue(JMethod method) {
- if (method != null) {
- if (!referencedNonTypes.contains(method)) {
- referencedNonTypes.add(method);
- accept(method);
- if (method.isNative()) {
- /*
- * SPECIAL: returning from this method passes a value from
- * JavaScript into Java.
- */
- maybeRescueJavaScriptObjectPassingIntoJava(method.getType());
- }
- return true;
- }
- }
- return false;
- }
-
- private void rescue(JReferenceType type, boolean isReferenced,
- boolean isInstantiated) {
- if (type != null) {
-
- boolean doVisit = false;
- if (isInstantiated && !instantiatedTypes.contains(type)) {
- instantiatedTypes.add(type);
- doVisit = true;
- }
-
- if (isReferenced && !referencedTypes.contains(type)) {
- referencedTypes.add(type);
- doVisit = true;
- }
-
- if (doVisit) {
- accept(type);
- }
- }
- }
-
- private void rescue(JVariable var) {
- if (var != null) {
- if (!referencedNonTypes.contains(var)) {
- referencedNonTypes.add(var);
- }
- }
- }
-
- /**
- * Handle special rescues needed implicitly to support concat.
- */
- private void rescueByConcat(JType type) {
- JClassType stringType = program.getTypeJavaLangString();
- JPrimitiveType charType = program.getTypePrimitiveChar();
- if (type instanceof JReferenceType && type != stringType
- && type != program.getTypeNull()) {
- /*
- * Any reference types (except String, which works by default) that take
- * part in a concat must rescue java.lang.Object.toString().
- *
- * TODO: can we narrow the focus by walking up the type hierarchy or
- * doing explicit toString calls?
- */
- JMethod toStringMethod = program.getIndexedMethod("Object.toString");
- rescue(toStringMethod);
- } else if (type == charType) {
- /*
- * Characters must rescue String.valueOf(char)
- */
- if (stringValueOfChar == null) {
- for (int i = 0; i < stringType.methods.size(); ++i) {
- JMethod meth = stringType.methods.get(i);
- if (meth.getName().equals("valueOf")) {
- List<JType> params = meth.getOriginalParamTypes();
- if (params.size() == 1) {
- if (params.get(0) == charType) {
- stringValueOfChar = meth;
- break;
- }
- }
- }
- }
- assert (stringValueOfChar != null);
- }
- rescue(stringValueOfChar);
- }
- }
- }
-
- /**
- * A method that isn't called directly can still be needed, if it overrides or
- * implements any methods that are called.
- */
- private class UpRefVisitor extends JVisitor {
-
- private boolean didRescue = false;
- private final RescueVisitor rescuer;
-
- public UpRefVisitor(RescueVisitor rescuer) {
- this.rescuer = rescuer;
- }
-
- public boolean didRescue() {
- return didRescue;
- }
-
- @Override
- public boolean visit(JMethod x, Context ctx) {
- if (referencedNonTypes.contains(x)) {
- return false;
- }
-
- for (JMethod override : program.typeOracle.getAllOverrides(x)) {
- if (referencedNonTypes.contains(override)) {
- rescuer.rescue(x);
- didRescue = true;
- return false;
- }
- }
- return false;
- }
-
- @Override
- public boolean visit(JClassType x, Context ctx) {
- return rescuer.instantiatedTypes.contains(x);
- }
-
- @Override
- public boolean visit(JProgram x, Context ctx) {
- didRescue = false;
- return true;
- }
- }
-
public static boolean exec(JProgram program, boolean noSpecialTypes) {
return new Pruner(program, noSpecialTypes).execImpl();
}
- private final Map<JMethod, ArrayList<JParameter>> methodToOriginalParamsMap = new HashMap<JMethod, ArrayList<JParameter>>();
private final JProgram program;
- private final Set<JNode> referencedNonTypes = new HashSet<JNode>();
- private final Set<JReferenceType> referencedTypes = new HashSet<JReferenceType>();
private final boolean saveCodeGenTypes;
- private JMethod stringValueOfChar = null;
private Pruner(JProgram program, boolean saveCodeGenTypes) {
this.program = program;
@@ -962,35 +450,52 @@
private boolean execImpl() {
boolean madeChanges = false;
while (true) {
- RescueVisitor rescuer = new RescueVisitor();
- for (JReferenceType type : program.codeGenTypes) {
- rescuer.rescue(type, true, saveCodeGenTypes);
+ ControlFlowAnalyzer livenessAnalyzer = new ControlFlowAnalyzer(program);
+ if (saveCodeGenTypes) {
+ /*
+ * SPECIAL: Some classes contain methods used by code generation later.
+ * Unless those transforms have already been performed, we must rescue
+ * all contained methods for later user.
+ */
+ traverseFromCodeGenTypes(livenessAnalyzer);
}
- for (JMethod method : program.entryMethods) {
- rescuer.rescue(method);
+ for (JMethod method : program.getAllEntryMethods()) {
+ livenessAnalyzer.traverseFrom(method);
}
+ livenessAnalyzer.finishTraversal();
- UpRefVisitor upRefer = new UpRefVisitor(rescuer);
- do {
- rescuer.commitInstantiatedTypes();
- upRefer.accept(program);
- } while (upRefer.didRescue());
+ program.typeOracle.setInstantiatedTypes(livenessAnalyzer.getInstantiatedTypes());
- PruneVisitor pruner = new PruneVisitor();
+ PruneVisitor pruner = new PruneVisitor(
+ livenessAnalyzer.getReferencedTypes(),
+ livenessAnalyzer.getLiveFieldsAndMethods());
pruner.accept(program);
if (!pruner.didChange()) {
break;
}
- CleanupRefsVisitor cleaner = new CleanupRefsVisitor();
- cleaner.accept(program);
+ CleanupRefsVisitor cleaner = new CleanupRefsVisitor(
+ livenessAnalyzer.getLiveFieldsAndMethods(),
+ pruner.getMethodToOriginalParamsMap());
+ cleaner.accept(program.getDeclaredTypes());
- referencedTypes.clear();
- referencedNonTypes.clear();
- methodToOriginalParamsMap.clear();
madeChanges = true;
}
return madeChanges;
}
+ /**
+ * Traverse from all methods in the program's code-gen types. See
+ * {@link JProgram#CODEGEN_TYPES_SET}.
+ */
+ private void traverseFromCodeGenTypes(ControlFlowAnalyzer livenessAnalyzer) {
+ for (JReferenceType type : program.codeGenTypes) {
+ livenessAnalyzer.traverseFromReferenceTo(type);
+ for (int i = 0; i < type.methods.size(); ++i) {
+ JMethod method = type.methods.get(i);
+ livenessAnalyzer.traverseFrom(method);
+ }
+ }
+ }
+
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
new file mode 100644
index 0000000..f9dfa3c
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2008 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.core.ext.TreeLogger;
+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.JMethod;
+import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JType;
+
+/**
+ * Replaces calls to
+ * {@link com.google.gwt.core.client.GWT#runAsync(com.google.gwt.core.client.RunAsyncCallback)}"
+ * by calls to a fragment loader.
+ */
+public class ReplaceRunAsyncs {
+ private class AsyncCreateVisitor extends JModVisitor {
+ private JMethod currentMethod;
+ private int entryCount = 1;
+
+ @Override
+ public void endVisit(JMethodCall x, Context ctx) {
+ JMethod method = x.getTarget();
+ if (method == program.getIndexedMethod("GWT.runAsync")) {
+ assert (x.getArgs().size() == 1);
+ JExpression asyncCallback = x.getArgs().get(0);
+
+ int entryNumber = entryCount++;
+ logger.log(TreeLogger.TRACE, "Using island loader #" + entryNumber
+ + " in method " + currentMethod);
+
+ JClassType loader = getFragmentLoader(entryNumber);
+ JMethod loadMethod = getRunAsyncMethod(loader);
+ assert loadMethod != null;
+
+ JMethodCall methodCall = new JMethodCall(program, x.getSourceInfo(),
+ null, loadMethod);
+ methodCall.getArgs().add(asyncCallback);
+
+ program.addEntryMethod(getOnLoadMethod(loader), entryNumber);
+
+ ctx.replaceMe(methodCall);
+ }
+ }
+
+ @Override
+ public boolean visit(JMethod x, Context ctx) {
+ currentMethod = x;
+ return true;
+ }
+ }
+
+ public static int exec(TreeLogger logger, JProgram program) {
+ return new ReplaceRunAsyncs(logger, program).execImpl();
+ }
+
+ private final TreeLogger logger;
+ private JProgram program;
+
+ private ReplaceRunAsyncs(TreeLogger logger, JProgram program) {
+ this.logger = logger.branch(TreeLogger.TRACE,
+ "Replacing GWT.runAsync with island loader calls");
+ this.program = program;
+ }
+
+ private int execImpl() {
+ AsyncCreateVisitor visitor = new AsyncCreateVisitor();
+ visitor.accept(program);
+ return visitor.entryCount;
+ }
+
+ private JClassType getFragmentLoader(int fragmentNumber) {
+ String fragmentLoaderClassName = FragmentLoaderCreator.ASYNC_LOADER_PACKAGE
+ + "." + FragmentLoaderCreator.ASYNC_LOADER_CLASS_PREFIX
+ + fragmentNumber;
+ JType result = program.getFromTypeMap(fragmentLoaderClassName);
+ assert (result != null);
+ assert (result instanceof JClassType);
+ return (JClassType) result;
+ }
+
+ private JMethod getOnLoadMethod(JClassType loaderType) {
+ assert loaderType != null;
+ assert loaderType.methods != null;
+ for (JMethod method : loaderType.methods) {
+ if (method.getName().equals("onLoad")) {
+ assert (method.isStatic());
+ assert (method.params.size() == 0);
+ return method;
+ }
+ }
+ assert false;
+ return null;
+ }
+
+ private JMethod getRunAsyncMethod(JClassType loaderType) {
+ assert loaderType != null;
+ assert loaderType.methods != null;
+ for (JMethod method : loaderType.methods) {
+ if (method.getName().equals("runAsync")) {
+ assert (method.isStatic());
+ assert (method.params.size() == 1);
+ assert (method.params.get(0).getType().getName().equals(FragmentLoaderCreator.RUN_ASYNC_CALLBACK));
+ return method;
+ }
+ }
+ return null;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitor.java
index ad1a780..4a15d4a 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitor.java
@@ -18,6 +18,7 @@
import com.google.gwt.dev.js.ast.JsBlock;
import com.google.gwt.dev.js.ast.JsContext;
import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsProgramFragment;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.util.TextOutput;
@@ -30,11 +31,19 @@
super(out);
}
+ @Override
public boolean visit(JsProgram x, JsContext<JsProgram> ctx) {
// Descend naturally.
return true;
}
+ @Override
+ public boolean visit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+ // Descend naturally.
+ return true;
+ }
+
+ @Override
public boolean visit(JsBlock x, JsContext<JsStatement> ctx) {
printJsBlockOptionalTruncate(x, false);
return false;
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java b/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
index f7f33e7..09336f3 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
@@ -34,6 +34,7 @@
import com.google.gwt.dev.js.ast.JsNullLiteral;
import com.google.gwt.dev.js.ast.JsPrefixOperation;
import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsProgramFragment;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.js.ast.JsUnaryOperator;
import com.google.gwt.dev.js.ast.JsValueLiteral;
@@ -103,6 +104,11 @@
}
@Override
+ public void endVisit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+ scopeStack.pop();
+ }
+
+ @Override
public boolean visit(JsBlock x, JsContext<JsStatement> ctx) {
if (x == scopeStack.peek()) {
ListIterator<JsStatement> itr = x.getStatements().listIterator();
@@ -159,6 +165,12 @@
scopeStack.push(x.getGlobalBlock());
return true;
}
+
+ @Override
+ public boolean visit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+ scopeStack.push(x.getGlobalBlock());
+ return true;
+ }
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java b/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java
index 9bbbc72..4c10370 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStringInterner.java
@@ -32,6 +32,7 @@
import com.google.gwt.dev.js.ast.JsVars.JsVar;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -162,19 +163,7 @@
StringVisitor v = new StringVisitor(scope);
v.accept(block);
- if (v.toCreate.size() > 0) {
- // Create the pool of variable names.
- JsVars vars = new JsVars(program.createSourceInfoSynthetic(
- JsStringInterner.class, "Interned string pool"));
- SourceInfo sourceInfo = program.createSourceInfoSynthetic(
- JsStringInterner.class, "Interned string assignment");
- for (Map.Entry<JsStringLiteral, JsName> entry : v.toCreate.entrySet()) {
- JsVar var = new JsVar(sourceInfo, entry.getValue());
- var.setInitExpr(entry.getKey());
- vars.add(var);
- }
- block.getStatements().add(0, vars);
- }
+ createVars(program, block, v.toCreate);
return v.didChange();
}
@@ -186,10 +175,39 @@
* global block.
*
* @param program the JsProgram
- * @return <code>true</code> if any changes were made to the program
+ * @return a map from each created JsName to the string it is a literal for.
*/
- public static boolean exec(JsProgram program) {
- return exec(program, program.getGlobalBlock(), program.getScope());
+ public static Map<JsName, String> exec(JsProgram program) {
+ StringVisitor v = new StringVisitor(program.getScope());
+ for (int i = 0; i < program.getFragmentCount(); i++) {
+ v.accept(program.getFragmentBlock(i));
+ }
+
+ Map<JsName, String> map = new HashMap<JsName, String>();
+ for (JsStringLiteral stringLit : v.toCreate.keySet()) {
+ map.put(v.toCreate.get(stringLit), stringLit.getValue());
+ }
+
+ createVars(program, program.getGlobalBlock(), v.toCreate);
+
+ return map;
+ }
+
+ private static void createVars(JsProgram program, JsBlock block,
+ SortedMap<JsStringLiteral, JsName> toCreate) {
+ if (toCreate.size() > 0) {
+ // Create the pool of variable names.
+ JsVars vars = new JsVars(program.createSourceInfoSynthetic(
+ JsStringInterner.class, "Interned string pool"));
+ SourceInfo sourceInfo = program.createSourceInfoSynthetic(
+ JsStringInterner.class, "Interned string assignment");
+ for (Map.Entry<JsStringLiteral, JsName> entry : toCreate.entrySet()) {
+ JsVar var = new JsVar(sourceInfo, entry.getValue());
+ var.setInitExpr(entry.getKey());
+ vars.add(var);
+ }
+ block.getStatements().add(0, vars);
+ }
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
index 13fb936..f2ce3e9 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
@@ -51,6 +51,7 @@
import com.google.gwt.dev.js.ast.JsPostfixOperation;
import com.google.gwt.dev.js.ast.JsPrefixOperation;
import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsProgramFragment;
import com.google.gwt.dev.js.ast.JsPropertyInitializer;
import com.google.gwt.dev.js.ast.JsRegExp;
import com.google.gwt.dev.js.ast.JsReturn;
@@ -658,6 +659,12 @@
}
@Override
+ public boolean visit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+ p.print("<JsProgramFragment>");
+ return false;
+ }
+
+ @Override
public boolean visit(JsPropertyInitializer x,
JsContext<JsPropertyInitializer> ctx) {
// Since there are separators, we actually print the property init
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java b/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java
index 68e4988..e6b6b9e 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java
@@ -31,10 +31,12 @@
private final JsEmpty emptyStmt = new JsEmpty(createSourceInfoSynthetic(
JsProgram.class, "Empty statement"));
+ private boolean enableSourceInfoDescendants;
+
private final JsBooleanLiteral falseLiteral = new JsBooleanLiteral(
createSourceInfoSynthetic(JsProgram.class, "false literal"), false);
- private final JsGlobalBlock globalBlock;
+ private JsProgramFragment[] fragments;
private final JsNullLiteral nullLiteral = new JsNullLiteral(
createSourceInfoSynthetic(JsProgram.class, "null literal"));
@@ -52,8 +54,6 @@
private final JsBooleanLiteral trueLiteral = new JsBooleanLiteral(
createSourceInfoSynthetic(JsProgram.class, "true literal"), true);
- private boolean enableSourceInfoDescendants;
-
/**
* Constructs a JavaScript program object.
*/
@@ -61,10 +61,9 @@
super(SourceInfoJs.INTRINSIC.makeChild(JsProgram.class,
"JavaScript program"));
rootScope = new JsRootScope(this);
- globalBlock = new JsGlobalBlock(createSourceInfoSynthetic(JsProgram.class,
- "global block"));
topScope = new JsScope(rootScope, "Global");
objectScope = new JsScope(rootScope, "Object");
+ setFragmentCount(1);
}
public SourceInfo createSourceInfo(int lineNumber, String location) {
@@ -102,11 +101,22 @@
return falseLiteral;
}
+ public JsBlock getFragmentBlock(int fragment) {
+ if (fragment < 0 || fragment >= fragments.length) {
+ throw new IllegalArgumentException("Invalid fragment: " + fragment);
+ }
+ return fragments[fragment].getGlobalBlock();
+ }
+
+ public int getFragmentCount() {
+ return this.fragments.length;
+ }
+
/**
* Gets the one and only global block.
*/
public JsBlock getGlobalBlock() {
- return globalBlock;
+ return getFragmentBlock(0);
}
public JsNullLiteral getNullLiteral() {
@@ -144,6 +154,11 @@
return topScope;
}
+ /**
+ * Note: This is currently assumed not to be called after
+ * GenerateJavaScriptAST has finished. If it ever is, then GWT.runAsync needs
+ * to be updated to account for such string literals.
+ */
public JsStringLiteral getStringLiteral(SourceInfo sourceInfo, String value) {
JsStringLiteral lit = stringLiteralMap.get(value);
if (lit == null) {
@@ -174,9 +189,19 @@
enableSourceInfoDescendants = enable;
}
+ public void setFragmentCount(int fragments) {
+ this.fragments = new JsProgramFragment[fragments];
+ for (int i = 0; i < fragments; i++) {
+ this.fragments[i] = new JsProgramFragment(createSourceInfoSynthetic(
+ JsProgram.class, "fragment " + i));
+ }
+ }
+
public void traverse(JsVisitor v, JsContext<JsProgram> ctx) {
if (v.visit(this, ctx)) {
- v.accept(globalBlock);
+ for (JsProgramFragment fragment : fragments) {
+ v.accept(fragment);
+ }
}
v.endVisit(this, ctx);
}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsProgramFragment.java b/dev/core/src/com/google/gwt/dev/js/ast/JsProgramFragment.java
new file mode 100644
index 0000000..2b685aa
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsProgramFragment.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2008 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.js.ast;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+/**
+ * One independently loadable fragment of a {@link JsProgram}.
+ */
+public class JsProgramFragment extends JsNode<JsProgramFragment> {
+ private final JsGlobalBlock globalBlock;
+
+ public JsProgramFragment(SourceInfo sourceInfo) {
+ super(sourceInfo);
+ this.globalBlock = new JsGlobalBlock(sourceInfo.makeChild(
+ JsProgramFragment.class, "global block for a fragment"));
+ }
+
+ public JsBlock getGlobalBlock() {
+ return globalBlock;
+ }
+
+ public void traverse(JsVisitor v, JsContext<JsProgramFragment> ctx) {
+ if (v.visit(this, ctx)) {
+ v.accept(globalBlock);
+ }
+ v.endVisit(this, ctx);
+ }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsVars.java b/dev/core/src/com/google/gwt/dev/js/ast/JsVars.java
index 86e1e41..5b37e06 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsVars.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsVars.java
@@ -72,6 +72,10 @@
vars.add(var);
}
+ public int getNumVars() {
+ return vars.size();
+ }
+
public boolean isEmpty() {
return vars.isEmpty();
}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
index ffaf3dd..e3151f5 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
@@ -91,6 +91,8 @@
};
public final <T extends JsVisitable> T accept(T node) {
+ // The following cast to T is needed for javac 1.5.0_13
+ // as shipped on OS X
return (T) doAccept(node);
}
@@ -201,6 +203,9 @@
public void endVisit(JsProgram x, JsContext<JsProgram> ctx) {
}
+ public void endVisit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+ }
+
public void endVisit(JsPropertyInitializer x,
JsContext<JsPropertyInitializer> ctx) {
}
@@ -355,6 +360,10 @@
return true;
}
+ public boolean visit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) {
+ return true;
+ }
+
public boolean visit(JsPropertyInitializer x,
JsContext<JsPropertyInitializer> ctx) {
return true;
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
index b04fcf4..88dc709 100644
--- a/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
+++ b/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java
@@ -27,6 +27,7 @@
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
+import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -165,6 +166,9 @@
classPath.add(entry);
}
continue;
+ } catch (AccessControlException e) {
+ logger.log(TreeLogger.DEBUG, "Skipping URL due to access restrictions: " + url);
+ continue;
} catch (URISyntaxException e) {
caught = e;
} catch (IOException e) {
diff --git a/dev/core/src/com/google/gwt/dev/util/Util.java b/dev/core/src/com/google/gwt/dev/util/Util.java
index aabd864..31f6972 100644
--- a/dev/core/src/com/google/gwt/dev/util/Util.java
+++ b/dev/core/src/com/google/gwt/dev/util/Util.java
@@ -56,6 +56,7 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
+import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@@ -132,6 +133,16 @@
* formatted for use as a file name
*/
public static String computeStrongName(byte[] content) {
+ return computeStrongName(new byte[][] {content});
+ }
+
+ /**
+ * Computes the MD5 hash of the specified byte arrays.
+ *
+ * @return a big fat string encoding of the MD5 for the content, suitably
+ * formatted for use as a file name
+ */
+ public static String computeStrongName(byte[][] contents) {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
@@ -139,7 +150,23 @@
throw new RuntimeException("Error initializing MD5", e);
}
- md5.update(content);
+ /*
+ * Include the lengths of the contents components in the hash, so that the
+ * hashed sequence of bytes is in a one-to-one correspondence with the
+ * possible arguments to this method.
+ */
+ ByteBuffer b = ByteBuffer.allocate((contents.length + 1) * 4);
+ b.putInt(contents.length);
+ for (int i = 0; i < contents.length; i++) {
+ b.putInt(contents[i].length);
+ }
+ b.flip();
+ md5.update(b);
+
+ // Now hash the actual contents of the arrays
+ for (int i = 0; i < contents.length; i++) {
+ md5.update(contents[i]);
+ }
return toHexString(md5.digest());
}
@@ -296,6 +323,14 @@
}
}
+ public static byte[][] getBytes(String[] s) {
+ byte[][] bytes = new byte[s.length][];
+ for (int i = 0; i < s.length; i++) {
+ bytes[i] = getBytes(s[i]);
+ }
+ return bytes;
+ }
+
/**
* @param cls A class whose name you want.
* @return The base name for the specified class.
@@ -543,6 +578,25 @@
logger.log(TreeLogger.INFO, "Unable to dump source to disk", caught);
}
+ public static byte[][] readFileAndSplit(File file) {
+ RandomAccessFile f = null;
+ try {
+ f = new RandomAccessFile(file, "r");
+ int length = f.readInt();
+ byte[][] results = new byte[length][];
+ for (int i = 0; i < length; i++) {
+ int nextLength = f.readInt();
+ results[i] = new byte[nextLength];
+ f.read(results[i]);
+ }
+ return results;
+ } catch (IOException e) {
+ return null;
+ } finally {
+ Utility.close(f);
+ }
+ }
+
public static byte[] readFileAsBytes(File file) {
FileInputStream fileInputStream = null;
try {
@@ -877,6 +931,14 @@
return toArray(String.class, coll);
}
+ public static String[] toStrings(byte[][] bytes) {
+ String[] strings = new String[bytes.length];
+ for (int i = 0; i < bytes.length; i++) {
+ strings[i] = toString(bytes[i]);
+ }
+ return strings;
+ }
+
public static URL toURL(File f) {
try {
return f.toURI().toURL();
@@ -1094,6 +1156,31 @@
}
/**
+ * Write all of the supplied bytes to the file, in a way that they can be read
+ * back by {@link #readFileAndSplit(File).
+ */
+ public static boolean writeStringsAsFile(TreeLogger branch,
+ File makePermFilename, String[] js) {
+ RandomAccessFile f = null;
+ try {
+ makePermFilename.delete();
+ makePermFilename.getParentFile().mkdirs();
+ f = new RandomAccessFile(makePermFilename, "rwd");
+ f.writeInt(js.length);
+ for (String s : js) {
+ byte[] b = getBytes(s);
+ f.writeInt(b.length);
+ f.write(b);
+ }
+ return true;
+ } catch (IOException e) {
+ return false;
+ } finally {
+ Utility.close(f);
+ }
+ }
+
+ /**
* Reads the specified number of bytes from the {@link InputStream}.
*
* @param byteLength number of bytes to read
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/ContentWidget.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/ContentWidget.java
index 3f422b6..f89e59b 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/ContentWidget.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/ContentWidget.java
@@ -24,6 +24,8 @@
import com.google.gwt.i18n.client.HasDirection;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DeckPanel;
import com.google.gwt.user.client.ui.HTML;
@@ -80,6 +82,11 @@
private static String loadingImage;
/**
+ * The tab bar of options.
+ */
+ protected TabBar tabBar = null;
+
+ /**
* An instance of the constants.
*/
private CwConstants constants;
@@ -116,11 +123,6 @@
private HTML styleWidget = null;
/**
- * The tab bar of options.
- */
- private TabBar tabBar = null;
-
- /**
* Constructor.
*
* @param constants the constants
@@ -223,12 +225,21 @@
add(styleWidget, constants.contentWidgetStyle());
}
- // Initialize the widget and add it to the page
- Widget widget = onInitialize();
- if (widget != null) {
- vPanel.add(widget);
- }
- onInitializeComplete();
+ asyncOnInitialize(new AsyncCallback<Widget>() {
+
+ public void onFailure(Throwable caught) {
+ Window.alert("exception: " + caught);
+ }
+
+ public void onSuccess(Widget result) {
+ // Initialize the widget and add it to the page
+ Widget widget = result;
+ if (widget != null) {
+ vPanel.add(widget);
+ }
+ onInitializeComplete();
+ }
+ });
}
}
@@ -304,6 +315,8 @@
tabBar.selectTab(index);
}
+ protected abstract void asyncOnInitialize(final AsyncCallback<Widget> callback);
+
@Override
protected void onLoad() {
// Initialize this widget if we haven't already
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsExample.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsExample.java
index 13602aa..6db6ae0 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsExample.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsExample.java
@@ -16,12 +16,14 @@
package com.google.gwt.sample.showcase.client.content.i18n;
import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseConstants;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseRaw;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
@@ -37,7 +39,7 @@
/**
* Example file.
*/
-@ShowcaseRaw({"ExampleConstants.java", "ExampleConstants.properties"})
+@ShowcaseRaw( {"ExampleConstants.java", "ExampleConstants.properties"})
public class CwConstantsExample extends ContentWidget {
/**
* The constants used in this Content Widget.
@@ -189,6 +191,20 @@
}
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Add a tab to this example to show the messages interface.
*/
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsWithLookupExample.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsWithLookupExample.java
index 82d69ce..38e55db 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsWithLookupExample.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwConstantsWithLookupExample.java
@@ -16,12 +16,14 @@
package com.google.gwt.sample.showcase.client.content.i18n;
import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseConstants;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseRaw;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
@@ -37,7 +39,7 @@
/**
* Example file.
*/
-@ShowcaseRaw({"ColorConstants.java", "ColorConstants.properties"})
+@ShowcaseRaw( {"ColorConstants.java", "ColorConstants.properties"})
public class CwConstantsWithLookupExample extends ContentWidget {
/**
* The constants used in this Content Widget.
@@ -211,6 +213,20 @@
}
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Add a tab to this example to show the messages interface.
*/
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDateTimeFormat.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDateTimeFormat.java
index db1e90a..7789094 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDateTimeFormat.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDateTimeFormat.java
@@ -15,12 +15,15 @@
*/
package com.google.gwt.sample.showcase.client.content.i18n;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
@@ -178,6 +181,20 @@
return layout;
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Show an error message. Pass in null to clear the error message.
*
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDictionaryExample.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDictionaryExample.java
index a6d78fe..16a02bf 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDictionaryExample.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwDictionaryExample.java
@@ -15,12 +15,15 @@
*/
package com.google.gwt.sample.showcase.client.content.i18n;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.i18n.client.Dictionary;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.VerticalPanel;
@@ -117,4 +120,18 @@
// Return the layout Widget
return layout;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwMessagesExample.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwMessagesExample.java
index ff5183c..11e771b 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwMessagesExample.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwMessagesExample.java
@@ -16,12 +16,14 @@
package com.google.gwt.sample.showcase.client.content.i18n;
import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseConstants;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseRaw;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
@@ -243,6 +245,20 @@
}
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Add a tab to this example to show the {@link ErrorMessages} source code.
*/
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwNumberFormat.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwNumberFormat.java
index ad91de0..32ac953 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwNumberFormat.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/i18n/CwNumberFormat.java
@@ -15,12 +15,15 @@
*/
package com.google.gwt.sample.showcase.client.content.i18n;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.KeyboardListenerAdapter;
@@ -172,6 +175,20 @@
return layout;
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Show an error message. Pass in null to clear the error message.
*
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwListBox.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwListBox.java
index e9715a5..2a98d82 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwListBox.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwListBox.java
@@ -15,11 +15,14 @@
*/
package com.google.gwt.sample.showcase.client.content.lists;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
@@ -131,6 +134,20 @@
return hPanel;
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Display the options for a given category in the list box.
*
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwMenuBar.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwMenuBar.java
index 34c48f8..c022dbc 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwMenuBar.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwMenuBar.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.sample.showcase.client.content.lists;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
@@ -22,6 +24,7 @@
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.Widget;
@@ -29,7 +32,8 @@
/**
* Example file.
*/
-@ShowcaseStyle({".gwt-MenuBar", ".gwt-MenuBarPopup", "html>body .gwt-MenuBarPopup",
+@ShowcaseStyle( {
+ ".gwt-MenuBar", ".gwt-MenuBarPopup", "html>body .gwt-MenuBarPopup",
"* html .gwt-MenuBarPopup"})
public class CwMenuBar extends ContentWidget {
/**
@@ -161,4 +165,18 @@
menu.ensureDebugId("cwMenuBar");
return menu;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwStackPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwStackPanel.java
index 1b8cc3b..cbe4706 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwStackPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwStackPanel.java
@@ -16,11 +16,13 @@
package com.google.gwt.sample.showcase.client.content.lists;
import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
@@ -38,7 +40,8 @@
/**
* Example file.
*/
-@ShowcaseStyle({".gwt-DecoratedStackPanel", "html>body .gwt-DecoratedStackPanel",
+@ShowcaseStyle( {
+ ".gwt-DecoratedStackPanel", "html>body .gwt-DecoratedStackPanel",
"* html .gwt-DecoratedStackPanel", ".cw-StackPanelHeader"})
public class CwStackPanel extends ContentWidget {
/**
@@ -164,6 +167,20 @@
return stackPanel;
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Create the list of Contacts.
*
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwSuggestBox.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwSuggestBox.java
index 49a850f..1d720be 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwSuggestBox.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwSuggestBox.java
@@ -15,11 +15,14 @@
*/
package com.google.gwt.sample.showcase.client.content.lists;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
import com.google.gwt.user.client.ui.SuggestBox;
@@ -29,7 +32,8 @@
/**
* Example file.
*/
-@ShowcaseStyle({".gwt-SuggestBox", ".gwt-SuggestBoxPopup",
+@ShowcaseStyle({
+ ".gwt-SuggestBox", ".gwt-SuggestBoxPopup",
"html>body .gwt-SuggestBoxPopup", "* html .gwt-SuggestBoxPopup"})
public class CwSuggestBox extends ContentWidget {
/**
@@ -96,4 +100,18 @@
// Return the panel
return suggestPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwTree.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwTree.java
index 942a881..62f480b 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwTree.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/lists/CwTree.java
@@ -15,12 +15,15 @@
*/
package com.google.gwt.sample.showcase.client.content.lists;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
import com.google.gwt.user.client.Random;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.DecoratorPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
@@ -150,6 +153,20 @@
return grid;
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Add a new section of music created by a specific composer.
*
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwAnimation.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwAnimation.java
index 8e56c1f..409a587 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwAnimation.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwAnimation.java
@@ -16,11 +16,14 @@
package com.google.gwt.sample.showcase.client.content.other;
import com.google.gwt.animation.client.Animation;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.Showcase;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
@@ -238,6 +241,20 @@
return mainLayout;
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Create an options panel that allows users to select a widget and reposition
* it.
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwCookies.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwCookies.java
index 2cd0cc1..b660bb8 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwCookies.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwCookies.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.sample.showcase.client.content.other;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
@@ -23,6 +25,7 @@
import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
@@ -110,12 +113,12 @@
public String getName() {
return constants.cwCookiesName();
}
-
+
@Override
public boolean hasStyle() {
return false;
}
-
+
/**
* Initialize this example.
*/
@@ -175,7 +178,8 @@
deleteCookieButton.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
int selectedIndex = existingCookiesBox.getSelectedIndex();
- if (selectedIndex > -1 && selectedIndex < existingCookiesBox.getItemCount()) {
+ if (selectedIndex > -1
+ && selectedIndex < existingCookiesBox.getItemCount()) {
String cookieName = existingCookiesBox.getValue(selectedIndex);
Cookies.removeCookie(cookieName);
existingCookiesBox.removeItem(selectedIndex);
@@ -189,6 +193,20 @@
return mainLayout;
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Refresh the list of existing cookies.
*
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwFrame.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwFrame.java
index b46826f..b6dbcb3 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwFrame.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/other/CwFrame.java
@@ -16,10 +16,12 @@
package com.google.gwt.sample.showcase.client.content.other;
import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Frame;
@@ -71,7 +73,7 @@
public String getName() {
return constants.cwFrameName();
}
-
+
@Override
public boolean hasStyle() {
return false;
@@ -121,4 +123,18 @@
vPanel.add(frame);
return vPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwAbsolutePanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwAbsolutePanel.java
index 26f55e4..3342df2 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwAbsolutePanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwAbsolutePanel.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.sample.showcase.client.content.panels;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.Showcase;
@@ -22,6 +24,7 @@
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ChangeListener;
@@ -190,6 +193,20 @@
});
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Create an options panel that allows users to select a widget and reposition
* it.
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDecoratorPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDecoratorPanel.java
index efcdaf1..71bb6ee 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDecoratorPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDecoratorPanel.java
@@ -15,11 +15,14 @@
*/
package com.google.gwt.sample.showcase.client.content.panels;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.DecoratorPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
@@ -30,7 +33,9 @@
/**
* Example file.
*/
-@ShowcaseStyle({".gwt-DecoratorPanel", "html>body .gwt-DecoratorPanel", "* html .gwt-DecoratorPanel"})
+@ShowcaseStyle({
+ ".gwt-DecoratorPanel", "html>body .gwt-DecoratorPanel",
+ "* html .gwt-DecoratorPanel"})
public class CwDecoratorPanel extends ContentWidget {
/**
* The constants used in this Content Widget.
@@ -108,4 +113,18 @@
decPanel.setWidget(layout);
return decPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDisclosurePanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDisclosurePanel.java
index 0f89957..4250a27 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDisclosurePanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDisclosurePanel.java
@@ -15,11 +15,14 @@
*/
package com.google.gwt.sample.showcase.client.content.panels;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.DecoratorPanel;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.FlexTable;
@@ -103,6 +106,20 @@
return vPanel;
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Create a form that contains undisclosed advanced options.
*/
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDockPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDockPanel.java
index b2f5ba1..60677f0 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDockPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwDockPanel.java
@@ -15,11 +15,14 @@
*/
package com.google.gwt.sample.showcase.client.content.panels;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.ScrollPanel;
@@ -112,4 +115,18 @@
dock.ensureDebugId("cwDockPanel");
return dock;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwFlowPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwFlowPanel.java
index 84b17fa..dd19fa5 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwFlowPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwFlowPanel.java
@@ -15,11 +15,14 @@
*/
package com.google.gwt.sample.showcase.client.content.panels;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Widget;
@@ -88,4 +91,18 @@
// Return the content
return flowPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalPanel.java
index c6abae3..2f49674 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalPanel.java
@@ -15,10 +15,13 @@
*/
package com.google.gwt.sample.showcase.client.content.panels;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Widget;
@@ -90,4 +93,18 @@
hPanel.ensureDebugId("cwHorizontalPanel");
return hPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalSplitPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalSplitPanel.java
index 1fc379b..b989453 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalSplitPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwHorizontalSplitPanel.java
@@ -15,11 +15,14 @@
*/
package com.google.gwt.sample.showcase.client.content.panels;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.DecoratorPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalSplitPanel;
@@ -96,4 +99,18 @@
// Return the content
return decPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java
index 356e88b..5c96d24 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java
@@ -15,12 +15,15 @@
*/
package com.google.gwt.sample.showcase.client.content.panels;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.Showcase;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.DecoratedTabPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.VerticalPanel;
@@ -29,7 +32,8 @@
/**
* Example file.
*/
-@ShowcaseStyle({".gwt-DecoratedTabBar", "html>body .gwt-DecoratedTabBar",
+@ShowcaseStyle({
+ ".gwt-DecoratedTabBar", "html>body .gwt-DecoratedTabBar",
"* html .gwt-DecoratedTabBar", ".gwt-TabPanel"})
public class CwTabPanel extends ContentWidget {
/**
@@ -105,4 +109,18 @@
tabPanel.ensureDebugId("cwTabPanel");
return tabPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalPanel.java
index dd9ea5f..0ecd7db 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalPanel.java
@@ -15,10 +15,13 @@
*/
package com.google.gwt.sample.showcase.client.content.panels;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
@@ -90,4 +93,18 @@
vPanel.ensureDebugId("cwVerticalPanel");
return vPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalSplitPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalSplitPanel.java
index d7d0198..0f60296 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalSplitPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwVerticalSplitPanel.java
@@ -15,11 +15,14 @@
*/
package com.google.gwt.sample.showcase.client.content.panels;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.DecoratorPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.VerticalSplitPanel;
@@ -96,4 +99,18 @@
// Return the content
return decPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwBasicPopup.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwBasicPopup.java
index f9d0df9..91e5c5c 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwBasicPopup.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwBasicPopup.java
@@ -15,12 +15,15 @@
*/
package com.google.gwt.sample.showcase.client.content.popups;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.Showcase;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.DecoratedPopupPanel;
@@ -140,4 +143,18 @@
// Return the panel
return vPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwDialogBox.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwDialogBox.java
index 581d88a..9d06e55 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwDialogBox.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwDialogBox.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.sample.showcase.client.content.popups;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.sample.showcase.client.ContentWidget;
@@ -22,6 +24,7 @@
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.DialogBox;
@@ -35,8 +38,9 @@
/**
* Example file.
*/
-@ShowcaseStyle({".gwt-DialogBox", "html>body .gwt-DialogBox",
- "* html .gwt-DialogBox", ".cw-DialogBox"})
+@ShowcaseStyle({
+ ".gwt-DialogBox", "html>body .gwt-DialogBox", "* html .gwt-DialogBox",
+ ".cw-DialogBox"})
public class CwDialogBox extends ContentWidget {
/**
* The constants used in this Content Widget.
@@ -129,6 +133,20 @@
return vPanel;
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Create the dialog box for this example.
*
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwFlexTable.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwFlexTable.java
index ed969a1..1e41a7c 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwFlexTable.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwFlexTable.java
@@ -15,12 +15,15 @@
*/
package com.google.gwt.sample.showcase.client.content.tables;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.Showcase;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlexTable;
@@ -127,6 +130,20 @@
return flexTable;
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Add a row to the flex table.
*/
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwGrid.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwGrid.java
index 85df366..2f0d1d8 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwGrid.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/tables/CwGrid.java
@@ -15,11 +15,14 @@
*/
package com.google.gwt.sample.showcase.client.content.tables;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.Showcase;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Widget;
@@ -91,4 +94,18 @@
grid.ensureDebugId("cwGrid");
return grid;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwBasicText.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwBasicText.java
index dd58750..f2f9d3d 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwBasicText.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwBasicText.java
@@ -15,11 +15,14 @@
*/
package com.google.gwt.sample.showcase.client.content.text;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
@@ -126,6 +129,20 @@
return vpanel;
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Create a TextBox example that includes the text box and an optional
* listener that updates a Label with the currently selected text.
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwRichText.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwRichText.java
index f582285..2e15e28 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwRichText.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/text/CwRichText.java
@@ -15,11 +15,14 @@
*/
package com.google.gwt.sample.showcase.client.content.text;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.RichTextArea;
import com.google.gwt.user.client.ui.Widget;
@@ -27,7 +30,8 @@
/**
* Example file.
*/
-@ShowcaseStyle({".gwt-RichTextArea", ".hasRichTextToolbar", ".gwt-RichTextToolbar",
+@ShowcaseStyle({
+ ".gwt-RichTextArea", ".hasRichTextToolbar", ".gwt-RichTextToolbar",
".cw-RichText"})
public class CwRichText extends ContentWidget {
/**
@@ -88,4 +92,18 @@
grid.setWidget(1, 0, area);
return grid;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwBasicButton.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwBasicButton.java
index 8e38535..e90f6ef 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwBasicButton.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwBasicButton.java
@@ -15,12 +15,15 @@
*/
package com.google.gwt.sample.showcase.client.content.widgets;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HorizontalPanel;
@@ -103,4 +106,18 @@
// Return the panel
return hPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCheckBox.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCheckBox.java
index ac3813f..1322790 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCheckBox.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCheckBox.java
@@ -20,6 +20,7 @@
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.VerticalPanel;
@@ -101,4 +102,12 @@
// Return the panel of checkboxes
return vPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ /*
+ * CheckBox is the first demo loaded, so go ahead and load it synchronously.
+ */
+ callback.onSuccess(onInitialize());
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCustomButton.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCustomButton.java
index 60035ec..472d5de 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCustomButton.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwCustomButton.java
@@ -15,12 +15,15 @@
*/
package com.google.gwt.sample.showcase.client.content.widgets;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.Showcase;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.PushButton;
@@ -122,4 +125,18 @@
// Return the panel
return vpanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwFileUpload.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwFileUpload.java
index cbf855a..6760125 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwFileUpload.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwFileUpload.java
@@ -15,12 +15,15 @@
*/
package com.google.gwt.sample.showcase.client.content.widgets;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FileUpload;
@@ -113,4 +116,18 @@
// Return the layout panel
return vPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwHyperlink.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwHyperlink.java
index 5999895..2a9abfe 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwHyperlink.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwHyperlink.java
@@ -15,12 +15,15 @@
*/
package com.google.gwt.sample.showcase.client.content.widgets;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseConstants;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Hyperlink;
import com.google.gwt.user.client.ui.VerticalPanel;
@@ -96,6 +99,20 @@
return vPanel;
}
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
+
/**
* Get a {@link Hyperlink} to a section based on the name of the
* {@link ContentWidget} example.
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwRadioButton.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwRadioButton.java
index a96d33c..dd5f1b8 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwRadioButton.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/widgets/CwRadioButton.java
@@ -15,11 +15,14 @@
*/
package com.google.gwt.sample.showcase.client.content.widgets;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseData;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.VerticalPanel;
@@ -115,4 +118,18 @@
return vPanel;
}
+
+ @Override
+ protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
+ GWT.runAsync(new RunAsyncCallback() {
+
+ public void onFailure(Throwable caught) {
+ callback.onFailure(caught);
+ }
+
+ public void onSuccess() {
+ callback.onSuccess(onInitialize());
+ }
+ });
+ }
}
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/generator/ShowcaseGenerator.java b/samples/showcase/src/com/google/gwt/sample/showcase/generator/ShowcaseGenerator.java
index 3d6bb38..2d5b079 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/generator/ShowcaseGenerator.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/generator/ShowcaseGenerator.java
@@ -71,7 +71,7 @@
String typeName) throws UnableToCompleteException {
this.logger = logger;
this.context = context;
- this.classLoader = getClass().getClassLoader();
+ this.classLoader = Thread.currentThread().getContextClassLoader();
// Only generate files on the first permutation
if (!isFirstPass()) {
diff --git a/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java b/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java
new file mode 100644
index 0000000..448472e
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/AsyncFragmentLoader.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2008 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.core.client;
+
+/**
+ * <p>
+ * Low-level support to download an extra fragment of code. This should not be
+ * invoked directly by user code.
+ * </p>
+ *
+ * <p>
+ * Different linkers have different requirements about how the code is
+ * downloaded and installed. Thus, when it is time to actually download the
+ * code, this class defers to a JavaScript function named
+ * <code>__gwtStartLoadingFragment</code>. Linkers must arrange that a
+ * suitable <code>__gwtStartLoadingFragment</code> function is in scope.
+ */
+public class AsyncFragmentLoader {
+ /**
+ * 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 entry) {
+ // There is nothing to do with the current fragmentation strategy
+ }
+
+ /**
+ * Loads the specified fragment asynchronously.
+ *
+ * @param fragment the fragment to load
+ */
+ public static void inject(int fragment) {
+ logEventProgress("download" + fragment, "begin");
+ startLoadingFragment(fragment);
+ }
+
+ /**
+ * Logs an event with the GWT lightweight metrics framework.
+ */
+ public static void logEventProgress(String eventGroup, String type) {
+ @SuppressWarnings("unused")
+ boolean toss = isStatsAvailable()
+ && stats(createStatsEvent(eventGroup, type));
+ }
+
+ /**
+ * Create an event object suitable for submitting to the lightweight metrics
+ * framework.
+ */
+ private static native JavaScriptObject createStatsEvent(String eventGroup,
+ String type) /*-{
+ return {
+ moduleName: @com.google.gwt.core.client.GWT::getModuleName()(),
+ subSystem: 'runAsync',
+ evtGroup: eventGroup,
+ millis: (new Date()).getTime(),
+ type: type
+ };
+ }-*/;
+
+ private static native boolean isStatsAvailable() /*-{
+ return !!$stats;
+ }-*/;
+
+ private static native void startLoadingFragment(int fragment) /*-{
+ __gwtStartLoadingFragment(fragment);
+ }-*/;
+
+ /**
+ * Always use this as {@link isStatsAvailable} &&
+ * {@link #stats(JavaScriptObject)}.
+ */
+ private static native boolean stats(JavaScriptObject data) /*-{
+ return $stats(data);
+ }-*/;
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/core/client/GWT.java b/user/src/com/google/gwt/core/client/GWT.java
index 9aa0335..1184d9e 100644
--- a/user/src/com/google/gwt/core/client/GWT.java
+++ b/user/src/com/google/gwt/core/client/GWT.java
@@ -184,11 +184,13 @@
}
/**
- * Run the supplied callback. The system is allowed to delay before running
- * the callback while additional code is downloaded, but that feature is not
- * yet implemented. Currently, the callback runs immediately.
+ * Run the specified callback once the necessary code for it has been loaded.
*/
public static void runAsync(RunAsyncCallback callback) {
+ /*
+ * By default, just call the callback. This allows using <code>runAsync</code>
+ * in code that might or might not run in a web browser.
+ */
callback.onSuccess();
}