Author: [email protected]
Date: Tue Jun 2 12:09:40 2009
New Revision: 5496
Added:
trunk/dev/core/src/com/google/gwt/core/ext/linker/StatementRanges.java
(contents, props changed)
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStatementRanges.java
(contents, props changed)
trunk/dev/core/test/com/google/gwt/core/linker/
trunk/dev/core/test/com/google/gwt/core/linker/ScriptChunkingTest.java
(contents, props changed)
Modified:
trunk/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
trunk/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
trunk/dev/core/src/com/google/gwt/dev/PermutationResult.java
trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/TextOutputVisitor.java
trunk/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
trunk/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
trunk/dev/core/src/com/google/gwt/dev/util/AbstractTextOutput.java
trunk/dev/core/src/com/google/gwt/dev/util/TextOutput.java
trunk/user/src/com/google/gwt/core/CompilerParameters.gwt.xml
trunk/user/test/com/google/gwt/dev/jjs/CompilerSuite.gwt.xml
trunk/user/test/com/google/gwt/emultest/EmulSuite.gwt.xml
Log:
This patch modifies the IFrame linker to use multiple script tags instead
of one
giant one. This improves parsing time on Firefox, and it should improve
overall
loading latency on any browser whenever the network latency is significant.
Review by: scottb
Modified:
trunk/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
==============================================================================
---
trunk/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
(original)
+++
trunk/dev/core/src/com/google/gwt/core/ext/linker/CompilationResult.java
Tue Jun 2 12:09:40 2009
@@ -50,6 +50,12 @@
public abstract SortedSet<SortedMap<SelectionProperty, String>>
getPropertyMap();
/**
+ * Returns the statement ranges for the JavaScript returned by
+ * {...@link #getJavaScript()}.
+ */
+ public abstract StatementRanges[] getStatementRanges();
+
+ /**
* Return a string that uniquely identifies this compilation result.
Typically
* this is a cryptographic hash of the compiled data.
*/
Added:
trunk/dev/core/src/com/google/gwt/core/ext/linker/StatementRanges.java
==============================================================================
--- (empty file)
+++ trunk/dev/core/src/com/google/gwt/core/ext/linker/StatementRanges.java
Tue Jun 2 12:09:40 2009
@@ -0,0 +1,37 @@
+/*
+ * 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.linker;
+
+/**
+ * Describes the source-code positions of top-level statements in a string
of
+ * JavaScript.
+ */
+public interface StatementRanges {
+ /**
+ * The end of the ith statement.
+ */
+ int end(int i);
+
+ /**
+ * The number of statements in the associated JavaScript.
+ */
+ int numStatements();
+
+ /**
+ * The start of the ith statement.
+ */
+ int start(int i);
+}
Modified:
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
==============================================================================
---
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
(original)
+++
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
Tue Jun 2 12:09:40 2009
@@ -117,8 +117,7 @@
throws UnableToCompleteException {
String[] js = result.getJavaScript();
byte[][] bytes = new byte[js.length][];
- bytes[0] = generatePrimaryFragment(logger, context, js[0],
- result.getStrongName(), js.length);
+ bytes[0] = generatePrimaryFragment(logger, context, result, js);
for (int i = 1; i < js.length; i++) {
bytes[i] = Util.getBytes(js[i]);
}
@@ -159,6 +158,21 @@
+ ".nocache.js", lastModified);
}
+ /**
+ * Generate the primary fragment. The default implementation is based on
+ * {...@link #getModulePrefix(TreeLogger, LinkerContext, String, int)} and
+ * {...@link #getModuleSuffix(TreeLogger, LinkerContext)}.
+ */
+ protected byte[] generatePrimaryFragment(TreeLogger logger,
+ LinkerContext context, CompilationResult result, String[] js)
+ throws UnableToCompleteException {
+ StringBuffer b = new StringBuffer();
+ b.append(getModulePrefix(logger, context, result.getStrongName(),
js.length));
+ b.append(js[0]);
+ b.append(getModuleSuffix(logger, context));
+ return Util.getBytes(b.toString());
+ }
+
protected String generatePropertyProvider(SelectionProperty prop) {
StringBuffer toReturn = new StringBuffer();
@@ -385,14 +399,4 @@
protected abstract String getSelectionScriptTemplate(TreeLogger logger,
LinkerContext context) throws UnableToCompleteException;
-
- private byte[] generatePrimaryFragment(TreeLogger logger,
- LinkerContext context, String js, String strongName, int
numFragments)
- throws UnableToCompleteException {
- StringBuffer b = new StringBuffer();
- b.append(getModulePrefix(logger, context, strongName, numFragments));
- b.append(js);
- b.append(getModuleSuffix(logger, context));
- return Util.getBytes(b.toString());
- }
}
Modified:
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
==============================================================================
---
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
(original)
+++
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
Tue Jun 2 12:09:40 2009
@@ -17,6 +17,7 @@
import com.google.gwt.core.ext.linker.CompilationResult;
import com.google.gwt.core.ext.linker.SelectionProperty;
+import com.google.gwt.core.ext.linker.StatementRanges;
import com.google.gwt.core.ext.linker.SymbolData;
import com.google.gwt.dev.util.DiskCache;
@@ -74,12 +75,14 @@
private final SortedSet<SortedMap<SelectionProperty, String>>
propertyValues = new TreeSet<SortedMap<SelectionProperty, String>>(
MAP_COMPARATOR);
+ private final StatementRanges[] statementRanges;
+
private final String strongName;
private final long symbolToken;
public StandardCompilationResult(String strongName, byte[][] js,
- byte[] serializedSymbolMap) {
+ byte[] serializedSymbolMap, StatementRanges[] statementRanges) {
super(StandardLinkerContext.class);
this.strongName = strongName;
jsToken = new long[js.length];
@@ -87,6 +90,7 @@
jsToken[i] = diskCache.writeByteArray(js[i]);
}
symbolToken = diskCache.writeByteArray(serializedSymbolMap);
+ this.statementRanges = statementRanges;
}
/**
@@ -112,6 +116,11 @@
@Override
public SortedSet<SortedMap<SelectionProperty, String>> getPropertyMap() {
return Collections.unmodifiableSortedSet(propertyValues);
+ }
+
+ @Override
+ public StatementRanges[] getStatementRanges() {
+ return statementRanges;
}
@Override
Modified:
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
==============================================================================
---
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
(original)
+++
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
Tue Jun 2 12:09:40 2009
@@ -350,7 +350,8 @@
StandardCompilationResult result = resultsByStrongName.get(strongName);
if (result == null) {
result = new StandardCompilationResult(strongName, js,
- permutationResult.getSerializedSymbolMap());
+ permutationResult.getSerializedSymbolMap(),
+ permutationResult.getStatementRanges());
resultsByStrongName.put(result.getStrongName(), result);
artifacts.add(result);
Added:
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStatementRanges.java
==============================================================================
--- (empty file)
+++
trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardStatementRanges.java
Tue Jun 2 12:09:40 2009
@@ -0,0 +1,55 @@
+/*
+ * 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.linker.impl;
+
+import com.google.gwt.core.ext.linker.StatementRanges;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+/**
+ * The standard implementation of {...@link StatementRanges}.
+ */
+public class StandardStatementRanges implements StatementRanges,
Serializable {
+ private static int[] toArray(ArrayList<Integer> list) {
+ int[] ary = new int[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ ary[i] = list.get(i);
+ }
+ return ary;
+ }
+
+ private final int[] ends;
+ private final int[] starts;
+
+ public StandardStatementRanges(ArrayList<Integer> starts,
ArrayList<Integer> ends) {
+ assert starts.size() == ends.size();
+ this.starts = toArray(starts);
+ this.ends = toArray(ends);
+ }
+
+ public int end(int i) {
+ return ends[i];
+ }
+
+ public int numStatements() {
+ return starts.length;
+ }
+
+ public int start(int i) {
+ return starts[i];
+ }
+}
Modified: trunk/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java
(original)
+++ trunk/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java Tue
Jun 2 12:09:40 2009
@@ -19,18 +19,23 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.CompilationResult;
+import com.google.gwt.core.ext.linker.ConfigurationProperty;
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.LinkerOrder;
+import com.google.gwt.core.ext.linker.StatementRanges;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import com.google.gwt.core.ext.linker.impl.HostedModeLinker;
import com.google.gwt.core.ext.linker.impl.SelectionScriptLinker;
import com.google.gwt.dev.About;
import com.google.gwt.dev.util.DefaultTextOutput;
+import com.google.gwt.dev.util.Util;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
+import java.util.SortedSet;
/**
* Implements the canonical GWT bootstrap sequence that loads the GWT
module in
@@ -38,6 +43,62 @@
*/
@LinkerOrder(Order.PRIMARY)
public class IFrameLinker extends SelectionScriptLinker {
+ /**
+ * This string is inserted between script chunks. It is made default
access
+ * for testing.
+ */
+ static final String SCRIPT_CHUNK_SEPARATOR
= "--></script>\n<script><!--\n";
+
+ /**
+ * A configuration property indicating how large each script tag should
be.
+ */
+ private static final String CHUNK_SIZE_PROPERTY
= "iframe.linker.script.chunk.size";
+
+ /**
+ * Split a JavaScript string into multiple chunks, at statement
boundaries.
+ * Insert and end-script tag and a start-script tag in between each
chunk.
+ * This method is made default access for testing.
+ */
+ static String splitPrimaryJavaScript(StatementRanges ranges, String js,
+ int charsPerChunk) {
+ if (charsPerChunk < 0) {
+ return js;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ int bytesInCurrentTag = 0;
+
+ for (int i = 0; i < ranges.numStatements(); i++) {
+ int start = ranges.start(i);
+ int end = ranges.end(i);
+ int length = end - start;
+ if (bytesInCurrentTag > 0
+ && bytesInCurrentTag + length > charsPerChunk) {
+ if (lastChar(sb) != '\n') {
+ sb.append('\n');
+ }
+ sb.append(SCRIPT_CHUNK_SEPARATOR);
+ bytesInCurrentTag = 0;
+ }
+ if (bytesInCurrentTag > 0) {
+ char lastChar = lastChar(sb);
+ if (lastChar != '\n' && lastChar != ';' && lastChar != '}') {
+ /*
+ * Make sure this statement has a separator from the last one.
+ */
+ sb.append(";");
+ }
+ }
+ sb.append(js, start, end);
+ bytesInCurrentTag += length;
+ }
+ return sb.toString();
+ }
+
+ private static char lastChar(StringBuilder sb) {
+ return sb.charAt(sb.length() - 1);
+ }
+
@Override
public String getDescription() {
return "Standard";
@@ -87,6 +148,26 @@
return toReturn;
}
+ /**
+ * This implementation divides the code of the initial fragment into
multiple
+ * script tags. These chunked script tags loads faster on Firefox even
when
+ * the data is cached. Additionally, having the script tags separated
means
+ * that the early ones can be evaluated before the later ones have
finished
+ * downloading. As a result of this parallelism, the overall time to get
the
+ * JavaScript downloaded and evaluated can lower.
+ */
+ @Override
+ protected byte[] generatePrimaryFragment(TreeLogger logger,
+ LinkerContext context, CompilationResult result, String[] js)
+ throws UnableToCompleteException {
+ StringBuffer b = new StringBuffer();
+ b.append(getModulePrefix(logger, context, result.getStrongName(),
js.length));
+ b.append(splitPrimaryJavaScript(result.getStatementRanges()[0], js[0],
+ charsPerChunk(context, logger)));
+ b.append(getModuleSuffix(logger, context));
+ return Util.getBytes(b.toString());
+ }
+
@Override
protected String getCompilationExtension(TreeLogger logger,
LinkerContext context) {
@@ -132,11 +213,33 @@
return out.toString();
}
-
+
@Override
protected String getSelectionScriptTemplate(TreeLogger logger,
LinkerContext context) {
return "com/google/gwt/core/linker/IFrameTemplate.js";
+ }
+
+ protected String modifyPrimaryJavaScript(String js) {
+ return js;
+ }
+
+ /**
+ * Extract via {...@link #CHUNK_SIZE_PROPERTY} the number of characters to
be
+ * included in each script tag.
+ */
+ private int charsPerChunk(LinkerContext context, TreeLogger logger)
+ throws UnableToCompleteException {
+ SortedSet<ConfigurationProperty> configProps =
context.getConfigurationProperties();
+ for (ConfigurationProperty prop : configProps) {
+ if (prop.getName().equals(CHUNK_SIZE_PROPERTY)) {
+ return Integer.parseInt(prop.getValues().get(0));
+ }
+ }
+
+ logger.log(TreeLogger.ERROR, "Unable to find configuration property "
+ + CHUNK_SIZE_PROPERTY);
+ throw new UnableToCompleteException();
}
/**
Modified: trunk/dev/core/src/com/google/gwt/dev/PermutationResult.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/PermutationResult.java
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/PermutationResult.java Tue Jun
2
12:09:40 2009
@@ -16,6 +16,7 @@
package com.google.gwt.dev;
import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.StatementRanges;
import java.io.Serializable;
@@ -38,4 +39,9 @@
* The symbol map for the permutation.
*/
byte[] getSerializedSymbolMap();
+
+ /**
+ * The statement ranges for the code returned by {...@link #getJs()}.
+ */
+ StatementRanges[] getStatementRanges();
}
Modified:
trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
Tue Jun 2 12:09:40 2009
@@ -19,6 +19,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.StatementRanges;
import com.google.gwt.core.ext.linker.SymbolData;
import com.google.gwt.core.ext.linker.impl.StandardCompilationAnalysis;
import com.google.gwt.core.ext.linker.impl.StandardSymbolData;
@@ -95,7 +96,6 @@
import com.google.gwt.dev.js.JsSymbolResolver;
import com.google.gwt.dev.js.JsUnusedFunctionRemover;
import com.google.gwt.dev.js.JsVerboseNamer;
-import com.google.gwt.dev.js.JsReportGenerationVisitor.CountingTextOutput;
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsStatement;
@@ -137,8 +137,10 @@
private final ArtifactSet artifacts = new ArtifactSet();
private final byte[][] js;
private final byte[] serializedSymbolMap;
+ private final StatementRanges[] statementRanges;
- public PermutationResultImpl(String[] js, SymbolData[] symbolMap) {
+ public PermutationResultImpl(String[] js, SymbolData[] symbolMap,
+ StatementRanges[] statementRanges) {
byte[][] bytes = new byte[js.length][];
for (int i = 0; i < js.length; ++i) {
bytes[i] = Util.getBytes(js[i]);
@@ -152,6 +154,7 @@
throw new RuntimeException("Should never happen with in-memory
stream",
e);
}
+ this.statementRanges = statementRanges;
}
public ArtifactSet getArtifacts() {
@@ -165,6 +168,10 @@
public byte[] getSerializedSymbolMap() {
return serializedSymbolMap;
}
+
+ public StatementRanges[] getStatementRanges() {
+ return statementRanges;
+ }
}
/**
@@ -303,28 +310,28 @@
List<Map<Range, SourceInfo>> sourceInfoMaps = options.isSoycEnabled()
? new ArrayList<Map<Range,
SourceInfo>>(jsProgram.getFragmentCount())
: null;
-
+ StatementRanges[] ranges = new StatementRanges[js.length];
for (int i = 0; i < js.length; i++) {
+ DefaultTextOutput out = new DefaultTextOutput(
+ options.getOutput().shouldMinimize());
+ JsSourceGenerationVisitor v;
if (sourceInfoMaps != null) {
- CountingTextOutput out = new CountingTextOutput(
- options.getOutput().shouldMinimize());
- JsReportGenerationVisitor v = new JsReportGenerationVisitor(out);
- v.accept(jsProgram.getFragmentBlock(i));
- js[i] = out.toString();
- sourceInfoMaps.add(v.getSourceInfoMap());
+ v = new JsReportGenerationVisitor(out);
} else {
- DefaultTextOutput out = new DefaultTextOutput(
- options.getOutput().shouldMinimize());
- JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out);
- v.accept(jsProgram.getFragmentBlock(i));
- js[i] = out.toString();
+ v = new JsSourceGenerationVisitor(out);
+ }
+ v.accept(jsProgram.getFragmentBlock(i));
+ js[i] = out.toString();
+ if (sourceInfoMaps != null) {
+ sourceInfoMaps.add(((JsReportGenerationVisitor)
v).getSourceInfoMap());
}
+ ranges[i] = v.getStatementRanges();
}
PermutationResult toReturn = new PermutationResultImpl(js,
- makeSymbolMap(symbolTable));
+ makeSymbolMap(symbolTable), ranges);
- if (sourceInfoMaps != null) {
+ if (sourceInfoMaps != null) {
long soycStart = System.currentTimeMillis();
System.out.println("Computing SOYC output");
// Free up memory.
Modified:
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
==============================================================================
---
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
(original)
+++
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
Tue Jun 2 12:09:40 2009
@@ -1502,7 +1502,8 @@
JsFunction nullFunc = new JsFunction(sourceInfo, topScope,
nullMethodName, true);
nullFunc.setBody(new JsBlock(sourceInfo));
- globalStatements.add(nullFunc.makeStmt());
+ // Add it first, so that script-tag chunking in IFrameLinker works
+ globalStatements.add(0, nullFunc.makeStmt());
}
private void generateSeedFuncAndPrototype(JClassType x,
Modified:
trunk/dev/core/src/com/google/gwt/dev/jjs/impl/TextOutputVisitor.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/jjs/impl/TextOutputVisitor.java
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/jjs/impl/TextOutputVisitor.java
Tue Jun 2 12:09:40 2009
@@ -30,6 +30,10 @@
this.textOutput = textOutput;
}
+ public int getPosition() {
+ return textOutput.getPosition();
+ }
+
public void indentIn() {
textOutput.indentIn();
}
Modified:
trunk/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
Tue Jun 2 12:09:40 2009
@@ -19,10 +19,8 @@
import com.google.gwt.dev.jjs.HasSourceInfo;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.js.ast.JsVisitable;
-import com.google.gwt.dev.util.AbstractTextOutput;
+import com.google.gwt.dev.util.TextOutput;
-import java.io.PrintWriter;
-import java.io.StringWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -33,35 +31,10 @@
* locations of SourceInfo objects in the output.
*/
public class JsReportGenerationVisitor extends JsSourceGenerationVisitor {
- /**
- * A TextOutput that can return the total number of characters that have
been
- * printed.
- */
- public static class CountingTextOutput extends AbstractTextOutput {
- private final StringWriter sw = new StringWriter();
- private final PrintWriter out = new PrintWriter(sw);
-
- public CountingTextOutput(boolean compact) {
- super(compact);
- setPrintWriter(out);
- }
-
- public int getCount() {
- out.flush();
- return sw.getBuffer().length();
- }
-
- @Override
- public String toString() {
- out.flush();
- return sw.toString();
- }
- }
-
private final Map<Range, SourceInfo> sourceInfoMap = new HashMap<Range,
SourceInfo>();
- private final CountingTextOutput out;
+ private final TextOutput out;
- public JsReportGenerationVisitor(CountingTextOutput out) {
+ public JsReportGenerationVisitor(TextOutput out) {
super(out);
this.out = out;
}
@@ -73,11 +46,11 @@
@Override
protected <T extends JsVisitable<T>> T doAccept(T node) {
boolean addEntry = node instanceof HasSourceInfo;
- int start = addEntry ? out.getCount() : 0;
+ int start = addEntry ? out.getPosition() : 0;
T toReturn = super.doAccept(node);
if (addEntry) {
SourceInfo info = ((HasSourceInfo) node).getSourceInfo();
- sourceInfoMap.put(new Range(start, out.getCount()), info);
+ sourceInfoMap.put(new Range(start, out.getPosition()), info);
}
return toReturn;
}
Modified:
trunk/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
==============================================================================
---
trunk/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
(original)
+++
trunk/dev/core/src/com/google/gwt/dev/js/JsToStringGenerationVisitor.java
Tue Jun 2 12:09:40 2009
@@ -15,6 +15,8 @@
*/
package com.google.gwt.dev.js;
+import com.google.gwt.core.ext.linker.StatementRanges;
+import com.google.gwt.core.ext.linker.impl.StandardStatementRanges;
import com.google.gwt.dev.js.ast.HasName;
import com.google.gwt.dev.js.ast.JsArrayAccess;
import com.google.gwt.dev.js.ast.JsArrayLiteral;
@@ -69,8 +71,11 @@
import com.google.gwt.dev.js.ast.JsWhile;
import com.google.gwt.dev.js.ast.JsVars.JsVar;
import com.google.gwt.dev.util.TextOutput;
+import com.google.gwt.dev.util.collect.HashSet;
+import java.util.ArrayList;
import java.util.Iterator;
+import java.util.Set;
import java.util.regex.Pattern;
/**
@@ -120,12 +125,26 @@
private static final Pattern VALID_NAME_PATTERN =
Pattern.compile("[a-zA-Z_$][\\w$]*");
protected boolean needSemi = true;
+
+ /**
+ * "Global" blocks are either the global block of a fragment, or a block
+ * nested directly within some other global block. This definition
matters
+ * because the statements designated by statementEnds and
statementStarts are
+ * those that appear directly within these global blocks.
+ */
+ private Set<JsBlock> globalBlocks = new HashSet<JsBlock>();
private final TextOutput p;
+ private ArrayList<Integer> statementEnds = new ArrayList<Integer>();
+ private ArrayList<Integer> statementStarts = new ArrayList<Integer>();
public JsToStringGenerationVisitor(TextOutput out) {
this.p = out;
}
+ public StatementRanges getStatementRanges() {
+ return new StandardStatementRanges(statementStarts, statementEnds);
+ }
+
@Override
public boolean visit(JsArrayAccess x, JsContext<JsExpression> ctx) {
JsExpression arrayExpr = x.getArrayExpr();
@@ -816,6 +835,8 @@
int count = 0;
for (Iterator<JsStatement> iter = x.getStatements().iterator();
iter.hasNext(); ++count) {
+ boolean isGlobal = x.isGlobalBlock() || globalBlocks.contains(x);
+
if (truncate && count > JSBLOCK_LINES_TO_PRINT) {
p.print("[...]");
_newlineOpt();
@@ -823,7 +844,22 @@
}
JsStatement stmt = iter.next();
needSemi = true;
+ boolean shouldRecordPositions = isGlobal && !(stmt instanceof
JsBlock);
+ boolean stmtIsGlobalBlock = false;
+ if (isGlobal) {
+ if (stmt instanceof JsBlock) {
+ // A block inside a global block is still considered global
+ stmtIsGlobalBlock = true;
+ globalBlocks.add((JsBlock) stmt);
+ }
+ }
+ if (shouldRecordPositions) {
+ statementStarts.add(p.getPosition());
+ }
accept(stmt);
+ if (stmtIsGlobalBlock) {
+ globalBlocks.remove(stmt);
+ }
if (needSemi) {
/*
* Special treatment of function decls: If they are the only item
in a
@@ -852,6 +888,10 @@
}
_newlineOpt();
}
+ }
+ if (shouldRecordPositions) {
+ assert (statementStarts.size() == statementEnds.size() + 1);
+ statementEnds.add(p.getPosition());
}
}
Modified: trunk/dev/core/src/com/google/gwt/dev/util/AbstractTextOutput.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/util/AbstractTextOutput.java
(original)
+++ trunk/dev/core/src/com/google/gwt/dev/util/AbstractTextOutput.java Tue
Jun 2 12:09:40 2009
@@ -28,11 +28,16 @@
private char[][] indents = new char[][] {new char[0]};
private boolean justNewlined;
private PrintWriter out;
+ private int position = 0;
protected AbstractTextOutput(boolean compact) {
this.compact = compact;
}
+ public int getPosition() {
+ return position;
+ }
+
public void indentIn() {
++identLevel;
if (identLevel >= indents.length) {
@@ -55,14 +60,16 @@
if (compact) {
out.print('\n');
} else {
- out.println();
+ out.print('\n');
}
+ position++;
justNewlined = true;
}
public void newlineOpt() {
if (!compact) {
- out.println();
+ out.print('\n');
+ position++;
justNewlined = true;
}
}
@@ -70,18 +77,19 @@
public void print(char c) {
maybeIndent();
out.print(c);
+ position++;
justNewlined = false;
}
public void print(char[] s) {
maybeIndent();
- out.print(s);
+ printAndCount(s);
justNewlined = false;
}
public void print(String s) {
maybeIndent();
- out.print(s);
+ printAndCount(s.toCharArray());
justNewlined = false;
}
@@ -89,20 +97,21 @@
if (!compact) {
maybeIndent();
out.print(c);
+ position += 1;
}
}
public void printOpt(char[] s) {
if (!compact) {
maybeIndent();
- out.print(s);
+ printAndCount(s);
}
}
public void printOpt(String s) {
if (!compact) {
maybeIndent();
- out.print(s);
+ printAndCount(s.toCharArray());
}
}
@@ -112,8 +121,13 @@
private void maybeIndent() {
if (justNewlined && !compact) {
- out.print(indents[identLevel]);
+ printAndCount(indents[identLevel]);
justNewlined = false;
}
+ }
+
+ private void printAndCount(char[] chars) {
+ position += chars.length;
+ out.print(chars);
}
}
Modified: trunk/dev/core/src/com/google/gwt/dev/util/TextOutput.java
==============================================================================
--- trunk/dev/core/src/com/google/gwt/dev/util/TextOutput.java (original)
+++ trunk/dev/core/src/com/google/gwt/dev/util/TextOutput.java Tue Jun 2
12:09:40 2009
@@ -19,6 +19,8 @@
* Interface used for printing text output.
*/
public interface TextOutput {
+ int getPosition();
+
void indentIn();
void indentOut();
Added:
trunk/dev/core/test/com/google/gwt/core/linker/ScriptChunkingTest.java
==============================================================================
--- (empty file)
+++ trunk/dev/core/test/com/google/gwt/core/linker/ScriptChunkingTest.java
Tue Jun 2 12:09:40 2009
@@ -0,0 +1,121 @@
+/*
+ * 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.linker;
+
+import com.google.gwt.core.ext.linker.StatementRanges;
+import com.google.gwt.core.ext.linker.impl.StandardStatementRanges;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+
+/**
+ * Tests the script chunking in the {...@link IFrameLinker}.
+ */
+public class ScriptChunkingTest extends TestCase {
+ /**
+ * A class for building up JavaScript that has statements and
non-statement
+ * code interleaved.
+ */
+ private static class ScriptWithRangesBuilder {
+ private final ArrayList<Integer> ends = new ArrayList<Integer>();
+ private final StringBuffer script = new StringBuffer();
+ private final ArrayList<Integer> starts = new ArrayList<Integer>();
+
+ public void addNonStatement(String string) {
+ script.append(string);
+ }
+
+ public void addStatement(String string) {
+ starts.add(script.length());
+ script.append(string);
+ ends.add(script.length());
+ }
+
+ public String getJavaScript() {
+ return script.toString();
+ }
+
+ public StatementRanges getRanges() {
+ return new StandardStatementRanges(starts, ends);
+ }
+ }
+
+ public void testBasics() {
+ ScriptWithRangesBuilder builder = new ScriptWithRangesBuilder();
+ builder.addNonStatement("{");
+ String stmt1 = "x=1;";
+ builder.addStatement(stmt1);
+ String stmt2 = "function x(){x = 2}\n";
+ builder.addStatement(stmt2);
+ String stmt3 = "x=3";
+ /*
+ * This one has no terminator, so the chunker must add one
+ */
+ builder.addStatement(stmt3);
+ builder.addNonStatement("}\n{");
+ String stmt4 = "x=5";
+ builder.addStatement(stmt4);
+ builder.addNonStatement("}");
+
+ String split = IFrameLinker.splitPrimaryJavaScript(builder.getRanges(),
+ builder.getJavaScript(), stmt1.length() + stmt2.length());
+ assertEquals(stmt1 + stmt2 + IFrameLinker.SCRIPT_CHUNK_SEPARATOR +
stmt3
+ + ';' + stmt4, split);
+ }
+
+ /**
+ * Test a chunk size large enough that no splitting happens.
+ */
+ public void testLongChunkSize() {
+ ScriptWithRangesBuilder builder = new ScriptWithRangesBuilder();
+ builder.addNonStatement("{");
+ builder.addNonStatement("{");
+ String stmt1 = "x=1;";
+ builder.addStatement(stmt1);
+ String stmt2 = "function x(){x = 2}\n";
+ builder.addStatement(stmt2);
+ String stmt3 = "x=3";
+ builder.addStatement(stmt3);
+ builder.addNonStatement("}\n{");
+ String stmt4 = "x=5";
+ builder.addStatement(stmt4);
+ builder.addNonStatement("}");
+
+ String split = IFrameLinker.splitPrimaryJavaScript(builder.getRanges(),
+ builder.getJavaScript(), 10000);
+ assertEquals(stmt1 + stmt2 + stmt3 + ';' + stmt4, split);
+ }
+
+ /**
+ * Test with chunking disabled.
+ */
+ public void testNegativeChunkSize() {
+ ScriptWithRangesBuilder builder = new ScriptWithRangesBuilder();
+ builder.addNonStatement("{");
+ builder.addNonStatement("{");
+ builder.addStatement("x=1;");
+ builder.addStatement("function x(){x = 2}\n");
+ builder.addStatement("x=3");
+ builder.addNonStatement("}\n{");
+ builder.addStatement("x=5");
+ builder.addNonStatement("}");
+
+ String split = IFrameLinker.splitPrimaryJavaScript(builder.getRanges(),
+ builder.getJavaScript(), -1);
+ assertEquals(builder.getJavaScript(), split);
+ }
+}
Modified: trunk/user/src/com/google/gwt/core/CompilerParameters.gwt.xml
==============================================================================
--- trunk/user/src/com/google/gwt/core/CompilerParameters.gwt.xml
(original)
+++ trunk/user/src/com/google/gwt/core/CompilerParameters.gwt.xml Tue Jun
2 12:09:40 2009
@@ -16,11 +16,23 @@
<!--
-->
<module>
<!--
- This is the maximum number of variables in any var statement GWT
- will emit. This avoids a bug in some browsers including
- the initial beta of Safari 4. See Issue 3455. If it is set to -1,
- then there is no limit.
+ This is the maximum number of variables in any var statement GWT will
emit. This avoids a bug in
+ some browsers including the initial beta of Safari 4. See Issue 3455.
If it is set to -1, then
+ there is no limit.
-->
- <define-configuration-property name='compiler.max.vars.per.var'
is-multi-valued='false' />
+ <define-configuration-property name='compiler.max.vars.per.var'
+ is-multi-valued='false' />
<set-configuration-property name='compiler.max.vars.per.var'
value='2400' />
+
+ <!--
+ The iframe linker chunks its output into multiple <script> tags. The
default size is fine for
+ production use; it is overridable mainly for test cases. This size
must be small enough that
+ block-size restrictions in IE are satisfied, because the script tag
chunking undoes
+ JsIEBlockSizeVisitor. If it's set to -1, then no chunking is
performed and
+ JsIEBlockSizeVisitor has its usual effect.
+ -->
+ <define-configuration-property name="iframe.linker.script.chunk.size"
+ is-multi-valued="false" />
+ <set-configuration-property name="iframe.linker.script.chunk.size"
+ value="30000" />
</module>
Modified: trunk/user/test/com/google/gwt/dev/jjs/CompilerSuite.gwt.xml
==============================================================================
--- trunk/user/test/com/google/gwt/dev/jjs/CompilerSuite.gwt.xml
(original)
+++ trunk/user/test/com/google/gwt/dev/jjs/CompilerSuite.gwt.xml Tue Jun
2
12:09:40 2009
@@ -13,9 +13,18 @@
<!-- limitations under the
License. -->
<module>
+ <inherits name="com.google.gwt.core.Core"/>
+
<source path='test' />
<generate-with class="com.google.gwt.dev.jjs.UnstableGenerator">
<when-type-assignable
class="com.google.gwt.dev.jjs.test.UnstableGeneratorTest.UnstableResult" />
</generate-with>
+
+ <!--
+ Specify some tighter compiler settings so that those compiler paths
+ are tested by the normal GWT regression suite.
+ -->
+ <set-configuration-property name='compiler.max.vars.per.var' value='10'
/>
+ <set-configuration-property name='iframe.linker.script.chunk.size'
value='100'/>
</module>
Modified: trunk/user/test/com/google/gwt/emultest/EmulSuite.gwt.xml
==============================================================================
--- trunk/user/test/com/google/gwt/emultest/EmulSuite.gwt.xml (original)
+++ trunk/user/test/com/google/gwt/emultest/EmulSuite.gwt.xml Tue Jun 2
12:09:40 2009
@@ -16,10 +16,4 @@
<inherits name='com.google.gwt.junit.JUnit' />
<inherits name='org.apache.commons.Collections' />
<source path='java' />
-
- <!--
- Specify some tighter compiler settings so that those compiler paths
- are tested by the normal GWT regression suite
- -->
- <set-configuration-property name='compiler.max.vars.per.var' value='10'
/>
</module>
--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---