| /* |
| * 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.ast.Context; |
| import com.google.gwt.dev.jjs.ast.JBinaryOperation; |
| import com.google.gwt.dev.jjs.ast.JBinaryOperator; |
| import com.google.gwt.dev.jjs.ast.JConditional; |
| import com.google.gwt.dev.jjs.ast.JDeclarationStatement; |
| import com.google.gwt.dev.jjs.ast.JExpression; |
| import com.google.gwt.dev.jjs.ast.JMethod; |
| import com.google.gwt.dev.jjs.ast.JMethodCall; |
| import com.google.gwt.dev.jjs.ast.JModVisitor; |
| import com.google.gwt.dev.jjs.ast.JNewArray; |
| import com.google.gwt.dev.jjs.ast.JParameter; |
| import com.google.gwt.dev.jjs.ast.JPrimitiveType; |
| import com.google.gwt.dev.jjs.ast.JProgram; |
| import com.google.gwt.dev.jjs.ast.JReturnStatement; |
| import com.google.gwt.dev.jjs.ast.JType; |
| |
| import java.util.List; |
| |
| /** |
| * Synthesize explicit casts to and from the primitive long type where such a |
| * cast would have been implicit. The explicit casts serve as markers for |
| * {@link ImplementCastsAndTypeChecks}. |
| */ |
| public class LongCastNormalizer { |
| |
| /** |
| * Synthesize casts to longs and from long to trigger conversions. |
| */ |
| private class ImplicitCastVisitor extends JModVisitor { |
| |
| private JMethod currentMethod; |
| private final JPrimitiveType longType; |
| |
| public ImplicitCastVisitor(JPrimitiveType longType) { |
| this.longType = longType; |
| } |
| |
| @Override |
| public void endVisit(JBinaryOperation x, Context ctx) { |
| JType lhsType = x.getLhs().getType(); |
| JType rhsType = x.getRhs().getType(); |
| JType resultType = x.getType(); |
| JBinaryOperator op = x.getOp(); |
| |
| if (program.isJavaLangString(resultType)) { |
| // Don't mess with concat. |
| return; |
| } |
| |
| if (lhsType == JPrimitiveType.BOOLEAN |
| && (op == JBinaryOperator.AND || op == JBinaryOperator.OR)) { |
| // Don't mess with if rewriter. |
| return; |
| } |
| |
| // Special case: shift operators always coerce a long RHS to int. |
| if (op.isShiftOperator()) { |
| if (rhsType == longType) { |
| rhsType = program.getTypePrimitiveInt(); |
| } |
| } else if (lhsType == longType || rhsType == longType) { |
| // We must coerce lhs and rhs to the same type, either long or a float. |
| |
| // Assume a long type. |
| JType coerceTo = longType; |
| |
| // But double / float takes precedence over long. |
| JPrimitiveType floatType = program.getTypePrimitiveFloat(); |
| JPrimitiveType doubleType = program.getTypePrimitiveDouble(); |
| // See if the lhs can coerce the rhs |
| if ((lhsType == floatType || lhsType == doubleType)) { |
| coerceTo = lhsType; |
| } |
| if (op.isAssignment()) { |
| // In an assignment, the lhs must coerce the rhs |
| coerceTo = lhsType; |
| } else if ((rhsType == floatType || rhsType == doubleType)) { |
| coerceTo = rhsType; |
| } |
| lhsType = rhsType = coerceTo; |
| } |
| |
| JExpression newLhs = checkAndReplace(x.getLhs(), lhsType); |
| JExpression newRhs = checkAndReplace(x.getRhs(), rhsType); |
| if (newLhs != x.getLhs() || newRhs != x.getRhs()) { |
| JBinaryOperation binOp = |
| new JBinaryOperation(x.getSourceInfo(), resultType, op, newLhs, newRhs); |
| ctx.replaceMe(binOp); |
| } |
| } |
| |
| @Override |
| public void endVisit(JConditional x, Context ctx) { |
| JExpression newThen = checkAndReplace(x.getThenExpr(), x.getType()); |
| JExpression newElse = checkAndReplace(x.getElseExpr(), x.getType()); |
| if (newThen != x.getThenExpr() || newElse != x.getElseExpr()) { |
| JConditional newCond = |
| new JConditional(x.getSourceInfo(), x.getType(), x.getIfTest(), newThen, newElse); |
| ctx.replaceMe(newCond); |
| } |
| } |
| |
| @Override |
| public void endVisit(JDeclarationStatement x, Context ctx) { |
| JExpression init = x.getInitializer(); |
| if (init != null) { |
| init = checkAndReplace(init, x.getVariableRef().getType()); |
| if (init != x.getInitializer()) { |
| JDeclarationStatement newStmt = |
| new JDeclarationStatement(x.getSourceInfo(), x.getVariableRef(), init); |
| ctx.replaceMe(newStmt); |
| } |
| } |
| } |
| |
| @Override |
| public void endVisit(JMethod x, Context ctx) { |
| currentMethod = null; |
| } |
| |
| @Override |
| public void endVisit(JMethodCall x, Context ctx) { |
| List<JParameter> params = x.getTarget().getParams(); |
| for (int i = 0; i < params.size(); ++i) { |
| JParameter param = params.get(i); |
| JExpression arg = x.getArgs().get(i); |
| JExpression newArg = checkAndReplace(arg, param.getType()); |
| if (arg != newArg) { |
| x.setArg(i, newArg); |
| madeChanges(); |
| } |
| } |
| } |
| |
| @Override |
| public void endVisit(JNewArray x, Context ctx) { |
| JType elementType = x.getArrayType().getElementType(); |
| List<JExpression> initializers = x.getInitializers(); |
| if (initializers != null) { |
| for (int i = 0; i < initializers.size(); ++i) { |
| JExpression initializer = initializers.get(i); |
| JExpression newInitializer = checkAndReplace(initializer, elementType); |
| if (initializer != newInitializer) { |
| initializers.set(i, newInitializer); |
| madeChanges(); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void endVisit(JReturnStatement x, Context ctx) { |
| JExpression expr = x.getExpr(); |
| if (expr != null) { |
| JExpression newExpr = checkAndReplace(expr, currentMethod.getType()); |
| if (expr != newExpr) { |
| ctx.replaceMe(newExpr.makeReturnStatement()); |
| } |
| } |
| } |
| |
| @Override |
| public boolean visit(JMethod x, Context ctx) { |
| currentMethod = x; |
| return true; |
| } |
| |
| /** |
| * Returns an explicit cast if the target type is long and the input |
| * expression is not a long, or if the target type is floating point and the |
| * expression is a long. TODO(spoon): there is no floating point in this |
| * method; update the comment |
| */ |
| private JExpression checkAndReplace(JExpression arg, JType targetType) { |
| if (targetType != longType && arg.getType() != longType) { |
| return arg; |
| } |
| return Simplifier.cast(targetType, arg); |
| } |
| } |
| |
| public static void exec(JProgram program) { |
| new LongCastNormalizer(program).execImpl(); |
| } |
| |
| private final JProgram program; |
| |
| private LongCastNormalizer(JProgram program) { |
| this.program = program; |
| } |
| |
| private void execImpl() { |
| ImplicitCastVisitor visitor = new ImplicitCastVisitor(program.getTypePrimitiveLong()); |
| visitor.accept(program); |
| } |
| } |