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(); }