All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.dev.jjs.impl.JsIEBlockTextTransformer Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * 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.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.
 */
public class JsIEBlockTextTransformer extends JsAbstractTextTransformer {

  // uncomment to test

  // private static final int MAX_BLOCK_SIZE = 10;
  private static final int MAX_BLOCK_SIZE = 1 << 15 - 1;

  private int currentStatementCount;

  private boolean doSplits;
  
  private Set statementsAddedBlockClose = new HashSet();
  
  private Set statementsAddedBlockOpen = new HashSet();

  public JsIEBlockTextTransformer(JsAbstractTextTransformer xformer) {
    super(xformer);
  }

  public JsIEBlockTextTransformer(String js, StatementRanges statementRanges, 
      Map sourceInfoMap) {
    super(js, statementRanges, sourceInfoMap);
  }

  /**
   * Do not perform clustering, only fix up IE7 block issue.
   */
  @Override
  public void exec() {
    doSplits = statementRanges.numStatements() > MAX_BLOCK_SIZE;
    if (doSplits) {
      int statementIndices[] = new int[statementRanges.numStatements()];
      for (int i = 0; i < statementRanges.numStatements(); i++) {
        statementIndices[i] = i;
      }
      recomputeJsAndStatementRanges(statementIndices);
    }
  }
  
  public Set getStatementsAddedBlockClose() {
    return statementsAddedBlockClose;
  }
  
  public Set getStatementsAddedBlockOpen() {
    return statementsAddedBlockOpen;
  }

  /**
   * Record start of statement, and optionally inject new open block.
   */
  @Override
  protected void beginStatement(int index, StringBuilder newJs, ArrayList starts) {
    if (doSplits && currentStatementCount == 0) {
      super.beginStatement(index, newJs, starts);
      newJs.append('{');
      statementsAddedBlockOpen.add(Integer.valueOf(index));
    } else if (!doSplits) {
      super.beginStatement(index, newJs, starts);
    }
  }

  @Override
  protected void beginStatements(StringBuilder newJs, ArrayList starts,
      ArrayList ends) {
    super.beginStatements(newJs, starts, ends);
    currentStatementCount = 0;
  }

  /**
   * Record end of statement, and optionally inject close block, if block is
   * full.
   */
  @Override
  protected void endStatement(int index, StringBuilder newJs, ArrayList ends) {
    currentStatementCount++;
    if (doSplits && currentStatementCount == MAX_BLOCK_SIZE) {
      newJs.append('}');
      super.endStatement(index, newJs, ends);
      currentStatementCount = 0;
      statementsAddedBlockClose.add(Integer.valueOf(index));
    } else if (!doSplits) {
      super.endStatement(index, newJs, ends);
    }
  }

  /**
   * Used to close a trailing block which never filled.
   */
  @Override
  protected void endStatements(StringBuilder newJs, ArrayList starts,
      ArrayList ends) {
    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 updatedInfoMap = new HashMap();
      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 shiftAdded = new HashSet();
      
      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 ends) {
    if (doSplits && currentStatementCount > 0 && currentStatementCount < MAX_BLOCK_SIZE) {
      newJs.append("}");
      ends.add(newJs.length());
      statementsAddedBlockClose.add(Integer.valueOf(ends.size() - 1));
    }
  }
  
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy