Ray Cromwell has uploaded a new change for review.
https://gwt-review.googlesource.com/2581
Change subject: Adds new configuration property to specify minimum fragment
size.
......................................................................
Adds new configuration property to specify minimum fragment size.
<set-configuration-property name="compiler.splitpoint.leftovermerge.size"
value="size in bytes"/>
Any exclusive fragment smaller than this limit will be merged into the left
overs fragment.
Change-Id: Ifcf035d88db70e890c739eab3997b85fa2e5677e
---
M dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
M dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
M dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter2.java
M
dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
M dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
M dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitter2Test.java
M user/src/com/google/gwt/core/CompilerParameters.gwt.xml
7 files changed, 267 insertions(+), 27 deletions(-)
diff --git a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
index c0d6ebb..3f57ee5 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
@@ -597,8 +597,8 @@
options.produceReferenceInfo = true;
// Turn off all warnings, saves some memory / speed.
- options.reportUnusedDeclaredThrownExceptionIncludeDocCommentReference
= false;
- options.reportUnusedDeclaredThrownExceptionExemptExceptionAndThrowable
= false;
+//
options.reportUnusedDeclaredThrownExceptionIncludeDocCommentReference =
false;
+//
options.reportUnusedDeclaredThrownExceptionExemptExceptionAndThrowable =
false;
options.warningThreshold = 0;
options.inlineJsrBytecode = true;
return options;
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 b7ccabb..197e6a3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -400,7 +400,9 @@
// merging.
if (fragmentsMerge > 0) {
CodeSplitter2.exec(logger, jprogram, jsProgram, jjsmap,
fragmentsMerge,
- chooseDependencyRecorder(options.isSoycEnabled(), baos));
+ chooseDependencyRecorder(options.isSoycEnabled(), baos),
+ findIntegerConfigurationProperty(propertyOracles, logger,
+ CodeSplitter2.LEFTOVERMERGE_SIZE, 0));
} else {
CodeSplitter.exec(logger, jprogram, jsProgram, jjsmap,
chooseDependencyRecorder(options
.isSoycEnabled(), baos));
@@ -569,6 +571,25 @@
return toReturn;
}
+ /**
+ * Look for a configuration property in all property oracles.
+ */
+ public static int findIntegerConfigurationProperty(
+ PropertyOracle[] propertyOracles, TreeLogger logger,
+ String name, int def) {
+ int toReturn = def;
+ for (PropertyOracle oracle : propertyOracles) {
+ try {
+ com.google.gwt.core.ext.ConfigurationProperty property =
oracle.getConfigurationProperty(name);
+ toReturn = Integer.parseInt(property.getValues().get(0));
+ } catch (Exception e) {
+ break;
+ }
+ }
+ return toReturn;
+ }
+
+
public static UnifiedAst precompile(TreeLogger logger, ModuleDef module,
RebindPermutationOracle rpo, String[] declEntryPts, String[]
additionalRootTypes,
JJSOptions options, boolean singlePermutation) throws
UnableToCompleteException {
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 2c871f4..897464d 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
@@ -43,6 +43,7 @@
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.JsToStringGenerationVisitor;
import com.google.gwt.dev.js.ast.JsBlock;
import com.google.gwt.dev.js.ast.JsContext;
import com.google.gwt.dev.js.ast.JsModVisitor;
@@ -50,6 +51,7 @@
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.util.JsniRef;
+import com.google.gwt.dev.util.TextOutput;
import com.google.gwt.dev.util.collect.HashMap;
import com.google.gwt.dev.util.collect.Lists;
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
@@ -277,7 +279,10 @@
* The property key for a list of initially loaded split points.
*/
private static final String PROP_INITIAL_SEQUENCE
= "compiler.splitpoint.initial.sequence";
-
+
+ public static final String LEFTOVERMERGE_SIZE =
+ "compiler.splitpoint.leftovermerge.size";
+
public static ControlFlowAnalyzer computeInitiallyLive(JProgram
jprogram) {
return computeInitiallyLive(jprogram, CodeSplitter.NULL_RECORDER);
}
@@ -295,16 +300,19 @@
return cfa;
}
- public static void exec(TreeLogger logger, JProgram jprogram, JsProgram
jsprogram,
+ public static void exec(TreeLogger logger, JProgram jprogram,
+ JsProgram jsprogram,
JavaToJavaScriptMap map, int fragmentsToMerge,
- MultipleDependencyGraphRecorder dependencyRecorder) {
+ MultipleDependencyGraphRecorder dependencyRecorder,
+ int leftOverMergeLimit) {
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, fragmentsToMerge,
dependencyRecorder).execImpl();
+ logger, jprogram, jsprogram, map, fragmentsToMerge,
+ dependencyRecorder, leftOverMergeLimit).execImpl();
codeSplitterEvent.end();
}
@@ -648,7 +656,8 @@
* Number of split points to merge.
*/
private final int splitPointsMerge;
-
+ private int leftOverMergeLimit;
+
/**
* Maps the split point index X to Y where where that split point X would
* appear in the Y.cache.js
@@ -665,12 +674,15 @@
*/
private final int[] splitPointToFragmentMap;
- private CodeSplitter2(TreeLogger logger, JProgram jprogram, JsProgram
jsprogram,
+ private CodeSplitter2(TreeLogger logger, JProgram jprogram,
+ JsProgram jsprogram,
JavaToJavaScriptMap map, int splitPointsMerge,
- MultipleDependencyGraphRecorder dependencyRecorder) {
+ MultipleDependencyGraphRecorder dependencyRecorder,
+ int leftOverMergeLimit) {
this.jprogram = jprogram;
this.jsprogram = jsprogram;
this.splitPointsMerge = splitPointsMerge;
+ this.leftOverMergeLimit = leftOverMergeLimit;
this.fragmentExtractor = new FragmentExtractor(jprogram, jsprogram,
map);
this.initialLoadSequence = new
LinkedHashSet<Integer>(jprogram.getSplitPointInitialSequence());
@@ -713,7 +725,100 @@
stats.addAll(stmtsToAppend);
fragmentStats.put(splitPoint, stats);
}
-
+
+ private boolean fragmentSizeBelowMergeLimit(List<JsStatement> stats,
+ final int leftOverMergeLimit) {
+ int sizeInBytes = 0;
+ TextOutput out = new TextOutput() {
+ int count = 0;
+
+ @Override
+ public int getColumn() {
+ return 0;
+ }
+
+ @Override
+ public int getLine() {
+ return 0;
+ }
+
+ @Override
+ public int getPosition() {
+ return count;
+ }
+
+ @Override
+ public void indentIn() {
+ }
+
+ @Override
+ public void indentOut() {
+ }
+
+ @Override
+ public void newline() {
+ inc(1);
+ }
+
+ @Override
+ public void newlineOpt() {
+ }
+
+ @Override
+ public void print(char c) {
+ inc(1);
+ }
+
+ @Override
+ public void print(char[] s) {
+ inc(s.length);
+ }
+
+ @Override
+ public void print(String s) {
+ inc(s.length());
+ }
+
+ private void inc(int length) {
+ count += length;
+ if (count >= leftOverMergeLimit) {
+ // yucky, but necessary, early exit
+ throw new MergeLimitExceededException();
+ }
+ }
+
+ @Override
+ public void printOpt(char c) {
+ }
+
+ @Override
+ public void printOpt(char[] s) {
+ }
+
+ @Override
+ public void printOpt(String s) {
+ }
+ };
+
+ try {
+ JsToStringGenerationVisitor v = new JsToStringGenerationVisitor(out);
+ for (JsStatement stat : stats) {
+ v.accept(stat);
+ }
+ sizeInBytes += out.getPosition();
+ } catch (InternalCompilerException me) {
+ if (me.getCause().getClass() == MergeLimitExceededException.class) {
+ return false;
+ } else {
+ throw me;
+ }
+ }
+ return sizeInBytes < leftOverMergeLimit;
+ }
+
+ private static class MergeLimitExceededException extends
RuntimeException {
+ }
+
private ControlFlowAnalyzer computeAllButNCfas(
ControlFlowAnalyzer liveAfterInitialSequence, List<Integer> sp) {
List<ControlFlowAnalyzer> allButOnes = new
ArrayList<ControlFlowAnalyzer>();
@@ -867,7 +972,8 @@
}
}
allFields.addAll(everything.getFieldsWritten());
-
+ ArrayList<JsStatement> leftOverMergeStats = new
ArrayList<JsStatement>();
+
// Search for all the atoms that are exclusively needed in each split
point.
for (int i = 1; i < splitPointToFragmentMap.length; i++) {
@@ -925,9 +1031,25 @@
LivenessPredicate alreadyLoaded = new
ExclusivityMapLivenessPredicate(fragmentMap, 0);
LivenessPredicate liveNow = new
ExclusivityMapLivenessPredicate(fragmentMap, i);
- List<JsStatement> statsToAppend =
fragmentExtractor.createOnLoadedCall(cacheIndex);
- addFragment(i, alreadyLoaded, liveNow, statsToAppend, fragmentStats);
+ List<JsStatement> exclusiveStats =
fragmentExtractor.extractStatements(liveNow, alreadyLoaded);
+ if (fragmentSizeBelowMergeLimit(exclusiveStats, leftOverMergeLimit))
{
+ leftOverMergeStats.addAll(exclusiveStats);
+ // merged to leftovers
+ splitPointToFragmentMap[i] = -1;
+ continue;
+ } else {
+ List<JsStatement> statsToAppend =
fragmentExtractor.createOnLoadedCall(cacheIndex);
+ addFragment(i, alreadyLoaded, liveNow, statsToAppend,
fragmentStats);
+ }
cacheIndex++;
+ }
+
+
+ for (int i = 0; i < splitPointToFragmentMap.length; i++) {
+ if (splitPointToFragmentMap[i] == -1) {
+ // set fragment number -1 to be leftovers fragment number
+ splitPointToFragmentMap[i] = splitPointToFragmentMap.length;
+ }
}
/*
@@ -937,7 +1059,8 @@
LivenessPredicate alreadyLoaded = new
CfaLivenessPredicate(liveAfterInitialSequence);
LivenessPredicate liveNow = new
ExclusivityMapLivenessPredicate(fragmentMap, 0);
List<JsStatement> statsToAppend =
fragmentExtractor.createOnLoadedCall(cacheIndex);
- addFragment(splitPointToFragmentMap.length, alreadyLoaded, liveNow,
statsToAppend, fragmentStats);
+ leftOverMergeStats.addAll(statsToAppend);
+ addFragment(splitPointToFragmentMap.length, alreadyLoaded, liveNow,
leftOverMergeStats, fragmentStats);
}
// now install the new statements in the program fragments
diff --git
a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
index e471cc3..d7c6953 100644
---
a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
+++
b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
@@ -28,7 +28,6 @@
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsProgramFragment;
-import com.google.gwt.dev.js.ast.JsSeedIdOf;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.js.ast.JsVars.JsVar;
import com.google.gwt.dev.js.ast.JsVisitable;
@@ -82,12 +81,6 @@
public boolean visit(JsProgramFragment x, JsContext ctx) {
// Descend naturally.
return true;
- }
-
- @Override
- public boolean visit(JsSeedIdOf x, JsContext ctx) {
- out.print(String.valueOf(x.getSeedId()));
- return false;
}
@Override
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 a10de34..ed1f569 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
@@ -59,6 +59,7 @@
import com.google.gwt.dev.js.ast.JsPropertyInitializer;
import com.google.gwt.dev.js.ast.JsRegExp;
import com.google.gwt.dev.js.ast.JsReturn;
+import com.google.gwt.dev.js.ast.JsSeedIdOf;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.js.ast.JsStringLiteral;
import com.google.gwt.dev.js.ast.JsSwitch;
@@ -896,6 +897,12 @@
}
@Override
+ public boolean visit(JsSeedIdOf x, JsContext ctx) {
+ p.print(String.valueOf(x.getSeedId()));
+ return false;
+ }
+
+ @Override
public boolean visit(JsStringLiteral x, JsContext ctx) {
printStringLiteral(x.getValue());
return false;
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 162e3d8..105b169 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
@@ -15,9 +15,6 @@
*/
package com.google.gwt.dev.jjs.impl;
-import java.util.Map;
-import java.util.TreeMap;
-
import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.SymbolData;
@@ -38,6 +35,9 @@
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsVisitor;
+
+import java.util.Map;
+import java.util.TreeMap;
/**
* Unit test for {@link CodeSplitter2}.
@@ -104,7 +104,63 @@
// functionC must be in the initial fragment.
assertInFragment("functionC", 0);
}
-
+
+ public void testMergeLeftOvers() throws UnableToCompleteException {
+ StringBuffer code = new StringBuffer();
+ code.append("package test;\n");
+ code.append("import com.google.gwt.core.client.GWT;\n");
+ code.append("import com.google.gwt.core.client.RunAsyncCallback;\n");
+ code.append("public class EntryPoint {\n");
+ code.append(functionA);
+ code.append(functionB);
+ code.append(functionC);
+ code.append(" public static void onModuleLoad() {\n");
+ // Fragment #1
+ code.append(createRunAsync("functionA();"));
+ // Fragment #2
+ code.append(createRunAsync("functionB();"));
+ // Fragment #3
+ code.append(createRunAsync("functionC();"));
+ code.append(" }\n");
+ code.append("}\n");
+ compileSnippetWithLeftoverMerge(code.toString(),
+ 10 * 1024 /* 10k minumum */);
+
+ // init + leftover.
+ assertFragmentCount(2);
+ assertInFragment("functionA", 1);
+ assertInFragment("functionB", 1);
+ assertInFragment("functionC", 1);
+ }
+
+ public void testDontMergeLeftOvers() throws UnableToCompleteException {
+ StringBuffer code = new StringBuffer();
+ code.append("package test;\n");
+ code.append("import com.google.gwt.core.client.GWT;\n");
+ code.append("import com.google.gwt.core.client.RunAsyncCallback;\n");
+ code.append("public class EntryPoint {\n");
+ code.append(functionA);
+ code.append(functionB);
+ code.append(functionC);
+ code.append(" public static void onModuleLoad() {\n");
+ // Fragment #1
+ code.append(createRunAsync("functionA();"));
+ // Fragment #2
+ code.append(createRunAsync("functionB();"));
+ // Fragment #3
+ code.append(createRunAsync("functionC();"));
+ code.append(" }\n");
+ code.append("}\n");
+ // we want don't want them to be merged
+ compileSnippetWithLeftoverMerge(code.toString(), 10);
+
+ // init + leftover.
+ assertFragmentCount(5);
+ assertNotInFragment("functionA", 4);
+ assertNotInFragment("functionB", 4);
+ assertNotInFragment("functionC", 4);
+ }
+
public void testNoMergeMoreThanTwo() throws UnableToCompleteException {
StringBuffer code = new StringBuffer();
code.append("package test;\n");
@@ -217,8 +273,41 @@
JavaToJavaScriptMap map = GenerateJavaScriptAST.exec(
jProgram, jsProgram, JsOutputOption.PRETTY, symbolTable, new
PropertyOracle[]{
new StaticPropertyOracle(orderedProps, orderedPropValues,
configProps)}).getLeft();
- CodeSplitter2.exec(logger, jProgram, jsProgram, map, 4, null);
+ CodeSplitter2.exec(logger, jProgram, jsProgram, map, 4, null, 0);
}
+
+ /**
+ * Compiles a Java class <code>test.EntryPoint</code> and use the code
splitter on it
+ * with leftover merge enabled.
+ */
+ protected void compileSnippetWithLeftoverMerge(final String code,
+ int mergeLimit) throws UnableToCompleteException {
+ addMockIntrinsic();
+ sourceOracle.addOrReplace(new MockJavaResource("test.EntryPoint") {
+ @Override
+ public CharSequence getContent() {
+ return code;
+ }
+ });
+ addBuiltinClasses(sourceOracle);
+ CompilationState state =
+ CompilationStateBuilder.buildFrom(logger,
sourceOracle.getResources(),
+ getAdditionalTypeProviderDelegate());
+ jProgram =
+ JavaAstConstructor.construct(logger, state, "test.EntryPoint",
+ "com.google.gwt.lang.Exceptions");
+ jProgram.addEntryMethod(findMethod(jProgram, "onModuleLoad"));
+ CastNormalizer.exec(jProgram, false);
+ ArrayNormalizer.exec(jProgram);
+ Map<StandardSymbolData, JsName> symbolTable =
+ new TreeMap<StandardSymbolData, JsName>(new
SymbolData.ClassIdentComparator());
+ JavaToJavaScriptMap map = GenerateJavaScriptAST.exec(
+ jProgram, jsProgram, JsOutputOption.PRETTY, symbolTable, new
PropertyOracle[]{
+ new StaticPropertyOracle(orderedProps, orderedPropValues,
configProps)}).getLeft();
+ CodeSplitter2.exec(logger, jProgram, jsProgram, map, 4, null,
+ mergeLimit);
+ }
+
private static String createRunAsync(String body) {
return "GWT.runAsync(new RunAsyncCallback() {" +
diff --git a/user/src/com/google/gwt/core/CompilerParameters.gwt.xml
b/user/src/com/google/gwt/core/CompilerParameters.gwt.xml
index 81435a2..72acb09 100644
--- a/user/src/com/google/gwt/core/CompilerParameters.gwt.xml
+++ b/user/src/com/google/gwt/core/CompilerParameters.gwt.xml
@@ -23,6 +23,13 @@
is-multi-valued='true' />
<!--
+ A user-specified limit in bytes where fragments smaller than the limit
are
+ rolled into the leftovers fragment.
+ -->
+ <define-configuration-property
name='compiler.splitpoint.leftovermerge.size'
+ is-multi-valued='false' />
+
+ <!--
Whether or not the compiler should predeclare variables that are
defined
outside the initial download and are referenced from a different code
fragment than the one defining them. This is usually determined by
which
--
To view, visit https://gwt-review.googlesource.com/2581
To unsubscribe, visit https://gwt-review.googlesource.com/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ifcf035d88db70e890c739eab3997b85fa2e5677e
Gerrit-PatchSet: 1
Gerrit-Project: gwt
Gerrit-Branch: master
Gerrit-Owner: Ray Cromwell <cromwell...@google.com>
--
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors
---
You received this message because you are subscribed to the Google Groups "Google Web Toolkit Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to google-web-toolkit-contributors+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.