Speed up SOYC compilation.
Review by: spoon@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5482 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/Range.java b/dev/core/src/com/google/gwt/core/ext/soyc/Range.java
index 8414263..f98fe68 100644
--- a/dev/core/src/com/google/gwt/core/ext/soyc/Range.java
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/Range.java
@@ -54,8 +54,8 @@
}
};
- private final int end;
- private final int start;
+ final int end;
+ final int start;
/**
* Constructor.
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/Story.java b/dev/core/src/com/google/gwt/core/ext/soyc/Story.java
index e0f3ad4..f1eac77 100644
--- a/dev/core/src/com/google/gwt/core/ext/soyc/Story.java
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/Story.java
@@ -31,6 +31,7 @@
package com.google.gwt.core.ext.soyc;
import java.io.Serializable;
+import java.util.Set;
import java.util.SortedSet;
/**
@@ -82,6 +83,5 @@
* string literals) that appear in multiple locations in the source may be
* merged by the compiler into a single Story.
*/
- SortedSet<Origin> getSourceOrigin();
-
-}
\ No newline at end of file
+ Set<Origin> getSourceOrigin();
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java
index f5335ca..9bdc649 100644
--- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/DependencyRecorder.java
@@ -19,12 +19,10 @@
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer;
-import com.google.gwt.dev.util.HtmlTextOutput;
import com.google.gwt.util.tools.Utility;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.zip.GZIPOutputStream;
@@ -42,10 +40,7 @@
new DependencyRecorder().recordDependenciesImpl(logger, out, jprogram);
}
- private HtmlTextOutput htmlOut;
- private PrintWriter pw;
-
- private OutputStreamWriter writer;
+ private StringBuilder builder;
private DependencyRecorder() {
}
@@ -74,22 +69,29 @@
dependencyAnalyzer.setDependencyRecorder(this);
try {
- writer = new OutputStreamWriter(new GZIPOutputStream(out), "UTF-8");
- pw = new PrintWriter(writer);
- htmlOut = new HtmlTextOutput(pw, false);
+ OutputStreamWriter writer
+ = new OutputStreamWriter(new GZIPOutputStream(out), "UTF-8");
+
+ StringBuilder localBuilder = new StringBuilder();
+ this.builder = localBuilder;
+
+ printPre();
+ for (JMethod method : jprogram.getAllEntryMethods()) {
+ dependencyAnalyzer.traverseFrom(method);
+ if (localBuilder.length() > 8 * 1024) {
+ writer.write(localBuilder.toString());
+ localBuilder.setLength(0);
+ }
+ }
+ printPost();
+
+ writer.write(localBuilder.toString());
+ Utility.close(writer);
+
+ logger.log(TreeLogger.INFO, "Done");
} catch (Throwable e) {
- logger.log(TreeLogger.ERROR, "Could not open dependency file.", e);
+ logger.log(TreeLogger.ERROR, "Could not write dependency file.", e);
}
-
- printPre();
- for (JMethod method : jprogram.getAllEntryMethods()) {
- dependencyAnalyzer.traverseFrom(method);
- }
- printPost();
- pw.close();
- Utility.close(writer);
-
- logger.log(TreeLogger.INFO, "Done");
}
/**
@@ -99,55 +101,45 @@
* @param dependencyChain
*/
private void printMethodDependency(ArrayList<JMethod> dependencyChain) {
- String curLine;
- for (int i = dependencyChain.size() - 1; i >= 0; i--) {
- JMethod curMethod = dependencyChain.get(i);
- String sFullMethodString = curMethod.getName();
- if (curMethod.getEnclosingType() != null) {
- sFullMethodString = curMethod.getEnclosingType().getName() + "::"
- + curMethod.getName();
- }
- if (i == dependencyChain.size() - 1) {
- curLine = "<method name=\"" + sFullMethodString + "\">";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
- htmlOut.indentIn();
- htmlOut.indentIn();
- } else {
- curLine = "<called by=\"" + sFullMethodString + "\"/>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
- }
+ int size = dependencyChain.size();
+ if (size == 0) {
+ return;
}
- htmlOut.indentOut();
- htmlOut.indentOut();
- curLine = "</method>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
+
+ JMethod curMethod = dependencyChain.get(size - 1);
+ builder.append("<method name=\"");
+ if (curMethod.getEnclosingType() != null) {
+ builder.append(curMethod.getEnclosingType().getName());
+ builder.append("::");
+ }
+ builder.append(curMethod.getName());
+ builder.append("\">\n");
+
+ for (int i = size - 2; i >= 0; i--) {
+ curMethod = dependencyChain.get(i);
+ builder.append("<called by=\"");
+ if (curMethod.getEnclosingType() != null) {
+ builder.append(curMethod.getEnclosingType().getName());
+ builder.append("::");
+ }
+ builder.append(curMethod.getName());
+ builder.append("\"/>\n");
+ }
+ builder.append("</method>\n");
}
/**
* Prints the closing lines necessary for the dependencies file.
*/
private void printPost() {
- String curLine = "</soyc-dependencies>";
- htmlOut.indentOut();
- htmlOut.indentOut();
- htmlOut.printRaw(curLine);
- htmlOut.newline();
+ builder.append("</soyc-dependencies>\n");
}
/**
* Prints the preamble necessary for the dependencies file.
*/
private void printPre() {
- String curLine = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
- curLine = "<soyc-dependencies>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
- htmlOut.indentIn();
- htmlOut.indentIn();
+ builder.append(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soyc-dependencies>\n");
}
}
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryImpl.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryImpl.java
index 6526b3e..b99bcbb 100644
--- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryImpl.java
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryImpl.java
@@ -19,8 +19,8 @@
import com.google.gwt.core.ext.soyc.Story;
import java.io.Serializable;
-import java.util.Collections;
import java.util.Comparator;
+import java.util.Set;
import java.util.SortedSet;
/**
@@ -43,14 +43,14 @@
private final int length;
private final String literalDescription;
private final SortedSet<Member> members;
- private final SortedSet<Origin> origins;
+ private final Set<Origin> origins;
/**
* Standard constructor. This constructor will create unmodifiable versions of
* the collections passed into it.
*/
public StoryImpl(int id, SortedSet<Member> members,
- SortedSet<Origin> origins, String literalDescription, int fragment,
+ Set<Origin> origins, String literalDescription, int fragment,
int length) {
assert members != null;
assert origins != null;
@@ -63,8 +63,8 @@
this.length = length;
this.literalDescription = literalDescription == null ? null
: literalDescription.intern();
- this.members = Collections.unmodifiableSortedSet(members);
- this.origins = Collections.unmodifiableSortedSet(origins);
+ this.members = members;
+ this.origins = origins;
}
/**
@@ -116,7 +116,7 @@
return members;
}
- public SortedSet<Origin> getSourceOrigin() {
+ public Set<Origin> getSourceOrigin() {
return origins;
}
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java
index c2dfafa..32dfb5d 100644
--- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryRecorder.java
@@ -29,12 +29,11 @@
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.js.ast.JsFunction;
-import com.google.gwt.dev.util.HtmlTextOutput;
+import com.google.gwt.dev.util.Util;
import com.google.gwt.util.tools.Utility;
+import java.io.IOException;
import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -67,6 +66,8 @@
}
}
+ private static final int MAX_STRING_BUILDER_SIZE = 65536;
+
/**
* Used to record dependencies of a program.
*/
@@ -75,35 +76,35 @@
new StoryRecorder().recordStoriesImpl(logger, out, sourceInfoMaps, js);
}
+ private StringBuilder builder;
+
private int curHighestFragment = 0;
- private HtmlTextOutput htmlOut;
+ private OutputStream gzipStream;
+
private String[] js;
/**
* Used by {@link #popAndRecord(Stack)} to determine start and end ranges.
*/
private int lastEnd = 0;
+
/**
* This is a class field for convenience, but it should be deleted at the end
* of the constructor.
*/
private transient Map<Correlation, Member> membersByCorrelation = new IdentityHashMap<Correlation, Member>();
- private PrintWriter pw;
-
+
/**
* This is a class field for convenience, but it should be deleted at the end
* of the constructor.
*/
private transient Map<SourceInfo, StoryImpl> storyCache = new IdentityHashMap<SourceInfo, StoryImpl>();
-
private Map<Story, Integer> storyIds = new HashMap<Story, Integer>();
- private OutputStreamWriter writer;
-
private StoryRecorder() {
}
-
+
protected void recordStoriesImpl(TreeLogger logger, OutputStream out,
List<Map<Range, SourceInfo>> sourceInfoMaps, String[] js) {
@@ -112,25 +113,8 @@
this.js = js;
try {
- writer = new OutputStreamWriter(new GZIPOutputStream(out), "UTF-8");
- pw = new PrintWriter(writer);
- htmlOut = new HtmlTextOutput(pw, false);
-
- String curLine = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
-
- curLine = "<soyc>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
- htmlOut.indentIn();
- htmlOut.indentIn();
-
- curLine = "<stories>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
- htmlOut.indentIn();
- htmlOut.indentIn();
+ builder = new StringBuilder(MAX_STRING_BUILDER_SIZE * 2);
+ gzipStream = new GZIPOutputStream(out);
/*
* Don't retain beyond the constructor to avoid lingering references to
@@ -145,13 +129,20 @@
Member.SOURCE_NAME_COMPARATOR);
Set<SourceInfo> sourceInfoSeen = new HashSet<SourceInfo>();
+ builder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soyc>\n<stories>\n");
+
int fragment = 0;
for (Map<Range, SourceInfo> sourceInfoMap : sourceInfoMaps) {
lastEnd = 0;
analyzeFragment(memberFactory, classesMutable, functionsMutable,
sourceInfoMap, sourceInfoSeen, fragment++);
+
+ // Flush output to improve memory locality
+ flushOutput();
}
+ builder.append("</stories>\n</soyc>\n");
+
/*
* Clear the member fields that we don't need anymore to allow GC of the
* SourceInfo objects
@@ -159,33 +150,20 @@
membersByCorrelation = null;
storyCache = null;
- htmlOut.indentOut();
- htmlOut.indentOut();
- curLine = "</stories>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
-
- htmlOut.indentOut();
- htmlOut.indentOut();
- curLine = "</soyc>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
-
- Utility.close(writer);
- pw.close();
+ Util.writeUtf8(builder, gzipStream);
+ Utility.close(gzipStream);
logger.log(TreeLogger.INFO, "Done");
-
} catch (Throwable e) {
- logger.log(TreeLogger.ERROR, "Could not open dependency file.", e);
+ logger.log(TreeLogger.ERROR, "Could not write dependency file.", e);
}
}
-
+
private void analyzeFragment(MemberFactory memberFactory,
TreeSet<ClassMember> classesMutable,
TreeSet<FunctionMember> functionsMutable,
Map<Range, SourceInfo> sourceInfoMap, Set<SourceInfo> sourceInfoSeen,
- int fragment) {
+ int fragment) throws IOException {
/*
* We want to iterate over the Ranges so that enclosing Ranges come before
* their enclosed Ranges...
@@ -215,7 +193,10 @@
// Possibly create and record Members
if (!sourceInfoSeen.contains(info)) {
sourceInfoSeen.add(info);
- for (Correlation c : info.getPrimaryCorrelations()) {
+ for (Correlation c : info.getPrimaryCorrelationsArray()) {
+ if (c == null) {
+ continue;
+ }
if (membersByCorrelation.containsKey(c)) {
continue;
}
@@ -255,17 +236,6 @@
}
}
- /*
- * Record dependencies as observed in the structure of the JS output. This
- * an an ad-hoc approach that just looks at which SourceInfos are used
- * within the Range of another SourceInfo.
- */
- Set<Correlation> correlationsInScope = new HashSet<Correlation>();
- for (RangeInfo outer : dependencyScope) {
- SourceInfo outerInfo = outer.info;
- correlationsInScope.addAll(outerInfo.getPrimaryCorrelations());
- }
-
dependencyScope.push(new RangeInfo(range, info));
}
@@ -283,8 +253,7 @@
assert dependencyOrder[0].getEnd() == lastEnd;
}
- private void emitStory(StoryImpl story, Range range) {
-
+ private void emitStory(StoryImpl story, Range range) throws IOException {
int storyNum;
if (storyIds.containsKey(story)) {
storyNum = storyIds.get(story);
@@ -293,109 +262,65 @@
storyIds.put(story, storyNum);
}
- String curLine = "<story id=\"story" + Integer.toString(storyNum) + "\"";
+ builder.append("<story id=\"story");
+ builder.append(storyNum);
if (story.getLiteralTypeName() != null) {
- curLine = curLine + " literal=\"" + story.getLiteralTypeName() + "\"";
+ builder.append("\" literal=\"");
+ builder.append(story.getLiteralTypeName());
}
- curLine = curLine + ">";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
+ builder.append("\">\n");
Set<Origin> origins = story.getSourceOrigin();
if (origins.size() > 0) {
- htmlOut.indentIn();
- htmlOut.indentIn();
-
- curLine = "<origins>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
-
- htmlOut.indentIn();
- htmlOut.indentIn();
- }
- for (Origin origin : origins) {
- curLine = "<origin lineNumber=\""
- + Integer.toString(origin.getLineNumber()) + "\" location=\""
- + origin.getLocation() + "\"/>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
- }
- if (origins.size() > 0) {
- htmlOut.indentOut();
- htmlOut.indentOut();
-
- curLine = "</origins>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
-
- htmlOut.indentOut();
- htmlOut.indentOut();
+ builder.append("<origins>\n");
+ for (Origin origin : origins) {
+ builder.append("<origin lineNumber=\"");
+ builder.append(origin.getLineNumber());
+ builder.append("\" location=\"");
+ builder.append(origin.getLocation());
+ builder.append("\"/>\n");
+
+ flushOutput();
+ }
+ builder.append("</origins>\n");
}
Set<Member> correlations = story.getMembers();
if (correlations.size() > 0) {
- htmlOut.indentIn();
- htmlOut.indentIn();
-
- curLine = "<correlations>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
-
- htmlOut.indentIn();
- htmlOut.indentIn();
- }
- for (Member correlation : correlations) {
- curLine = "<by idref=\"" + correlation.getSourceName() + "\"/>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
- }
- if (correlations.size() > 0) {
- htmlOut.indentOut();
- htmlOut.indentOut();
-
- curLine = "</correlations>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
-
- htmlOut.indentOut();
- htmlOut.indentOut();
+ builder.append("<correlations>\n");
+ for (Member correlation : correlations) {
+ builder.append("<by idref=\"");
+ builder.append(correlation.getSourceName());
+ builder.append("\"/>\n");
+
+ flushOutput();
+ }
+ builder.append("</correlations>\n");
}
- htmlOut.indentIn();
- htmlOut.indentIn();
+ builder.append("<js fragment=\"");
+ builder.append(curHighestFragment);
+ builder.append("\"/>\n<storyref idref=\"story");
+ builder.append(storyNum);
- curLine = "<js fragment=\"" + curHighestFragment + "\"/>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
-
- String jsCode = js[curHighestFragment].substring(range.getStart(),
- range.getEnd());
- jsCode = escapeXml(jsCode);
- if ((jsCode.length() == 0) || (jsCode.compareTo("\n") == 0)) {
- curLine = "<storyref idref=\"story" + Integer.toString(storyNum) + "\"/>";
+ int start = range.getStart();
+ int end = range.getEnd();
+ String jsCode = js[curHighestFragment];
+ if ((start == end) || ((end == start + 1) && jsCode.charAt(start) == '\n')) {
+ builder.append("\"/>\n</story>\n");
} else {
- curLine = "<storyref idref=\"story" + Integer.toString(storyNum) + "\">"
- + jsCode + "</storyref>";
+ builder.append("\">");
+ Util.escapeXml(jsCode, start, end, false, builder);
+ builder.append("</storyref>\n</story>\n");
}
-
- htmlOut.printRaw(curLine);
- htmlOut.newline();
-
- htmlOut.indentOut();
- htmlOut.indentOut();
-
- curLine = "</story>";
- htmlOut.printRaw(curLine);
- htmlOut.newline();
}
- private String escapeXml(String unescaped) {
- String escaped = unescaped.replaceAll("\\&", "&");
- escaped = escaped.replaceAll("\\<", "<");
- escaped = escaped.replaceAll("\\>", ">");
- escaped = escaped.replaceAll("\\\"", """);
- // escaped = escaped.replaceAll("\\'", "'");
- return escaped;
+ private void flushOutput() throws IOException {
+ // Flush output to improve memory locality
+ if (builder.length() > MAX_STRING_BUILDER_SIZE) {
+ Util.writeUtf8(builder, gzipStream);
+ builder.setLength(0);
+ }
}
/**
@@ -403,7 +328,8 @@
* the right length, possibly sub-dividing the super-enclosing Range in the
* process.
*/
- private void popAndRecord(Stack<RangeInfo> dependencyScope, int fragment) {
+ private void popAndRecord(Stack<RangeInfo> dependencyScope, int fragment)
+ throws IOException {
RangeInfo rangeInfo = dependencyScope.pop();
Range toStore = rangeInfo.range;
@@ -416,7 +342,7 @@
assert !dependencyScope.isEmpty();
SourceInfo gapInfo = dependencyScope.peek().info;
- recordStory(gapInfo, fragment, newRange.length(), newRange);
+ recordStory(gapInfo, fragment, newRange.length(), newRange, builder);
lastEnd += newRange.length();
}
@@ -429,13 +355,13 @@
if (lastEnd < toStore.getEnd()) {
Range newRange = new Range(Math.max(lastEnd, toStore.getStart()),
toStore.getEnd());
- recordStory(rangeInfo.info, fragment, newRange.length(), newRange);
+ recordStory(rangeInfo.info, fragment, newRange.length(), newRange, builder);
lastEnd += newRange.length();
}
}
private void recordStory(SourceInfo info, int fragment, int length,
- Range range) {
+ Range range, StringBuilder builder) throws IOException {
assert info != null;
assert storyCache != null;
@@ -445,20 +371,19 @@
StoryImpl theStory;
if (!storyCache.containsKey(info)) {
-
SortedSet<Member> members = new TreeSet<Member>(
Member.TYPE_AND_SOURCE_NAME_COMPARATOR);
+ Set<Origin> origins = new HashSet<Origin>();
for (Correlation c : info.getAllCorrelations()) {
Member m = membersByCorrelation.get(c);
if (m != null) {
members.add(m);
}
- }
-
- SortedSet<Origin> origins = new TreeSet<Origin>();
- for (Correlation c : info.getAllCorrelations(Axis.ORIGIN)) {
- origins.add(new OriginImpl(c.getOrigin()));
+
+ if (c.getAxis() == Axis.ORIGIN) {
+ origins.add(new OriginImpl(c.getOrigin()));
+ }
}
String literalType = null;
@@ -477,4 +402,5 @@
emitStory(theStory, range);
}
+
}
diff --git a/dev/core/src/com/google/gwt/dev/CompilePerms.java b/dev/core/src/com/google/gwt/dev/CompilePerms.java
index e3bd98d..13929d3 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePerms.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePerms.java
@@ -196,7 +196,7 @@
List<FileBackedObject<PermutationResult>> resultFiles)
throws UnableToCompleteException {
final TreeLogger branch = logger.branch(TreeLogger.INFO, "Compiling "
- + perms.length + " permutations");
+ + perms.length + " permutation" + (perms.length > 1 ? "s" : ""));
PermutationWorkerFactory.compilePermutations(logger, precompilation, perms,
localWorkers, resultFiles);
branch.log(TreeLogger.INFO, "Permutation compile succeeded");
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 7b0de33..f440994 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -183,6 +183,7 @@
public static PermutationResult compilePermutation(TreeLogger logger,
UnifiedAst unifiedAst, Map<String, String> rebindAnswers,
PropertyOracle[] propertyOracles, int permutationId) throws UnableToCompleteException {
+ long permStart = System.currentTimeMillis();
try {
if (JProgram.isTracingEnabled()) {
System.out.println("------------------------------------------------------------");
@@ -323,10 +324,13 @@
PermutationResult toReturn = new PermutationResultImpl(js,
makeSymbolMap(symbolTable));
- if (sourceInfoMaps != null) {
+ if (sourceInfoMaps != null) {
+ long soycStart = System.currentTimeMillis();
+ System.out.println("Computing SOYC output");
// Free up memory.
symbolTable = null;
+ long start = System.currentTimeMillis();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// get method dependencies
StoryRecorder.recordStories(logger, baos, sourceInfoMaps, js);
@@ -334,20 +338,34 @@
+ ".xml.gz", baos.toByteArray());
// Free up memory.
js = null;
+ System.out.println("Record stories: "
+ + (System.currentTimeMillis() - start) + " ms");
+ start = System.currentTimeMillis();
baos.reset();
DependencyRecorder.recordDependencies(logger, baos, jprogram);
SoycArtifact dependencies = new SoycArtifact("dependencies"
+ permutationId + ".xml.gz", baos.toByteArray());
+ System.out.println("Record dependencies: "
+ + (System.currentTimeMillis() - start) + " ms");
+ start = System.currentTimeMillis();
baos.reset();
SplitPointRecorder.recordSplitPoints(jprogram, baos, logger);
SoycArtifact splitPoints = new SoycArtifact("splitPoints"
+ permutationId + ".xml.gz", baos.toByteArray());
+ System.out.println("Record split points: "
+ + (System.currentTimeMillis() - start) + " ms");
toReturn.getArtifacts().add(
new StandardCompilationAnalysis(dependencies, stories, splitPoints));
+
+ System.out.println("Completed SOYC phase in "
+ + (System.currentTimeMillis() - soycStart) + " ms");
}
+
+ System.out.println("Permutation took "
+ + (System.currentTimeMillis() - permStart) + " ms");
return toReturn;
} catch (Throwable e) {
throw logAndTranslateException(logger, e);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java b/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
index baaf810..a34a2b0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
@@ -64,6 +64,13 @@
* has been set.
*/
Set<Correlation> getPrimaryCorrelations();
+
+ /**
+ * Returns the first Correlations added along each Axis on which a Correlation
+ * has been set. Some entries may be null and should be ignored. The
+ * returned array must not be modified.
+ */
+ Correlation[] getPrimaryCorrelationsArray();
int getStartLine();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/SourceInfoCorrelation.java b/dev/core/src/com/google/gwt/dev/jjs/SourceInfoCorrelation.java
index d7221ca..cb96475 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/SourceInfoCorrelation.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/SourceInfoCorrelation.java
@@ -36,9 +36,11 @@
* Micro-opt for {@link #makeChild(Class, String)}.
*/
private static final SourceInfo[] EMPTY_SOURCEINFO_ARRAY = new SourceInfo[0];
+
+ private static final int numCorrelationAxes = Axis.values().length;
private static int numCorrelationAxes() {
- return Axis.values().length;
+ return numCorrelationAxes;
}
/**
@@ -165,6 +167,10 @@
}
return toReturn;
}
+
+ public Correlation[] getPrimaryCorrelationsArray() {
+ return primaryCorrelations;
+ }
public int getStartLine() {
return getOrigin().getStartLine();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java b/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java
index 8224780..f06dba8 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java
@@ -166,6 +166,10 @@
public Set<Correlation> getPrimaryCorrelations() {
return Collections.emptySet();
}
+
+ public Correlation[] getPrimaryCorrelationsArray() {
+ return new Correlation[0];
+ }
public int getStartLine() {
return startLine;
diff --git a/dev/core/src/com/google/gwt/dev/util/Util.java b/dev/core/src/com/google/gwt/dev/util/Util.java
index 70cf7e3..f06b447 100644
--- a/dev/core/src/com/google/gwt/dev/util/Util.java
+++ b/dev/core/src/com/google/gwt/dev/util/Util.java
@@ -307,12 +307,65 @@
* Escapes '&', '<', '>', '"', and '\'' to their XML entity equivalents.
*/
public static String escapeXml(String unescaped) {
- String escaped = unescaped.replaceAll("\\&", "&");
- escaped = escaped.replaceAll("\\<", "<");
- escaped = escaped.replaceAll("\\>", ">");
- escaped = escaped.replaceAll("\\\"", """);
- escaped = escaped.replaceAll("\\'", "'");
- return escaped;
+ StringBuilder builder = new StringBuilder();
+ escapeXml(unescaped, 0, unescaped.length(), true, builder);
+ return builder.toString();
+ }
+
+ /**
+ * Escapes '&', '<', '>', '"', and optionally ''' to their XML entity
+ * equivalents. The portion of the input string between start (inclusive) and
+ * end (exclusive) is scanned. The output is appended to the given
+ * StringBuilder.
+ *
+ * @param code the input String
+ * @param start the first character position to scan.
+ * @param end the character position following the last character to scan.
+ * @param quoteApostrophe if true, the ' character is quoted as
+ * &apos;
+ * @param builder a StringBuilder to be appended with the output.
+ */
+ public static void escapeXml(String code, int start, int end,
+ boolean quoteApostrophe, StringBuilder builder) {
+ int lastIndex = 0;
+ int len = end - start;
+ char[] c = new char[len];
+
+ code.getChars(start, end, c, 0);
+ for (int i = 0; i < len; i++) {
+ switch (c[i]) {
+ case '&':
+ builder.append(c, lastIndex, i - lastIndex);
+ builder.append("&");
+ lastIndex = i + 1;
+ break;
+ case '>':
+ builder.append(c, lastIndex, i - lastIndex);
+ builder.append(">");
+ lastIndex = i + 1;
+ break;
+ case '<':
+ builder.append(c, lastIndex, i - lastIndex);
+ builder.append("<");
+ lastIndex = i + 1;
+ break;
+ case '\"':
+ builder.append(c, lastIndex, i - lastIndex);
+ builder.append(""");
+ lastIndex = i + 1;
+ break;
+ case '\'':
+ if (quoteApostrophe) {
+ builder.append(c, lastIndex, i - lastIndex);
+ builder.append("'");
+ lastIndex = i + 1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ builder.append(c, lastIndex, len - lastIndex);
}
public static URL findSourceInClassPath(ClassLoader cl, String sourceTypeName) {
@@ -1211,6 +1264,72 @@
Utility.close(stream);
}
}
+
+ /**
+ * Writes the contents of a StringBuilder to an OutputStream, encoding
+ * each character using the UTF-* encoding. Unicode characters between
+ * U+0000 and U+10FFFF are supported.
+ */
+ public static void writeUtf8(StringBuilder builder, OutputStream out)
+ throws IOException {
+ // Rolling our own converter avoids the following:
+ //
+ // o Instantiating the entire builder as a String
+ // o Creating CharEncoders and NIO buffer
+ // o Passing through an OutputStreamWriter
+
+ int buflen = 1024;
+ char[] inBuf = new char[buflen];
+ byte[] outBuf = new byte[4 * buflen];
+
+ int length = builder.length();
+ int start = 0;
+
+ while (start < length) {
+ int end = Math.min(start + buflen, length);
+ builder.getChars(start, end, inBuf, 0);
+
+ int index = 0;
+ int len = end - start;
+ for (int i = 0; i < len; i++) {
+ int c = inBuf[i] & 0xffff;
+ if (c < 0x80) {
+ outBuf[index++] = (byte) c;
+ } else if (c < 0x800) {
+ int y = c >> 8;
+ int x = c & 0xff;
+ outBuf[index++] = (byte) (0xc0 | (y << 2) | (x >> 6)); // 110yyyxx
+ outBuf[index++] = (byte) (0x80 | (x & 0x3f)); // 10xxxxxx
+ } else if (c < 0xD800 || c > 0xDFFF) {
+ int y = (c >> 8) & 0xff;
+ int x = c & 0xff;
+ outBuf[index++] = (byte) (0xe0 | (y >> 4)); // 1110yyyy
+ outBuf[index++] = (byte) (0x80 | ((y << 2) & 0x3c) | (x >> 6)); // 10yyyyxx
+ outBuf[index++] = (byte) (0x80 | (x & 0x3f)); // 10xxxxxx
+ } else {
+ // Ignore if no second character (which is not be legal unicode)
+ if (i + 1 < len) {
+ int hi = c & 0x3ff;
+ int lo = inBuf[i + 1] & 0x3ff;
+
+ int full = 0x10000 + ((hi << 10) | lo);
+ int z = (full >> 16) & 0xff;
+ int y = (full >> 8) & 0xff;
+ int x = full & 0xff;
+
+ outBuf[index++] = (byte) (0xf0 | (z >> 5));
+ outBuf[index++] = (byte) (0x80 | ((z << 4) & 0x30) | (y >> 4));
+ outBuf[index++] = (byte) (0x80 | ((y << 2) & 0x3c) | (x >> 6));
+ outBuf[index++] = (byte) (0x80 | (x & 0x3f));
+
+ i++; // char has been consumed
+ }
+ }
+ }
+ out.write(outBuf, 0, index);
+ start = end;
+ }
+ }
// /**
// * Write all of the supplied bytes to the file, in a way that they can be
diff --git a/dev/core/test/com/google/gwt/core/ext/util/UtilSuite.java b/dev/core/test/com/google/gwt/core/ext/util/UtilSuite.java
new file mode 100644
index 0000000..291dc81
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/util/UtilSuite.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.util;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class UtilSuite extends TestSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite("Test suite for Util");
+ suite.addTestSuite(UtilTest.class);
+ return suite;
+ }
+}
diff --git a/dev/core/test/com/google/gwt/core/ext/util/UtilTest.java b/dev/core/test/com/google/gwt/core/ext/util/UtilTest.java
new file mode 100644
index 0000000..5b3c394
--- /dev/null
+++ b/dev/core/test/com/google/gwt/core/ext/util/UtilTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.util;
+
+import com.google.gwt.dev.util.Util;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Tests for some com.google.gwt.dev.util.Util methods.
+ */
+public class UtilTest extends TestCase {
+
+ public UtilTest() {
+ }
+
+ public void testExpandXml() {
+ String[] INPUTS = {
+ "ab&yoohoo<ok",
+ "ab&yoohoo<",
+ "&yoohoo<ok",
+ "ab&<ok",
+ "ab&yoohoo<ok",
+ "&",
+ "\'",
+ "\"",
+ ">",
+ "<"
+ };
+
+ String[] OUTPUTS = {
+ "ab&yoohoo<ok",
+ "ab&yoohoo<",
+ "&yoohoo<ok",
+ "ab&<ok",
+ "ab&yoohoo<ok",
+ "&",
+ "'",
+ """,
+ ">",
+ "<"
+ };
+
+ for (int i = 0; i < INPUTS.length; i++) {
+ assertEquals(Util.escapeXml(INPUTS[i]), OUTPUTS[i]);
+ }
+ }
+
+ private void testWriteUtf8(String input) throws IOException {
+ // Built-in encoder
+ ByteArrayOutputStream out1 = new ByteArrayOutputStream();
+ Writer writer = new OutputStreamWriter(out1, "UTF-8");
+ writer.write(input);
+ writer.close();
+ byte[] bytes1 = out1.toByteArray();
+
+ // Util encoder
+ StringBuilder builder = new StringBuilder();
+ builder.append(input);
+ ByteArrayOutputStream out2 = new ByteArrayOutputStream();
+ Util.writeUtf8(builder, out2);
+ byte[] bytes2 = out2.toByteArray();
+
+ assertEquals(bytes1.length, bytes2.length);
+ for (int i = 0; i < bytes1.length; i++) {
+ assertEquals("input = " + input + " at byte " + i, bytes1[i], bytes2[i]);
+ }
+ }
+
+ public void testWriteUtf8() {
+ String[] INPUTS = {
+ "灰色",
+ "plain ascii text",
+ "té×ţ ŵıŦƕ lŎts œf wƎiƦd ƇhÅrȿ",
+ "ĝréý",
+ "$ᄋme סf everりthınğ งฆฅฦศ ႳႹႩႨႵ ᄃᄥᄷᄨᎤᎸᎹᎺᴓ",
+ "ᴄᴅᴈᴇἓἤἥἨὙΩ№℗℘←↑→↓✤✥✦✧�ﺕﺖꀕꀖꀗ逅逖逡遇違遝遭遷",
+ "Surrogate pairs: 𝍠𐀁𐀵𐅇𐅋𐅊𝄐𝄑𝄢𝄫𝄞𝍤𝍦𝍨再善"
+ };
+
+ try {
+ for (String input : INPUTS) {
+ testWriteUtf8(input);
+ }
+ } catch (IOException e) {
+ fail("Got IOException");
+ }
+ }
+}