blob: bcab11d357828908205a351c01baafb69566fead [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* 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.android.jack.transformations.ast;
import com.android.jack.Options;
import com.android.jack.ir.SideEffectOperation;
import com.android.jack.ir.ast.JAbsentArrayDimension;
import com.android.jack.ir.ast.JArrayRef;
import com.android.jack.ir.ast.JBinaryOperation;
import com.android.jack.ir.ast.JConditionalExpression;
import com.android.jack.ir.ast.JDoStatement;
import com.android.jack.ir.ast.JDynamicCastOperation;
import com.android.jack.ir.ast.JExpression;
import com.android.jack.ir.ast.JFieldInitializer;
import com.android.jack.ir.ast.JForStatement;
import com.android.jack.ir.ast.JIfStatement;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JMethodCall;
import com.android.jack.ir.ast.JNewArray;
import com.android.jack.ir.ast.JPrimitiveType;
import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
import com.android.jack.ir.ast.JReturnStatement;
import com.android.jack.ir.ast.JSwitchStatement;
import com.android.jack.ir.ast.JType;
import com.android.jack.ir.ast.JUnaryOperation;
import com.android.jack.ir.ast.JVisitor;
import com.android.jack.ir.ast.JWhileStatement;
import com.android.jack.ir.types.JNumericType;
import com.android.jack.util.filter.Filter;
import com.android.sched.item.Description;
import com.android.sched.schedulable.Constraint;
import com.android.sched.schedulable.RunnableSchedulable;
import com.android.sched.util.config.ThreadConfig;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nonnull;
/**
* Check that there are no more implicit casts and boxing/unboxing between numeric types.
*/
@Description("Check that there are no more implicit casts and boxing/unboxing between numeric"
+ " types.")
@Constraint(no = {SideEffectOperation.class, InitInNewArray.class})
public class NumericConversionChecker implements RunnableSchedulable<JMethod> {
@Nonnull
private final Filter<JMethod> filter = ThreadConfig.get(Options.METHOD_FILTER);
static class Visitor extends JVisitor {
@Nonnull
private static final String MISSING_UNBOXING_ERROR = "Missing unboxing";
@Nonnull
private static final String MISSING_BOXING_ERROR = "Missing boxing";
@Nonnull
private static final String MISSING_CAST_ERROR = "Missing numeric cast";
@Override
public void endVisit(@Nonnull JReturnStatement returnStatement) {
JExpression returnExpr = returnStatement.getExpr();
if (returnExpr != null) {
JType expectedType = returnStatement.getParent(JMethod.class).getType();
checkBoxingOrUnboxing(returnExpr, expectedType);
checkCast(returnExpr, expectedType);
}
super.endVisit(returnStatement);
}
@Override
public void endVisit(@Nonnull JForStatement forStmt) {
checkUnboxing(forStmt.getTestExpr());
super.endVisit(forStmt);
}
@Override
public void endVisit(@Nonnull JWhileStatement whileStmt) {
checkUnboxing(whileStmt.getTestExpr());
super.endVisit(whileStmt);
}
@Override
public void endVisit(@Nonnull JDoStatement doStmt) {
checkUnboxing(doStmt.getTestExpr());
super.endVisit(doStmt);
}
@Override
public void endVisit(@Nonnull JConditionalExpression conditional) {
checkUnboxing(conditional.getIfTest());
JExpression thenExpr = conditional.getThenExpr();
JExpression elseExpr = conditional.getElseExpr();
JType conditionalType = conditional.getType();
checkBoxingOrUnboxing(thenExpr, conditionalType);
checkBoxingOrUnboxing(elseExpr, conditionalType);
checkCast(thenExpr, conditionalType);
checkCast(elseExpr, conditionalType);
super.endVisit(conditional);
}
@Override
public void endVisit(@Nonnull JIfStatement ifStmt) {
checkUnboxing(ifStmt.getIfExpr());
super.endVisit(ifStmt);
}
@Override
public void endVisit(@Nonnull JSwitchStatement switchStmt) {
checkUnboxing(switchStmt.getExpr());
super.endVisit(switchStmt);
}
@Override
public void endVisit(@Nonnull JDynamicCastOperation cast) {
checkBoxingOrUnboxing(cast.getExpr(), cast.getCastType());
super.endVisit(cast);
}
@Override
public void endVisit(@Nonnull JBinaryOperation binary) {
JExpression lhs = binary.getLhs();
JType lhsType = lhs.getType();
JExpression rhs = binary.getRhs();
JType rhsType = rhs.getType();
switch (binary.getOp()) {
case CONCAT:
case ASG_CONCAT:
case ASG_ADD:
case ASG_DIV:
case ASG_MOD:
case ASG_MUL:
case ASG_SUB:
case ASG_BIT_AND:
case ASG_BIT_OR:
case ASG_BIT_XOR:
case ASG_SHL:
case ASG_SHR:
case ASG_SHRU: {
// not concerned
break;
}
case ASG: {
checkBoxingOrUnboxing(rhs, lhsType);
if (lhsType instanceof JNumericType) {
checkCast(rhs, lhsType);
}
break;
}
case SHL:
case SHR:
case SHRU: {
checkUnboxing(lhs);
checkUnboxing(rhs);
checkCast(lhs, binary.getType());
checkCast(rhs, JPrimitiveTypeEnum.INT.getType());
break;
}
case BIT_AND:
case BIT_OR:
case BIT_XOR:
case AND:
case OR:
case ADD:
case DIV:
case MOD:
case MUL:
case SUB: {
JType expectedType = binary.getType();
checkUnboxing(lhs);
checkUnboxing(rhs);
checkCast(lhs, expectedType);
checkCast(rhs, expectedType);
break;
}
case GT:
case GTE:
case LT:
case LTE: {
JType expectedType = JPrimitiveType.getBinaryPromotionType(lhsType, rhsType);
checkUnboxing(lhs);
checkUnboxing(rhs);
checkCast(lhs, expectedType);
checkCast(rhs, expectedType);
break;
}
case EQ:
case NEQ: {
if (lhsType instanceof JNumericType || rhsType instanceof JNumericType) {
JType expectedType = JPrimitiveType.getBinaryPromotionType(lhsType, rhsType);
checkUnboxing(lhs);
checkUnboxing(rhs);
checkCast(lhs, expectedType);
checkCast(rhs, expectedType);
} else if (rhsType == JPrimitiveTypeEnum.BOOLEAN.getType()
|| lhsType == JPrimitiveTypeEnum.BOOLEAN.getType()) {
checkUnboxing(lhs);
checkUnboxing(rhs);
}
break;
}
}
super.endVisit(binary);
}
@Override
public void endVisit(@Nonnull JFieldInitializer init) {
JExpression initializer = init.getInitializer();
JType expectedType = init.getFieldRef().getType();
checkBoxingOrUnboxing(initializer, expectedType);
checkCast(initializer, expectedType);
super.endVisit(init);
}
@Override
public void endVisit(@Nonnull JMethodCall call) {
List<JExpression> args = call.getArgs();
List<JType> parameterTypes = call.getMethodId().getParamTypes();
assert args.size() == parameterTypes.size();
Iterator<JType> paramTypeIterator = parameterTypes.iterator();
for (JExpression jExpression : args) {
JType expectedType = paramTypeIterator.next();
checkBoxingOrUnboxing(jExpression, expectedType);
checkCast(jExpression, expectedType);
}
super.endVisit(call);
}
@Override
public void endVisit(@Nonnull JNewArray newArray) {
for (JExpression dimension : newArray.getDims()) {
if (!(dimension instanceof JAbsentArrayDimension)) {
if (dimension.getType() instanceof JPrimitiveType) {
checkCast(dimension, JPrimitiveTypeEnum.INT.getType());
} else {
throw new AssertionError(MISSING_UNBOXING_ERROR);
}
}
}
assert newArray.getInitializers().isEmpty() || newArray.hasConstantInitializer();
super.endVisit(newArray);
}
@Override
public void endVisit(@Nonnull JArrayRef arrayRef) {
JExpression indexExpr = arrayRef.getIndexExpr();
checkUnboxing(indexExpr);
checkCast(indexExpr, JPrimitiveTypeEnum.INT.getType());
super.endVisit(arrayRef);
}
@Override
public void endVisit(@Nonnull JUnaryOperation unary) {
switch (unary.getOp()) {
case DEC:
case INC:
case NOT:
case BIT_NOT:
case NEG: {
checkUnboxing(unary.getArg());
checkCast(unary.getArg(), unary.getType());
break;
}
}
super.endVisit(unary);
}
private void checkUnboxing(@Nonnull JExpression expr) {
if (!(expr.getType() instanceof JPrimitiveType)) {
throw new AssertionError(MISSING_UNBOXING_ERROR);
}
}
private void checkBoxingOrUnboxing(
@Nonnull JExpression expr, @Nonnull JType expectedType) {
JType type = expr.getType();
if (!(expectedType instanceof JPrimitiveType) && (type instanceof JPrimitiveType)) {
throw new AssertionError(MISSING_BOXING_ERROR);
} else if ((expectedType instanceof JPrimitiveType) && !(type instanceof JPrimitiveType)) {
throw new AssertionError(MISSING_UNBOXING_ERROR);
}
}
private void checkCast(@Nonnull JExpression exprToCast, @Nonnull JType expectedType) {
if (expectedType instanceof JNumericType && exprToCast.getType() != expectedType) {
throw new AssertionError(MISSING_CAST_ERROR);
}
}
}
@Override
public void run(@Nonnull JMethod method) throws Exception {
if (method.getEnclosingType().isExternal() || method.isNative() || method.isAbstract()
|| !filter.accept(this.getClass(), method)) {
return;
}
Visitor visitor = new Visitor();
visitor.accept(method);
}
}