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

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

There is a newer version: 2.10.0
Show newest version
/*
 * 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.jjs.impl;

import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.ast.JBinaryOperation;
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
import com.google.gwt.dev.jjs.ast.JBlock;
import com.google.gwt.dev.jjs.ast.JBooleanLiteral;
import com.google.gwt.dev.jjs.ast.JCastOperation;
import com.google.gwt.dev.jjs.ast.JConditional;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JExpressionStatement;
import com.google.gwt.dev.jjs.ast.JIfStatement;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JNode;
import com.google.gwt.dev.jjs.ast.JPrefixOperation;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JReturnStatement;
import com.google.gwt.dev.jjs.ast.JStatement;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JUnaryOperation;
import com.google.gwt.dev.jjs.ast.JUnaryOperator;
import com.google.gwt.dev.jjs.ast.JValueLiteral;
import com.google.gwt.dev.jjs.ast.js.JMultiExpression;

import java.util.List;

/**
 * Methods that both construct and try to simplify AST nodes. If simplification
 * fails, then the methods will return an original, unmodified version of the
 * node if one is supplied. The routines do not recurse into their arguments;
 * the arguments are assumed to already be simplified as much as possible.
 */
public class Simplifier {
  /**
   * TODO: if the AST were normalized, we wouldn't need this.
   */
  public static boolean isEmpty(JStatement stmt) {
    if (stmt == null) {
      return true;
    }
    return (stmt instanceof JBlock && ((JBlock) stmt).getStatements().isEmpty());
  }

  /**
   * Negate the supplied expression if negating it makes the expression shorter.
   * Otherwise, return null.
   */
  private static JExpression maybeUnflipBoolean(JExpression expr) {
    if (expr instanceof JUnaryOperation) {
      JUnaryOperation unop = (JUnaryOperation) expr;
      if (unop.getOp() == JUnaryOperator.NOT) {
        return unop.getArg();
      }
    }
    return null;
  }

  private static  List allButLast(List list) {
    return list.subList(0, list.size() - 1);
  }

  private static  T last(List list) {
    return list.get(list.size() - 1);
  }

  /**
   * This class provides only static methods. No instances will ever be created.
   */
  private Simplifier() {
  }

