Revision: 10224
Author: [email protected]
Date: Wed May 25 07:29:58 2011
Log: Implemented expression range shifting for IE block size
transformation. Moved expression range shifting for both transformations
inside the transformation classes. Modified constructors/instance variables
for the JsAbstractTextTransformer to support updating the expression ranges
in the SourceInfo map.
Review at http://gwt-code-reviews.appspot.com/1451801
Review by: [email protected]
http://code.google.com/p/google-web-toolkit/source/detail?r=10224
Modified:
/trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JsAbstractTextTransformer.java
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JsFunctionClusterer.java
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JsIEBlockTextTransformer.java
/trunk/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
/trunk/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
Tue May 24 12:34:23 2011
+++
/trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
Wed May 25 07:29:58 2011
@@ -86,6 +86,7 @@
import com.google.gwt.dev.jjs.impl.HandleCrossFragmentReferences;
import com.google.gwt.dev.jjs.impl.ImplementClassLiteralsAsFields;
import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
+import com.google.gwt.dev.jjs.impl.JsAbstractTextTransformer;
import com.google.gwt.dev.jjs.impl.JsFunctionClusterer;
import com.google.gwt.dev.jjs.impl.JsIEBlockTextTransformer;
import com.google.gwt.dev.jjs.impl.JsoDevirtualizer;
@@ -1034,41 +1035,53 @@
for (int i = 0; i < js.length; i++) {
DefaultTextOutput out = new
DefaultTextOutput(options.getOutput().shouldMinimize());
JsSourceGenerationVisitorWithSizeBreakdown v;
+
if (sourceInfoMaps != null) {
v = new JsReportGenerationVisitor(out, jjsMap);
} else {
v = new JsSourceGenerationVisitorWithSizeBreakdown(out, jjsMap);
}
v.accept(jsProgram.getFragmentBlock(i));
-
+
+ StatementRanges statementRanges = v.getStatementRanges();
+ String code = out.toString();
+ Map<Range, SourceInfo> infoMap = (sourceInfoMaps != null) ?
v.getSourceInfoMap() : null;
+
+ JsAbstractTextTransformer transformer =
+ new JsAbstractTextTransformer(code, statementRanges, infoMap) {
+ @Override public void exec() { }
+ @Override protected void updateSourceInfoMap() { }
+ };
+
/**
* Reorder function decls to improve compression ratios. Also
restructures
* the top level blocks into sub-blocks if they exceed 32767
statements.
*/
Event functionClusterEvent =
SpeedTracerLogger.start(CompilerEventType.FUNCTION_CLUSTER);
- JsFunctionClusterer clusterer =
- new JsFunctionClusterer(out.toString(), v.getStatementRanges());
// only cluster for obfuscated mode
if (options.isAggressivelyOptimize() && options.getOutput() ==
JsOutputOption.OBFUSCATED) {
- clusterer.exec();
+ transformer = new JsFunctionClusterer(transformer);
+ transformer.exec();
}
functionClusterEvent.end();
+
// rewrite top-level blocks to limit the number of statements
- JsIEBlockTextTransformer ieXformer = new
JsIEBlockTextTransformer(clusterer);
if (splitBlocks) {
- ieXformer.exec();
- }
- js[i] = ieXformer.getJs();
+ transformer = new JsIEBlockTextTransformer(transformer);
+ transformer.exec();
+ }
+
+ js[i] = transformer.getJs();
+ ranges[i] = transformer.getStatementRanges();
if (sizeBreakdowns != null) {
sizeBreakdowns[i] = v.getSizeBreakdown();
}
if (sourceInfoMaps != null) {
- sourceInfoMaps.add(((JsReportGenerationVisitor)
v).getSourceInfoMap());
- }
- ranges[i] = ieXformer.getStatementRanges();
+ sourceInfoMaps.add(transformer.getSourceInfoMap());
+ }
}
}
-
+
/**
* This method can be used to fetch the list of referenced classs.
*
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JsAbstractTextTransformer.java
Tue Apr 19 10:10:18 2011
+++
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JsAbstractTextTransformer.java
Wed May 25 07:29:58 2011
@@ -17,8 +17,11 @@
import com.google.gwt.core.ext.linker.StatementRanges;
import com.google.gwt.core.ext.linker.impl.StandardStatementRanges;
+import com.google.gwt.core.ext.soyc.Range;
+import com.google.gwt.dev.jjs.SourceInfo;
import java.util.ArrayList;
+import java.util.Map;
/**
* Base class for transforming program text.
@@ -26,17 +29,23 @@
public abstract class JsAbstractTextTransformer {
protected String js;
+
+ protected StatementRanges originalStatementRanges;
protected StatementRanges statementRanges;
+
+ protected Map<Range, SourceInfo> sourceInfoMap;
public JsAbstractTextTransformer(JsAbstractTextTransformer xformer) {
- this.js = xformer.getJs();
- this.statementRanges = xformer.getStatementRanges();
+ this(xformer.getJs(), xformer.getStatementRanges(),
xformer.getSourceInfoMap());
}
- public JsAbstractTextTransformer(String js, StatementRanges
statementRanges) {
+ public JsAbstractTextTransformer(String js, StatementRanges
statementRanges,
+ Map<Range, SourceInfo> sourceInfoMap) {
this.js = js;
this.statementRanges = statementRanges;
+ this.originalStatementRanges = statementRanges;
+ this.sourceInfoMap = sourceInfoMap;
}
public abstract void exec();
@@ -44,23 +53,26 @@
public String getJs() {
return js;
}
+
+ public Map<Range, SourceInfo> getSourceInfoMap() {
+ return sourceInfoMap;
+ }
public StatementRanges getStatementRanges() {
return statementRanges;
}
- protected void addStatement(String code, StringBuilder newJs,
ArrayList<Integer> starts,
- ArrayList<Integer> ends) {
- beginStatement(newJs, starts);
+ protected void addStatement(int index, String code, StringBuilder newJs,
+ ArrayList<Integer> starts, ArrayList<Integer> ends) {
+ beginStatement(index, newJs, starts);
newJs.append(code);
- endStatement(newJs, ends);
+ endStatement(index, newJs, ends);
}
- protected void beginStatement(StringBuilder newJs, ArrayList<Integer>
starts) {
+ protected void beginStatement(int index, StringBuilder newJs,
ArrayList<Integer> starts) {
starts.add(newJs.length());
}
- // FIXME document parameters
/**
* Called if any operations need to be performed before all statements
have
* been processed.
@@ -73,16 +85,10 @@
ArrayList<Integer> ends) {
}
- // FIXME document
- /**
- * @param newJs
- * @param ends
- */
- protected void endStatement(StringBuilder newJs, ArrayList<Integer>
ends) {
+ protected void endStatement(int index, StringBuilder newJs,
ArrayList<Integer> ends) {
ends.add(newJs.length());
}
- // FIXME document parameters
/**
* Called if any operations need to be performed after all statements
have
* been processed.
@@ -110,18 +116,26 @@
ArrayList<Integer> ends = new ArrayList<Integer>();
beginStatements(newJs, starts, ends);
- for (int stmtIndice : stmtIndices) {
- String code = getJsForRange(stmtIndice);
- addStatement(code, newJs, starts, ends);
+ for (int stmtIndex : stmtIndices) {
+ String code = getJsForRange(stmtIndex);
+ addStatement(stmtIndex, code, newJs, starts, ends);
}
endStatements(newJs, starts, ends);
assert starts.size() == ends.size() : "Size mismatch between start and"
+ " end statement ranges.";
- assert starts.get(0) == 0 && ends.get(ends.size() - 1) ==
newJs.length() : "statement ranges don't cover entire JS output string.";
-
- StandardStatementRanges newRanges = new
StandardStatementRanges(starts, ends);
+ assert starts.get(0) == 0 && ends.get(ends.size() - 1) ==
newJs.length() :
+ "statement ranges don't cover entire JS output string.";
+
js = newJs.toString();
- statementRanges = newRanges;
- }
-}
+ statementRanges = new StandardStatementRanges(starts, ends);
+ updateSourceInfoMap();
+ }
+
+ /**
+ * Update the expression ranges in the SourceInfo map after the
+ * transformer has manipulated the statements.
+ */
+ protected abstract void updateSourceInfoMap();
+
+}
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JsFunctionClusterer.java
Tue May 24 12:34:23 2011
+++
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JsFunctionClusterer.java
Wed May 25 07:29:58 2011
@@ -16,13 +16,18 @@
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.core.ext.linker.StatementRanges;
+import com.google.gwt.core.ext.soyc.Range;
+import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.util.editdistance.GeneralEditDistance;
import com.google.gwt.dev.util.editdistance.GeneralEditDistances;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.LinkedList;
+import java.util.Map;
import java.util.regex.Pattern;
/**
@@ -60,19 +65,32 @@
private static boolean isFunctionDeclaration(String code) {
return functionDeclarationPattern.matcher(code).lookingAt();
}
+
+ /**
+ * Number of function declarations found.
+ */
+ private int numFunctions;
+
+ /**
+ * The statement indices after clustering. The element at index j
represents
+ * the index of the statement in the original code that is moved to
index j
+ * in the new code after clustering.
+ */
+ private int[] reorderedIndices;
public JsFunctionClusterer(JsAbstractTextTransformer xformer) {
super(xformer);
}
- public JsFunctionClusterer(String js, StatementRanges statementRanges) {
- super(js, statementRanges);
+ public JsFunctionClusterer(String js, StatementRanges statementRanges,
+ Map<Range, SourceInfo> sourceInfoMap) {
+ super(js, statementRanges, sourceInfoMap);
}
@Override
public void exec() {
LinkedList<Integer> functionIndices = new LinkedList<Integer>();
-
+
// gather up all of the indices of function decl statements
for (int i = 0; i < statementRanges.numStatements(); i++) {
String code = getJsForRange(i);
@@ -80,6 +98,8 @@
functionIndices.add(i);
}
}
+
+ numFunctions = functionIndices.size();
// sort the indices according to size of statement range
Collections.sort(functionIndices, new Comparator<Integer>() {
@@ -125,21 +145,93 @@
functionIndices.remove(bestIndex);
}
+ reorderedIndices = Arrays.copyOf(clusteredIndices,
statementRanges.numStatements());
recomputeJsAndStatementRanges(clusteredIndices);
}
+ /**
+ * Returns the array of reordered statement indices after clustering.
+ * @return The array of indices, where the element at index j represents
+ * the index of the statement in the original code that is moved to
index j
+ * in the new code after clustering.
+ */
+ public int[] getReorderedIndices() {
+ return reorderedIndices;
+ }
@Override
protected void endStatements(StringBuilder newJs, ArrayList<Integer>
starts,
ArrayList<Integer> ends) {
+ int j = numFunctions;
// Then output everything else that is not a function.
for (int i = 0; i < statementRanges.numStatements(); i++) {
String code = getJsForRange(i);
if (!isFunctionDeclaration(code)) {
- addStatement(code, newJs, starts, ends);
+ addStatement(j, code, newJs, starts, ends);
+ reorderedIndices[j] = i;
+ j++;
}
}
super.endStatements(newJs, starts, ends);
}
+
+ /**
+ * Fixes the index ranges of individual expressions in the generated
+ * JS after function clustering has reordered statements. Loops over
+ * each expression, determines which statement it falls in, and shifts
+ * the indices according to where that statement moved.
+ */
+ @Override
+ protected void updateSourceInfoMap() {
+ if (sourceInfoMap != null) {
+ // create mapping of statement ranges
+ Map<Range, Range> statementShifts = new HashMap<Range, Range>();
+ for (int j = 0; j < statementRanges.numStatements(); j++) {
+ int permutedStart = statementRanges.start(j);
+ int permutedEnd = statementRanges.end(j);
+ int originalStart =
originalStatementRanges.start(reorderedIndices[j]);
+ int originalEnd = originalStatementRanges.end(reorderedIndices[j]);
+
+ statementShifts.put(new Range(originalStart, originalEnd),
+ new Range(permutedStart, permutedEnd));
+ }
+
+ Range[] oldStatementRanges = statementShifts.keySet().toArray(new
Range[0]);
+ Arrays.sort(oldStatementRanges, Range.SOURCE_ORDER_COMPARATOR);
+
+ Range[] oldExpressionRanges = sourceInfoMap.keySet().toArray(new
Range[0]);
+ Arrays.sort(oldExpressionRanges, Range.SOURCE_ORDER_COMPARATOR);
+
+
+ // iterate over expression ranges and shift
+ Map<Range, SourceInfo> updatedInfoMap = new HashMap<Range,
SourceInfo>();
+ Range entireProgram =
+ new Range(0, oldStatementRanges[oldStatementRanges.length -
1].getEnd());
+ for (int i = 0, j = 0; j < oldExpressionRanges.length; j++) {
+ Range oldExpression = oldExpressionRanges[j];
+ if (oldExpression.equals(entireProgram)) {
+ updatedInfoMap.put(oldExpression,
sourceInfoMap.get(oldExpression));
+ continue;
+ }
+
+ if (!oldStatementRanges[i].contains(oldExpressionRanges[j])) {
+ // expression should fall in the next statement
+ i++;
+ assert oldStatementRanges[i].contains(oldExpressionRanges[j]);
+ }
+
+ Range oldStatement = oldStatementRanges[i];
+ Range newStatement = statementShifts.get(oldStatement);
+ int shift = newStatement.getStart() - oldStatement.getStart();
+
+ Range oldExpressionRange = oldExpressionRanges[j];
+ Range newExpressionRange = new Range(oldExpressionRange.getStart()
+ shift,
+ oldExpressionRange.getEnd() + shift);
+ updatedInfoMap.put(newExpressionRange,
sourceInfoMap.get(oldExpressionRange));
+ }
+
+ sourceInfoMap = updatedInfoMap;
+ }
+ }
private int stmtSize(int index1) {
return statementRanges.end(index1) - statementRanges.start(index1);
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JsIEBlockTextTransformer.java
Tue Apr 19 10:10:18 2011
+++
/trunk/dev/core/src/com/google/gwt/dev/jjs/impl/JsIEBlockTextTransformer.java
Wed May 25 07:29:58 2011
@@ -16,8 +16,15 @@
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.core.ext.linker.StatementRanges;
+import com.google.gwt.core.ext.soyc.Range;
+import com.google.gwt.dev.jjs.SourceInfo;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
/**
* Limits top-level blocks to MAX_BLOCK_SIZE statements.
@@ -32,13 +39,18 @@
private int currentStatementCount;
private boolean doSplits;
+
+ private Set<Integer> statementsAddedBlockClose = new HashSet<Integer>();
+
+ private Set<Integer> statementsAddedBlockOpen = new HashSet<Integer>();
public JsIEBlockTextTransformer(JsAbstractTextTransformer xformer) {
super(xformer);
}
- public JsIEBlockTextTransformer(String js, StatementRanges
statementRanges) {
- super(js, statementRanges);
+ public JsIEBlockTextTransformer(String js, StatementRanges
statementRanges,
+ Map<Range, SourceInfo> sourceInfoMap) {
+ super(js, statementRanges, sourceInfoMap);
}
/**
@@ -55,17 +67,26 @@
recomputeJsAndStatementRanges(statementIndices);
}
}
+
+ public Set<Integer> getStatementsAddedBlockClose() {
+ return statementsAddedBlockClose;
+ }
+
+ public Set<Integer> getStatementsAddedBlockOpen() {
+ return statementsAddedBlockOpen;
+ }
/**
* Record start of statement, and optionally inject new open block.
*/
@Override
- protected void beginStatement(StringBuilder newJs, ArrayList<Integer>
starts) {
+ protected void beginStatement(int index, StringBuilder newJs,
ArrayList<Integer> starts) {
if (doSplits && currentStatementCount == 0) {
- super.beginStatement(newJs, starts);
+ super.beginStatement(index, newJs, starts);
newJs.append('{');
+ statementsAddedBlockOpen.add(Integer.valueOf(index));
} else if (!doSplits) {
- super.beginStatement(newJs, starts);
+ super.beginStatement(index, newJs, starts);
}
}
@@ -81,14 +102,15 @@
* full.
*/
@Override
- protected void endStatement(StringBuilder newJs, ArrayList<Integer>
ends) {
+ protected void endStatement(int index, StringBuilder newJs,
ArrayList<Integer> ends) {
currentStatementCount++;
if (doSplits && currentStatementCount == MAX_BLOCK_SIZE) {
newJs.append('}');
- super.endStatement(newJs, ends);
+ super.endStatement(index, newJs, ends);
currentStatementCount = 0;
+ statementsAddedBlockClose.add(Integer.valueOf(index));
} else if (!doSplits) {
- super.endStatement(newJs, ends);
+ super.endStatement(index, newJs, ends);
}
}
@@ -101,14 +123,91 @@
optionallyCloseLastBlock(newJs, ends);
super.endStatements(newJs, starts, ends);
}
+
+ /**
+ * Fixes the index ranges of individual expressions in the generated
+ * JS after chunking statements into blocks that satisfy the IE block
+ * size problem. Loops over each expression, determines whether the
+ * statement in which it falls has a brace inserted before/after, and
+ * shifts forward according to where it falls in the block.
+ */
+ @Override
+ protected void updateSourceInfoMap() {
+ if (sourceInfoMap != null) {
+ Range[] oldExpressionRanges = sourceInfoMap.keySet().toArray(new
Range[0]);
+ Arrays.sort(oldExpressionRanges, Range.SOURCE_ORDER_COMPARATOR);
+
+ // iterate over expression ranges and shift
+ Map<Range, SourceInfo> updatedInfoMap = new HashMap<Range,
SourceInfo>();
+ Range entireProgram =
+ new Range(0,
originalStatementRanges.end(originalStatementRanges.numStatements() - 1));
+ int shift = 0;
+
+ // set to keep track of which statements have already shifted.
+ // need to account for when a shift has already been added for the
extra
+ // open brace in a statement--sometimes there are multiple
expressions
+ // that all start at the same place a the beginning of a statement in
+ // the expression list
+ // ex: _.gC=function x()... yields the expressions _, _.gC, _.gC
= ...
+ Set<Integer> shiftAdded = new HashSet<Integer>();
+
+ for (int i = 0, j = 0; j < oldExpressionRanges.length; j++) {
+ Range oldExpression = oldExpressionRanges[j];
+ if (oldExpression.equals(entireProgram)) {
+ continue;
+ }
+
+ if (originalStatementRanges.start(i) > oldExpression.getStart()
+ || oldExpression.getEnd() > originalStatementRanges.end(i)) {
+
+ // expression should fall in the next statement
+ i++;
+ assert originalStatementRanges.start(i) <=
oldExpression.getStart()
+ && oldExpression.getEnd() <= originalStatementRanges.end(i);
+
+ if (statementsAddedBlockClose.contains(Integer.valueOf(i - 1))) {
+ // there's an extra statement index in the addedBlockClose
list,
+ // which corresponds to the extra closing brace at the end of
the
+ // program. but this index doesn't match up to the indices in
the
+ // old statement ranges--it's equal to the # of statements in
the
+ // original code divided by the IE block size
+ if (i != statementRanges.numStatements()) {
+ shift++;
+ }
+ }
+ }
+
+ if (statementsAddedBlockOpen.contains(Integer.valueOf(i))
+ && oldExpression.getStart() == originalStatementRanges.start(i)
+ && !shiftAdded.contains(i)) {
+
+ shift++;
+ shiftAdded.add(Integer.valueOf(i));
+ }
+
+ int newStart = oldExpression.getStart() + shift;
+ int newEnd = oldExpression.getEnd() + shift;
+
+ Range newExpression = new Range(newStart, newEnd);
+ updatedInfoMap.put(newExpression,
sourceInfoMap.get(oldExpression));
+ }
+
+ updatedInfoMap.put(new Range(0, entireProgram.getEnd() + shift),
+ sourceInfoMap.get(entireProgram));
+
+ sourceInfoMap = updatedInfoMap;
+ }
+ }
/**
* Close last block if it never filled.
*/
private void optionallyCloseLastBlock(StringBuilder newJs,
ArrayList<Integer> ends) {
- if (doSplits && currentStatementCount > 1 && currentStatementCount <
MAX_BLOCK_SIZE) {
+ if (doSplits && currentStatementCount > 0 && currentStatementCount <
MAX_BLOCK_SIZE) {
newJs.append("}");
ends.add(newJs.length());
+ statementsAddedBlockClose.add(Integer.valueOf(ends.size() - 1));
}
}
-}
+
+}
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
Wed Feb 2 13:13:58 2011
+++
/trunk/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
Wed May 25 07:29:58 2011
@@ -41,6 +41,7 @@
this.out = out;
}
+ @Override
public Map<Range, SourceInfo> getSourceInfoMap() {
return Collections.unmodifiableMap(sourceInfoMap);
}
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
Wed Feb 2 13:13:58 2011
+++
/trunk/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
Wed May 25 07:29:58 2011
@@ -15,6 +15,8 @@
*/
package com.google.gwt.dev.js;
+import com.google.gwt.core.ext.soyc.Range;
+import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.impl.FragmentExtractor;
@@ -55,6 +57,11 @@
public SizeBreakdown getSizeBreakdown() {
return new SizeBreakdown(out.getPosition(), sizeMap);
}
+
+ public Map<Range, SourceInfo> getSourceInfoMap() {
+ // override if your child class creates sourceinfo
+ return null;
+ }
@Override
public boolean visit(JsBlock x, JsContext ctx) {
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors