Re-implement runAsync to improve code size.
The new formulation doesn't try to tickle optimizers so much, and as a result can share a lot more code than the old implementation.
http://gwt-code-reviews.appspot.com/1442807/
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10216 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java
index d44153b..3432dd3 100644
--- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java
@@ -19,6 +19,7 @@
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JRunAsync;
import com.google.gwt.dev.jjs.impl.CodeSplitter.MultipleDependencyGraphRecorder;
import com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer;
import com.google.gwt.util.tools.Utility;
@@ -112,10 +113,14 @@
try {
printPre();
- for (JMethod method : jprogram.getAllEntryMethods()) {
+ for (JMethod method : jprogram.getEntryMethods()) {
dependencyAnalyzer.traverseFrom(method);
maybeFlushOutput();
}
+ for (JRunAsync runAsync : jprogram.getRunAsyncs()) {
+ dependencyAnalyzer.traverseFromRunAsync(runAsync);
+ maybeFlushOutput();
+ }
printPost();
flushOutput();
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java
index 8630ef4..bb507f8 100644
--- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java
@@ -16,17 +16,15 @@
package com.google.gwt.core.ext.soyc.impl;
import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
-import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs.RunAsyncReplacement;
+import com.google.gwt.dev.jjs.ast.JRunAsync;
import com.google.gwt.dev.util.HtmlTextOutput;
-import com.google.gwt.dev.util.collect.HashMap;
import com.google.gwt.util.tools.Utility;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
-import java.util.Map;
+import java.util.List;
import java.util.zip.GZIPOutputStream;
/**
@@ -55,21 +53,21 @@
htmlOut.indentIn();
htmlOut.indentIn();
- Map<Integer, String> splitPointMap = splitPointNames(jprogram);
- if (splitPointMap.size() > 0) {
+ List<JRunAsync> runAsyncs = jprogram.getRunAsyncs();
+ if (runAsyncs.size() > 0) {
curLine = "<splitpoints>";
htmlOut.printRaw(curLine);
htmlOut.newline();
htmlOut.indentIn();
htmlOut.indentIn();
- for (int sp = 1; sp <= splitPointMap.size(); sp++) {
- String location = splitPointMap.get(sp);
- assert location != null;
- curLine = "<splitpoint id=\"" + sp + "\" location=\"" + location + "\"/>";
+ for (JRunAsync runAsync : runAsyncs) {
+ int sp = runAsync.getSplitPoint();
+ String name = runAsync.getName();
+ curLine = "<splitpoint id=\"" + sp + "\" location=\"" + name + "\"/>";
htmlOut.printRaw(curLine);
htmlOut.newline();
if (logger.isLoggable(TreeLogger.TRACE)) {
- logger.log(TreeLogger.TRACE, "Assigning split point #" + sp + " in method " + location);
+ logger.log(TreeLogger.TRACE, "Assigning split point #" + sp + " for '" + name + "'");
}
}
htmlOut.indentOut();
@@ -113,37 +111,6 @@
}
}
- private static String fullMethodDescription(JMethod method) {
- return (method.getEnclosingType().getName() + "." + JProgram.getJsniSig(method));
- }
-
- /**
- * Choose human-readable names for the split points.
- */
- private static Map<Integer, String> splitPointNames(JProgram program) {
- Map<Integer, String> names = new HashMap<Integer, String>();
- Map<String, Integer> counts = new HashMap<String, Integer>();
- for (RunAsyncReplacement replacement : program.getRunAsyncReplacements().values()) {
- int entryNumber = replacement.getNumber();
- String methodDescription;
- if (replacement.getName() != null) {
- methodDescription = replacement.getName();
- } else {
- methodDescription = "@" + fullMethodDescription(replacement.getEnclosingMethod());
- if (counts.containsKey(methodDescription)) {
- counts.put(methodDescription, counts.get(methodDescription) + 1);
- methodDescription += "#" + Integer.toString(counts.get(methodDescription));
- } else {
- counts.put(methodDescription, 1);
- }
- }
-
- names.put(entryNumber, methodDescription);
- }
-
- return names;
- }
-
private SplitPointRecorder() {
}
}
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 6f03ac9..fe180a1 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
@@ -25,10 +25,8 @@
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;
/**
@@ -54,7 +52,6 @@
}
}
- public static final String ASYNC_MAGIC_METHOD = "runAsync";
public static final String MAGIC_CLASS = "com.google.gwt.core.client.GWT";
public static final String REBIND_MAGIC_METHOD = "create";
@@ -68,8 +65,6 @@
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) {
@@ -78,10 +73,8 @@
}
String methodName = String.valueOf(messageSend.selector);
- 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.
+ if (!methodName.equals(REBIND_MAGIC_METHOD)) {
+ // Not the create() method.
return;
}
@@ -95,33 +88,13 @@
MessageSendSite site = new MessageSendSite(messageSend, scope);
Expression[] args = messageSend.arguments;
- 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 && args.length != 2) {
- reportRebindProblem(site, "GWT.runAsync() should take one or two arguments");
- return;
- }
- if (args.length == 2) {
- if (!(args[0] instanceof ClassLiteralAccess)) {
- reportRebindProblem(site,
- "Only class literals may be used to name a call to GWT.runAsync()");
- return;
- }
- }
+ if (args.length != 1) {
+ reportRebindProblem(site, "GWT.create() should take exactly one argument");
+ return;
}
- if (asyncMagicMethod) {
- runAsyncCalls.add(new MessageSendSite(messageSend, scope));
+ if (!(args[0] instanceof ClassLiteralAccess)) {
+ reportRebindProblem(site, "Only class literals may be used as arguments to GWT.create()");
return;
}
@@ -133,13 +106,6 @@
}
}
- /**
- * 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 aa43758..3492108 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
@@ -19,7 +19,6 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.javac.ArtificialRescueChecker;
import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor.MessageSendSite;
-import com.google.gwt.dev.jjs.impl.FragmentLoaderCreator;
import com.google.gwt.dev.jjs.impl.TypeLinker;
import com.google.gwt.dev.util.Empty;
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
@@ -57,19 +56,14 @@
return results;
}
- 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.
+ * Construct a WebModeCompilerFrontEnd.
*/
private WebModeCompilerFrontEnd(RebindPermutationOracle rebindPermOracle, TypeLinker linker) {
super(rebindPermOracle.getCompilationState(), linker);
this.rebindPermOracle = rebindPermOracle;
- this.fragmentLoaderCreator = new FragmentLoaderCreator(rebindPermOracle.getGeneratorContext());
}
@Override
@@ -110,26 +104,6 @@
}
}
- /*
- * 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()) {
- String resultType;
- try {
- resultType = fragmentLoaderCreator.create(logger);
- dependentTypeNames.add(resultType);
- doFinish = true;
- } catch (UnableToCompleteException e) {
- FindDeferredBindingSitesVisitor.reportRebindProblem(site,
- "Failed to create a runAsync fragment loader");
- }
- }
-
if (doFinish) {
rebindPermOracle.getGeneratorContext().finish(logger);
}
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 0845492..4336215 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -80,7 +80,6 @@
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.HandleCrossFragmentReferences;
@@ -516,7 +515,6 @@
Collections.addAll(allRootTypes, additionalRootTypes);
allRootTypes.addAll(JProgram.CODEGEN_TYPES_SET);
allRootTypes.addAll(JProgram.INDEX_TYPES_SET);
- allRootTypes.add(FragmentLoaderCreator.ASYNC_FRAGMENT_LOADER);
/*
* Add all SingleJsoImpl types that we know about. It's likely that the
* concrete types are never explicitly referenced.
@@ -1295,12 +1293,7 @@
ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(program);
cfa.setDependencyRecorder(deps);
- for (List<JMethod> entryList : program.entryMethods) {
- for (JMethod entry : entryList) {
- cfa.traverseFrom(entry);
- }
- }
-
+ cfa.traverseEntryMethods();
deps.endDependencyGraph();
deps.close();
}
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 9e27444..17e7b1f 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
@@ -25,9 +25,7 @@
import com.google.gwt.dev.jjs.ast.js.JsCastMap;
import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
import com.google.gwt.dev.jjs.impl.CodeSplitter;
-import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs.RunAsyncReplacement;
import com.google.gwt.dev.util.collect.Lists;
-import com.google.gwt.dev.util.collect.Maps;
import java.io.IOException;
import java.io.ObjectInputStream;
@@ -198,7 +196,7 @@
}
public static String getJsniSig(JMethod method, boolean addReturnType) {
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
sb.append(method.getName());
sb.append("(");
for (int i = 0; i < method.getOriginalParamTypes().size(); ++i) {
@@ -303,14 +301,6 @@
public final List<JClassType> codeGenTypes = new ArrayList<JClassType>();
- /**
- * 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 JTypeOracle typeOracle = new JTypeOracle(this);
/**
@@ -327,6 +317,8 @@
*/
private final CorrelationFactory correlator;
+ private final List<JMethod> entryMethods = new ArrayList<JMethod>();
+
private final Map<String, JField> indexedFields = new HashMap<String, JField>();
private final Map<String, JMethod> indexedMethods = new HashMap<String, JMethod>();
@@ -340,7 +332,7 @@
/**
* Filled in by ReplaceRunAsync, once the numbers are known.
*/
- private Map<Integer, RunAsyncReplacement> runAsyncReplacements = Maps.create();
+ private List<JRunAsync> runAsyncs = Lists.create();
private List<Integer> splitPointInitialSequence = Lists.create();
@@ -385,18 +377,8 @@
}
public void addEntryMethod(JMethod entryPoint) {
- addEntryMethod(entryPoint, 0);
- }
-
- public void addEntryMethod(JMethod entryPoint, int fragmentNumber) {
- assert entryPoint.isStatic();
- while (fragmentNumber >= entryMethods.size()) {
- entryMethods.add(new ArrayList<JMethod>());
- }
- List<JMethod> methods = entryMethods.get(fragmentNumber);
- if (!methods.contains(entryPoint)) {
- methods.add(entryPoint);
- }
+ assert !entryMethods.contains(entryPoint);
+ entryMethods.add(entryPoint);
}
public JClassType createClass(SourceInfo info, String name, boolean isAbstract, boolean isFinal) {
@@ -734,14 +716,6 @@
return result;
}
- public List<JMethod> getAllEntryMethods() {
- List<JMethod> allEntryMethods = new ArrayList<JMethod>();
- for (List<JMethod> entries : entryMethods) {
- allEntryMethods.addAll(entries);
- }
- return allEntryMethods;
- }
-
public JsCastMap getCastMap(JReferenceType referenceType) {
// ensure jsonCastableTypeMaps has been initialized
// it might not have been if the CastNormalizer has not been run
@@ -759,12 +733,13 @@
return allTypes;
}
- public int getEntryCount(int fragment) {
- return entryMethods.get(fragment).size();
+ public List<JMethod> getEntryMethods() {
+ return entryMethods;
}
public int getFragmentCount() {
- return entryMethods.size();
+ // Initial fragment is the +1.
+ return runAsyncs.size() + 1;
}
public JDeclaredType getFromTypeMap(String qualifiedBinaryOrSourceName) {
@@ -864,8 +839,8 @@
return integer.intValue();
}
- public Map<Integer, RunAsyncReplacement> getRunAsyncReplacements() {
- return runAsyncReplacements;
+ public List<JRunAsync> getRunAsyncs() {
+ return runAsyncs;
}
public List<Integer> getSplitPointInitialSequence() {
@@ -1037,9 +1012,8 @@
this.typesByQueryId = typesByQueryId;
}
- public void setRunAsyncReplacements(Map<Integer, RunAsyncReplacement> map) {
- assert runAsyncReplacements.isEmpty();
- runAsyncReplacements = map;
+ public void setRunAsyncs(List<JRunAsync> runAsyncs) {
+ this.runAsyncs = Lists.normalizeUnmodifiable(runAsyncs);
}
public void setSplitPointInitialSequence(List<Integer> list) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JRunAsync.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JRunAsync.java
new file mode 100644
index 0000000..b8d6844
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JRunAsync.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2011 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.ast;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+/**
+ * Represents a GWT.runAsync() call.
+ */
+public class JRunAsync extends JExpression {
+
+ private final String name;
+ private final JExpression onSuccessCall;
+ private JExpression runAsyncCall;
+ private final int splitPoint;
+
+ public JRunAsync(SourceInfo info, int splitPoint, String name, JExpression runAsyncCall,
+ JExpression onSuccessCall) {
+ super(info);
+ this.splitPoint = splitPoint;
+ assert name != null;
+ this.name = name;
+ this.runAsyncCall = runAsyncCall;
+ this.onSuccessCall = onSuccessCall;
+ }
+
+ /**
+ * Based on either explicit class literal, or the jsni name of the containing
+ * method.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns a call expression akin to {@code callback.onSuccess()}.
+ * {@link com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer} makes a synthetic
+ * visit to this call on the "far" side of the split point, ie, the code that
+ * runs when the fragment is through downloading.
+ */
+ public JExpression getOnSuccessCall() {
+ return onSuccessCall;
+ }
+
+ /**
+ * Returns a call expression akin to
+ * {@code AsyncFragmentLoader.runAsync(7, callback)}. This represents the
+ * "near" side of the split point, calling into the machinery that queues up
+ * the fragment download.
+ */
+ public JExpression getRunAsyncCall() {
+ return runAsyncCall;
+ }
+
+ /**
+ * Returns the split point number, 1-based.
+ */
+ public int getSplitPoint() {
+ return splitPoint;
+ }
+
+ @Override
+ public JType getType() {
+ return JPrimitiveType.VOID;
+ }
+
+ @Override
+ public boolean hasSideEffects() {
+ return true;
+ }
+
+ public void traverse(JVisitor visitor, Context ctx) {
+ if (visitor.visit(this, ctx)) {
+ /*
+ * Normal code flow treats this node like the "near" side call into
+ * AsyncFragmentLoader. We only visit the onSuccessCall "far" side
+ * explicitly.
+ */
+ runAsyncCall = visitor.accept(runAsyncCall);
+ }
+ visitor.endVisit(this, ctx);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
index d653233..885d8e7 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
@@ -413,6 +413,10 @@
endVisit((JStatement) x, ctx);
}
+ public void endVisit(JRunAsync x, Context ctx) {
+ endVisit((JExpression) x, ctx);
+ }
+
public void endVisit(JsCastMap x, Context ctx) {
endVisit((JsonArray) x, ctx);
}
@@ -733,6 +737,10 @@
return visit((JStatement) x, ctx);
}
+ public boolean visit(JRunAsync x, Context ctx) {
+ return visit((JExpression) x, ctx);
+ }
+
public boolean visit(JsCastMap x, Context ctx) {
return visit((JsonArray) x, ctx);
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
index b953a47..628841c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
@@ -44,6 +44,7 @@
import com.google.gwt.dev.jjs.ast.JParameterRef;
import com.google.gwt.dev.jjs.ast.JPostfixOperation;
import com.google.gwt.dev.jjs.ast.JPrefixOperation;
+import com.google.gwt.dev.jjs.ast.JRunAsync;
import com.google.gwt.dev.jjs.ast.JStringLiteral;
import com.google.gwt.dev.jjs.ast.JThisRef;
import com.google.gwt.dev.jjs.ast.JVisitor;
@@ -224,6 +225,16 @@
}
@Override
+ public boolean visit(JRunAsync x, Context ctx) {
+ // Only the runAsync call itself needs cloning, the onSuccess can be shared.
+ JExpression runAsyncCall = cloneExpression(x.getRunAsyncCall());
+ expression =
+ new JRunAsync(x.getSourceInfo(), x.getSplitPoint(), x.getName(), runAsyncCall, x
+ .getOnSuccessCall());
+ return false;
+ }
+
+ @Override
public boolean visit(JMultiExpression x, Context ctx) {
JMultiExpression multi = new JMultiExpression(x.getSourceInfo());
multi.exprs.addAll(cloneExpressions(x.exprs));
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
index 9add414..647b8f4 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
@@ -34,6 +34,7 @@
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.JRunAsync;
import com.google.gwt.dev.jjs.ast.JStringLiteral;
import com.google.gwt.dev.jjs.ast.JVisitor;
import com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer.DependencyRecorder;
@@ -41,7 +42,6 @@
import com.google.gwt.dev.jjs.impl.FragmentExtractor.LivenessPredicate;
import com.google.gwt.dev.jjs.impl.FragmentExtractor.NothingAlivePredicate;
import com.google.gwt.dev.jjs.impl.FragmentExtractor.StatementLogger;
-import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs.RunAsyncReplacement;
import com.google.gwt.dev.js.ast.JsBlock;
import com.google.gwt.dev.js.ast.JsExprStmt;
import com.google.gwt.dev.js.ast.JsExpression;
@@ -247,7 +247,7 @@
public static void exec(TreeLogger logger, JProgram jprogram, JsProgram jsprogram,
JavaToJavaScriptMap map, MultipleDependencyGraphRecorder dependencyRecorder) {
- if (jprogram.entryMethods.size() == 1) {
+ if (jprogram.getRunAsyncs().size() == 0) {
// Don't do anything if there is no call to runAsync
return;
}
@@ -266,9 +266,7 @@
throws UnableToCompleteException {
Event codeSplitterEvent =
SpeedTracerLogger.start(CompilerEventType.CODE_SPLITTER, "phase", "findSplitPoint");
- Map<JMethod, List<Integer>> methodToSplitPoint =
- reverseByEnclosingMethod(program.getRunAsyncReplacements());
- Map<String, List<Integer>> nameToSplitPoint = reverseByName(program.getRunAsyncReplacements());
+ Map<String, List<Integer>> nameToSplitPoint = reverseByName(program.getRunAsyncs());
if (refString.startsWith("@")) {
JsniRef jsniRef = JsniRef.parse(refString);
@@ -297,7 +295,8 @@
}
JMethod method = (JMethod) referent;
- List<Integer> splitPoints = methodToSplitPoint.get(method);
+ String canonicalName = ReplaceRunAsyncs.getImplicitName(method);
+ List<Integer> splitPoints = nameToSplitPoint.get(canonicalName);
if (splitPoints == null) {
branch.log(TreeLogger.ERROR, "Method does not enclose a runAsync call: " + jsniRef);
throw new UnableToCompleteException();
@@ -451,7 +450,7 @@
ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(jprogram);
cfa.setDependencyRecorder(dependencyRecorder);
- traverseEntry(jprogram, cfa, 0);
+ cfa.traverseEntryMethods();
traverseClassArray(jprogram, cfa);
dependencyRecorder.endDependencyGraph();
@@ -538,29 +537,9 @@
logger.log(TreeLogger.TRACE, message.toString());
}
- /**
- * Reverses a runAsync map, returning a map from methods to the split point
- * numbers invoked from within that method.
- */
- private static Map<JMethod, List<Integer>> reverseByEnclosingMethod(
- Map<Integer, RunAsyncReplacement> runAsyncMap) {
- Map<JMethod, List<Integer>> revmap = new HashMap<JMethod, List<Integer>>();
- for (RunAsyncReplacement replacement : runAsyncMap.values()) {
- JMethod method = replacement.getEnclosingMethod();
- List<Integer> list = revmap.get(method);
- if (list == null) {
- list = new ArrayList<Integer>();
- revmap.put(method, list);
- }
- list.add(replacement.getNumber());
- }
- return revmap;
- }
-
- private static Map<String, List<Integer>> reverseByName(
- Map<Integer, RunAsyncReplacement> runAsyncReplacements) {
+ private static Map<String, List<Integer>> reverseByName(List<JRunAsync> runAsyncs) {
Map<String, List<Integer>> revmap = new HashMap<String, List<Integer>>();
- for (RunAsyncReplacement replacement : runAsyncReplacements.values()) {
+ for (JRunAsync replacement : runAsyncs) {
String name = replacement.getName();
if (name != null) {
List<Integer> list = revmap.get(name);
@@ -568,7 +547,7 @@
list = new ArrayList<Integer>();
revmap.put(name, list);
}
- list.add(replacement.getNumber());
+ list.add(replacement.getSplitPoint());
}
}
return revmap;
@@ -595,16 +574,6 @@
}
}
- /**
- * Traverse all code in the program that is reachable via split point
- * <code>splitPoint</code>.
- */
- private static void traverseEntry(JProgram jprogram, ControlFlowAnalyzer cfa, int splitPoint) {
- for (JMethod entryMethod : jprogram.entryMethods.get(splitPoint)) {
- cfa.traverseFrom(entryMethod);
- }
- }
-
private static <T> Set<T> union(Set<? extends T> set1, Set<? extends T> set2) {
Set<T> union = new HashSet<T>();
union.addAll(set1);
@@ -660,7 +629,7 @@
this.dependencyRecorder = dependencyRecorder;
this.initialLoadSequence = new LinkedHashSet<Integer>(jprogram.getSplitPointInitialSequence());
- numEntries = jprogram.entryMethods.size();
+ numEntries = jprogram.getRunAsyncs().size() + 1;
logging = Boolean.getBoolean(PROP_LOG_FRAGMENT_MAP);
fieldToLiteralOfClass = buildFieldToClassLiteralMap(jprogram);
fragmentExtractor = new FragmentExtractor(jprogram, jsprogram, map);
@@ -704,21 +673,26 @@
private List<ControlFlowAnalyzer> computeAllButOneCfas() {
String dependencyGraphNameAfterInitialSequence = dependencyGraphNameAfterInitialSequence();
- List<ControlFlowAnalyzer> allButOnes = new ArrayList<ControlFlowAnalyzer>(numEntries - 1);
-
- for (int entry = 1; entry < numEntries; entry++) {
- if (isInitial(entry)) {
+ List<ControlFlowAnalyzer> allButOnes = new ArrayList<ControlFlowAnalyzer>();
+ for (JRunAsync runAsync : jprogram.getRunAsyncs()) {
+ int splitPoint = runAsync.getSplitPoint();
+ if (isInitial(splitPoint)) {
allButOnes.add(null);
continue;
}
- dependencyRecorder
- .startDependencyGraph("sp" + entry, dependencyGraphNameAfterInitialSequence);
+ dependencyRecorder.startDependencyGraph("sp" + splitPoint,
+ dependencyGraphNameAfterInitialSequence);
ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(liveAfterInitialSequence);
cfa.setDependencyRecorder(dependencyRecorder);
- traverseAllButEntry(cfa, entry);
- // Traverse leftoversFragmentHasLoaded, because it should not
- // go into any of the exclusive fragments.
- cfa.traverseFromLeftoversFragmentHasLoaded();
+ for (JRunAsync otherRunAsync : jprogram.getRunAsyncs()) {
+ if (isInitial(otherRunAsync.getSplitPoint())) {
+ continue;
+ }
+ if (otherRunAsync == runAsync) {
+ continue;
+ }
+ cfa.traverseFromRunAsync(otherRunAsync);
+ }
dependencyRecorder.endDependencyGraph();
allButOnes.add(cfa);
}
@@ -733,10 +707,8 @@
dependencyRecorder.startDependencyGraph("total", null);
ControlFlowAnalyzer everything = new ControlFlowAnalyzer(jprogram);
everything.setDependencyRecorder(dependencyRecorder);
- for (int entry = 0; entry < numEntries; entry++) {
- traverseEntry(everything, entry);
- }
- everything.traverseFromLeftoversFragmentHasLoaded();
+ everything.traverseEntryMethods();
+ everything.traverseFromRunAsyncs();
dependencyRecorder.endDependencyGraph();
return everything;
}
@@ -794,12 +766,14 @@
extendsCfa = depGraphName;
ControlFlowAnalyzer liveAfterSp = new ControlFlowAnalyzer(liveAfterInitialSequence);
- traverseEntry(liveAfterSp, sp);
+ JRunAsync runAsync = jprogram.getRunAsyncs().get(sp - 1);
+ assert runAsync.getSplitPoint() == sp;
+ liveAfterSp.traverseFromRunAsync(runAsync);
dependencyRecorder.endDependencyGraph();
LivenessPredicate liveNow = new CfaLivenessPredicate(liveAfterSp);
- List<JsStatement> statsToAppend = fragmentExtractor.createCallsToEntryMethods(sp);
+ List<JsStatement> statsToAppend = fragmentExtractor.createOnLoadedCall(sp);
addFragment(sp, alreadyLoaded, liveNow, statsToAppend, fragmentStats);
@@ -812,13 +786,14 @@
* Compute the exclusively live fragments. Each includes everything
* exclusively live after entry point i.
*/
- for (int i = 1; i < numEntries; i++) {
+ for (JRunAsync runAsync : jprogram.getRunAsyncs()) {
+ int i = runAsync.getSplitPoint();
if (isInitial(i)) {
continue;
}
LivenessPredicate alreadyLoaded = new ExclusivityMapLivenessPredicate(fragmentMap, 0);
LivenessPredicate liveNow = new ExclusivityMapLivenessPredicate(fragmentMap, i);
- List<JsStatement> statsToAppend = fragmentExtractor.createCallsToEntryMethods(i);
+ List<JsStatement> statsToAppend = fragmentExtractor.createOnLoadedCall(i);
addFragment(i, alreadyLoaded, liveNow, statsToAppend, fragmentStats);
}
@@ -828,7 +803,7 @@
{
LivenessPredicate alreadyLoaded = new CfaLivenessPredicate(liveAfterInitialSequence);
LivenessPredicate liveNow = new ExclusivityMapLivenessPredicate(fragmentMap, 0);
- List<JsStatement> statsToAppend = fragmentExtractor.createCallToLeftoversFragmentHasLoaded();
+ List<JsStatement> statsToAppend = fragmentExtractor.createOnLoadedCall(numEntries);
addFragment(numEntries, alreadyLoaded, liveNow, statsToAppend, fragmentStats);
}
@@ -994,17 +969,19 @@
}
allFields.addAll(everything.getFieldsWritten());
- for (int entry = 1; entry < numEntries; entry++) {
- if (isInitial(entry)) {
+ for (JRunAsync runAsync : jprogram.getRunAsyncs()) {
+ int splitPoint = runAsync.getSplitPoint();
+ if (isInitial(splitPoint)) {
continue;
}
- ControlFlowAnalyzer allButOne = allButOnes.get(entry - 1);
+ ControlFlowAnalyzer allButOne = allButOnes.get(splitPoint - 1);
Set<JNode> allLiveNodes =
union(allButOne.getLiveFieldsAndMethods(), allButOne.getFieldsWritten());
- updateMap(entry, fragmentMap.fields, allLiveNodes, allFields);
- updateMap(entry, fragmentMap.methods, allButOne.getLiveFieldsAndMethods(), allMethods);
- updateMap(entry, fragmentMap.strings, allButOne.getLiveStrings(), everything.getLiveStrings());
- updateMap(entry, fragmentMap.types, declaredTypesIn(allButOne.getInstantiatedTypes()),
+ updateMap(splitPoint, fragmentMap.fields, allLiveNodes, allFields);
+ updateMap(splitPoint, fragmentMap.methods, allButOne.getLiveFieldsAndMethods(), allMethods);
+ updateMap(splitPoint, fragmentMap.strings, allButOne.getLiveStrings(), everything
+ .getLiveStrings());
+ updateMap(splitPoint, fragmentMap.types, declaredTypesIn(allButOne.getInstantiatedTypes()),
declaredTypesIn(everything.getInstantiatedTypes()));
}
}
@@ -1023,21 +1000,4 @@
(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);
- }
- }
- }
-
- private void traverseEntry(ControlFlowAnalyzer cfa, int splitPoint) {
- traverseEntry(jprogram, cfa, splitPoint);
- }
}
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
index 591b3ae..38568a2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
@@ -42,6 +42,7 @@
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.JRunAsync;
import com.google.gwt.dev.jjs.ast.JStringLiteral;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JVariable;
@@ -89,6 +90,7 @@
*/
private class RescueVisitor extends JVisitor {
private final ArrayList<JMethod> curMethodStack = new ArrayList<JMethod>();
+ private JMethod currentMethod;
@Override
public boolean visit(JArrayType type, Context ctx) {
@@ -366,6 +368,21 @@
@Override
public boolean visit(JMethodCall call, Context ctx) {
JMethod method = call.getTarget();
+ if (method == runAsyncOnsuccess) {
+ if (currentMethod != null
+ && currentMethod.getEnclosingType() == program.getIndexedType("AsyncFragmentLoader")) {
+ /*
+ * Magic magic magic: don't allow code flow from the
+ * AsyncFragmentLoader implementation back into the
+ * callback.onSuccess(). If we did, the rescue path would look like
+ * JRunAsync -> AsyncFragmentLoader.runAsync() ->
+ * callback.onSuccess(). This would completely defeat code splitting
+ * as all the code on the other side of the barrier would become
+ * reachable.
+ */
+ return true;
+ }
+ }
if (method.isStatic() || program.isJavaScriptObject(method.getEnclosingType())
|| instantiatedTypes.contains(method.getEnclosingType())) {
rescue(method);
@@ -548,7 +565,10 @@
curMethodStack.add(method);
dependencyRecorder.methodIsLiveBecause(method, curMethodStack);
}
+ JMethod lastMethod = currentMethod;
+ currentMethod = method;
accept(method);
+ currentMethod = lastMethod;
if (dependencyRecorder != null) {
curMethodStack.remove(curMethodStack.size() - 1);
}
@@ -791,9 +811,9 @@
*/
private Map<JParameter, List<JExpression>> argsToRescueIfParameterRead;
+ private final JMethod asyncFragmentOnLoad;
private final JDeclaredType baseArrayType;
private DependencyRecorder dependencyRecorder;
-
private Set<JField> fieldsWritten = new HashSet<JField>();
private Set<JReferenceType> instantiatedTypes = new HashSet<JReferenceType>();
private Set<JNode> liveFieldsAndMethods = new HashSet<JNode>();
@@ -814,13 +834,15 @@
private Map<JMethod, List<JMethod>> methodsThatOverrideMe;
private final JProgram program;
-
private Set<JReferenceType> referencedTypes = new HashSet<JReferenceType>();
private final RescueVisitor rescuer = new RescueVisitor();
+ private final JMethod runAsyncOnsuccess;
private JMethod stringValueOfChar = null;
public ControlFlowAnalyzer(ControlFlowAnalyzer cfa) {
program = cfa.program;
+ asyncFragmentOnLoad = cfa.asyncFragmentOnLoad;
+ runAsyncOnsuccess = cfa.runAsyncOnsuccess;
baseArrayType = cfa.baseArrayType;
fieldsWritten = new HashSet<JField>(cfa.fieldsWritten);
instantiatedTypes = new HashSet<JReferenceType>(cfa.instantiatedTypes);
@@ -839,6 +861,8 @@
public ControlFlowAnalyzer(JProgram program) {
this.program = program;
+ asyncFragmentOnLoad = program.getIndexedMethod("AsyncFragmentLoader.onLoad");
+ runAsyncOnsuccess = program.getIndexedMethod("RunAsyncCallback.onSuccess");
baseArrayType = program.getIndexedType("Array");
buildMethodsOverriding();
}
@@ -893,10 +917,26 @@
}
/**
- * Traverse all code executed by <code>expr</code>.
+ * Traverse the program entry points, but don't traverse any runAsync
+ * fragments.
*/
- public void traverseFrom(JExpression expr) {
- rescuer.accept(expr);
+ public void traverseEntryMethods() {
+ for (JMethod method : program.getEntryMethods()) {
+ traverseFrom(method);
+ }
+ if (program.getRunAsyncs().size() > 0) {
+ /*
+ * Explicitly rescue AsyncFragmentLoader.onLoad(). It is never explicitly
+ * called anyway, until late code gen. Also, we want it in the initial
+ * fragment so all other fragments can share the code.
+ */
+ traverseFrom(asyncFragmentOnLoad);
+ /*
+ * Keep callback.onSuccess() from being pruned since we explicitly avoid
+ * visiting it.
+ */
+ liveFieldsAndMethods.add(runAsyncOnsuccess);
+ }
}
/**
@@ -914,17 +954,26 @@
rescuer.rescue(type, true, true);
}
- public void traverseFromLeftoversFragmentHasLoaded() {
- if (program.entryMethods.size() > 1) {
- traverseFrom(program
- .getIndexedMethod("AsyncFragmentLoader.browserLoaderLeftoversFragmentHasLoaded"));
- }
- }
-
public void traverseFromReferenceTo(JDeclaredType type) {
rescuer.rescue(type, true, false);
}
+ /**
+ * Traverse the fragment for a specific runAsync.
+ */
+ public void traverseFromRunAsync(JRunAsync runAsync) {
+ rescuer.accept(runAsync.getOnSuccessCall());
+ }
+
+ /**
+ * Traverse the fragments for all runAsyncs.
+ */
+ public void traverseFromRunAsyncs() {
+ for (JRunAsync runAsync : program.getRunAsyncs()) {
+ traverseFromRunAsync(runAsync);
+ }
+ }
+
private void buildMethodsOverriding() {
methodsThatOverrideMe = new HashMap<JMethod, List<JMethod>>();
for (JDeclaredType type : program.getDeclaredTypes()) {
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
index 72cbd99..a522b6e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
@@ -34,6 +34,7 @@
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsNameRef;
import com.google.gwt.dev.js.ast.JsNew;
+import com.google.gwt.dev.js.ast.JsNumberLiteral;
import com.google.gwt.dev.js.ast.JsObjectLiteral;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsStatement;
@@ -190,8 +191,6 @@
return map.vtableInitToMethod(stat);
}
- private Set<JsName> entryMethodNames;
-
private final JProgram jprogram;
private final JsProgram jsprogram;
@@ -208,38 +207,18 @@
this.jprogram = jprogram;
this.jsprogram = jsprogram;
this.map = map;
-
- buildEntryMethodSet();
}
/**
- * Add direct calls to the entry methods of the specified entry number.
+ * Create a call to {@link AsyncFragmentLoader#onLoad}.
*/
- public List<JsStatement> createCallsToEntryMethods(int splitPoint) {
- List<JsStatement> callStats = new ArrayList<JsStatement>(jprogram.entryMethods.size());
- for (JMethod entryMethod : jprogram.entryMethods.get(splitPoint)) {
- JsName name = map.nameForMethod(entryMethod);
- assert name != null;
- SourceInfo sourceInfo = jsprogram.getSourceInfo();
- JsInvocation call = new JsInvocation(sourceInfo);
- call.setQualifier(wrapWithEntry(name.makeRef(sourceInfo)));
- callStats.add(call.makeStmt());
- }
- return callStats;
- }
-
- /**
- * Create a call to
- * {@link com.google.gwt.core.client.impl.AsyncFragmentLoader#leftoversFragmentHasLoaded()}
- * .
- */
- public List<JsStatement> createCallToLeftoversFragmentHasLoaded() {
- JMethod loadedMethod =
- jprogram.getIndexedMethod("AsyncFragmentLoader.browserLoaderLeftoversFragmentHasLoaded");
- JsName loadedMethodName = map.nameForMethod(loadedMethod);
+ public List<JsStatement> createOnLoadedCall(int splitPoint) {
+ JMethod loadMethod = jprogram.getIndexedMethod("AsyncFragmentLoader.onLoad");
+ JsName loadMethodName = map.nameForMethod(loadMethod);
SourceInfo sourceInfo = jsprogram.getSourceInfo();
JsInvocation call = new JsInvocation(sourceInfo);
- call.setQualifier(wrapWithEntry(loadedMethodName.makeRef(sourceInfo)));
+ call.setQualifier(wrapWithEntry(loadMethodName.makeRef(sourceInfo)));
+ call.getArguments().add(new JsNumberLiteral(sourceInfo, splitPoint));
List<JsStatement> newStats = Collections.<JsStatement> singletonList(call.makeStmt());
return newStats;
}
@@ -260,45 +239,41 @@
*/
JClassType 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;
- JClassType vtableTypeAssigned = vtableTypeAssigned(stat);
- if (vtableTypeAssigned != null && livenessPredicate.isLive(vtableTypeAssigned)) {
- JsExprStmt result =
- extractPrototypeSetup(livenessPredicate, alreadyLoadedPredicate, stat,
- vtableTypeAssigned);
- if (result != null) {
- stat = result;
- keepIt = true;
- } else {
- keepIt = false;
- }
- } else if (containsRemovableVars(stat)) {
- stat = removeSomeVars((JsVars) stat, livenessPredicate, alreadyLoadedPredicate);
- keepIt = !(stat instanceof JsEmpty);
+ // Since we haven't run yet.
+ assert jsprogram.getFragmentCount() == 1;
+ List<JsStatement> stats = jsprogram.getGlobalBlock().getStatements();
+ for (JsStatement stat : stats) {
+ boolean keepIt;
+ JClassType vtableTypeAssigned = vtableTypeAssigned(stat);
+ if (vtableTypeAssigned != null && livenessPredicate.isLive(vtableTypeAssigned)) {
+ JsExprStmt result =
+ extractPrototypeSetup(livenessPredicate, alreadyLoadedPredicate, stat,
+ vtableTypeAssigned);
+ if (result != null) {
+ stat = result;
+ keepIt = true;
} else {
- keepIt = isLive(stat, livenessPredicate) && !isLive(stat, alreadyLoadedPredicate);
+ keepIt = false;
}
+ } else if (containsRemovableVars(stat)) {
+ stat = removeSomeVars((JsVars) stat, livenessPredicate, alreadyLoadedPredicate);
+ keepIt = !(stat instanceof JsEmpty);
+ } else {
+ keepIt = isLive(stat, livenessPredicate) && !isLive(stat, alreadyLoadedPredicate);
+ }
- statementLogger.logStatement(stat, keepIt);
+ statementLogger.logStatement(stat, keepIt);
- if (keepIt) {
- if (vtableTypeAssigned != null) {
- currentVtableType = vtableTypeAssigned;
- }
- JClassType vtableType = vtableTypeNeeded(stat);
- if (vtableType != null && vtableType != currentVtableType) {
- extractedStats.add(vtableStatFor(vtableType));
- currentVtableType = vtableType;
- }
- extractedStats.add(stat);
+ if (keepIt) {
+ if (vtableTypeAssigned != null) {
+ currentVtableType = vtableTypeAssigned;
}
+ JClassType vtableType = vtableTypeNeeded(stat);
+ if (vtableType != null && vtableType != currentVtableType) {
+ extractedStats.add(vtableStatFor(vtableType));
+ currentVtableType = vtableType;
+ }
+ extractedStats.add(stat);
}
}
@@ -327,23 +302,6 @@
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);
- }
-
- JMethod leftoverFragmentLoaded =
- jprogram.getIndexedMethod("AsyncFragmentLoader.browserLoaderLeftoversFragmentHasLoaded");
- if (leftoverFragmentLoaded != null) {
- JsName name = map.nameForMethod(leftoverFragmentLoaded);
- assert name != null;
- entryMethodNames.add(name);
- }
- }
-
/**
* Check whether this statement is a <code>JsVars</code> that contains
* individual vars that could be removed. If it does, then
@@ -416,27 +374,6 @@
return result;
}
- /**
- * 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) {
JClassType type = map.typeForStatement(stat);
if (type != 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
deleted file mode 100644
index 6bfa2de..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentLoaderCreator.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * 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.dev.cfg.BindingProperty;
-import com.google.gwt.dev.cfg.ConfigurationProperty;
-import com.google.gwt.dev.cfg.StaticPropertyOracle;
-import com.google.gwt.dev.javac.StandardGeneratorContext;
-import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor;
-
-import java.io.PrintWriter;
-
-/**
- * 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.impl.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 BROWSER_LOADER = "AsyncFragmentLoader.BROWSER_LOADER";
- public static final String CALLBACK_LIST_SUFFIX = "__Callback";
- public static final String LOADER_METHOD_RUN_ASYNC = "runAsync";
- public static final String RUN_ASYNC_CALLBACK = "com.google.gwt.core.client.RunAsyncCallback";
- public static final String RUN_CALLBACKS = "runCallbacks";
- private static final String GWT_CLASS = FindDeferredBindingSitesVisitor.MAGIC_CLASS;
- private static final String PROP_RUN_ASYNC_NEVER_RUNS = "gwt.jjs.runAsyncNeverRuns";
- private static final String UNCAUGHT_EXCEPTION_HANDLER_CLASS = "GWT.UncaughtExceptionHandler";
-
- private final StandardGeneratorContext context;
- private int entryNumber = 0;
- private final PropertyOracle propOracle;
-
- /**
- * Construct a FragmentLoaderCreator. The reason it needs so many parameters
- * is that it uses generator infrastructure.
- */
- public FragmentLoaderCreator(StandardGeneratorContext context) {
- // An empty property oracle is fine, because fragment loaders aren't
- // affected by properties anyway
- this.propOracle =
- new StaticPropertyOracle(new BindingProperty[0], new String[0],
- new ConfigurationProperty[0]);
- this.context = context;
- }
-
- public String create(TreeLogger logger) throws UnableToCompleteException {
- // First entry is 1.
- ++entryNumber;
- context.setPropertyOracle(propOracle);
- PrintWriter loaderWriter = getSourceWriterForLoader(logger, context);
- if (loaderWriter == null) {
- logger.log(TreeLogger.ERROR, "Failed to create island loader named "
- + getLoaderQualifiedName());
- throw new UnableToCompleteException();
- }
-
- generateLoaderFields(loaderWriter);
- generateOnLoadMethod(loaderWriter);
- generateRunAsyncMethod(loaderWriter);
- generateRunCallbacksMethod(loaderWriter);
- generateRunCallbackOnFailuresMethod(loaderWriter);
-
- loaderWriter.println("}");
- loaderWriter.close();
- context.commit(logger, loaderWriter);
-
- writeCallbackListClass(logger, context);
-
- return getLoaderQualifiedName();
- }
-
- private void generateLoaderFields(PrintWriter srcWriter) {
- srcWriter.println("// Callbacks that are pending");
- srcWriter.println("private static " + getCallbackListSimpleName() + " callbacksHead = null;");
-
- srcWriter.println("// The tail of the callbacks list");
- srcWriter.println("private static " + getCallbackListSimpleName() + " callbacksTail = null;");
-
- srcWriter.println("// A callback caller for this entry point");
- srcWriter.println("private static " + getLoaderSimpleName() + " instance = null;");
- }
-
- private void generateOnLoadMethod(PrintWriter srcWriter) {
- srcWriter.println("public static void onLoad() {");
- srcWriter.println("instance = new " + getLoaderSimpleName() + "();");
- srcWriter.println(BROWSER_LOADER + ".fragmentHasLoaded(" + entryNumber + ");");
-
- srcWriter.println(BROWSER_LOADER + ".logEventProgress(\"" + RUN_CALLBACKS + entryNumber
- + "\", \"begin\");");
- srcWriter.println("instance." + RUN_CALLBACKS + "();");
- srcWriter.println(BROWSER_LOADER + ".logEventProgress(\"" + RUN_CALLBACKS + entryNumber
- + "\", \"end\");");
-
- 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 " + LOADER_METHOD_RUN_ASYNC
- + "(RunAsyncCallback callback) {");
- srcWriter.println(getCallbackListSimpleName() + " newCallback = new "
- + getCallbackListSimpleName() + "();");
- srcWriter.println("newCallback.callback = callback;");
-
- srcWriter.println("if (callbacksTail != null) {");
- srcWriter.println(" callbacksTail.next = newCallback;");
- srcWriter.println("}");
-
- srcWriter.println("callbacksTail = newCallback;");
- srcWriter.println("if (callbacksHead == null) {");
- srcWriter.println(" callbacksHead = newCallback;");
- srcWriter.println("}");
-
- srcWriter.println("if (instance != null) {");
- srcWriter.println(" instance." + RUN_CALLBACKS + "();");
- srcWriter.println(" return;");
- srcWriter.println("}");
- srcWriter.println("if (!" + BROWSER_LOADER + ".isLoading(" + entryNumber + ")) {");
- srcWriter.println(" " + BROWSER_LOADER + ".inject(" + entryNumber + ",");
- srcWriter.println(" new AsyncFragmentLoader.LoadTerminatedHandler() {");
- srcWriter.println(" public void loadTerminated(Throwable reason) {");
- srcWriter.println(" runCallbackOnFailures(reason);");
- srcWriter.println(" }");
- srcWriter.println(" });");
- srcWriter.println("}");
- srcWriter.println("}");
- }
-
- private void generateRunCallbackOnFailuresMethod(PrintWriter srcWriter) {
- srcWriter.println("private static void runCallbackOnFailures(Throwable e) {");
- srcWriter.println("while (callbacksHead != null) {");
- srcWriter.println(" callbacksHead.callback.onFailure(e);");
- srcWriter.println(" callbacksHead = callbacksHead.next;");
- srcWriter.println("}");
- srcWriter.println("callbacksTail = null;");
- srcWriter.println("}");
- }
-
- private void generateRunCallbacksMethod(PrintWriter srcWriter) {
- srcWriter.println("public void " + RUN_CALLBACKS + "() {");
-
- srcWriter.println("while (callbacksHead != null) {");
-
- srcWriter.println(" " + UNCAUGHT_EXCEPTION_HANDLER_CLASS + " handler = "
- + "GWT.getUncaughtExceptionHandler();");
-
- srcWriter.println(" " + getCallbackListSimpleName() + " next = callbacksHead;");
- srcWriter.println(" callbacksHead = callbacksHead.next;");
- srcWriter.println(" if (callbacksHead == null) {");
- srcWriter.println(" callbacksTail = null;");
- srcWriter.println(" }");
-
- if (!Boolean.getBoolean(PROP_RUN_ASYNC_NEVER_RUNS)) {
- // TODO(spoon): this runs the callbacks immediately; deferred would be
- // better
- srcWriter.println(" if (handler == null) {");
- srcWriter.println(" next.callback.onSuccess();");
- srcWriter.println(" } else {");
- srcWriter.println(" try {");
- srcWriter.println(" next.callback.onSuccess();");
- srcWriter.println(" } catch (Throwable e) {");
- srcWriter.println(" handler.onUncaughtException(e);");
- srcWriter.println(" }");
- srcWriter.println(" }");
- }
-
- srcWriter.println("}");
- srcWriter.println("}");
- }
-
- private String getCallbackListQualifiedName() {
- return ASYNC_LOADER_PACKAGE + getCallbackListSimpleName();
- }
-
- private String getCallbackListSimpleName() {
- return getLoaderSimpleName() + CALLBACK_LIST_SUFFIX;
- }
-
- private String getLoaderQualifiedName() {
- return ASYNC_LOADER_PACKAGE + "." + getLoaderSimpleName();
- }
-
- private String getLoaderSimpleName() {
- return ASYNC_LOADER_CLASS_PREFIX + entryNumber;
- }
-
- 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[]{GWT_CLASS, RUN_ASYNC_CALLBACK, ASYNC_FRAGMENT_LOADER};
- for (String imp : imports) {
- printWriter.println("import " + imp + ";");
- }
-
- printWriter.println("public class " + getLoaderSimpleName() + " {");
- return printWriter;
- }
-
- 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);
- }
-}
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 ff3e1f7..1f552ec 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
@@ -150,11 +150,11 @@
import com.google.gwt.dev.js.ast.JsWhile;
import com.google.gwt.dev.util.StringInterner;
import com.google.gwt.dev.util.collect.IdentityHashSet;
+import com.google.gwt.dev.util.collect.Lists;
import com.google.gwt.dev.util.collect.Maps;
import java.io.StringReader;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
@@ -585,14 +585,14 @@
/**
* The JavaScript functions corresponding to the entry methods of the
- * program ({@link JProgram#getAllEntryMethods()}).
+ * program ({@link JProgram#getEntryMethods()}).
*/
private JsFunction[] entryFunctions;
/**
* A reverse index for the entry methods of the program (
- * {@link JProgram#getAllEntryMethods()}). Each entry method is mapped to
- * its integer index.
+ * {@link JProgram#getEntryMethods()}). Each entry method is mapped to its
+ * integer index.
*/
private Map<JMethod, Integer> entryMethodToIndex;
@@ -1214,7 +1214,7 @@
List<JsStatement> globalStmts = jsProgram.getGlobalBlock().getStatements();
// Generate entry methods
- generateGwtOnLoad(Arrays.asList(entryFunctions).subList(0, x.getEntryCount(0)), globalStmts);
+ generateGwtOnLoad(Lists.create(entryFunctions), globalStmts);
// Add a few things onto the beginning.
@@ -1238,28 +1238,13 @@
}
}
- /*
- * Add calls to all non-initial entry points. That way, if the code
- * splitter does not run, the resulting code will still function.
- * Likewise, add a call to
- * AsyncFragmentLoader.leftoversFragmentHasLoaded().
- */
- List<JsFunction> nonInitialEntries =
- Arrays.asList(entryFunctions).subList(x.getEntryCount(0), entryFunctions.length);
- if (!nonInitialEntries.isEmpty()) {
- JMethod loadedMethod =
- program.getIndexedMethod("AsyncFragmentLoader.browserLoaderLeftoversFragmentHasLoaded");
- JsName loadedMethodName = names.get(loadedMethod);
- JsInvocation call = new JsInvocation(jsProgram.getSourceInfo());
- call.setQualifier(loadedMethodName.makeRef(jsProgram.getSourceInfo().makeChild()));
- globalStmts.add(call.makeStmt());
- }
- for (JsFunction func : nonInitialEntries) {
- if (func != null) {
- JsInvocation call = new JsInvocation(jsProgram.getSourceInfo());
- call.setQualifier(func.getName().makeRef(jsProgram.getSourceInfo().makeChild()));
- globalStmts.add(call.makeStmt());
- }
+ if (program.getRunAsyncs().size() > 0) {
+ // Prevent onLoad from being pruned.
+ JMethod onLoadMethod = program.getIndexedMethod("AsyncFragmentLoader.onLoad");
+ JsName name = names.get(onLoadMethod);
+ assert name != null;
+ JsFunction func = (JsFunction) name.getStaticRef();
+ func.setArtificiallyRescued(true);
}
}
@@ -1422,7 +1407,7 @@
* Arrange for entryFunctions to be filled in as functions are visited.
* See their Javadoc comments for more details.
*/
- List<JMethod> entryMethods = x.getAllEntryMethods();
+ List<JMethod> entryMethods = x.getEntryMethods();
entryFunctions = new JsFunction[entryMethods.size()];
entryMethodToIndex = new IdentityHashMap<JMethod, Integer>();
for (int i = 0; i < entryMethods.size(); i++) {
@@ -1703,6 +1688,7 @@
JsName gwtOnLoadName = topScope.declareName("gwtOnLoad");
gwtOnLoadName.setObfuscatable(false);
JsFunction gwtOnLoad = new JsFunction(sourceInfo, topScope, gwtOnLoadName, true);
+ gwtOnLoad.setArtificiallyRescued(true);
globalStmts.add(gwtOnLoad.makeStmt());
JsBlock body = new JsBlock(sourceInfo);
gwtOnLoad.setBody(body);
@@ -2050,7 +2036,7 @@
@Override
public void endVisit(JProgram x, Context ctx) {
// Entry methods can be called externally, so they must run clinit.
- crossClassTargets.addAll(x.getAllEntryMethods());
+ crossClassTargets.addAll(x.getEntryMethods());
}
@Override
@@ -2091,9 +2077,7 @@
@Override
public void endVisit(JProgram x, Context ctx) {
- for (List<JMethod> methods : x.entryMethods) {
- Collections.sort(methods, hasNameSort);
- }
+ Collections.sort(x.getEntryMethods(), hasNameSort);
Collections.sort(x.getDeclaredTypes(), hasNameSort);
}
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsFunctionClusterer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsFunctionClusterer.java
index 6bef7d4..825b433 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JsFunctionClusterer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsFunctionClusterer.java
@@ -81,6 +81,11 @@
}
}
+ if (functionIndices.size() < 2) {
+ // No need to sort 0 or 1 functions.
+ return;
+ }
+
// sort the indices according to size of statement range
Collections.sort(functionIndices, new Comparator<Integer>() {
public int compare(Integer index1, Integer index2) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
index aef2ca7..0c2cfd3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
@@ -304,17 +304,6 @@
*/
return;
}
-
- if (isRunCallbacksMethod(x.getTarget())) {
- /*
- * Don't devirtualize these calls created by FragmentLoaderCreator,
- * because it spoils code splitting.
- *
- * TODO(spoon) remove this once FragmentLoaderCreator is gone
- */
- return;
- }
-
ctx.replaceMe(makeStaticCall(x, newMethod));
}
@@ -366,18 +355,6 @@
}
}
- private static boolean isRunCallbacksMethod(JMethod method) {
- if (method.getEnclosingType() != null
- && method.getEnclosingType().getName().startsWith(
- FragmentLoaderCreator.ASYNC_LOADER_PACKAGE + "."
- + FragmentLoaderCreator.ASYNC_LOADER_CLASS_PREFIX)
- && method.getName().equals(FragmentLoaderCreator.RUN_CALLBACKS)) {
- return true;
- }
-
- return false;
- }
-
protected Set<JMethod> toBeMadeStatic = new HashSet<JMethod>();
private final JProgram program;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
index 199ab83..5f3f141 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
@@ -99,13 +99,6 @@
return stats;
}
- /**
- * Tighten method calls that occur within <code>node</code> and its children.
- */
- public static void exec(JProgram program, JNode node) {
- new MethodCallTightener(program).execImpl(node);
- }
-
private final JProgram program;
private MethodCallTightener(JProgram program) {
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 5b37451..9a74db2 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
@@ -466,7 +466,7 @@
@Override
public boolean visit(JProgram program, Context ctx) {
- for (JMethod method : program.getAllEntryMethods()) {
+ for (JMethod method : program.getEntryMethods()) {
accept(method);
}
for (Iterator<JDeclaredType> it = program.getDeclaredTypes().iterator(); it.hasNext();) {
@@ -607,10 +607,8 @@
*/
traverseFromCodeGenTypes(livenessAnalyzer);
}
- for (JMethod method : program.getAllEntryMethods()) {
- livenessAnalyzer.traverseFrom(method);
- }
- livenessAnalyzer.traverseFromLeftoversFragmentHasLoaded();
+ livenessAnalyzer.traverseEntryMethods();
+ livenessAnalyzer.traverseFromRunAsyncs();
program.typeOracle.setInstantiatedTypes(livenessAnalyzer.getInstantiatedTypes());
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
index d0ae938..2323b87 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
@@ -24,17 +24,18 @@
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.JIntLiteral;
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.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JProgram;
-import com.google.gwt.dev.jjs.ast.JType;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JRunAsync;
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
-import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -50,63 +51,8 @@
* by an equivalent call using an integer rather than a class literal.
*/
public class ReplaceRunAsyncs {
- /**
- * Information about the replacement of one runAsync call by a call to a
- * generated code-loading method.
- */
- public static class RunAsyncReplacement implements Serializable {
- private final JMethod enclosingMethod;
- private final JMethod loadMethod;
- private final String name;
- private final int number;
-
- RunAsyncReplacement(int number, JMethod enclosingMethod, JMethod loadMethod, String name) {
- this.number = number;
- this.enclosingMethod = enclosingMethod;
- this.loadMethod = loadMethod;
- this.name = name;
- }
-
- /**
- * Can be null if the enclosing method cannot be designated with a JSNI
- * reference.
- */
- public JMethod getEnclosingMethod() {
- return enclosingMethod;
- }
-
- /**
- * The load method to request loading the code for this method.
- */
- public JMethod getLoadMethod() {
- return loadMethod;
- }
-
- /**
- * Return the name of this runAsync, which is specified by a class literal
- * in the two-argument version of runAsync(). Returns <code>null</code> if
- * there is no name for the call.
- */
- public String getName() {
- return name;
- }
-
- /**
- * The index of this runAsync, numbered from 1 to n.
- */
- public int getNumber() {
- return number;
- }
-
- @Override
- public String toString() {
- return "#" + number + ": " + enclosingMethod.toString();
- }
- }
-
private class AsyncCreateVisitor extends JModVisitor {
private JMethod currentMethod;
- private int entryCount = 1;
@Override
public void endVisit(JMethodCall x, Context ctx) {
@@ -116,11 +62,17 @@
String name;
switch (x.getArgs().size()) {
case 1:
- name = null;
+ name = getImplicitName(currentMethod);
asyncCallback = x.getArgs().get(0);
break;
case 2:
- name = nameFromClassLiteral((JClassLiteral) x.getArgs().get(0));
+ JExpression arg0 = x.getArgs().get(0);
+ if (!(arg0 instanceof JClassLiteral)) {
+ error(arg0.getSourceInfo(),
+ "Only class literals may be used to name a call to GWT.runAsync()");
+ return;
+ }
+ name = nameFromClassLiteral((JClassLiteral) arg0);
asyncCallback = x.getArgs().get(1);
break;
default:
@@ -128,21 +80,30 @@
"runAsync call found with neither 1 nor 2 arguments: " + x);
}
- int entryNumber = entryCount++;
- JClassType loader = getFragmentLoader(entryNumber);
- JMethod loadMethod = getRunAsyncMethod(loader);
- assert loadMethod != null;
- runAsyncReplacements.put(entryNumber, new RunAsyncReplacement(entryNumber, currentMethod,
- loadMethod, name));
+ int splitPoint = runAsyncs.size() + 1;
+ SourceInfo info = x.getSourceInfo();
- JMethodCall methodCall = new JMethodCall(x.getSourceInfo(), null, loadMethod);
- methodCall.addArg(asyncCallback);
+ JMethod runAsyncMethod = program.getIndexedMethod("AsyncFragmentLoader.runAsync");
+ assert runAsyncMethod != null;
+ JMethodCall runAsyncCall = new JMethodCall(info, null, runAsyncMethod);
+ runAsyncCall.addArg(JIntLiteral.get(splitPoint));
+ runAsyncCall.addArg(asyncCallback);
- tightenCallbackType(entryNumber, asyncCallback.getType());
+ JReferenceType callbackType = (JReferenceType) asyncCallback.getType();
+ callbackType = callbackType.getUnderlyingType();
+ JMethod callbackMethod;
+ if (callbackType instanceof JClassType) {
+ callbackMethod =
+ program.typeOracle.getPolyMethod((JClassType) callbackType, "onSuccess()V");
+ } else {
+ callbackMethod = program.getIndexedMethod("RunAsyncCallback.onSuccess");
+ }
+ assert callbackMethod != null;
+ JMethodCall onSuccessCall = new JMethodCall(info, asyncCallback, callbackMethod);
- program.addEntryMethod(getOnLoadMethod(loader), entryNumber);
-
- ctx.replaceMe(methodCall);
+ JRunAsync runAsyncNode = new JRunAsync(info, splitPoint, name, runAsyncCall, onSuccessCall);
+ runAsyncs.add(runAsyncNode);
+ ctx.replaceMe(runAsyncNode);
}
}
@@ -159,62 +120,20 @@
return method.getEnclosingType() == program.getIndexedType("GWT")
&& method.getName().equals("runAsync");
}
-
- /**
- * Tighten some types and method calls immediately, in case the optimizer is
- * not run. Without a little bit of tightening, code splitting will be
- * completely ineffective.
- *
- * Note that {@link FragmentLoaderCreator} can't simply generate the tighter
- * types to begin with, because when it runs, it doesn't know which runAsync
- * call it is generating a loader for.
- *
- * This method can be deleted if {@link FragmentLoaderCreator} is
- * eliminated.
- */
- private void tightenCallbackType(int entryNumber, JType callbackType) {
- JClassType loaderClass = getFragmentLoader(entryNumber);
-
- /*
- * Before: class AsyncLoader3 { static void runAsync(RunAsyncCallback cb)
- * { ... } }
- *
- * After: class AsyncLoader3 { static void runAsync(RunAsyncCallback$3 cb)
- * { ... } }
- */
- JMethod loadMethod = getRunAsyncMethod(loaderClass);
- loadMethod.getParams().get(0).setType(callbackType);
-
- /*
- * Before: class AsyncLoader3__Callback { RunAsyncCallback callback; }
- *
- * After: class AsyncLoader3__Callback { RunAsyncCallback$3 callback; }
- */
- JClassType callbackListType = getFragmentLoaderCallbackList(entryNumber);
- JField callbackField = getField(callbackListType, "callback");
-
- /*
- * The method AsyncLoaderNNN.runCallbacks has a lot of calls to onSuccess
- * methods where the target is onSuccess in the RunAsyncCallback
- * interface. Use MethodCallTightener to tighten those calls down to
- * target the onSuccess method of a specific callback class.
- */
- callbackField.setType(callbackType);
- JMethod runCallbacksMethod = getMethod(loaderClass, FragmentLoaderCreator.RUN_CALLBACKS);
- MethodCallTightener.exec(program, runCallbacksMethod);
- }
}
private class ReplaceRunAsyncResources extends JModVisitor {
- private final Map<String, List<RunAsyncReplacement>> replacementsByName;
+ private final Map<String, List<JRunAsync>> replacementsByName;
+ private final JMethod runAsyncCode;
public ReplaceRunAsyncResources() {
- replacementsByName = new HashMap<String, List<RunAsyncReplacement>>();
- for (RunAsyncReplacement replacement : runAsyncReplacements.values()) {
+ replacementsByName = new HashMap<String, List<JRunAsync>>();
+ runAsyncCode = program.getIndexedMethod("RunAsyncCode.runAsyncCode");
+ for (JRunAsync replacement : runAsyncs) {
String name = replacement.getName();
if (name != null) {
- List<RunAsyncReplacement> list = replacementsByName.get(name);
+ List<JRunAsync> list = replacementsByName.get(name);
if (list == null) {
- list = new ArrayList<RunAsyncReplacement>();
+ list = new ArrayList<JRunAsync>();
replacementsByName.put(name, list);
}
list.add(replacement);
@@ -224,7 +143,7 @@
@Override
public void endVisit(JMethodCall x, Context ctx) {
- if (x.getTarget() == program.getIndexedMethod("RunAsyncCode.runAsyncCode")) {
+ if (x.getTarget() == runAsyncCode) {
JExpression arg0 = x.getArgs().get(0);
if (!(arg0 instanceof JClassLiteral)) {
error(arg0.getSourceInfo(), "Only a class literal may be passed to runAsyncCode");
@@ -232,42 +151,28 @@
}
JClassLiteral lit = (JClassLiteral) arg0;
String name = nameFromClassLiteral(lit);
- List<RunAsyncReplacement> matches = replacementsByName.get(name);
+ List<JRunAsync> matches = replacementsByName.get(name);
+ SourceInfo info = x.getSourceInfo();
if (matches == null || matches.size() == 0) {
- error(x.getSourceInfo(), "No runAsync call is named " + name);
+ error(info, "No runAsync call is named " + name);
return;
}
if (matches.size() > 1) {
- TreeLogger branch = error(x.getSourceInfo(), "Multiple runAsync calls are named " + name);
- for (RunAsyncReplacement match : matches) {
- branch.log(TreeLogger.ERROR, "One call is in "
- + methodDescription(match.getEnclosingMethod()));
+ TreeLogger branch = error(info, "Multiple runAsync calls are named " + name);
+ for (JRunAsync match : matches) {
+ branch.log(TreeLogger.ERROR, "One call is at '" + match.getSourceInfo().getFileName()
+ + ':' + match.getSourceInfo().getStartLine() + "'");
}
return;
}
- Integer splitPoint = matches.get(0).getNumber();
-
+ int splitPoint = matches.get(0).getSplitPoint();
JMethodCall newCall =
- new JMethodCall(x.getSourceInfo(), null, program
+ new JMethodCall(info, null, program
.getIndexedMethod("RunAsyncCode.forSplitPointNumber"));
newCall.addArg(program.getLiteralInt(splitPoint));
ctx.replaceMe(newCall);
}
}
-
- private String methodDescription(JMethod method) {
- StringBuilder desc = new StringBuilder();
- desc.append(method.getEnclosingType().getName());
- desc.append(".");
- desc.append(method.getName());
- desc.append(" (");
- desc.append(method.getSourceInfo().getFileName());
- desc.append(':');
- desc.append(method.getSourceInfo().getStartLine());
- desc.append(")");
-
- return desc.toString();
- }
}
public static void exec(TreeLogger logger, JProgram program) throws UnableToCompleteException {
@@ -290,33 +195,15 @@
return initializerCall;
}
- private static JMethod getMethod(JClassType type, String name) {
- for (JMethod method : type.getMethods()) {
- if (method.getName().equals(name)) {
- return method;
- }
- }
- throw new InternalCompilerException("Method not found: " + type.getName() + "." + name);
- }
-
- private static JMethod getOnLoadMethod(JClassType loaderType) {
- assert loaderType != null;
- assert loaderType.getMethods() != null;
- JMethod method = getMethod(loaderType, "onLoad");
- assert method.isStatic();
- assert method.getParams().size() == 0;
- return method;
- }
-
- private static JMethod getRunAsyncMethod(JClassType loaderType) {
- assert loaderType != null;
- assert loaderType.getMethods() != null;
- JMethod method = getMethod(loaderType, "runAsync");
- assert (method.isStatic());
- assert (method.getParams().size() == 1);
- assert (method.getParams().get(0).getType().getName()
- .equals(FragmentLoaderCreator.RUN_ASYNC_CALLBACK));
- return method;
+ static String getImplicitName(JMethod method) {
+ String name;
+ StringBuilder sb = new StringBuilder();
+ sb.append('@');
+ sb.append(method.getEnclosingType().getName());
+ sb.append("::");
+ sb.append(JProgram.getJsniSig(method, false));
+ name = sb.toString();
+ return name;
}
/**
@@ -330,8 +217,7 @@
private final TreeLogger logger;
private final JProgram program;
- private final Map<Integer, RunAsyncReplacement> runAsyncReplacements =
- new HashMap<Integer, RunAsyncReplacement>();
+ private final List<JRunAsync> runAsyncs = new ArrayList<JRunAsync>();
private ReplaceRunAsyncs(TreeLogger logger, JProgram program) {
this.logger = logger;
@@ -341,7 +227,7 @@
private TreeLogger error(SourceInfo info, String message) {
errorsFound = true;
TreeLogger fileLogger =
- logger.branch(TreeLogger.ERROR, "Error in '" + info.getFileName() + "'");
+ logger.branch(TreeLogger.ERROR, "Errors in '" + info.getFileName() + "'");
String linePrefix = "";
if (info.getStartLine() > 0) {
linePrefix = "Line " + info.getStartLine() + ": ";
@@ -353,43 +239,14 @@
private void execImpl() throws UnableToCompleteException {
AsyncCreateVisitor visitor = new AsyncCreateVisitor();
visitor.accept(program);
- setNumEntriesInAsyncFragmentLoader(visitor.entryCount);
- program.setRunAsyncReplacements(runAsyncReplacements);
+ setNumEntriesInAsyncFragmentLoader(runAsyncs.size() + 1);
+ program.setRunAsyncs(runAsyncs);
new ReplaceRunAsyncResources().accept(program);
if (errorsFound) {
throw new UnableToCompleteException();
}
}
- private JField getField(JClassType type, String name) {
- for (JField field : type.getFields()) {
- if (field.getName().equals(name)) {
- return field;
- }
- }
- throw new InternalCompilerException("Field not found: " + type.getName() + "." + name);
- }
-
- 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 JClassType getFragmentLoaderCallbackList(int fragmentNumber) {
- String className =
- FragmentLoaderCreator.ASYNC_LOADER_PACKAGE + "."
- + FragmentLoaderCreator.ASYNC_LOADER_CLASS_PREFIX + fragmentNumber
- + FragmentLoaderCreator.CALLBACK_LIST_SUFFIX;
- JType result = program.getFromTypeMap(className);
- assert (result != null);
- return (JClassType) result;
- }
-
private void setNumEntriesInAsyncFragmentLoader(int entryCount) {
JMethodCall constructorCall = getBrowserLoaderConstructor(program);
assert constructorCall.getArgs().get(0).getType() == JPrimitiveType.INT;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index 016e0bf..63ff5c6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -674,11 +674,7 @@
if (refType instanceof JArrayType) {
JArrayType arrayType = (JArrayType) refType;
- JArrayType newArrayType = nullifyArrayType(arrayType);
- if (arrayType != newArrayType) {
- x.setType(newArrayType);
- madeChanges();
- }
+ refType = nullifyArrayType(arrayType);
}
// tighten based on leaf types
@@ -739,7 +735,7 @@
}
}
- if (refType != resultType) {
+ if (x.getType() != resultType) {
x.setType(resultType);
madeChanges();
}
diff --git a/dev/core/src/com/google/gwt/dev/js/JsUnusedFunctionRemover.java b/dev/core/src/com/google/gwt/dev/js/JsUnusedFunctionRemover.java
index 042fdbf5..1083bba 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsUnusedFunctionRemover.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsUnusedFunctionRemover.java
@@ -25,42 +25,17 @@
import com.google.gwt.dev.js.ast.JsNameRef;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsVisitor;
+import com.google.gwt.dev.util.collect.IdentityHashSet;
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Set;
/**
* Removes JsFunctions that are never referenced in the program.
*/
public class JsUnusedFunctionRemover {
- /**
- * Finds all functions in the program.
- */
- private class JsFunctionVisitor extends JsVisitor {
-
- @Override
- public void endVisit(JsFunction x, JsContext ctx) {
- // Anonymous function, ignore it
- if (x.getName() != null && !x.isArtificiallyRescued()) {
- toRemove.put(x.getName(), x);
- }
- }
- }
-
- /**
- * Finds all function references in the program.
- */
- private class JsNameRefVisitor extends JsVisitor {
-
- @Override
- public void endVisit(JsNameRef x, JsContext ctx) {
- toRemove.remove(x.getName());
- }
- }
-
private class RemovalVisitor extends JsModVisitor {
@Override
@@ -72,22 +47,29 @@
JsFunction f = (JsFunction) x.getExpression();
JsName name = f.getName();
- if (toRemove.containsKey(name)) {
- // Removing a static initializer indicates a problem in
- // JsInliner.
- if (name.getIdent().equals("$clinit")) {
- throw new InternalCompilerException("Tried to remove clinit "
- + name.getStaticRef().toSource());
- }
-
- if (!name.isObfuscatable()) {
- // This is intended to be used externally (e.g. gwtOnLoad)
- return;
- }
-
- // Remove the statement
- ctx.removeMe();
+ // Anonymous function, ignore it
+ if (name == null || seen.contains(name) || f.isArtificiallyRescued()) {
+ return;
}
+
+ // Removing a static initializer indicates a problem in JsInliner.
+ if (f.getExecuteOnce()) {
+ throw new InternalCompilerException("Tried to remove clinit "
+ + name.getStaticRef().toSource());
+ }
+ // Remove the statement
+ ctx.removeMe();
+ }
+ }
+
+ /**
+ * Finds all function references in the program.
+ */
+ private class RescueVisitor extends JsVisitor {
+
+ @Override
+ public void endVisit(JsNameRef x, JsContext ctx) {
+ seen.add(x.getName());
}
}
@@ -102,7 +84,7 @@
}
private final JsProgram program;
- private final Map<JsName, JsFunction> toRemove = new HashMap<JsName, JsFunction>();
+ private final Set<JsName> seen = new IdentityHashSet<JsName>();
public JsUnusedFunctionRemover(JsProgram program) {
this.program = program;
@@ -111,11 +93,8 @@
public OptimizerStats execImpl() {
OptimizerStats stats = new OptimizerStats(NAME);
- // Find all functions
- (new JsFunctionVisitor()).accept(program);
-
- // Remove the functions that are referenced from the hit list
- (new JsNameRefVisitor()).accept(program);
+ // Rescue all referenced functions.
+ new RescueVisitor().accept(program);
// Remove the unused functions from the JsProgram
RemovalVisitor removalVisitor = new RemovalVisitor();
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
index 98c3c23..85487b2 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
@@ -51,6 +51,26 @@
*/
public class JavaAstConstructor {
+ public static final MockJavaResource ASYNCFRAGMENTLOADER = new MockJavaResource(
+ "com.google.gwt.core.client.impl.AsyncFragmentLoader") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.core.client.impl;\n");
+ code.append("import com.google.gwt.core.client.RunAsyncCallback;\n");
+ code.append("public class AsyncFragmentLoader {\n");
+ code.append(" public static void onLoad(int fragment) { }\n");
+ code.append(" public static void runAsync(int fragment, RunAsyncCallback callback) { }\n");
+ code.append(" public static AsyncFragmentLoader BROWSER_LOADER =\n");
+ code.append(" makeBrowserLoader(1, new int[] {});\n");
+ code.append(" private static AsyncFragmentLoader makeBrowserLoader(\n");
+ code.append(" int numSp, int[] initial) {\n");
+ code.append(" return null;\n");
+ code.append(" }\n");
+ code.append("}\n");
+ return code;
+ }
+ };
public static final MockJavaResource ARRAY = new MockJavaResource("com.google.gwt.lang.Array") {
@Override
public CharSequence getContent() {
@@ -153,7 +173,23 @@
public CharSequence getContent() {
StringBuffer code = new StringBuffer();
code.append("package com.google.gwt.core.client;\n");
- code.append("public interface RunAsyncCallback { }\n");
+ code.append("public interface RunAsyncCallback {\n");
+ code.append(" void onSuccess();\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+ public static final MockJavaResource RUNASYNCCODE = new MockJavaResource(
+ "com.google.gwt.core.client.prefetch.RunAsyncCode") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.core.client.prefetch;\n");
+ code.append("public class RunAsyncCode {\n");
+ code.append(" public static RunAsyncCode runAsyncCode(Class<?> splitPoint) {\n");
+ code.append(" return null;\n");
+ code.append(" }");
+ code.append("}");
return code;
}
};
@@ -255,7 +291,8 @@
// Replace the basic Class and Enum with a compiler-specific one.
result.remove(JavaResourceBase.CLASS);
result.remove(JavaResourceBase.ENUM);
- Collections.addAll(result, ARRAY, CAST, CLASS, CLASSLITERALHOLDER, ENUM, GWT, RUNASYNCCALLBACK);
+ Collections.addAll(result, ASYNCFRAGMENTLOADER, ARRAY, CAST, CLASS, CLASSLITERALHOLDER, ENUM,
+ GWT, RUNASYNCCALLBACK, RUNASYNCCODE);
return result.toArray(new MockJavaResource[result.size()]);
}
}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncsErrorMessagesTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncsErrorMessagesTest.java
index d91d4d2..f8f9808 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncsErrorMessagesTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncsErrorMessagesTest.java
@@ -50,101 +50,25 @@
return code;
}
});
+
addSnippetImport("test.SplitPoint3");
-
- expectError("Line 15: Multiple runAsync calls are named test.SplitPoint1");
- expectError("One call is in test.SplitPoint1.doStuff (/mock/test/SplitPoint1.java:4)");
- expectError("One call is in test.SplitPoint3.doStuff (/mock/test/SplitPoint3.java:4)");
-
+ expectError("Line 8: Multiple runAsync calls are named test.SplitPoint1");
+ expectError("One call is at '/mock/test/SplitPoint1.java:5'");
+ expectError("One call is at '/mock/test/SplitPoint3.java:5'");
testSnippet("RunAsyncCode.runAsyncCode(SplitPoint1.class);");
}
public void testNonClassLiteral() {
- expectError("Line 14: Only a class literal may be passed to runAsyncCode");
+ expectError("Line 7: Only a class literal may be passed to runAsyncCode");
testSnippet("RunAsyncCode.runAsyncCode(new SplitPoint1().getClass());");
}
public void testNonExistentSplitPoint() {
- expectError("Line 14: No runAsync call is named java.lang.String");
+ expectError("Line 7: No runAsync call is named java.lang.String");
testSnippet("RunAsyncCode.runAsyncCode(String.class);");
}
- private void addAsyncLoader(final int sp) {
- sourceOracle.addOrReplace(new MockJavaResource("com.google.gwt.lang.asyncloaders.AsyncLoader"
- + sp) {
- @Override
- public CharSequence getContent() {
- StringBuffer code = new StringBuffer();
- code.append("package com.google.gwt.lang.asyncloaders;\n");
- code.append("import com.google.gwt.core.client.RunAsyncCallback;");
- code.append("public class AsyncLoader" + sp + " {\n");
- code.append(" public static void onLoad() { }\n");
- code.append(" public static void runAsync(RunAsyncCallback cb) { }\n");
- code.append(" public static void runCallbacks() { }\n");
- code.append("}\n");
- return code;
- }
- });
-
- addSnippetImport("com.google.gwt.lang.asyncloaders.AsyncLoader" + sp);
-
- sourceOracle.addOrReplace(new MockJavaResource("com.google.gwt.lang.asyncloaders.AsyncLoader"
- + sp + FragmentLoaderCreator.CALLBACK_LIST_SUFFIX) {
- @Override
- public CharSequence getContent() {
- StringBuffer code = new StringBuffer();
- code.append("package com.google.gwt.lang.asyncloaders;\n");
- code.append("import com.google.gwt.core.client.RunAsyncCallback;");
- code.append("public class AsyncLoader" + sp + FragmentLoaderCreator.CALLBACK_LIST_SUFFIX
- + "{\n");
- code.append(" RunAsyncCallback callback;\n");
- code.append("}\n");
- return code;
- }
- });
-
- addSnippetImport("com.google.gwt.lang.asyncloaders.AsyncLoader" + sp
- + FragmentLoaderCreator.CALLBACK_LIST_SUFFIX);
- }
-
private void addCommonTestCode() {
- addAsyncLoader(1);
- addAsyncLoader(2);
- addAsyncLoader(3);
-
- sourceOracle.addOrReplace(new MockJavaResource(
- "com.google.gwt.core.client.impl.AsyncFragmentLoader") {
- @Override
- public CharSequence getContent() {
- StringBuffer code = new StringBuffer();
- code.append("package com.google.gwt.core.client.impl;\n");
- code.append("public class AsyncFragmentLoader {\n");
- code.append(" private static AsyncFragmentLoader BROWSER_LOADER =\n");
- code.append(" makeBrowserLoader(1, new int[] {});\n");
- code.append(" private static AsyncFragmentLoader makeBrowserLoader(\n");
- code.append(" int numSp, int[] initial) {\n");
- code.append(" return null;\n");
- code.append(" }\n");
- code.append("}\n");
- return code;
- }
- });
- addSnippetImport("com.google.gwt.core.client.impl.AsyncFragmentLoader");
-
- sourceOracle.addOrReplace(new MockJavaResource(
- "com.google.gwt.core.client.prefetch.RunAsyncCode") {
- @Override
- public CharSequence getContent() {
- StringBuffer code = new StringBuffer();
- code.append("package com.google.gwt.core.client.prefetch;\n");
- code.append("public class RunAsyncCode {\n");
- code.append(" public static RunAsyncCode runAsyncCode(Class<?> splitPoint) {\n");
- code.append(" return null;\n");
- code.append(" }");
- code.append("}");
- return code;
- }
- });
addSnippetImport("com.google.gwt.core.client.prefetch.RunAsyncCode");
sourceOracle.addOrReplace(new MockJavaResource("test.SplitPoint1") {
@@ -187,7 +111,7 @@
private void initializeTestLoggerBuilder() {
testLoggerBuilder = new UnitTestTreeLogger.Builder();
testLoggerBuilder.setLowestLogLevel(TreeLogger.ERROR);
- expectError("Error in '/mock/test/EntryPoint.java'");
+ expectError("Errors in '/mock/test/EntryPoint.java'");
}
private void testSnippet(String codeSnippet) {
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/RunAsyncNameTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/RunAsyncNameTest.java
index e35dd65..77de550 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/RunAsyncNameTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/RunAsyncNameTest.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
+import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.util.UnitTestTreeLogger;
/**
@@ -61,14 +62,14 @@
builder.expectError("Errors in '/mock/test/EntryPoint.java'", null);
builder.expectError(
"Line 5: Only class literals may be used to name a call to GWT.runAsync()", null);
- builder.expectError("Cannot proceed due to previous errors", null);
logger = builder.createLogger();
this.logger = logger;
}
addSnippetImport("com.google.gwt.core.client.GWT");
try {
- compileSnippet("void", "GWT.runAsync((new Object()).getClass(), null);");
+ JProgram program = compileSnippet("void", "GWT.runAsync((new Object()).getClass(), null);");
+ ReplaceRunAsyncs.exec(logger, program);
fail("Expected compilation to fail");
} catch (UnableToCompleteException e) {
// expected
diff --git a/tools/api-checker/config/gwt23_24userApi.conf b/tools/api-checker/config/gwt23_24userApi.conf
index 88547b8..3e00070 100644
--- a/tools/api-checker/config/gwt23_24userApi.conf
+++ b/tools/api-checker/config/gwt23_24userApi.conf
@@ -116,7 +116,8 @@
##############################################
#excluded packages colon separated list
-excludedPackages com.google.gwt.editor.client.impl\
+excludedPackages com.google.gwt.core.client.impl\
+:com.google.gwt.editor.client.impl\
:com.google.gwt.junit.client.impl\
:com.google.gwt.benchmarks.client.impl\
diff --git a/user/src/com/google/gwt/core/client/GWT.java b/user/src/com/google/gwt/core/client/GWT.java
index 8861641..4e66b6c 100644
--- a/user/src/com/google/gwt/core/client/GWT.java
+++ b/user/src/com/google/gwt/core/client/GWT.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.core.client;
-import com.google.gwt.core.client.impl.AsyncFragmentLoader;
import com.google.gwt.core.client.impl.Impl;
/**
@@ -246,14 +245,14 @@
*/
@SuppressWarnings("unused") // parameter will be used following replacement
public static void runAsync(Class<?> name, RunAsyncCallback callback) {
- runAsyncWithoutCodeSplitting(callback);
+ callback.onSuccess();
}
/**
* Run the specified callback once the necessary code for it has been loaded.
*/
public static void runAsync(RunAsyncCallback callback) {
- runAsyncWithoutCodeSplitting(callback);
+ callback.onSuccess();
}
/**
@@ -283,36 +282,4 @@
private static native String getVersion0() /*-{
return $gwt_version;
}-*/;
-
- /**
- * This implementation of runAsync simply calls the callback. It is only used
- * when no code splitting has occurred.
- */
- private static void runAsyncWithoutCodeSplitting(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.
- */
- if (isScript()) {
- /*
- * It's possible that the code splitter does not run, even for a
- * production build. Signal a lightweight event, anyway, just so that
- * there isn't a complete lack of lightweight events for runAsync.
- */
- AsyncFragmentLoader.BROWSER_LOADER.logEventProgress("noDownloadNeeded", "begin");
- AsyncFragmentLoader.BROWSER_LOADER.logEventProgress("noDownloadNeeded", "end");
- }
-
- UncaughtExceptionHandler handler = sUncaughtExceptionHandler;
- if (handler == null) {
- callback.onSuccess();
- } else {
- try {
- callback.onSuccess();
- } catch (Throwable e) {
- handler.onUncaughtException(e);
- }
- }
- }
}
diff --git a/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java b/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
index c95c5ea..a86590c 100644
--- a/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
+++ b/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
@@ -17,6 +17,7 @@
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.RunAsyncCallback;
/**
* <p>
@@ -265,13 +266,22 @@
public static AsyncFragmentLoader BROWSER_LOADER = makeBrowserLoader(1, new int[]{});
/**
- * A helper static method that invokes
- * BROWSER_LOADER.leftoversFragmentHasLoaded(). Such a call is generated by
- * the compiler, as it is much simpler if there is a static method to wrap up
- * the call.
+ * Called by compiler-generated code when a fragment is loaded.
+ *
+ * @param fragment the fragment number
*/
- public static void browserLoaderLeftoversFragmentHasLoaded() {
- BROWSER_LOADER.leftoversFragmentHasLoaded();
+ public static void onLoad(int fragment) {
+ BROWSER_LOADER.onLoadImpl(fragment);
+ }
+
+ /**
+ * Called by the compiler to implement {@link GWT#runAsync}.
+ *
+ * @param fragment the fragment number
+ * @param callback the callback to run
+ */
+ public static void runAsync(int fragment, RunAsyncCallback callback) {
+ BROWSER_LOADER.runAsyncImpl(fragment, callback);
}
/**
@@ -291,6 +301,11 @@
}
/**
+ * Callbacks indexed by fragment number.
+ */
+ private final RunAsyncCallback[][] allCallbacks;
+
+ /**
* The fragment currently loading, or -1 if there aren't any.
*/
private int fragmentLoading = -1;
@@ -358,15 +373,46 @@
this.loadingStrategy = loadingStrategy;
this.logger = logger;
int numEntriesPlusOne = numEntries + 1;
- requestedExclusives = new BoundedIntQueue(numEntriesPlusOne);
- isLoaded = new boolean[numEntriesPlusOne];
- pendingDownloadErrorHandlers = new LoadTerminatedHandler[numEntriesPlusOne];
+ this.allCallbacks = new RunAsyncCallback[numEntriesPlusOne][];
+ this.requestedExclusives = new BoundedIntQueue(numEntriesPlusOne);
+ this.isLoaded = new boolean[numEntriesPlusOne];
+ this.pendingDownloadErrorHandlers = new LoadTerminatedHandler[numEntriesPlusOne];
+ }
+
+ public boolean isAlreadyLoaded(int splitPoint) {
+ return isLoaded[splitPoint];
+ }
+
+ /**
+ * Request that a sequence of split points be prefetched. Code for the split
+ * points in <code>splitPoints</code> will be downloaded and installed
+ * whenever there is nothing else to download. Each call to this method
+ * overwrites the entire prefetch queue with the newly specified one.
+ */
+ public void setPrefetchQueue(int... runAsyncSplitPoints) {
+ if (prefetchQueue == null) {
+ prefetchQueue = new BoundedIntQueue(numEntries);
+ }
+ prefetchQueue.clear();
+ for (int sp : runAsyncSplitPoints) {
+ prefetchQueue.add(sp);
+ }
+ startLoadingNextFragment();
+ }
+
+ public void startPrefetching() {
+ prefetching = true;
+ startLoadingNextFragment();
+ }
+
+ public void stopPrefetching() {
+ prefetching = false;
}
/**
* Inform the loader that a fragment has now finished loading.
*/
- public void fragmentHasLoaded(int fragment) {
+ void fragmentHasLoaded(int fragment) {
logFragmentLoaded(fragment);
if (fragment < pendingDownloadErrorHandlers.length) {
pendingDownloadErrorHandlers[fragment] = null;
@@ -395,7 +441,7 @@
*
* @param splitPoint the split point whose code needs to be loaded
*/
- public void inject(int splitPoint, LoadTerminatedHandler loadErrorHandler) {
+ void inject(int splitPoint, LoadTerminatedHandler loadErrorHandler) {
pendingDownloadErrorHandlers[splitPoint] = loadErrorHandler;
if (!isInitial(splitPoint)) {
requestedExclusives.add(splitPoint);
@@ -403,49 +449,8 @@
startLoadingNextFragment();
}
- public boolean isAlreadyLoaded(int splitPoint) {
- return isLoaded[splitPoint];
- }
-
- public boolean isLoading(int splitPoint) {
- return pendingDownloadErrorHandlers[splitPoint] != null;
- }
-
- public void leftoversFragmentHasLoaded() {
- fragmentHasLoaded(leftoversFragment());
- }
-
- /**
- * Log an event with the {@Logger} this instance was provided.
- */
- public void logEventProgress(String eventGroup, String type) {
- logEventProgress(eventGroup, type, -1, -1);
- }
-
- /**
- * Request that a sequence of split points be prefetched. Code for the split
- * points in <code>splitPoints</code> will be downloaded and installed
- * whenever there is nothing else to download. Each call to this method
- * overwrites the entire prefetch queue with the newly specified one.
- */
- public void setPrefetchQueue(int... runAsyncSplitPoints) {
- if (prefetchQueue == null) {
- prefetchQueue = new BoundedIntQueue(numEntries);
- }
- prefetchQueue.clear();
- for (int sp : runAsyncSplitPoints) {
- prefetchQueue.add(sp);
- }
- startLoadingNextFragment();
- }
-
- public void startPrefetching() {
- prefetching = true;
- startLoadingNextFragment();
- }
-
- public void stopPrefetching() {
- prefetching = false;
+ void leftoversFragmentHasLoaded() {
+ onLoadImpl(leftoversFragment());
}
private boolean anyPrefetchesRequested() {
@@ -522,6 +527,10 @@
return false;
}
+ private boolean isLoading(int splitPoint) {
+ return pendingDownloadErrorHandlers[splitPoint] != null;
+ }
+
private int leftoversFragment() {
return numEntries;
}
@@ -531,6 +540,13 @@
}
/**
+ * Log an event with the {@Logger} this instance was provided.
+ */
+ private void logEventProgress(String eventGroup, String type) {
+ logEventProgress(eventGroup, type, -1, -1);
+ }
+
+ /**
* Log event progress via the {@link Logger} this instance was provided. The
* <code>fragment</code> and <code>size</code> objects are allowed to be
* <code>null</code>.
@@ -544,6 +560,58 @@
logEventProgress(logGroup, LwmLabels.END, fragment, -1);
}
+ private void onLoadImpl(int fragment) {
+ fragmentHasLoaded(fragment);
+ RunAsyncCallback[] callbacks = allCallbacks[fragment];
+ if (callbacks != null) {
+ logEventProgress("runCallbacks" + fragment, "begin");
+ allCallbacks[fragment] = null;
+ GWT.UncaughtExceptionHandler handler = GWT.getUncaughtExceptionHandler();
+ for (RunAsyncCallback callback : callbacks) {
+ if (handler == null) {
+ callback.onSuccess();
+ } else {
+ try {
+ callback.onSuccess();
+ } catch (Throwable e) {
+ handler.onUncaughtException(e);
+ }
+ }
+ }
+ logEventProgress("runCallbacks" + fragment, "end");
+ }
+ }
+
+ private void runAsyncImpl(final int fragment, RunAsyncCallback callback) {
+ if (isLoaded[fragment]) {
+ assert allCallbacks[fragment] == null;
+ callback.onSuccess();
+ return;
+ }
+
+ RunAsyncCallback[] callbacks = allCallbacks[fragment];
+ if (callbacks == null) {
+ callbacks = allCallbacks[fragment] = new RunAsyncCallback[0];
+ }
+ // Take advantage of no range checking in web mode.
+ assert GWT.isScript();
+ callbacks[callbacks.length] = callback;
+
+ if (!isLoading(fragment)) {
+ inject(fragment, new AsyncFragmentLoader.LoadTerminatedHandler() {
+ public void loadTerminated(Throwable reason) {
+ RunAsyncCallback[] callbacks = allCallbacks[fragment];
+ if (callbacks != null) {
+ allCallbacks[fragment] = null;
+ for (RunAsyncCallback callback : callbacks) {
+ callback.onFailure(reason);
+ }
+ }
+ }
+ });
+ }
+ }
+
private void startLoadingFragment(int fragment) {
assert (fragmentLoading < 0);
fragmentLoading = fragment;
diff --git a/user/src/com/google/gwt/core/client/prefetch/RunAsyncCode.java b/user/src/com/google/gwt/core/client/prefetch/RunAsyncCode.java
index 7d9c4be..5fa8101 100644
--- a/user/src/com/google/gwt/core/client/prefetch/RunAsyncCode.java
+++ b/user/src/com/google/gwt/core/client/prefetch/RunAsyncCode.java
@@ -57,7 +57,7 @@
* Ask whether this code has already been loaded.
*/
public boolean isLoaded() {
- if (GWT.isScript()) {
+ if (!GWT.isScript()) {
return true;
}
return AsyncFragmentLoader.BROWSER_LOADER.isAlreadyLoaded(splitPoint);
diff --git a/user/test/com/google/gwt/dev/jjs/test/RunAsyncTest.java b/user/test/com/google/gwt/dev/jjs/test/RunAsyncTest.java
index 44a5adf..d47185f 100644
--- a/user/test/com/google/gwt/dev/jjs/test/RunAsyncTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/RunAsyncTest.java
@@ -116,11 +116,10 @@
// save the original handler
final UncaughtExceptionHandler originalHandler = GWT.getUncaughtExceptionHandler();
- // set a handler that catches toThrow and nothing else
- GWT.setUncaughtExceptionHandler(new GWT.UncaughtExceptionHandler() {
+ // set a handler that looks for toThrow
+ GWT.UncaughtExceptionHandler myHandler = new GWT.UncaughtExceptionHandler() {
public void onUncaughtException(Throwable e) {
GWT.setUncaughtExceptionHandler(originalHandler);
-
if (e == toThrow) {
// expected
finishTest();
@@ -129,17 +128,22 @@
throw new RuntimeException(e);
}
}
- });
-
+ };
+ GWT.setUncaughtExceptionHandler(myHandler);
delayTestFinish(RUNASYNC_TIMEOUT);
- GWT.runAsync(new RunAsyncCallback() {
- public void onFailure(Throwable caught) {
- }
+ try {
+ GWT.runAsync(new RunAsyncCallback() {
+ public void onFailure(Throwable caught) {
+ }
- public void onSuccess() {
- throw toThrow;
- }
- });
+ public void onSuccess() {
+ throw toThrow;
+ }
+ });
+ } catch (Throwable e) {
+ // runAsync can either throw immediately, or throw uncaught.
+ myHandler.onUncaughtException(e);
+ }
}
}