Add a JsVisitor to restructure block-like structures to ensure compatibility with limits on the number of statements in a block in IE7.
Resolves issue 1440.
Patch by: bobv
Review by: scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1916 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 1812878..1def354 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -57,6 +57,7 @@
import com.google.gwt.dev.js.JsSourceGenerationVisitor;
import com.google.gwt.dev.js.JsStringInterner;
import com.google.gwt.dev.js.JsSymbolResolver;
+import com.google.gwt.dev.js.JsIEBlockSizeVisitor;
import com.google.gwt.dev.js.JsUnusedFunctionRemover;
import com.google.gwt.dev.js.JsVerboseNamer;
import com.google.gwt.dev.js.ast.JsProgram;
@@ -422,6 +423,10 @@
throw new InternalCompilerException("Unknown output mode");
}
+ // (12) Work around an IE7 bug
+ // http://code.google.com/p/google-web-toolkit/issues/detail?id=1440
+ JsIEBlockSizeVisitor.exec(jsProgram);
+
DefaultTextOutput out = new DefaultTextOutput(
options.getOutput().shouldMinimize());
JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out);
diff --git a/dev/core/src/com/google/gwt/dev/js/JsIEBlockSizeVisitor.java b/dev/core/src/com/google/gwt/dev/js/JsIEBlockSizeVisitor.java
new file mode 100644
index 0000000..56d6d85
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/JsIEBlockSizeVisitor.java
@@ -0,0 +1,123 @@
+/*
+ * 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.dev.js;
+
+import com.google.gwt.dev.js.ast.JsBlock;
+import com.google.gwt.dev.js.ast.JsCase;
+import com.google.gwt.dev.js.ast.JsContext;
+import com.google.gwt.dev.js.ast.JsDefault;
+import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsStatement;
+import com.google.gwt.dev.js.ast.JsSwitchMember;
+import com.google.gwt.dev.js.ast.JsVisitor;
+
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * Some versions of IE7 have a limit on the number of statements that can appear
+ * within a JsBlock. This visitor will restructure blocks and other block-like
+ * structures with too many statements in order to reduce the total number of
+ * statements that appear within any given block to fewer than
+ * {@value #MAX_BLOCK_SIZE} statements by creating nested blocks:
+ *
+ * <pre>
+ * {
+ * { statements }
+ * { statements }
+ * remainder of statements
+ * }
+ * </pre>
+ *
+ * This change is purely structural, it will not affect code flow.
+ */
+public class JsIEBlockSizeVisitor {
+
+ /**
+ * Visits every block-like structure.
+ */
+ private static class BlockVisitor extends JsVisitor {
+
+ @Override
+ public void endVisit(JsBlock x, JsContext<JsStatement> ctx) {
+ restructure(x.getStatements());
+ }
+
+ @Override
+ public void endVisit(JsCase x, JsContext<JsSwitchMember> ctx) {
+ restructure(x.getStmts());
+ }
+
+ @Override
+ public void endVisit(JsDefault x, JsContext<JsSwitchMember> ctx) {
+ restructure(x.getStmts());
+ }
+
+ /**
+ * Perform the restructuring on a list of statements. Blocks are created as
+ * necessary to prevent any given block from exceeding the maximum size.
+ */
+ private void restructure(List<JsStatement> statements) {
+ // This outer loop will collapse the newly-created block into super-blocks
+ while (statements.size() > MAX_BLOCK_SIZE) {
+ ListIterator<JsStatement> i = statements.listIterator();
+ List<JsStatement> statementsInNewBlock = null;
+
+ // This loop represents a single fold over the list of statements
+ while (statements.size() > MAX_BLOCK_SIZE && i.hasNext()) {
+ JsStatement current = i.next();
+
+ if (statementsInNewBlock == null) {
+ // Replace the current statement with a new block
+ JsBlock newBlock = new JsBlock();
+ statementsInNewBlock = newBlock.getStatements();
+ i.set(newBlock);
+ } else {
+ /*
+ * There's an open replacement block, remove the statement from its
+ * current block.
+ */
+ i.remove();
+ }
+
+ // Move the statement into the new block
+ statementsInNewBlock.add(current);
+
+ /*
+ * If we hit the cap on a new block, discard the reference to create a
+ * new block for the next statement that we see.
+ */
+ if (statementsInNewBlock.size() == MAX_BLOCK_SIZE) {
+ statementsInNewBlock = null;
+ }
+ }
+ }
+ assert statements.size() <= MAX_BLOCK_SIZE;
+ }
+ }
+
+ // Use this value instead to test the effects of the visitor
+ // private static final int MAX_BLOCK_SIZE = 1 << 5;
+
+ private static final int MAX_BLOCK_SIZE = 1 << 15 - 1;
+
+ /**
+ * Entry point.
+ */
+ public static void exec(JsProgram program) {
+ (new BlockVisitor()).accept(program);
+ }
+}