  /**
   * Simplify cast operations. Used when creating a cast in DeadCodeElimination. For simplifying
   * casts that are actually in the AST, cast(JCastOperation) is used instead.
   *
   * 
   * (int) 1 -> 1
   * (A) (a,b) -> (a, (A) b)
   * 
* * @param type the Type to cast the expression exp to. * @param exp the current JExpression under the cast as it is being simplified. * @return the simplified expression. */ public static JExpression cast(JType type, JExpression exp) { return castImpl(null, exp.getSourceInfo(), type, exp); } /** * Simplify cast operations. * *
   * (int) 1 -> 1
   * (A) (a,b) -> (a, (A) b)
   * 
* * @param exp a JCastOperation to be simplified. * @return the simplified expression if a simplification was possible; exp otherwise. */ public static JExpression cast(JCastOperation exp) { return castImpl(exp, exp.getSourceInfo(), exp.getCastType(), exp.getExpr()); } private static JExpression castImpl(JExpression original, SourceInfo info, JType type, JExpression exp) { info = getBestSourceInfo(original, info, exp); if (exp instanceof JMultiExpression) { // (T)(a,b,c) -> a,b,(T) c JMultiExpression expMulti = (JMultiExpression) exp; JMultiExpression newMulti = new JMultiExpression(info); newMulti.addExpressions(allButLast(expMulti.getExpressions())); newMulti.addExpressions(castImpl(null, info, type, last(expMulti.getExpressions()))); // TODO(rluble): immediately simplify the resulting multi. // TODO(rluble): refactor common outward JMultiExpression movement. return newMulti; } if (type == exp.getType()) { return exp; } if ((type instanceof JPrimitiveType) && (exp instanceof JValueLiteral)) { // Statically evaluate casting literals. JPrimitiveType primitiveType = (JPrimitiveType) type; JValueLiteral expLit = (JValueLiteral) exp; JValueLiteral casted = primitiveType.coerce(expLit); if (casted != null) { return casted; } } /* * Discard casts from byte or short to int, because such casts are always * implicit anyway. Cannot coerce char since that would change the semantics * of concat. */ if (type == JPrimitiveType.INT) { JType expType = exp.getType(); if ((expType == JPrimitiveType.SHORT) || (expType == JPrimitiveType.BYTE)) { return exp; } } // no simplification made if (original != null) { return original; } return new JCastOperation(info, type, exp); } /** * Simplify conditional expressions. * *
   * (a,b,c)?d:e -> a,b,(c?d:e)
   * true ? then : else -> then
   * false ? then : else -> else
   * cond ? true : else) -> cond || else
   * cond ? false : else -> !cond && else
   * cond ? then : true -> !cond || then
   * cond ? then : false -> cond && then
   * !cond ? then : else -> cond ? else : then
   * 
* * @param exp a JCondintional to be simplified. * @return the simplified expression if a simplification was possible; exp otherwise. */ public static JExpression conditional(JConditional exp) { return conditionalImpl(exp, exp.getSourceInfo(), exp.getType(), exp.getIfTest(), exp.getThenExpr(), exp.getElseExpr()); } private static JExpression conditionalImpl(JConditional original, SourceInfo info, JType type, JExpression condExpr, JExpression thenExpr, JExpression elseExpr) { info = getBestSourceInfo(original, info, condExpr); if (condExpr instanceof JMultiExpression) { // (a,b,c)?d:e -> a,b,(c?d:e) // TODO(spoon): do this outward multi movement for all AST nodes JMultiExpression condMulti = (JMultiExpression) condExpr; JMultiExpression newMulti = new JMultiExpression(info); newMulti.addExpressions(allButLast(condMulti.getExpressions())); newMulti.addExpressions(conditionalImpl(null, info, type, last(condMulti.getExpressions()), thenExpr, elseExpr)); // TODO(spoon): immediately simplify the resulting multi return newMulti; } if (condExpr instanceof JBooleanLiteral) { if (((JBooleanLiteral) condExpr).getValue()) { // e.g. (true ? then : else) -> then return thenExpr; } else { // e.g. (false ? then : else) -> else return elseExpr; } } else if (thenExpr instanceof JBooleanLiteral) { if (((JBooleanLiteral) thenExpr).getValue()) { // e.g. (cond ? true : else) -> cond || else return orImpl(null, info, condExpr, elseExpr); } else { // e.g. (cond ? false : else) -> !cond && else JExpression notCondExpr = notImpl(null, condExpr.getSourceInfo(), condExpr); return andImpl(null, info, notCondExpr, elseExpr); } } else if (elseExpr instanceof JBooleanLiteral) { if (((JBooleanLiteral) elseExpr).getValue()) { // e.g. (cond ? then : true) -> !cond || then JExpression notCondExpr = notImpl(null, condExpr.getSourceInfo(), condExpr); return orImpl(null, info, notCondExpr, thenExpr); } else { // e.g. (cond ? then : false) -> cond && then return andImpl(null, info, condExpr, thenExpr); } } else { // e.g. (!cond ? then : else) -> (cond ? else : then) JExpression unflipped = maybeUnflipBoolean(condExpr); if (unflipped != null) { return new JConditional(info, type, unflipped, elseExpr, thenExpr); } } // no simplification made if (original != null) { return original; } return new JConditional(info, type, condExpr, thenExpr, elseExpr); } /** * Simplifies an ifthenelse statement. * *
   * if(a,b,c) d [else e] -> {a; b; if(c) d [else e]; }
   * if(true) a [else b] -> a
   * if(false) a else b -> b
   * if(notImpl(c)) a else b -> if(c) b else a
   * if(true) ; else b -> true
   * if(false) a [else ;] -> false
   * if(c) ; [else ;] -> c
   *
* * @param stmt the statement to simplify. * @param currentMethod the method where the statement resides * @return the simplified statement if a simplification could be done and stmt * otherwise. */ public static JStatement ifStatement(JIfStatement stmt, JMethod currentMethod) { return ifStatementImpl(stmt, stmt.getSourceInfo(), stmt.getIfExpr(), stmt.getThenStmt(), stmt.getElseStmt(), currentMethod); } private static JStatement ifStatementImpl(JIfStatement original, SourceInfo info, JExpression condExpr, JStatement thenStmt,JStatement elseStmt, JMethod currentMethod) { info = getBestSourceInfo(original, info, condExpr); if (condExpr instanceof JMultiExpression) { // if(a,b,c) d else e -> {a; b; if(c) d else e; } JMultiExpression condMulti = (JMultiExpression) condExpr; JBlock newBlock = new JBlock(info); for (JExpression expr : allButLast(condMulti.getExpressions())) { newBlock.addStmt(expr.makeStatement()); } newBlock.addStmt(ifStatementImpl(null, info, last(condMulti.getExpressions()), thenStmt, elseStmt, currentMethod)); // TODO(spoon): immediately simplify the resulting block return newBlock; } if (condExpr instanceof JBooleanLiteral) { JBooleanLiteral booleanLiteral = (JBooleanLiteral) condExpr; boolean boolVal = booleanLiteral.getValue(); if (boolVal && !isEmpty(thenStmt)) { // If true, replace myself with then statement return thenStmt; } else if (!boolVal && !isEmpty(elseStmt)) { // If false, replace myself with else statement return elseStmt; } else { // just prune me return condExpr.makeStatement(); } } if (isEmpty(thenStmt) && isEmpty(elseStmt)) { return condExpr.makeStatement(); } if (!isEmpty(elseStmt)) { // if (!cond) foo else bar -> if (cond) bar else foo JExpression unflipped = Simplifier.maybeUnflipBoolean(condExpr); if (unflipped != null) { // Force sub-parts to blocks, otherwise we break else-if chains. // TODO: this goes away when we normalize the Java AST properly. thenStmt = ensureBlock(thenStmt); elseStmt = ensureBlock(elseStmt); return ifStatementImpl(null, info, unflipped, elseStmt, thenStmt, currentMethod); } } JStatement rewritenStatement = rewriteIfIntoBoolean(info, condExpr, thenStmt, elseStmt, currentMethod); if (rewritenStatement != null) { return rewritenStatement; } // no simplification made if (original != null) { return original; } return new JIfStatement(info, condExpr, thenStmt, elseStmt); } /** * Simplifies an negation expression. * * if(a,b,c) d else e -> {a; b; if(c) d else e; } * * @param expr the expression to simplify. * @return the simplified expression if a simplification could be done and expr * otherwise. */ public static JExpression not(JPrefixOperation expr) { return notImpl(expr, expr.getSourceInfo(), expr.getArg()); } private static JExpression notImpl(JPrefixOperation original, SourceInfo info, JExpression arg) { info = getBestSourceInfo(original, info, arg); if (arg instanceof JMultiExpression) { // !(a,b,c) -> (a,b,!c) JMultiExpression argMulti = (JMultiExpression) arg; JMultiExpression newMulti = new JMultiExpression(info); newMulti.addExpressions(allButLast(argMulti.getExpressions())); newMulti.addExpressions(notImpl(null, info, last(argMulti.getExpressions()))); // TODO(spoon): immediately simplify the newMulti return newMulti; } if (arg instanceof JBinaryOperation) { // try to invert the binary operator JBinaryOperation argOp = (JBinaryOperation) arg; JBinaryOperator op = argOp.getOp(); JBinaryOperator newOp = null; if (op == JBinaryOperator.EQ) { // e.g. !(x == y) -> x != y newOp = JBinaryOperator.NEQ; } else if (op == JBinaryOperator.NEQ) { // e.g. !(x != y) -> x == y newOp = JBinaryOperator.EQ; } else if (op == JBinaryOperator.GT) { // e.g. !(x > y) -> x <= y newOp = JBinaryOperator.LTE; } else if (op == JBinaryOperator.LTE) { // e.g. !(x <= y) -> x > y newOp = JBinaryOperator.GT; } else if (op == JBinaryOperator.GTE) { // e.g. !(x >= y) -> x < y newOp = JBinaryOperator.LT; } else if (op == JBinaryOperator.LT) { // e.g. !(x < y) -> x >= y newOp = JBinaryOperator.GTE; } if (newOp != null) { JBinaryOperation newBinOp = new JBinaryOperation(info, argOp.getType(), newOp, argOp.getLhs(), argOp.getRhs()); return newBinOp; } } else if (arg instanceof JPrefixOperation) { // try to invert the unary operator JPrefixOperation argOp = (JPrefixOperation) arg; JUnaryOperator op = argOp.getOp(); // e.g. !!x -> x if (op == JUnaryOperator.NOT) { return argOp.getArg(); } } else if (arg instanceof JBooleanLiteral) { JBooleanLiteral booleanLit = (JBooleanLiteral) arg; return JBooleanLiteral.get(!booleanLit.getValue()); } // no simplification made if (original != null) { return original; } return new JPrefixOperation(info, JUnaryOperator.NOT, arg); } /** * Simplify short circuit AND expressions. * *
   * true && isWhatever() -> isWhatever()
   * false && isWhatever() -> false
   *
   * isWhatever() && true -> isWhatever()
   * isWhatever() && false -> false, unless side effects
   *
   * (a, b) && c -> (a, b && c)
   * 
* * @param exp an AND JBinaryExpression to be simplified. * @return the simplified expression if a simplification was possible; exp otherwise. * */ public static JExpression and(JBinaryOperation exp) { assert exp.getOp() == JBinaryOperator.AND : "Simplifier.and was called with " + exp; return andImpl(exp, null, exp.getLhs(), exp.getRhs()); } private static JExpression andImpl(JBinaryOperation original, SourceInfo info, JExpression lhs, JExpression rhs) { info = getBestSourceInfo(original, info, lhs); if (lhs instanceof JMultiExpression) { // (a,b,c)&&d -> a,b,(c&&d) JMultiExpression lhsMulti = (JMultiExpression) lhs; JMultiExpression newMulti = new JMultiExpression(info); newMulti.addExpressions(allButLast(lhsMulti.getExpressions())); newMulti.addExpressions(andImpl(null, info, last(lhsMulti.getExpressions()), rhs)); // TODO(rluble): immediately simplify the resulting multi. // TODO(rluble): refactor common outward JMultiExpression movement. return newMulti; } if (lhs instanceof JBooleanLiteral) { JBooleanLiteral booleanLiteral = (JBooleanLiteral) lhs; if (booleanLiteral.getValue()) { return rhs; } else { return lhs; } } else if (rhs instanceof JBooleanLiteral) { JBooleanLiteral booleanLiteral = (JBooleanLiteral) rhs; if (booleanLiteral.getValue()) { return lhs; } else if (!lhs.hasSideEffects()) { return rhs; } } // no simplification made if (original != null) { return original; } return new JBinaryOperation(info, rhs.getType(), JBinaryOperator.AND, lhs, rhs); } /** * Simplify short circuit OR expressions. * *
   * true || isWhatever() -> true
   * false || isWhatever() -> isWhatever()
   *
   * isWhatever() || false isWhatever()
   * isWhatever() || true -> true, unless side effects
   *
   * (a, b) || c -> (a, b || c)
   * 
* * @param exp an OR JBinaryExpression to be simplified. * @return the simplified expression if a simplification was possible; exp otherwise. * */ public static JExpression or(JBinaryOperation exp) { assert exp.getOp() == JBinaryOperator.OR : "Simplifier.and was called with " + exp; return orImpl(exp, null, exp.getLhs(), exp.getRhs()); } private static JExpression orImpl(JBinaryOperation original, SourceInfo info, JExpression lhs, JExpression rhs) { info = getBestSourceInfo(original, info, lhs); if (lhs instanceof JMultiExpression) { // (a,b,c)|| d -> a,b,(c||d) JMultiExpression lhsMulti = (JMultiExpression) lhs; JMultiExpression newMulti = new JMultiExpression(info); newMulti.addExpressions(allButLast(lhsMulti.getExpressions())); newMulti.addExpressions(orImpl(null, info, last(lhsMulti.getExpressions()), rhs)); // TODO(rluble): immediately simplify the resulting multi. // TODO(rluble): refactor common outward JMultiExpression movement. return newMulti; } if (lhs instanceof JBooleanLiteral) { JBooleanLiteral booleanLiteral = (JBooleanLiteral) lhs; if (booleanLiteral.getValue()) { return lhs; } else { return rhs; } } else if (rhs instanceof JBooleanLiteral) { JBooleanLiteral booleanLiteral = (JBooleanLiteral) rhs; if (!booleanLiteral.getValue()) { return lhs; } else if (!lhs.hasSideEffects()) { return rhs; } } // no simplification made if (original != null) { return original; } return new JBinaryOperation(info, rhs.getType(), JBinaryOperator.OR, lhs, rhs); } private static JStatement ensureBlock(JStatement stmt) { if (stmt == null) { return null; } if (!(stmt instanceof JBlock)) { JBlock block = new JBlock(stmt.getSourceInfo()); block.addStmt(stmt); stmt = block; } return stmt; } private static JExpression extractExpression(JStatement stmt) { if (stmt instanceof JExpressionStatement) { JExpressionStatement statement = (JExpressionStatement) stmt; return statement.getExpr(); } return null; } private static JStatement extractSingleStatement(JStatement stmt) { if (stmt instanceof JBlock) { JBlock block = (JBlock) stmt; if (block.getStatements().size() == 1) { return extractSingleStatement(block.getStatements().get(0)); } } return stmt; } /** * Determine the best SourceInfo to use in a particular transformation. * * @param original the original node that is being transformed. Can be null. * @param info an explicit SourceInfo that might be used, Can be null. * @param defaultNode a node from where to obtain the SourceInfo. * @return a SourceInfo chosen according to the following priority info>original>default. */ private static SourceInfo getBestSourceInfo(JNode original, SourceInfo info, JNode defaultNode) { if (info == null) { if (original == null) { info = defaultNode.getSourceInfo(); } else { info = original.getSourceInfo(); } } return info; } private static JStatement rewriteIfIntoBoolean(SourceInfo sourceInfo, JExpression condExpr, JStatement thenStmt, JStatement elseStmt, JMethod currentMethod) { thenStmt = extractSingleStatement(thenStmt); elseStmt = extractSingleStatement(elseStmt); if (thenStmt instanceof JReturnStatement && elseStmt instanceof JReturnStatement && currentMethod != null) { // Special case // if () { return ..; } else { return ..; } => // return ... ? ... : ...; JExpression thenExpression = ((JReturnStatement) thenStmt).getExpr(); JExpression elseExpression = ((JReturnStatement) elseStmt).getExpr(); if (thenExpression == null || elseExpression == null) { // empty returns are not supported. return null; } JConditional conditional = new JConditional(sourceInfo, currentMethod.getType(), condExpr, thenExpression, elseExpression); JReturnStatement returnStatement = conditional.makeReturnStatement(); return returnStatement; } if (elseStmt != null) { // if () { } else { } -> ... ? ... : ... ; JExpression thenExpression = extractExpression(thenStmt); JExpression elseExpression = extractExpression(elseStmt); if (thenExpression != null && elseExpression != null) { JConditional conditional = new JConditional(sourceInfo, JPrimitiveType.VOID, condExpr, thenExpression, elseExpression); return conditional.makeStatement(); } } else { // if () { } -> ... && ...; JExpression thenExpression = extractExpression(thenStmt); if (thenExpression != null) { JBinaryOperator binaryOperator = JBinaryOperator.AND; JExpression unflipExpression = maybeUnflipBoolean(condExpr); if (unflipExpression != null) { condExpr = unflipExpression; binaryOperator = JBinaryOperator.OR; } JBinaryOperation binaryOperation = new JBinaryOperation(sourceInfo, JPrimitiveType.VOID, binaryOperator, condExpr, thenExpression); return binaryOperation.makeStatement(); } } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy