/* * 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.jjs.SourceInfo; 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.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 { private final JsProgram program; public BlockVisitor(JsProgram program) { this.program = program; } @Override public void endVisit(JsBlock x, JsContext ctx) { // JsFunctionClusterer handles restructuring top-level statement blocks if (!x.isGlobalBlock()) { restructure(x.getStatements()); } } @Override public void endVisit(JsCase x, JsContext ctx) { restructure(x.getStmts()); } @Override public void endVisit(JsDefault x, JsContext 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) { SourceInfo sourceInfo = program.createSourceInfoSynthetic(JsIEBlockSizeVisitor.class); // 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(sourceInfo); 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(program)).accept(program); } }