Package com.google.gwt.dev.js

Source Code of com.google.gwt.dev.js.JsDuplicateCaseFolder$DuplicateCaseFolder

/*
* Copyright 2011 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.JsContext;
import com.google.gwt.dev.js.ast.JsModVisitor;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.js.ast.JsSwitch;
import com.google.gwt.dev.js.ast.JsSwitchMember;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
* Combine case labels with identical bodies. Case bodies that may fall through
* to the following case label and case bodies following a possible fallthrough
* are left undisturbed.
*
* For example, consider the following input:
*
* <pre>
* switch (x) {
*   case 0: y = 17; break;
*   case 1: if (z == 0) { y = 18; break; } else { y = 19 } // fallthrough else
*   case 2: return 22;
*   case 3: if (z == 0) { y = 18; break; } else { y = 19 } // fallthrough else
*   case 4: y = 17; break;
*   case 5: y = 17; break;
*   case 6: return 22;
* }
* </pre>
*
* This will be transformed into:
*
* <pre>
* switch (x) {
*   case 0: y = 17; break;
*   case 1: if (z == 0) { y = 18; break; } else { y = 19 }
*   case 6: case 2: return 22;
*   case 3: if (z == 0) { y = 18; break; } else { y = 19 }
*   case 5: case 4: y = 17; break;
* }
*
* <pre>
*
* Cases (2, 6) and (4, 5) have been coalesced.  Note that case 0 has not been
* combined with cases 4 and 5 since case 4 cannot be moved due to the potential
* fallthrough from case 3, and we currently only coalesce a given cases with a
* preceding case and so cannot move case 0 downward.
*
* Although this pattern is unlikely to occur frequently in hand-written code,
* it can account for a significant amount of space in generated code.
*/
public class JsDuplicateCaseFolder {

  private class DuplicateCaseFolder extends JsModVisitor {

    public DuplicateCaseFolder() {
    }

    @Override
    public boolean visit(JsSwitch x, JsContext ctx) {
      boolean modified = false;

      // A map from case body source code to the original case label
      // in which they appeared
      Map<String, JsSwitchMember> seen = new HashMap<String, JsSwitchMember>();

      // Original list of members
      List<JsSwitchMember> cases = x.getCases();
      // Coalesced list of members
      List<JsSwitchMember> newCases = new LinkedList<JsSwitchMember>();

      // Keep track of whether the previous case can fall through
      // to the current case
      boolean hasPreviousFallthrough = false;

      // Iterate over members and locate ones with bodies identical to
      // previous members
      for (JsSwitchMember member : cases) {
        List<JsStatement> stmts = member.getStmts();

        // Don't rewrite any cases that might fall through
        if (!unconditionalControlBreak(stmts)) {
          hasPreviousFallthrough = true;
          // copy the case into the output
          newCases.add(member);
          continue;
        }

        String body = toSource(stmts);
        JsSwitchMember previousCase = seen.get(body);
        if (previousCase == null || hasPreviousFallthrough) {
          // Don't coalesce a case that can be reached via fallthrough
          // from the previous case
          newCases.add(member);
          seen.put(body, member);
        } else {
          // Locate the position of the case that this case is to be
          // coalesced with. Note: linear search in output list
          int index = newCases.indexOf(previousCase);

          // Empty the case body and insert the case label into the output
          member.getStmts().clear();
          newCases.add(index, member);
          modified = true;
        }

        hasPreviousFallthrough = false;
      }

      // Rewrite the AST if any cases have changed
      if (modified) {
        didChange = true;
        cases.clear();
        cases.addAll(newCases);
      }

      return true;
    }

    private String toSource(List<JsStatement> stmts) {
      StringBuilder sb = new StringBuilder();
      for (JsStatement stmt : stmts) {
        sb.append(stmt.toSource(true));
        sb.append("\n"); // separate statements
      }
      return sb.toString();
    }

    /**
     * See {@link JsStatement#unconditionalControlBreak()}.
     */
    private boolean unconditionalControlBreak(List<JsStatement> stmts) {
      for (JsStatement stmt : stmts) {
        if (stmt.unconditionalControlBreak()) {
          return true;
        }
      }
      return false;
    }
  }

  // Needed for OptimizerTestBase
  public static boolean exec(JsProgram program) {
    return new JsDuplicateCaseFolder().execImpl(program.getFragmentBlock(0));
  }

  public JsDuplicateCaseFolder() {
  }

  private boolean execImpl(JsBlock fragment) {
    DuplicateCaseFolder dcf = new DuplicateCaseFolder();
    dcf.accept(fragment);
    return dcf.didChange();
  }
}
TOP

Related Classes of com.google.gwt.dev.js.JsDuplicateCaseFolder$DuplicateCaseFolder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.