Working CodeSplitter2.
-With this the people should be able to test the new CodeSplitter.
-It is protected by a -XfragmentMerge flag, it will not break anyone.
-While it is working in lots of internal projects, there are still room for improvement. There are enough todo's in places where work is needed.
Review at http://gwt-code-reviews.appspot.com/1631803
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10866 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/Link.java b/dev/core/src/com/google/gwt/dev/Link.java
index 4d033ed..d00aa4e 100644
--- a/dev/core/src/com/google/gwt/dev/Link.java
+++ b/dev/core/src/com/google/gwt/dev/Link.java
@@ -503,7 +503,7 @@
/**
* Logs the total script size for this permutation, as calculated by
- * {@link CodeSplitter#totalScriptSize(int[])}.
+ * {@link CodeSplitter2#totalScriptSize(int[])}.
*/
private static void logScriptSize(TreeLogger logger, int permId,
StandardCompilationResult compilation) {
@@ -518,6 +518,7 @@
jsLengths[i] = javaScript[i].length();
}
+ // TODO(acleung): This is broken for CodeSplitter2.
int totalSize = CodeSplitter.totalScriptSize(jsLengths);
if (logger.isLoggable(TreeLogger.TRACE)) {
diff --git a/dev/core/src/com/google/gwt/dev/PrecompileTaskArgProcessor.java b/dev/core/src/com/google/gwt/dev/PrecompileTaskArgProcessor.java
index 5f13163..770a6f5 100644
--- a/dev/core/src/com/google/gwt/dev/PrecompileTaskArgProcessor.java
+++ b/dev/core/src/com/google/gwt/dev/PrecompileTaskArgProcessor.java
@@ -29,6 +29,7 @@
import com.google.gwt.dev.util.arg.ArgHandlerDumpSignatures;
import com.google.gwt.dev.util.arg.ArgHandlerEnableAssertions;
import com.google.gwt.dev.util.arg.ArgHandlerEnableClosureCompiler;
+import com.google.gwt.dev.util.arg.ArgHandlerFragmentMerge;
import com.google.gwt.dev.util.arg.ArgHandlerGenDir;
import com.google.gwt.dev.util.arg.ArgHandlerMaxPermsPerPrecompile;
import com.google.gwt.dev.util.arg.ArgHandlerOptimize;
@@ -62,6 +63,7 @@
registerHandler(new ArgHandlerCompilerMetrics(options));
registerHandler(new ArgHandlerDisableSoycHtml(options));
registerHandler(new ArgHandlerEnableClosureCompiler(options));
+ registerHandler(new ArgHandlerFragmentMerge(options));
}
@Override
diff --git a/dev/core/src/com/google/gwt/dev/PrecompileTaskOptionsImpl.java b/dev/core/src/com/google/gwt/dev/PrecompileTaskOptionsImpl.java
index 1fea930..1a3ab05 100644
--- a/dev/core/src/com/google/gwt/dev/PrecompileTaskOptionsImpl.java
+++ b/dev/core/src/com/google/gwt/dev/PrecompileTaskOptionsImpl.java
@@ -49,6 +49,11 @@
}
@Override
+ public int getFragmentsMerge() {
+ return jjsOptions.getFragmentsMerge();
+ }
+
+ @Override
public File getGenDir() {
return genDir;
}
@@ -188,6 +193,11 @@
}
@Override
+ public void setFragmentsMerge(int numFragments) {
+ jjsOptions.setFragmentsMerge(numFragments);
+ }
+
+ @Override
public void setGenDir(File genDir) {
this.genDir = genDir;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
index 3162e5e..18fa84a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
@@ -20,6 +20,7 @@
import com.google.gwt.dev.util.arg.OptionDisableClassMetadata;
import com.google.gwt.dev.util.arg.OptionEnableAssertions;
import com.google.gwt.dev.util.arg.OptionEnableClosureCompiler;
+import com.google.gwt.dev.util.arg.OptionFragmentsMerge;
import com.google.gwt.dev.util.arg.OptionOptimize;
import com.google.gwt.dev.util.arg.OptionOptimizePrecompile;
import com.google.gwt.dev.util.arg.OptionRunAsyncEnabled;
@@ -36,6 +37,6 @@
OptionDisableClassMetadata, OptionDisableCastChecking, OptionEnableAssertions,
OptionRunAsyncEnabled, OptionScriptStyle, OptionSoycEnabled, OptionSoycDetailed,
OptionOptimizePrecompile, OptionStrict, OptionSoycHtmlDisabled,
- OptionEnableClosureCompiler {
+ OptionEnableClosureCompiler, OptionFragmentsMerge {
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
index c1695bb..173df6f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
@@ -38,6 +38,7 @@
private boolean soycHtmlDisabled = false;
private boolean strict = false;
private boolean closureCompilerEnabled;
+ private int fragmentsMerge = -1;
public JJSOptionsImpl() {
}
@@ -60,8 +61,14 @@
setSoycHtmlDisabled(other.isSoycHtmlDisabled());
setStrict(other.isStrict());
setClosureCompilerEnabled(other.isClosureCompilerEnabled());
+ setFragmentsMerge(other.getFragmentsMerge());
}
-
+
+ @Override
+ public int getFragmentsMerge() {
+ return fragmentsMerge;
+ }
+
@Override
public int getOptimizationLevel() {
return optimizationLevel;
@@ -167,6 +174,11 @@
}
@Override
+ public void setFragmentsMerge(int numFragments) {
+ this.fragmentsMerge = numFragments;
+ }
+
+ @Override
public void setOptimizationLevel(int level) {
optimizationLevel = level;
}
@@ -205,4 +217,5 @@
public void setStrict(boolean strict) {
this.strict = strict;
}
+
}
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 9a028a8..5f3c93d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -67,8 +67,9 @@
import com.google.gwt.dev.jjs.impl.AstDumper;
import com.google.gwt.dev.jjs.impl.CastNormalizer;
import com.google.gwt.dev.jjs.impl.CatchBlockNormalizer;
-import com.google.gwt.dev.jjs.impl.CodeSplitter;
import com.google.gwt.dev.jjs.impl.CodeSplitter.MultipleDependencyGraphRecorder;
+import com.google.gwt.dev.jjs.impl.CodeSplitter;
+import com.google.gwt.dev.jjs.impl.CodeSplitter2;
import com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer;
import com.google.gwt.dev.jjs.impl.DeadCodeElimination;
import com.google.gwt.dev.jjs.impl.EnumOrdinalizer;
@@ -348,10 +349,21 @@
// (10) Split up the program into fragments
SyntheticArtifact dependencies = null;
+
if (options.isRunAsyncEnabled()) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- CodeSplitter.exec(logger, jprogram, jsProgram, jjsmap, chooseDependencyRecorder(options
- .isSoycEnabled(), baos));
+ int fragmentsMerge = options.getFragmentsMerge();
+
+ // Pick and choose which code splitter to use. Only use the experimental
+ // one when the user explicitly decides the project needs fragment
+ // merging.
+ if (fragmentsMerge > 0) {
+ CodeSplitter2.exec(logger, jprogram, jsProgram, jjsmap, fragmentsMerge,
+ chooseDependencyRecorder(options.isSoycEnabled(), baos));
+ } else {
+ CodeSplitter.exec(logger, jprogram, jsProgram, jjsmap, chooseDependencyRecorder(options
+ .isSoycEnabled(), baos));
+ }
if (baos.size() == 0 && options.isSoycEnabled()) {
recordNonSplitDependencies(jprogram, baos);
}
@@ -618,7 +630,7 @@
// Fix up GWT.runAsync()
if (module != null && options.isRunAsyncEnabled()) {
ReplaceRunAsyncs.exec(logger, jprogram);
- CodeSplitter.pickInitialLoadSequence(logger, jprogram, module.getProperties());
+ CodeSplitter2.pickInitialLoadSequence(logger, jprogram, module.getProperties());
}
ImplementClassLiteralsAsFields.exec(jprogram);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNumericEntry.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNumericEntry.java
new file mode 100644
index 0000000..77e8c57
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNumericEntry.java
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+/**
+ * A place order a numeric value in the AST.
+ *
+ * It temporary holds an numeric value during compilation. In a later stage,
+ * a compiler pass easily replace these place holder. For example,
+ * {@link ReplaceRunAsyncs would }
+ */
+public final class JNumericEntry extends JExpression {
+
+ private final String key;
+ private int value;
+
+ public JNumericEntry(SourceInfo info, String key, int value) {
+ super(info);
+ this.key = key;
+ this.value = value;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public JType getType() {
+ return JPrimitiveType.INT;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean hasSideEffects() {
+ return false;
+ }
+
+ public void setValue(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public void traverse(JVisitor visitor, Context ctx) {
+ if (visitor.visit(this, ctx)) {
+ }
+ visitor.endVisit(this, ctx);
+ }
+
+}
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 95aba96..18835af 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
@@ -21,6 +21,7 @@
import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.jjs.ast.js.JsCastMap;
import com.google.gwt.dev.jjs.impl.CodeSplitter;
+import com.google.gwt.dev.jjs.impl.CodeSplitter2.FragmentPartitioningResult;
import com.google.gwt.dev.util.collect.Lists;
import java.io.IOException;
@@ -45,6 +46,7 @@
* Root for the AST representing an entire Java program.
*/
public class JProgram extends JNode {
+
private static final class ArrayTypeComparator implements Comparator<JArrayType>, Serializable {
public int compare(JArrayType o1, JArrayType o2) {
int comp = o1.getDims() - o2.getDims();
@@ -244,18 +246,29 @@
* be preferred whenever a JProgram instance is available.
*
* @param initialSeq The initial split point sequence of the program
+ * @param result The fragment partitioning result, null if it hasn't be partitioned.
* @param numSps The number of split points in the program
* @param firstFragment The first fragment to consider
* @param restFragments The rest of the fragments to consider
*/
- public static int lastFragmentLoadingBefore(List<Integer> initialSeq, int numSps,
- int firstFragment, int... restFragments) {
+ public static int lastFragmentLoadingBefore(List<Integer> initialSeq,
+ FragmentPartitioningResult result, int numSps, int firstFragment, int... restFragments) {
int latest = firstFragment;
for (int frag : restFragments) {
- latest = pairwiseLastFragmentLoadingBefore(initialSeq, numSps, latest, frag);
+ latest = pairwiseLastFragmentLoadingBefore(initialSeq, result, numSps, latest, frag);
}
return latest;
}
+
+ public static int lastFragmentLoadingBefore(List<Integer> initialSeq,
+ int numSps, int firstFragment, int... restFragments) {
+ int latest = firstFragment;
+ for (int frag : restFragments) {
+ latest = pairwiseLastFragmentLoadingBefore(initialSeq, null, numSps, latest, frag);
+ }
+ return latest;
+ }
+
public static void serializeTypes(List<JDeclaredType> types, ObjectOutputStream stream)
throws IOException {
@@ -272,8 +285,9 @@
* The main logic behind {@link #lastFragmentLoadingBefore(int, int...)} and
* {@link #lastFragmentLoadingBefore(List, int, int, int...)}.
*/
- private static int pairwiseLastFragmentLoadingBefore(List<Integer> initialSeq, int numSps,
- int frag1, int frag2) {
+ private static int pairwiseLastFragmentLoadingBefore(List<Integer> initialSeq,
+ FragmentPartitioningResult result, int numSps, int frag1, int frag2) {
+
if (frag1 == frag2) {
return frag1;
}
@@ -286,10 +300,22 @@
return 0;
}
- // See if either is in the initial sequence
- int initPos1 = initialSeq.indexOf(frag1);
- int initPos2 = initialSeq.indexOf(frag2);
+ // TODO(acleung): While the logic for this is correct, the terminology used
+ // in the code is not correct when fragment partitioning is on.
+ // Once the new code splitter is the default. This function needs to be
+ // rewritten.
+ int sp1 = frag1;
+ int sp2 = frag2;
+ // If there were some fragment merging.
+ if (result != null) {
+ sp1 = result.getSplitPointFromFragmnet(sp1);
+ sp2 = result.getSplitPointFromFragmnet(sp2);
+ }
+
+ int initPos1 = initialSeq.indexOf(sp1);
+ int initPos2 = initialSeq.indexOf(sp2);
+
// If both are in the initial sequence, then pick the earlier
if (initPos1 >= 0 && initPos2 >= 0) {
if (initPos1 < initPos2) {
@@ -309,8 +335,11 @@
assert (initPos1 < 0 && initPos2 < 0);
assert (frag1 != frag2);
- // They are both leftovers or exclusive. Leftovers goes first in all cases.
- return CodeSplitter.getLeftoversFragmentNumber(numSps);
+ if (result != null) {
+ return result.getLeftoverFragmentIndex();
+ } else {
+ return CodeSplitter.getLeftoversFragmentNumber(numSps);
+ }
}
public final List<JClassType> codeGenTypes = new ArrayList<JClassType>();
@@ -369,6 +398,8 @@
private JClassType typeSpecialJavaScriptObject;
private JClassType typeString;
+
+ private FragmentPartitioningResult fragmentPartitioninResult;
/**
* Constructor.
@@ -897,8 +928,8 @@
* supplied fragments, or it might be a common predecessor.
*/
public int lastFragmentLoadingBefore(int firstFragment, int... restFragments) {
- return lastFragmentLoadingBefore(splitPointInitialSequence, runAsyncs.size(), firstFragment,
- restFragments);
+ return lastFragmentLoadingBefore(splitPointInitialSequence, fragmentPartitioninResult,
+ runAsyncs.size(), firstFragment, restFragments);
}
public void putIntoTypeMap(String qualifiedBinaryName, JDeclaredType type) {
@@ -932,6 +963,10 @@
}
}
+ public void setFragmentPartitioningResult(FragmentPartitioningResult result) {
+ fragmentPartitioninResult = result;
+ }
+
public void setRunAsyncs(List<JRunAsync> runAsyncs) {
this.runAsyncs = Lists.normalizeUnmodifiable(runAsyncs);
}
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 8e17e97..3d332ff 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
@@ -376,6 +376,10 @@
public void endVisit(JNullType x, Context ctx) {
endVisit((JReferenceType) x, ctx);
}
+
+ public void endVisit(JNumericEntry x, Context ctx) {
+ endVisit((JExpression) x, ctx);
+ }
public void endVisit(JParameter x, Context ctx) {
endVisit((JVariable) x, ctx);
@@ -704,6 +708,10 @@
public boolean visit(JNullType x, Context ctx) {
return visit((JReferenceType) x, ctx);
}
+
+ public boolean visit(JNumericEntry x, Context ctx) {
+ return visit((JExpression) x, ctx);
+ }
public boolean visit(JParameter x, Context ctx) {
return visit((JVariable) 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 628841c..d1089da 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
@@ -41,6 +41,7 @@
import com.google.gwt.dev.jjs.ast.JNewArray;
import com.google.gwt.dev.jjs.ast.JNewInstance;
import com.google.gwt.dev.jjs.ast.JNullLiteral;
+import com.google.gwt.dev.jjs.ast.JNumericEntry;
import com.google.gwt.dev.jjs.ast.JParameterRef;
import com.google.gwt.dev.jjs.ast.JPostfixOperation;
import com.google.gwt.dev.jjs.ast.JPrefixOperation;
@@ -266,6 +267,12 @@
}
@Override
+ public boolean visit(JNumericEntry x, Context ctx) {
+ expression = new JNumericEntry(x.getSourceInfo(), x.getKey(), x.getValue());
+ return false;
+ }
+
+ @Override
public boolean visit(JNullLiteral x, Context ctx) {
expression = x;
return false;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter2.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter2.java
index 4e317f3..9d9e82b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter2.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter2.java
@@ -28,22 +28,25 @@
import com.google.gwt.dev.jjs.ast.JDeclaredType;
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.JNewArray;
import com.google.gwt.dev.jjs.ast.JNode;
+import com.google.gwt.dev.jjs.ast.JNumericEntry;
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;
+import com.google.gwt.dev.jjs.impl.CodeSplitter.MultipleDependencyGraphRecorder;
import com.google.gwt.dev.jjs.impl.FragmentExtractor.CfaLivenessPredicate;
import com.google.gwt.dev.jjs.impl.FragmentExtractor.LivenessPredicate;
import com.google.gwt.dev.jjs.impl.FragmentExtractor.NothingAlivePredicate;
import com.google.gwt.dev.js.ast.JsBlock;
+import com.google.gwt.dev.js.ast.JsContext;
+import com.google.gwt.dev.js.ast.JsModVisitor;
+import com.google.gwt.dev.js.ast.JsNumericEntry;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.util.JsniRef;
@@ -52,14 +55,12 @@
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 com.google.gwt.thirdparty.guava.common.base.Predicate;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -81,40 +82,62 @@
* code.
*
* TODO(acleung): Rename to CodeSplitter upon completion.
+ * TODO(acleung): Some of the data structures and methods are EXACT copy of the
+ * original CoderSplitter.java. This is intentional as we are going to remove
+ * the old one upon completion of this one.
* TODO(acleung): Figure out how to integrate with SOYC and dependency tracker.
* TODO(acleung): Insert SpeedTracer calls at performance sensitive places.
* TODO(acleung): Insert logger calls to generate meaningful logs.
- * TODO(acleung): Modify the fragment loader.
- * TODO(acleung): May be add back the odd heuristics if needed.
+ * TODO(acleung): May be add back the old heuristics if needed.
*/
public class CodeSplitter2 {
-
+
/**
- * A dependency recorder that can record multiple dependency graphs. It has
- * methods for starting and finishing new dependency graphs.
+ * A read-only class that holds some information about the result of the
+ * partition process.
*
- * TODO(acleung): This is currently broken. I need to investiage more on how we inform SOYC
- * the dependency results.
+ * Unlike the original code split where information about the fragments and
+ * be deduced from the JProgram, certain compiler passes needs to know what
+ * happened here in order to do their job correctly.
*/
- public interface MultipleDependencyGraphRecorder extends DependencyRecorder {
+ public static final class FragmentPartitioningResult {
+ private final int[] fragmentToSplitPoint;
+
+ private FragmentPartitioningResult(int[] splitPointToFragmentMap, int numFragments) {
+ fragmentToSplitPoint = new int[numFragments];
+ for (int i = 0, len = splitPointToFragmentMap.length - 1; i < len; i++) {
+ System.out.println("splitPointToFragmentMap[" + i + "] = " + splitPointToFragmentMap[i]);
+ }
+ for (int i = 1, len = splitPointToFragmentMap.length - 1; i < len; i++) {
+ if (fragmentToSplitPoint[splitPointToFragmentMap[i]] == 0) {
+ fragmentToSplitPoint[splitPointToFragmentMap[i]] = i;
+ } else {
+ fragmentToSplitPoint[splitPointToFragmentMap[i]] = -1;
+ }
+ }
+ }
+
/**
- * Stop recording dependencies.
+ * @return Fragment number of the left over fragment.
*/
- void close();
+ public int getLeftoverFragmentIndex() {
+ return getNumFragments() - 1;
+ }
+
+ /**
+ * @return Number of code fragments in the compilation. Leftover fragment and initial fragment.
+ */
+ public int getNumFragments() {
+ return fragmentToSplitPoint.length;
+ }
/**
- * Stop recording the current dependency graph.
+ * @return One of the split point number in a given fragment. If there
+ * are more than one splitpoints in the a fragment, -1 is returned.
*/
- void endDependencyGraph();
-
- void open();
-
- /**
- * Start a new dependency graph. It can be an extension of a previously
- * recorded dependency graph, in which case the dependencies in the previous
- * graph will not be repeated.
- */
- void startDependencyGraph(String name, String extnds);
+ public int getSplitPointFromFragmnet(int fragment) {
+ return fragmentToSplitPoint[fragment];
+ }
}
/**
@@ -133,6 +156,68 @@
*/
EDGE_GREEDY,
}
+
+ /**
+ * A map from program atoms to the split point, if any, that they are
+ * exclusive to. Atoms not exclusive to any split point are either mapped to 0
+ * or left out of the map entirely. Note that the map is incomplete; any entry
+ * not included has not been proven to be exclusive. Also, note that the
+ * initial load sequence is assumed to already be loaded.
+ */
+ private static class ExclusivityMap {
+ public Map<JField, Integer> fields = new HashMap<JField, Integer>();
+ public Map<JMethod, Integer> methods = new HashMap<JMethod, Integer>();
+ public Map<String, Integer> strings = new HashMap<String, Integer>();
+ public Map<JDeclaredType, Integer> types = new HashMap<JDeclaredType, Integer>();
+ }
+
+ /**
+ * A liveness predicate that is based on an exclusivity map.
+ */
+ private static class ExclusivityMapLivenessPredicate implements LivenessPredicate {
+ private final int fragment;
+ private final ExclusivityMap fragmentMap;
+
+ public ExclusivityMapLivenessPredicate(ExclusivityMap fragmentMap, int fragment) {
+ this.fragmentMap = fragmentMap;
+ this.fragment = fragment;
+ }
+
+ @Override
+ public boolean isLive(JDeclaredType type) {
+ return checkMap(fragmentMap.types, type);
+ }
+
+ @Override
+ public boolean isLive(JField field) {
+ return checkMap(fragmentMap.fields, field);
+ }
+
+ @Override
+ public boolean isLive(JMethod method) {
+ return checkMap(fragmentMap.methods, method);
+ }
+
+ @Override
+ public boolean isLive(String literal) {
+ return checkMap(fragmentMap.strings, literal);
+ }
+
+ @Override
+ public boolean miscellaneousStatementsAreLive() {
+ return true;
+ }
+
+ private <T> boolean checkMap(Map<T, Integer> map, T x) {
+ Integer entryForX = map.get(x);
+ if (entryForX == null) {
+ // unrecognized items are always live
+ return true;
+ } else {
+ return (fragment == entryForX) || (entryForX == 0);
+ }
+ }
+ }
/**
* Maps an atom to a set of split point that can be live (NOT necessary exclusively)
@@ -180,87 +265,19 @@
}
/**
- * LivenessPredicate to tell the fragment extractor what is live.
- */
- private static class LiveSplitPointsPredicate implements LivenessPredicate {
- private final LiveSplitPointMap map;
- private final Predicate<BitSet> mask;
- LiveSplitPointsPredicate(LiveSplitPointMap map, Predicate<BitSet> mask) {
- this.map = map;
- this.mask = mask;
- }
-
- @Override
- public boolean isLive(JDeclaredType type) {
- return isLive(map.types, type);
- }
-
- @Override
- public boolean isLive(JField field) {
- return isLive(map.fields, field);
- }
-
- @Override
- public boolean isLive(JMethod method) {
- return isLive(map.methods, method);
- }
-
- @Override
- public boolean isLive(String literal) {
- return isLive(map.strings, literal);
- }
-
- @Override
- public boolean miscellaneousStatementsAreLive() {
- return true;
- }
-
- private <T> boolean isLive(Map<T, BitSet> map, T atom) {
- BitSet value = map.get(atom);
- return value != null && mask.apply(value);
- }
- }
-
- /**
- * A {@link MultipleDependencyGraphRecorder} that does nothing.
- */
- public static final MultipleDependencyGraphRecorder NULL_RECORDER =
- new MultipleDependencyGraphRecorder() {
- public void close() {
- }
-
- public void endDependencyGraph() {
- }
-
- public void methodIsLiveBecause(JMethod liveMethod, ArrayList<JMethod> dependencyChain) {
- }
-
- public void open() {
- }
-
- public void startDependencyGraph(String name, String extnds) {
- }
- };
-
- /**
- * Number of split points to merge, this should be configurable by user later.
- */
- public static final int NUM_SPLITPOINTS_TO_MERGE = 12;
-
- /**
* The property key for a list of initially loaded split points.
*/
private static final String PROP_INITIAL_SEQUENCE = "compiler.splitpoint.initial.sequence";
public static ControlFlowAnalyzer computeInitiallyLive(JProgram jprogram) {
- return computeInitiallyLive(jprogram, NULL_RECORDER);
+ return computeInitiallyLive(jprogram, CodeSplitter.NULL_RECORDER);
}
-
+
public static ControlFlowAnalyzer computeInitiallyLive(
JProgram jprogram, MultipleDependencyGraphRecorder dependencyRecorder) {
- // Control Flow Analysis from a split point.
- ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(jprogram);
dependencyRecorder.startDependencyGraph("initial", null);
+
+ ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(jprogram);
cfa.setDependencyRecorder(dependencyRecorder);
cfa.traverseEntryMethods();
traverseClassArray(jprogram, cfa);
@@ -268,15 +285,17 @@
dependencyRecorder.endDependencyGraph();
return cfa;
}
-
+
public static void exec(TreeLogger logger, JProgram jprogram, JsProgram jsprogram,
- JavaToJavaScriptMap map, MultipleDependencyGraphRecorder dependencyRecorder) {
+ JavaToJavaScriptMap map, int fragmentsToMerge,
+ MultipleDependencyGraphRecorder dependencyRecorder) {
if (jprogram.getRunAsyncs().size() == 0) {
// Don't do anything if there is no call to runAsync
return;
}
Event codeSplitterEvent = SpeedTracerLogger.start(CompilerEventType.CODE_SPLITTER);
- new CodeSplitter2(logger, jprogram, jsprogram, map, dependencyRecorder).execImpl();
+ new CodeSplitter2(
+ logger, jprogram, jsprogram, map, fragmentsToMerge, dependencyRecorder).execImpl();
codeSplitterEvent.end();
}
@@ -298,8 +317,10 @@
throw new UnableToCompleteException();
}
final String lookupErrorHolder[] = new String[1];
+ @SuppressWarnings("deprecation")
JNode referent =
JsniRefLookup.findJsniRefTarget(jsniRef, program, new JsniRefLookup.ErrorReporter() {
+ @Override
public void reportError(String error) {
lookupErrorHolder[0] = error;
}
@@ -346,7 +367,7 @@
codeSplitterEvent.end();
return result;
}
-
+
/**
* Choose an initial load sequence of split points for the specified program.
* Do so by identifying split points whose code always load first, before any
@@ -356,6 +377,7 @@
*
* @throws UnableToCompleteException If the module specifies a bad load order
*/
+ @SuppressWarnings("javadoc")
public static void pickInitialLoadSequence(TreeLogger logger, JProgram program,
Properties properties) throws UnableToCompleteException {
Event codeSplitterEvent =
@@ -391,6 +413,7 @@
program.setSplitPointInitialSequence(new ArrayList<Integer>(initialLoadSequence));
codeSplitterEvent.end();
}
+
private static Map<JField, JClassLiteral> buildFieldToClassLiteralMap(JProgram jprogram) {
final Map<JField, JClassLiteral> map = new HashMap<JField, JClassLiteral>();
class BuildFieldToLiteralVisitor extends JVisitor {
@@ -402,6 +425,7 @@
(new BuildFieldToLiteralVisitor()).accept(jprogram);
return map;
}
+
private static <T> void countShardedAtomsOfType(Map<T, BitSet> livenessMap, int[][] matrix) {
// Count the number of atoms shared only by
for (Entry<T, BitSet> fieldLiveness : livenessMap.entrySet()) {
@@ -419,7 +443,20 @@
matrix[start][end]++;
}
}
-
+
+ /**
+ * Extract the types from a set that happen to be declared types.
+ */
+ private static Set<JDeclaredType> declaredTypesIn(Set<JReferenceType> types) {
+ Set<JDeclaredType> result = new HashSet<JDeclaredType>();
+ for (JReferenceType type : types) {
+ if (type instanceof JDeclaredType) {
+ result.add((JDeclaredType) type);
+ }
+ }
+ return result;
+ }
+
private static <T> int getOrZero(Map<T, BitSet> map, T key) {
BitSet value = map.get(key);
if (value != null && value.cardinality() == 1) {
@@ -427,7 +464,7 @@
}
return 0;
}
-
+
/**
* Installs the initial load sequence into AsyncFragmentLoader.BROWSER_LOADER.
* The initializer looks like this:
@@ -449,7 +486,7 @@
assert ((JNewArray) arg1).getArrayType() == arrayType;
List<JExpression> initializers = new ArrayList<JExpression>(initialLoadSequence.size());
for (int sp : initialLoadSequence) {
- initializers.add(JIntLiteral.get(sp));
+ initializers.add(new JNumericEntry(call.getSourceInfo(), "RunAsyncFragmentIndex", sp));
}
JNewArray newArray =
JNewArray.createInitializers(arg1.getSourceInfo(), arrayType, Lists
@@ -469,9 +506,7 @@
}
for (JField node : cfa.getFieldsWritten()) {
- if (node instanceof JField) {
- liveness.setLive((JField) node, idx);
- }
+ liveness.setLive(node, idx);
}
for (String s : cfa.getLiveStrings()) {
@@ -522,7 +557,7 @@
}
}
}
-
+
/**
* Any immortal codegen types must be part of the initial download.
*/
@@ -538,8 +573,33 @@
}
}
- private final Map<JField, JClassLiteral> fieldToLiteralOfClass;
+ private static <T> Set<T> union(Set<? extends T> set1, Set<? extends T> set2) {
+ Set<T> union = new HashSet<T>();
+ union.addAll(set1);
+ union.addAll(set2);
+ return union;
+ }
+ private static <T> void updateReverseMap(int entry, Map<T, Integer> map, Set<?> liveWithoutEntry,
+ Iterable<T> all) {
+ for (T each : all) {
+ if (!liveWithoutEntry.contains(each)) {
+ /*
+ * Note that it is fine to overwrite a preexisting entry in the map. If
+ * an atom is dead until split point i has been reached, and is also
+ * dead until entry j has been reached, then it is dead until both have
+ * been reached. Thus, it can be downloaded along with either i's or j's
+ * code.
+ */
+ map.put(each, entry);
+ }
+ }
+ }
+
+ ExclusivityMap fragmentMap = new ExclusivityMap();
+
+ private final Map<JField, JClassLiteral> fieldToLiteralOfClass;
+
private FragmentExtractor fragmentExtractor;
/**
@@ -561,6 +621,17 @@
private final Set<JMethod> methodsInJavaScript;
/**
+ * Number of split points to merge.
+ */
+ private final int splitPointsMerge;
+
+ /**
+ * Maps the split point index X to Y where where that split point X would
+ * appear in the Y.cache.js
+ */
+ private final int[] splitPointToCodeIndexMap;
+
+ /**
* Maps a split-point number to a fragment number.
*
* splitPointToFragmmentMap[x] = y implies split point #x is in fragment #y.
@@ -571,9 +642,11 @@
private final int[] splitPointToFragmentMap;
private CodeSplitter2(TreeLogger logger, JProgram jprogram, JsProgram jsprogram,
- JavaToJavaScriptMap map, MultipleDependencyGraphRecorder dependencyRecorder) {
+ JavaToJavaScriptMap map, int splitPointsMerge,
+ MultipleDependencyGraphRecorder dependencyRecorder) {
this.jprogram = jprogram;
this.jsprogram = jsprogram;
+ this.splitPointsMerge = splitPointsMerge;
this.fragmentExtractor = new FragmentExtractor(jprogram, jsprogram, map);
this.initialLoadSequence = new LinkedHashSet<Integer>(jprogram.getSplitPointInitialSequence());
@@ -583,6 +656,11 @@
splitPointToFragmentMap[i] = i;
}
+ this.splitPointToCodeIndexMap = new int[jprogram.getRunAsyncs().size() + 1];
+ for (int i = 0; i < splitPointToCodeIndexMap.length; i++) {
+ splitPointToCodeIndexMap[i] = 0;
+ }
+
// TODO(acleung): I don't full understand this. This is mostly from the old
// algorithm which patches up certain dependency after the control flow analysis.
fieldToLiteralOfClass = buildFieldToClassLiteralMap(jprogram);
@@ -612,6 +690,31 @@
fragmentStats.put(splitPoint, stats);
}
+ private ControlFlowAnalyzer computeAllButNCfas(
+ ControlFlowAnalyzer liveAfterInitialSequence, List<Integer> sp) {
+ List<ControlFlowAnalyzer> allButOnes = new ArrayList<ControlFlowAnalyzer>();
+ ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(liveAfterInitialSequence);
+ for (JRunAsync otherRunAsync : jprogram.getRunAsyncs()) {
+ if (isInitial(otherRunAsync.getSplitPoint())) {
+ continue;
+ }
+ if (sp.contains(otherRunAsync.getSplitPoint())) {
+ continue;
+ }
+ cfa.traverseFromRunAsync(otherRunAsync);
+ }
+ return cfa;
+ }
+
+ /**
+ * Compute a CFA that covers the entire live code of the program.
+ */
+ private ControlFlowAnalyzer computeCompleteCfa() {
+ ControlFlowAnalyzer everything = new ControlFlowAnalyzer(jprogram);
+ everything.traverseEverything();
+ return everything;
+ }
+
private ControlFlowAnalyzer computeLiveSet(
ControlFlowAnalyzer initiallyLive, LiveSplitPointMap liveness, JRunAsync runAsync) {
// Control Flow Analysis from a split point.
@@ -628,7 +731,7 @@
// Step #1: Compute all the initially live atoms that are part of entry points
// class inits..etc.
- initiallyLive = computeInitiallyLive(jprogram, NULL_RECORDER);
+ initiallyLive = computeInitiallyLive(jprogram, CodeSplitter.NULL_RECORDER);
recordLiveSet(initiallyLive, liveness, 0);
// Step #2: Incrementally add each split point that are classified as initial load sequence.
@@ -656,7 +759,10 @@
partitionFragments();
// Step #6: Extract fragments using the partition algorithm.
- extractStatements(initiallyLive);
+ extractStatements(computeInitiallyLive(jprogram, CodeSplitter.NULL_RECORDER));
+
+ // Step #7: Replaces the splitpoint number with the new fragment number.
+ replaceFragmentId();
}
private void extractStatements(ControlFlowAnalyzer initiallyLive) {
@@ -670,29 +776,44 @@
addFragment(0, alreadyLoaded, liveNow, noStats, fragmentStats);
}
- final List<Predicate<BitSet>> exclusivePredicates = new LinkedList<Predicate<BitSet>>();
-
- // Signifies what has been already loaded in the initial fragment.
- LivenessPredicate alreadyLoaded = new LiveSplitPointsPredicate(liveness, new Predicate<BitSet>() {
- @Override
- public boolean apply(BitSet value) {
- // Live if it is used by the first fragment.
- if (value.get(0)) {
- return true;
- } else {
- for (int sp : initialLoadSequence) {
- if (value.get(sp)) {
- return true;
- }
- }
- }
- return false;
+ ControlFlowAnalyzer liveAfterInitialSequence = new ControlFlowAnalyzer(initiallyLive);
+
+ int cacheIndex = 1;
+ // Initial Split Point.
+ {
+ for (final int sp : initialLoadSequence) {
+ splitPointToCodeIndexMap[sp] = cacheIndex;
+ LivenessPredicate alreadyLoaded = new CfaLivenessPredicate(liveAfterInitialSequence);
+ ControlFlowAnalyzer liveAfterSp = new ControlFlowAnalyzer(liveAfterInitialSequence);
+ JRunAsync runAsync = jprogram.getRunAsyncs().get(sp - 1);
+ liveAfterSp.traverseFromRunAsync(runAsync);
+ LivenessPredicate liveNow = new CfaLivenessPredicate(liveAfterSp);
+ List<JsStatement> statsToAppend = fragmentExtractor.createOnLoadedCall(cacheIndex);
+ addFragment(sp, alreadyLoaded, liveNow, statsToAppend, fragmentStats);
+ liveAfterInitialSequence = liveAfterSp;
+ cacheIndex++;
}
- });
+ }
+ ControlFlowAnalyzer everything = computeCompleteCfa();
+ Set<JField> allFields = new HashSet<JField>();
+ Set<JMethod> allMethods = new HashSet<JMethod>();
+
+ for (JNode node : everything.getLiveFieldsAndMethods()) {
+ if (node instanceof JField) {
+ allFields.add((JField) node);
+ }
+ if (node instanceof JMethod) {
+ allMethods.add((JMethod) node);
+ }
+ }
+ allFields.addAll(everything.getFieldsWritten());
+
// Search for all the atoms that are exclusively needed in each split point.
for (int i = 1; i < splitPointToFragmentMap.length; i++) {
+ ArrayList<Integer> splitPoints = new ArrayList<Integer>();
+
// This mean split point [i] has been merged with another split point, ignore it.
if (splitPointToFragmentMap[i] != i) {
continue;
@@ -703,64 +824,54 @@
continue;
}
- // Creates a mask that is used to check if an atom is exclusively needed by the current
- // fragment as well as all the fragment is was merged with.
- final BitSet mask = new BitSet(splitPointToFragmentMap.length);
- mask.set(i);
+ splitPoints.add(i);
+ splitPointToCodeIndexMap[i] = cacheIndex;
+
+
for (int j = i + 1; j < splitPointToFragmentMap.length; j++) {
if (initialLoadSequence.contains(j)) {
continue;
}
if (splitPointToFragmentMap[j] == i) {
- mask.set(j);
+ splitPointToCodeIndexMap[j] = cacheIndex;
+ splitPoints.add(j);
}
}
-
- // Creates a predicate
- final Predicate<BitSet> pred = new Predicate<BitSet>() {
- @Override
- public boolean apply(BitSet value) {
- if (value.get(0)) {
- return true;
- }
- for (int sp : initialLoadSequence) {
- if (value.get(sp)) {
- return true;
- }
- }
-
- BitSet valueOrMask = (BitSet) value.clone();
- valueOrMask.or(mask);
-
- // Returns true if it at least matches one of the set field in the mask but not
- // having a field set that is unset in the mask.
- return value.intersects(mask) && valueOrMask.cardinality() <= mask.cardinality();
- }
- };
- exclusivePredicates.add(pred);
+ ControlFlowAnalyzer allButOne = computeAllButNCfas(liveAfterInitialSequence, splitPoints);
+ Set<JNode> allLiveNodes =
+ union(allButOne.getLiveFieldsAndMethods(), allButOne.getFieldsWritten());
+ updateReverseMap(i, fragmentMap.fields, allLiveNodes, allFields);
+ updateReverseMap(i, fragmentMap.methods, allButOne.getLiveFieldsAndMethods(), allMethods);
+ updateReverseMap(i, fragmentMap.strings, allButOne.getLiveStrings(), everything
+ .getLiveStrings());
+ updateReverseMap(i, fragmentMap.types, declaredTypesIn(allButOne.getInstantiatedTypes()),
+ declaredTypesIn(everything.getInstantiatedTypes()));
+
+ // This mean split point [i] has been merged with another split point, ignore it.
+ if (splitPointToFragmentMap[i] != i) {
+ continue;
+ }
- LivenessPredicate liveNow = new LiveSplitPointsPredicate(liveness, pred);
- List<JsStatement> statsToAppend = fragmentExtractor.createOnLoadedCall(i);
+ // This was needed in the initial load sequence, ignore it.
+ if (initialLoadSequence.contains(i)) {
+ continue;
+ }
+
+ LivenessPredicate alreadyLoaded = new ExclusivityMapLivenessPredicate(fragmentMap, 0);
+ LivenessPredicate liveNow = new ExclusivityMapLivenessPredicate(fragmentMap, i);
+ List<JsStatement> statsToAppend = fragmentExtractor.createOnLoadedCall(cacheIndex);
addFragment(i, alreadyLoaded, liveNow, statsToAppend, fragmentStats);
+ cacheIndex++;
}
-
- // Left over fragments.
- {
- LivenessPredicate liveNow = new LiveSplitPointsPredicate(liveness, new Predicate<BitSet>() {
- @Override
- public boolean apply(BitSet value) {
- for (Predicate<BitSet> p : exclusivePredicates) {
- if (p.apply(value)) {
- return false;
- }
- }
- return true;
- }
- });
-
- List<JsStatement> statsToAppend = fragmentExtractor.createOnLoadedCall(splitPointToFragmentMap.length);
+ /*
+ * Compute the leftovers fragment.
+ */
+ {
+ LivenessPredicate alreadyLoaded = new CfaLivenessPredicate(liveAfterInitialSequence);
+ LivenessPredicate liveNow = new ExclusivityMapLivenessPredicate(fragmentMap, 0);
+ List<JsStatement> statsToAppend = fragmentExtractor.createOnLoadedCall(cacheIndex);
addFragment(splitPointToFragmentMap.length, alreadyLoaded, liveNow, statsToAppend, fragmentStats);
}
@@ -773,16 +884,17 @@
fragBlock.getStatements().addAll(fragmentStats.get(i));
}
- // TODO(acleung): This is an hack-ish way to put share the fragment map.
- // jprogram.splitPointToFragmentMap = splitPointToFragmentMap;
+ jprogram.setFragmentPartitioningResult(
+ new FragmentPartitioningResult(splitPointToCodeIndexMap, fragmentStats.size()));
}
-
+
private void fixUpLoadOrderDependencies(LiveSplitPointMap fragmentMap, int splitPoint) {
fixUpLoadOrderDependenciesForMethods(fragmentMap, splitPoint);
fixUpLoadOrderDependenciesForTypes(fragmentMap, splitPoint);
fixUpLoadOrderDependenciesForClassLiterals(fragmentMap, splitPoint);
fixUpLoadOrderDependenciesForFieldsInitializedToStrings(fragmentMap, splitPoint);
}
+
private void fixUpLoadOrderDependenciesForClassLiterals(LiveSplitPointMap fragmentMap, int splitPoint) {
int numClassLitStrings = 0;
int numFixups = 0;
@@ -825,7 +937,6 @@
}
}
}
-
private void fixUpLoadOrderDependenciesForMethods(LiveSplitPointMap fragmentMap, int splitPoint) {
int numFixups = 0;
@@ -870,6 +981,10 @@
}
}
}
+
+ private boolean isInitial(int entry) {
+ return initialLoadSequence.contains(entry);
+ }
/**
* We haves pinned down that fragment partition is an NP-Complete problem that maps right to
@@ -895,7 +1010,7 @@
countShardedAtomsOfType(liveness.strings, matrix);
countShardedAtomsOfType(liveness.types, matrix);
- for (int c = 0; c < NUM_SPLITPOINTS_TO_MERGE; c++) {
+ for (int c = 0; c < splitPointsMerge; c++) {
int bestI = 0, bestJ = 0, max = 0;
for (int i = 1; i < splitPointToFragmentMap.length; i++) {
if (initialLoadSequence.contains(i)) {
@@ -931,7 +1046,21 @@
}
}
}
-
+
+ private void replaceFragmentId() {
+ (new JsModVisitor() {
+ @Override
+ public void endVisit(JsNumericEntry x, JsContext ctx) {
+ if (x.getKey().equals("RunAsyncFragmentIndex")) {
+ x.setValue(splitPointToCodeIndexMap[x.getValue()]);
+ }
+ if (x.getKey().equals("RunAsyncFragmentCount")) {
+ x.setValue(jsprogram.getFragmentCount() - 1);
+ }
+ }
+ }).accept(jsprogram);
+ }
+
/**
* Traverse <code>exp</code> and find all string literals within it.
*/
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 7120ab7..8c42325 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
@@ -15,10 +15,10 @@
*/
package com.google.gwt.dev.jjs.impl;
-import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.linker.CastableTypeMap;
import com.google.gwt.core.ext.linker.impl.StandardCastableTypeMap;
import com.google.gwt.core.ext.linker.impl.StandardSymbolData;
+import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.dev.jjs.HasSourceInfo;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.JsOutputOption;
@@ -68,6 +68,7 @@
import com.google.gwt.dev.jjs.ast.JNewArray;
import com.google.gwt.dev.jjs.ast.JNewInstance;
import com.google.gwt.dev.jjs.ast.JNode;
+import com.google.gwt.dev.jjs.ast.JNumericEntry;
import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JParameterRef;
import com.google.gwt.dev.jjs.ast.JPostfixOperation;
@@ -130,6 +131,7 @@
import com.google.gwt.dev.js.ast.JsNormalScope;
import com.google.gwt.dev.js.ast.JsNullLiteral;
import com.google.gwt.dev.js.ast.JsNumberLiteral;
+import com.google.gwt.dev.js.ast.JsNumericEntry;
import com.google.gwt.dev.js.ast.JsObjectLiteral;
import com.google.gwt.dev.js.ast.JsParameter;
import com.google.gwt.dev.js.ast.JsPostfixOperation;
@@ -1248,7 +1250,12 @@
popList(newOp.getArguments(), x.getArgs().size()); // args
push(newOp);
}
-
+
+ @Override
+ public void endVisit(JNumericEntry x, Context ctx) {
+ push(new JsNumericEntry(x.getSourceInfo(), x.getKey(), x.getValue()));
+ }
+
@Override
public void endVisit(JParameter x, Context ctx) {
push(new JsParameter(x.getSourceInfo(), names.get(x)));
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 3543d4e..88cfd1c 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
@@ -264,7 +264,7 @@
// The target method was already pruned (TypeTightener will fix this).
return;
}
-
+
// Let's do it!
toBeMadeStatic.add(method);
}
@@ -288,7 +288,8 @@
public void endVisit(JMethodCall x, Context ctx) {
JMethod oldMethod = x.getTarget();
JMethod newMethod = program.getStaticImpl(oldMethod);
- if (newMethod == null || x.canBePolymorphic()) {
+
+ if (newMethod == null || x.canBePolymorphic()) {
return;
}
@@ -302,6 +303,7 @@
*/
return;
}
+
ctx.replaceMe(makeStaticCall(x, newMethod));
}
@@ -313,7 +315,7 @@
@Override
public boolean visit(JProgram x, Context ctx) {
- initiallyLive = CodeSplitter.computeInitiallyLive(x);
+ initiallyLive = CodeSplitter2.computeInitiallyLive(x);
return true;
}
}
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 7d50f49..2e9222a 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,10 +24,10 @@
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.JNumericEntry;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
@@ -100,7 +100,7 @@
JMethod runAsyncMethod = program.getIndexedMethod("AsyncFragmentLoader.runAsync");
assert runAsyncMethod != null;
JMethodCall runAsyncCall = new JMethodCall(info, null, runAsyncMethod);
- runAsyncCall.addArg(JIntLiteral.get(splitPoint));
+ runAsyncCall.addArg(new JNumericEntry(info, "RunAsyncFragmentIndex", splitPoint));
runAsyncCall.addArg(asyncCallback);
JReferenceType callbackType = (JReferenceType) asyncCallback.getType();
@@ -188,7 +188,7 @@
JMethodCall newCall =
new JMethodCall(info, null, program
.getIndexedMethod("RunAsyncCode.forSplitPointNumber"));
- newCall.addArg(program.getLiteralInt(splitPoint));
+ newCall.addArg(new JNumericEntry(info, "RunAsyncFragmentIndex", splitPoint));
ctx.replaceMe(newCall);
}
}
@@ -269,6 +269,7 @@
private void setNumEntriesInAsyncFragmentLoader(int entryCount) {
JMethodCall constructorCall = getBrowserLoaderConstructor(program);
assert constructorCall.getArgs().get(0).getType() == JPrimitiveType.INT;
- constructorCall.setArg(0, program.getLiteralInt(entryCount));
+ constructorCall.setArg(0,
+ new JNumericEntry(constructorCall.getSourceInfo(), "RunAsyncFragmentCount", entryCount));
}
}
diff --git a/dev/core/src/com/google/gwt/dev/js/JsHoister.java b/dev/core/src/com/google/gwt/dev/js/JsHoister.java
index 2917644..22ebc87 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsHoister.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsHoister.java
@@ -30,6 +30,7 @@
import com.google.gwt.dev.js.ast.JsNew;
import com.google.gwt.dev.js.ast.JsNullLiteral;
import com.google.gwt.dev.js.ast.JsNumberLiteral;
+import com.google.gwt.dev.js.ast.JsNumericEntry;
import com.google.gwt.dev.js.ast.JsObjectLiteral;
import com.google.gwt.dev.js.ast.JsPostfixOperation;
import com.google.gwt.dev.js.ast.JsPrefixOperation;
@@ -172,6 +173,11 @@
public void endVisit(JsNumberLiteral x, JsContext ctx) {
stack.push(x);
}
+
+ @Override
+ public void endVisit(JsNumericEntry x, JsContext ctx) {
+ stack.push(x);
+ }
@Override
public void endVisit(JsObjectLiteral x, JsContext ctx) {
diff --git a/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
index 564191c..a10de34 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
@@ -48,6 +48,7 @@
import com.google.gwt.dev.js.ast.JsNew;
import com.google.gwt.dev.js.ast.JsNullLiteral;
import com.google.gwt.dev.js.ast.JsNumberLiteral;
+import com.google.gwt.dev.js.ast.JsNumericEntry;
import com.google.gwt.dev.js.ast.JsObjectLiteral;
import com.google.gwt.dev.js.ast.JsOperator;
import com.google.gwt.dev.js.ast.JsParameter;
@@ -781,6 +782,12 @@
}
return false;
}
+
+ @Override
+ public boolean visit(JsNumericEntry x, JsContext ctx) {
+ p.print(Integer.toString(x.getValue()));
+ return false;
+ }
@Override
public boolean visit(JsObjectLiteral x, JsContext ctx) {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsNumericEntry.java b/dev/core/src/com/google/gwt/dev/js/ast/JsNumericEntry.java
new file mode 100644
index 0000000..68e0025
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsNumericEntry.java
@@ -0,0 +1,70 @@
+/*
+ * 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.js.ast;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+/**
+ * Represent an index that can be replacable by the compiler at compile time.
+ */
+public final class JsNumericEntry extends JsExpression {
+ private final String key;
+ private int value;
+
+ public JsNumericEntry(SourceInfo info, String key, int value) {
+ super(info);
+ this.key = key;
+ this.value = value;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public NodeKind getKind() {
+ return NodeKind.NUMBER;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean hasSideEffects() {
+ return false;
+ }
+
+ @Override
+ public boolean isDefinitelyNotNull() {
+ return true;
+ }
+
+ @Override
+ public boolean isDefinitelyNull() {
+ return false;
+ }
+
+ public void setValue(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public void traverse(JsVisitor v, JsContext ctx) {
+ v.visit(this, ctx);
+ v.endVisit(this, ctx);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
index ba5396c..77e27fb 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
@@ -202,6 +202,9 @@
public void endVisit(JsNumberLiteral x, JsContext ctx) {
}
+
+ public void endVisit(JsNumericEntry x, JsContext ctx) {
+ }
public void endVisit(JsObjectLiteral x, JsContext ctx) {
}
@@ -361,6 +364,10 @@
public boolean visit(JsNumberLiteral x, JsContext ctx) {
return true;
}
+
+ public boolean visit(JsNumericEntry x, JsContext ctx) {
+ return true;
+ }
public boolean visit(JsObjectLiteral x, JsContext ctx) {
return true;
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerFragmentMerge.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerFragmentMerge.java
new file mode 100644
index 0000000..fde83e1
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerFragmentMerge.java
@@ -0,0 +1,50 @@
+/*
+ * 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.util.arg;
+
+import com.google.gwt.util.tools.ArgHandlerInt;
+
+/**
+ * An ArgHandler to provide the -XfragmentMerge flag.
+ */
+public class ArgHandlerFragmentMerge extends ArgHandlerInt {
+
+ private final OptionFragmentsMerge option;
+
+ public ArgHandlerFragmentMerge(OptionFragmentsMerge option) {
+ this.option = option;
+ }
+
+ @Override
+ public String getPurpose() {
+ return "EXPERIMENTAL: Enables Fragment merging code splitter.";
+ }
+
+ @Override
+ public String getTag() {
+ return "-XfragmentMerge";
+ }
+
+ @Override
+ public String[] getTagArgs() {
+ return new String[] {"numFragments"};
+ }
+
+ @Override
+ public void setInt(int value) {
+ option.setFragmentsMerge(value);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionFragmentsMerge.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionFragmentsMerge.java
new file mode 100644
index 0000000..42d4ab1
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionFragmentsMerge.java
@@ -0,0 +1,35 @@
+/*
+ * 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.util.arg;
+
+/**
+ * Enable the new code splitter that auto-partitions.
+ */
+public interface OptionFragmentsMerge {
+
+ // TODO(acleung): This is currently an experimental frag. We should find a
+ // use case new splitter. Some possible approache:
+ //
+ // 1. Magically decide the number of fragments to merge. (May be too hard)
+ // 2. All the user to specify number of fragments they want to *keep* instead
+ // of the number they want to merge.
+ // 3. Ask the user what is the max (average) size of fragments. (This
+ // can only be an estimated.
+
+ int getFragmentsMerge();
+
+ void setFragmentsMerge(int numFragments);
+}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitter2Test.java b/dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitter2Test.java
index 9fa40e1..b14b839 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitter2Test.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitter2Test.java
@@ -217,7 +217,7 @@
JavaToJavaScriptMap map = GenerateJavaScriptAST.exec(
jProgram, jsProgram, JsOutputOption.PRETTY, symbolTable, new PropertyOracle[]{
new StaticPropertyOracle(orderedProps, orderedPropValues, configProps)});
- CodeSplitter2.exec(logger, jProgram, jsProgram, map, null);
+ CodeSplitter2.exec(logger, jProgram, jsProgram, map, 4, null);
}
private static String createRunAsync(String body) {