blob: 640964f7bc92b46b6962a51c37f950e02400c1b9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.codehaus.groovy.classgen.asm;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.DynamicVariable;
import org.codehaus.groovy.ast.Variable;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.classgen.AsmClassGenerator;
import org.codehaus.groovy.runtime.BytecodeInterface8;
import java.util.HashMap;
import java.util.Map;
import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.byte_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.char_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.double_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.float_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
import static org.codehaus.groovy.ast.tools.WideningCategories.isBigDecCategory;
import static org.codehaus.groovy.ast.tools.WideningCategories.isDoubleCategory;
import static org.codehaus.groovy.ast.tools.WideningCategories.isIntCategory;
import static org.codehaus.groovy.ast.tools.WideningCategories.isLongCategory;
import static org.codehaus.groovy.ast.tools.WideningCategories.isNumberCategory;
import static org.codehaus.groovy.syntax.TokenUtil.removeAssignment;
import static org.codehaus.groovy.syntax.Types.DIVIDE;
import static org.codehaus.groovy.syntax.Types.LEFT_SHIFT;
import static org.codehaus.groovy.syntax.Types.LEFT_SQUARE_BRACKET;
import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT;
import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT_UNSIGNED;
/**
* This class is for internal use only!
* This class will dispatch to the right type adapters according to the
* kind of binary expression that is provided.
*/
public class BinaryExpressionMultiTypeDispatcher extends BinaryExpressionHelper {
private static class BinaryCharExpressionHelper extends BinaryIntExpressionHelper {
public BinaryCharExpressionHelper(WriterController wc) {
super(wc, charArraySet, charArrayGet);
}
private static final MethodCaller
charArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "cArrayGet"),
charArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "cArraySet");
@Override protected ClassNode getArrayGetResultType() { return ClassHelper.char_TYPE; }
}
private static class BinaryByteExpressionHelper extends BinaryIntExpressionHelper {
public BinaryByteExpressionHelper(WriterController wc) {
super(wc, byteArraySet, byteArrayGet);
}
private static final MethodCaller
byteArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "bArrayGet"),
byteArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "bArraySet");
@Override protected ClassNode getArrayGetResultType() { return ClassHelper.byte_TYPE; }
}
private static class BinaryShortExpressionHelper extends BinaryIntExpressionHelper {
public BinaryShortExpressionHelper(WriterController wc) {
super(wc, shortArraySet, shortArrayGet);
}
private static final MethodCaller
shortArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "sArrayGet"),
shortArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "sArraySet");
@Override protected ClassNode getArrayGetResultType() { return ClassHelper.short_TYPE; }
}
protected BinaryExpressionWriter[] binExpWriter = initializeDelegateHelpers();
protected BinaryExpressionWriter[] initializeDelegateHelpers() {
return new BinaryExpressionWriter[]{
/* 0: dummy */ new BinaryObjectExpressionHelper(getController()),
/* 1: int */ new BinaryIntExpressionHelper(getController()),
/* 2: long */ new BinaryLongExpressionHelper(getController()),
/* 3: double */ new BinaryDoubleExpressionHelper(getController()),
/* 4: char */ new BinaryCharExpressionHelper(getController()),
/* 5: byte */ new BinaryByteExpressionHelper(getController()),
/* 6: short */ new BinaryShortExpressionHelper(getController()),
/* 7: float */ new BinaryFloatExpressionHelper(getController()),
/* 8: bool */ new BinaryBooleanExpressionHelper(getController()),
};
}
public static final Map<ClassNode,Integer> typeMap = new HashMap<ClassNode,Integer>(14);
static {
typeMap.put(int_TYPE, 1); typeMap.put(long_TYPE, 2);
typeMap.put(double_TYPE, 3); typeMap.put(char_TYPE, 4);
typeMap.put(byte_TYPE, 5); typeMap.put(short_TYPE, 6);
typeMap.put(float_TYPE, 7); typeMap.put(boolean_TYPE, 8);
}
public static final String[] typeMapKeyNames = {"dummy", "int", "long", "double", "char", "byte", "short", "float", "boolean"};
public BinaryExpressionMultiTypeDispatcher(WriterController wc) {
super(wc);
}
private static int getOperandConversionType(ClassNode leftType, ClassNode rightType) {
if (isIntCategory(leftType) && isIntCategory(rightType)) return 1;
if (isLongCategory(leftType) && isLongCategory(rightType)) return 2;
if (isBigDecCategory(leftType) && isBigDecCategory(rightType)) return 0;
if (isDoubleCategory(leftType) && isDoubleCategory(rightType)) return 3;
return 0;
}
protected int getOperandType(ClassNode type) {
Integer ret = typeMap.get(type);
if (ret==null) return 0;
return ret;
}
@Deprecated
protected boolean doPrimtiveCompare(ClassNode leftType, ClassNode rightType, BinaryExpression binExp) {
return doPrimitiveCompare(leftType, rightType, binExp);
}
protected boolean doPrimitiveCompare(ClassNode leftType, ClassNode rightType, BinaryExpression binExp) {
Expression leftExp = binExp.getLeftExpression();
Expression rightExp = binExp.getRightExpression();
int operation = binExp.getOperation().getType();
int operationType = getOperandConversionType(leftType,rightType);
BinaryExpressionWriter bew = binExpWriter[operationType];
if (!bew.write(operation, true)) return false;
AsmClassGenerator acg = getController().getAcg();
OperandStack os = getController().getOperandStack();
leftExp.visit(acg);
os.doGroovyCast(bew.getNormalOpResultType());
rightExp.visit(acg);
os.doGroovyCast(bew.getNormalOpResultType());
bew.write(operation, false);
return true;
}
@Override
protected void evaluateCompareExpression(final MethodCaller compareMethod, BinaryExpression binExp) {
ClassNode current = getController().getClassNode();
TypeChooser typeChooser = getController().getTypeChooser();
Expression leftExp = binExp.getLeftExpression();
ClassNode leftType = typeChooser.resolveType(leftExp, current);
Expression rightExp = binExp.getRightExpression();
ClassNode rightType = typeChooser.resolveType(rightExp, current);
if (!doPrimitiveCompare(leftType, rightType, binExp)) {
super.evaluateCompareExpression(compareMethod, binExp);
}
}
@Override
protected void evaluateBinaryExpression(final String message, BinaryExpression binExp) {
int operation = removeAssignment(binExp.getOperation().getType());
ClassNode current = getController().getClassNode();
Expression leftExp = binExp.getLeftExpression();
ClassNode leftTypeOrig = getController().getTypeChooser().resolveType(leftExp, current);
ClassNode leftType = leftTypeOrig;
Expression rightExp = binExp.getRightExpression();
ClassNode rightType = getController().getTypeChooser().resolveType(rightExp, current);
AsmClassGenerator acg = getController().getAcg();
OperandStack os = getController().getOperandStack();
if (operation==LEFT_SQUARE_BRACKET) {
leftType = leftTypeOrig.getComponentType();
int operationType = getOperandType(leftType);
BinaryExpressionWriter bew = binExpWriter[operationType];
if ( leftTypeOrig.isArray() && isIntCastableType(rightExp) &&
bew.arrayGet(operation, true) &&
!binExp.isSafe())
{
leftExp.visit(acg);
os.doGroovyCast(leftTypeOrig);
rightExp.visit(acg);
os.doGroovyCast(int_TYPE);
bew.arrayGet(operation, false);
os.replace(bew.getArrayGetResultType(),2);
} else {
super.evaluateBinaryExpression(message, binExp);
}
} else if (operation == DIVIDE) {
int operationType = getOperandType(getController().getTypeChooser().resolveType(binExp, current));
BinaryExpressionWriter bew = binExpWriter[operationType];
if (bew.writeDivision(true)) {
leftExp.visit(acg);
os.doGroovyCast(bew.getDevisionOpResultType());
rightExp.visit(acg);
os.doGroovyCast(bew.getDevisionOpResultType());
bew.writeDivision(false);
} else {
super.evaluateBinaryExpression(message, binExp);
}
} else {
int operationType = getOperandConversionType(leftType,rightType);
BinaryExpressionWriter bew = binExpWriter[operationType];
if ( isShiftOperation(operation) && isIntCastableType(rightExp) &&
bew.write(operation, true))
{
leftExp.visit(acg);
os.doGroovyCast(bew.getNormalOpResultType());
rightExp.visit(acg);
os.doGroovyCast(int_TYPE);
bew.write(operation, false);
} else if (bew.write(operation, true)) {
leftExp.visit(acg);
os.doGroovyCast(bew.getNormalOpResultType());
rightExp.visit(acg);
os.doGroovyCast(bew.getNormalOpResultType());
bew.write(operation, false);
} else {
super.evaluateBinaryExpression(message, binExp);
}
}
}
private boolean isIntCastableType(Expression rightExp) {
ClassNode type = getController().getTypeChooser().resolveType(rightExp, getController().getClassNode());
return isNumberCategory(type);
}
private static boolean isShiftOperation(int operation) {
return operation==LEFT_SHIFT ||
operation==RIGHT_SHIFT ||
operation==RIGHT_SHIFT_UNSIGNED;
}
private static boolean isAssignmentToArray(BinaryExpression binExp) {
Expression leftExpression = binExp.getLeftExpression();
if (!(leftExpression instanceof BinaryExpression)) return false;
BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
return leftBinExpr.getOperation().getType() == LEFT_SQUARE_BRACKET;
}
private boolean doAssignmentToArray(BinaryExpression binExp) {
if (!isAssignmentToArray(binExp)) return false;
// we need to handle only assignment to arrays combined with an operation
// special here. e.g x[a] += b
int operation = removeAssignment(binExp.getOperation().getType());
ClassNode current = getController().getClassNode();
Expression leftExp = binExp.getLeftExpression();
ClassNode leftType = getController().getTypeChooser().resolveType(leftExp, current);
Expression rightExp = binExp.getRightExpression();
ClassNode rightType = getController().getTypeChooser().resolveType(rightExp, current);
int operationType = getOperandType(leftType);
BinaryExpressionWriter bew = binExpWriter[operationType];
boolean simulationSuccess = bew.arrayGet(LEFT_SQUARE_BRACKET, true);
simulationSuccess = simulationSuccess && bew.write(operation, true);
simulationSuccess = simulationSuccess && bew.arraySet(true);
if (!simulationSuccess) return false;
AsmClassGenerator acg = getController().getAcg();
OperandStack operandStack = getController().getOperandStack();
CompileStack compileStack = getController().getCompileStack();
// for x[a] += b we have the structure:
// x = left(left(binExp))), b = right(binExp), a = right(left(binExp)))
// for array set we need these values on stack: array, index, right
// for array get we need these values on stack: array, index
// to eval the expression we need x[a] = x[a]+b
// -> arraySet(x,a, x[a]+b)
// -> arraySet(x,a, arrayGet(x,a,b))
// --> x,a, x,a, b as operands
// --> load x, load a, DUP2, call arrayGet, load b, call operation,call arraySet
// since we cannot DUP2 here easily we will save the subscript and DUP x
// --> sub=a, load x, DUP, load sub, call arrayGet, load b, call operation, load sub, call arraySet
BinaryExpression arrayWithSubscript = (BinaryExpression) leftExp;
Expression subscript = arrayWithSubscript.getRightExpression();
// load array index: sub=a [load x, DUP, load sub, call arrayGet, load b, call operation, load sub, call arraySet]
subscript.visit(acg);
operandStack.doGroovyCast(int_TYPE);
int subscriptValueId = compileStack.defineTemporaryVariable("$sub", ClassHelper.int_TYPE, true);
// load array: load x and DUP [load sub, call arrayGet, load b, call operation, load sub, call arraySet]
arrayWithSubscript.getLeftExpression().visit(acg);
operandStack.doGroovyCast(leftType.makeArray());
operandStack.dup();
// array get: load sub, call arrayGet [load b, call operation, load sub, call arraySet]
operandStack.load(ClassHelper.int_TYPE, subscriptValueId);
bew.arrayGet(LEFT_SQUARE_BRACKET, false);
operandStack.replace(leftType, 2);
// complete rhs: load b, call operation [load sub, call arraySet]
binExp.getRightExpression().visit(acg);
if (! (bew instanceof BinaryObjectExpressionHelper)) {
// in primopts we convert to the left type for supported binary operations
operandStack.doGroovyCast(leftType);
}
bew.write(operation, false);
// let us save that value for the return
operandStack.dup();
int resultValueId = compileStack.defineTemporaryVariable("$result", rightType, true);
// array set: load sub, call arraySet []
operandStack.load(ClassHelper.int_TYPE, subscriptValueId);
operandStack.swap();
bew.arraySet(false);
operandStack.remove(3); // 3 operands, the array, the index and the value!
// load return value
operandStack.load(rightType, resultValueId);
// cleanup
compileStack.removeVar(resultValueId);
compileStack.removeVar(subscriptValueId);
return true;
}
@Override
protected void evaluateBinaryExpressionWithAssignment(String method, BinaryExpression binExp) {
if (doAssignmentToArray(binExp)) return;
if (doAssignmentToLocalVariable(method, binExp)) return;
super.evaluateBinaryExpressionWithAssignment(method, binExp);
}
private boolean doAssignmentToLocalVariable(String method, BinaryExpression binExp) {
Expression left = binExp.getLeftExpression();
if (left instanceof VariableExpression) {
VariableExpression ve = (VariableExpression) left;
Variable v = ve.getAccessedVariable();
if (v instanceof DynamicVariable) return false;
if (v instanceof PropertyExpression) return false;
/* field and declaration we don't return false */
} else {
return false;
}
evaluateBinaryExpression(method, binExp);
getController().getOperandStack().dup();
getController().getCompileStack().pushLHS(true);
binExp.getLeftExpression().visit(getController().getAcg());
getController().getCompileStack().popLHS();
return true;
}
@Override
protected void assignToArray(Expression orig, Expression receiver, Expression index, Expression rhsValueLoader, boolean safe) {
ClassNode current = getController().getClassNode();
ClassNode arrayType = getController().getTypeChooser().resolveType(receiver, current);
ClassNode arrayComponentType = arrayType.getComponentType();
int operationType = getOperandType(arrayComponentType);
BinaryExpressionWriter bew = binExpWriter[operationType];
AsmClassGenerator acg = getController().getAcg();
if (bew.arraySet(true) && arrayType.isArray() && !safe) {
OperandStack operandStack = getController().getOperandStack();
// load the array
receiver.visit(acg);
operandStack.doGroovyCast(arrayType);
// load index
index.visit(acg);
operandStack.doGroovyCast(int_TYPE);
// load rhs
rhsValueLoader.visit(acg);
operandStack.doGroovyCast(arrayComponentType);
// store value in array
bew.arraySet(false);
// load return value && correct operand stack stack
operandStack.remove(3);
rhsValueLoader.visit(acg);
} else {
super.assignToArray(orig, receiver, index, rhsValueLoader, safe);
}
}
@Override
protected void writePostOrPrefixMethod(int op, String method, Expression expression, Expression orig) {
ClassNode type = getController().getTypeChooser().resolveType(orig, getController().getClassNode());
int operationType = getOperandType(type);
BinaryExpressionWriter bew = binExpWriter[operationType];
if (bew.writePostOrPrefixMethod(op,true)) {
OperandStack operandStack = getController().getOperandStack();
// at this point the receiver will be already on the stack
operandStack.doGroovyCast(type);
bew.writePostOrPrefixMethod(op,false);
operandStack.replace(bew.getNormalOpResultType());
} else {
super.writePostOrPrefixMethod(op, method, expression, orig);
}
}
}