Generate compareTo() properly

Take into account:
1) resolved call to compareTo (instead of always calling Comparable's method)
2) types of both caller and callee, when primitive, to avoid wrong casting
(instead of always using caller's type)

 #KT-3078 Fixed
This commit is contained in:
Alexander Udalov
2012-11-27 22:09:57 +04:00
parent 0c5960922a
commit c9bdfd2f07
16 changed files with 261 additions and 22 deletions
@@ -311,15 +311,6 @@ public class AsmUtil {
mv.visitInsn(L2I);
}
static StackValue compareExpressionsOnStack(InstructionAdapter v, IElementType opToken, Type operandType) {
if (operandType.getSort() == Type.OBJECT) {
v.invokeinterface("java/lang/Comparable", "compareTo", "(Ljava/lang/Object;)I");
v.iconst(0);
operandType = Type.INT_TYPE;
}
return StackValue.cmp(opToken, operandType);
}
static StackValue genNullSafeEquals(
InstructionAdapter v,
IElementType opToken,
@@ -394,7 +385,7 @@ public class AsmUtil {
boolean rightNullable
) {
if ((isNumberPrimitive(leftType) || leftType.getSort() == Type.BOOLEAN) && leftType == rightType) {
return compareExpressionsOnStack(v, opToken, leftType);
return StackValue.cmp(opToken, leftType);
}
else {
if (opToken == JetTokens.EQEQEQ || opToken == JetTokens.EXCLEQEQEQ) {
@@ -2311,7 +2311,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
}
else if (opToken == JetTokens.LT || opToken == JetTokens.LTEQ ||
opToken == JetTokens.GT || opToken == JetTokens.GTEQ) {
return generateCompareOp(expression.getLeft(), expression.getRight(), opToken, expressionType(expression.getLeft()));
return generateCompareTo(expression);
}
else if (opToken == JetTokens.ELVIS) {
return generateElvis(expression);
@@ -2519,10 +2519,25 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
return StackValue.onStack(exprType);
}
private StackValue generateCompareOp(JetExpression left, JetExpression right, IElementType opToken, Type operandType) {
gen(left, operandType);
gen(right, operandType);
return compareExpressionsOnStack(v, opToken, operandType);
private StackValue generateCompareTo(JetBinaryExpression expression) {
DeclarationDescriptor target = bindingContext.get(BindingContext.REFERENCE_TARGET, expression.getOperationReference());
assert target instanceof FunctionDescriptor : "compareTo target should be a function: " + target;
FunctionDescriptor descriptor = (FunctionDescriptor) target;
StackValue receiver = gen(expression.getLeft());
Callable callable = resolveToCallable(descriptor, false);
StackValue result;
if (callable instanceof IntrinsicMethod) {
result = ((IntrinsicMethod) callable).generate(
this, v, Type.INT_TYPE, expression, Collections.singletonList(expression.getRight()), receiver, state);
} else {
result = invokeOperation(expression, descriptor, (CallableMethod) callable);
}
result.put(Type.INT_TYPE, v);
v.iconst(0);
return StackValue.cmp(expression.getOperationToken(), Type.INT_TYPE);
}
private StackValue generateAssignmentExpression(JetBinaryExpression expression) {
@@ -28,6 +28,8 @@ import org.jetbrains.jet.lang.psi.JetExpression;
import java.util.List;
import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive;
/**
* @author alex.tkachman
*/
@@ -43,21 +45,25 @@ public class CompareTo implements IntrinsicMethod {
@NotNull GenerationState state
) {
assert arguments != null;
receiver.put(receiver.type, v);
codegen.gen(arguments.get(0), receiver.type);
if (receiver.type == Type.BYTE_TYPE || receiver.type == Type.SHORT_TYPE || receiver.type == Type.CHAR_TYPE) {
JetExpression argument = arguments.get(0);
Type type = comparisonOperandType(receiver.type, codegen.expressionType(argument));
receiver.put(type, v);
codegen.gen(argument, type);
if (type == Type.BYTE_TYPE || type == Type.SHORT_TYPE || type == Type.CHAR_TYPE) {
v.sub(Type.INT_TYPE);
}
else if (receiver.type == Type.INT_TYPE) {
else if (type == Type.INT_TYPE) {
v.invokestatic("jet/runtime/Intrinsics", "compare", "(II)I");
}
else if (receiver.type == Type.LONG_TYPE) {
else if (type == Type.LONG_TYPE) {
v.invokestatic("jet/runtime/Intrinsics", "compare", "(JJ)I");
}
else if (receiver.type == Type.FLOAT_TYPE) {
else if (type == Type.FLOAT_TYPE) {
v.invokestatic("java/lang/Float", "compare", "(FF)I");
}
else if (receiver.type == Type.DOUBLE_TYPE) {
else if (type == Type.DOUBLE_TYPE) {
v.invokestatic("java/lang/Double", "compare", "(DD)I");
}
else {
@@ -65,4 +71,11 @@ public class CompareTo implements IntrinsicMethod {
}
return StackValue.onStack(Type.INT_TYPE);
}
private static Type comparisonOperandType(Type left, Type right) {
if (left == Type.DOUBLE_TYPE || right == Type.DOUBLE_TYPE) return Type.DOUBLE_TYPE;
if (left == Type.FLOAT_TYPE || right == Type.FLOAT_TYPE) return Type.FLOAT_TYPE;
if (left == Type.LONG_TYPE || right == Type.LONG_TYPE) return Type.LONG_TYPE;
return Type.INT_TYPE;
}
}