Add Intrinsics.areEqual()

It's more safe, short and less error-prone (especially w.r.t. nullability of
generic types)
This commit is contained in:
Alexander Udalov
2013-02-05 22:06:39 +04:00
parent 41b379497d
commit 1021ec5ff4
5 changed files with 30 additions and 122 deletions
@@ -379,66 +379,6 @@ public class AsmUtil {
mv.visitInsn(L2I); mv.visitInsn(L2I);
} }
static StackValue genNullSafeEquals(
InstructionAdapter v,
IElementType opToken,
boolean leftNullable,
boolean rightNullable
) {
if (!leftNullable) {
v.invokevirtual("java/lang/Object", "equals", "(Ljava/lang/Object;)Z");
if (opToken == JetTokens.EXCLEQ) {
genInvertBoolean(v);
}
}
else {
if (rightNullable) {
v.dup2(); // left right left right
Label rightNull = new Label();
v.ifnull(rightNull);
Label leftNull = new Label();
v.ifnull(leftNull);
v.invokevirtual("java/lang/Object", "equals", "(Ljava/lang/Object;)Z");
if (opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ) {
genInvertBoolean(v);
}
Label end = new Label();
v.goTo(end);
v.mark(rightNull);
// left right left
Label bothNull = new Label();
v.ifnull(bothNull);
v.mark(leftNull);
v.pop2();
v.iconst(opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ ? 1 : 0);
v.goTo(end);
v.mark(bothNull);
v.pop2();
v.iconst(opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ ? 0 : 1);
v.mark(end);
}
else {
v.dup2(); // left right left right
v.pop();
Label leftNull = new Label();
v.ifnull(leftNull);
v.invokevirtual("java/lang/Object", "equals", "(Ljava/lang/Object;)Z");
if (opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ) {
genInvertBoolean(v);
}
Label end = new Label();
v.goTo(end);
// left right
v.mark(leftNull);
v.pop2();
v.iconst(opToken == JetTokens.EXCLEQ ? 1 : 0);
v.mark(end);
}
}
return StackValue.onStack(Type.BOOLEAN_TYPE);
}
static void genInvertBoolean(InstructionAdapter v) { static void genInvertBoolean(InstructionAdapter v) {
v.iconst(1); v.iconst(1);
v.xor(Type.INT_TYPE); v.xor(Type.INT_TYPE);
@@ -448,9 +388,7 @@ public class AsmUtil {
InstructionAdapter v, InstructionAdapter v,
IElementType opToken, IElementType opToken,
Type leftType, Type leftType,
Type rightType, Type rightType
boolean leftNullable,
boolean rightNullable
) { ) {
if ((isNumberPrimitive(leftType) || leftType.getSort() == Type.BOOLEAN) && leftType == rightType) { if ((isNumberPrimitive(leftType) || leftType.getSort() == Type.BOOLEAN) && leftType == rightType) {
return StackValue.cmp(opToken, leftType); return StackValue.cmp(opToken, leftType);
@@ -460,7 +398,13 @@ public class AsmUtil {
return StackValue.cmp(opToken, leftType); return StackValue.cmp(opToken, leftType);
} }
else { else {
return genNullSafeEquals(v, opToken, leftNullable, rightNullable); v.invokestatic("jet/runtime/Intrinsics", "areEqual", "(Ljava/lang/Object;Ljava/lang/Object;)Z");
if (opToken == JetTokens.EXCLEQ || opToken == JetTokens.EXCLEQEQEQ) {
genInvertBoolean(v);
}
return StackValue.onStack(Type.BOOLEAN_TYPE);
} }
} }
} }
@@ -2385,12 +2385,9 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
} }
private StackValue generateEquals(JetExpression left, JetExpression right, IElementType opToken) { private StackValue generateEquals(JetExpression left, JetExpression right, IElementType opToken) {
JetType leftJetType = bindingContext.get(BindingContext.EXPRESSION_TYPE, left); Type leftType = expressionType(left);
assert leftJetType != null; Type rightType = expressionType(right);
Type leftType = asmType(leftJetType);
JetType rightJetType = bindingContext.get(BindingContext.EXPRESSION_TYPE, right);
assert rightJetType != null;
Type rightType = asmType(rightJetType);
if (leftType.equals(JET_NOTHING_TYPE)) { if (leftType.equals(JET_NOTHING_TYPE)) {
return genCmpWithNull(right, rightType, opToken); return genCmpWithNull(right, rightType, opToken);
} }
@@ -2418,13 +2415,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
gen(right, rightType); gen(right, rightType);
} }
if (isPrimitive(leftType)) // both are primitive return genEqualsForExpressionsOnStack(v, opToken, leftType, rightType);
{
return genEqualsForExpressionsOnStack(v, opToken, leftType, rightType, false, false);
}
return
genEqualsForExpressionsOnStack(v, opToken, leftType, rightType, leftJetType.isNullable(), rightJetType.isNullable());
} }
private boolean isIntZero(JetExpression expr, Type exprType) { private boolean isIntZero(JetExpression expr, Type exprType) {
@@ -3292,16 +3283,11 @@ The "returned" value of try expression with no finally is either the last expres
return generateIsCheck(match, expression.getTypeRef(), expression.isNegated()); return generateIsCheck(match, expression.getTypeRef(), expression.isNegated());
} }
private StackValue generateExpressionMatch( private StackValue generateExpressionMatch(StackValue expressionToMatch, JetExpression patternExpression) {
StackValue expressionToMatch,
JetExpression patternExpression,
boolean expressionToMatchIsNullable
) {
if (expressionToMatch != null) { if (expressionToMatch != null) {
Type subjectType = expressionToMatch.type; Type subjectType = expressionToMatch.type;
expressionToMatch.dupReceiver(v); expressionToMatch.dupReceiver(v);
expressionToMatch.put(subjectType, v); expressionToMatch.put(subjectType, v);
boolean patternIsNullable = false;
JetType condJetType = bindingContext.get(BindingContext.EXPRESSION_TYPE, patternExpression); JetType condJetType = bindingContext.get(BindingContext.EXPRESSION_TYPE, patternExpression);
Type condType; Type condType;
if (isNumberPrimitive(subjectType) || subjectType.getSort() == Type.BOOLEAN) { if (isNumberPrimitive(subjectType) || subjectType.getSort() == Type.BOOLEAN) {
@@ -3314,11 +3300,9 @@ The "returned" value of try expression with no finally is either the last expres
} }
else { else {
condType = OBJECT_TYPE; condType = OBJECT_TYPE;
patternIsNullable = condJetType != null && condJetType.isNullable();
} }
gen(patternExpression, condType); gen(patternExpression, condType);
return genEqualsForExpressionsOnStack(v, JetTokens.EQEQ, subjectType, condType, expressionToMatchIsNullable, return genEqualsForExpressionsOnStack(v, JetTokens.EQEQ, subjectType, condType);
patternIsNullable);
} }
else { else {
return gen(patternExpression); return gen(patternExpression);
@@ -3394,9 +3378,7 @@ The "returned" value of try expression with no finally is either the last expres
if (!whenEntry.isElse()) { if (!whenEntry.isElse()) {
final JetWhenCondition[] conditions = whenEntry.getConditions(); final JetWhenCondition[] conditions = whenEntry.getConditions();
for (int i = 0; i < conditions.length; i++) { for (int i = 0; i < conditions.length; i++) {
StackValue conditionValue = generateWhenCondition(subjectType, subjectLocal, StackValue conditionValue = generateWhenCondition(subjectType, subjectLocal, conditions[i]);
subjectJetType != null && subjectJetType.isNullable(),
conditions[i], nextCondition);
conditionValue.condJump(nextCondition, true, v); conditionValue.condJump(nextCondition, true, v);
if (i < conditions.length - 1) { if (i < conditions.length - 1) {
v.goTo(thisEntry); v.goTo(thisEntry);
@@ -3426,10 +3408,7 @@ The "returned" value of try expression with no finally is either the last expres
return StackValue.onStack(resultType); return StackValue.onStack(resultType);
} }
private StackValue generateWhenCondition( private StackValue generateWhenCondition(Type subjectType, int subjectLocal, JetWhenCondition condition) {
Type subjectType, int subjectLocal, boolean subjectIsNullable,
JetWhenCondition condition, @Nullable Label nextEntry
) {
if (condition instanceof JetWhenConditionInRange) { if (condition instanceof JetWhenConditionInRange) {
JetWhenConditionInRange conditionInRange = (JetWhenConditionInRange) condition; JetWhenConditionInRange conditionInRange = (JetWhenConditionInRange) condition;
JetExpression rangeExpression = conditionInRange.getRangeExpression(); JetExpression rangeExpression = conditionInRange.getRangeExpression();
@@ -3461,7 +3440,7 @@ The "returned" value of try expression with no finally is either the last expres
} }
else if (condition instanceof JetWhenConditionWithExpression) { else if (condition instanceof JetWhenConditionWithExpression) {
JetExpression patternExpression = ((JetWhenConditionWithExpression) condition).getExpression(); JetExpression patternExpression = ((JetWhenConditionWithExpression) condition).getExpression();
return generateExpressionMatch(match, patternExpression, subjectIsNullable); return generateExpressionMatch(match, patternExpression);
} }
else { else {
throw new UnsupportedOperationException("unsupported kind of when condition"); throw new UnsupportedOperationException("unsupported kind of when condition");
@@ -458,8 +458,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
iv.store(2, AsmTypeConstants.OBJECT_TYPE); iv.store(2, AsmTypeConstants.OBJECT_TYPE);
for (PropertyDescriptor propertyDescriptor : properties) { for (PropertyDescriptor propertyDescriptor : properties) {
final JetType type = propertyDescriptor.getType(); Type asmType = typeMapper.mapType(propertyDescriptor.getType());
final Type asmType = typeMapper.mapType(type);
genPropertyOnStack(iv, propertyDescriptor, 0); genPropertyOnStack(iv, propertyDescriptor, 0);
genPropertyOnStack(iv, propertyDescriptor, 2); genPropertyOnStack(iv, propertyDescriptor, 2);
@@ -474,8 +473,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
} }
} }
else { else {
final StackValue value = StackValue value = genEqualsForExpressionsOnStack(iv, JetTokens.EQEQ, asmType, asmType);
genEqualsForExpressionsOnStack(iv, JetTokens.EQEQ, asmType, asmType, type.isNullable(), type.isNullable());
value.put(Type.BOOLEAN_TYPE, iv); value.put(Type.BOOLEAN_TYPE, iv);
} }
@@ -25,9 +25,7 @@ import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.codegen.state.GenerationState; import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.psi.JetCallExpression; import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants; import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.lexer.JetTokens;
import java.util.List; import java.util.List;
@@ -45,36 +43,20 @@ public class Equals implements IntrinsicMethod {
StackValue receiver, StackValue receiver,
@NotNull GenerationState state @NotNull GenerationState state
) { ) {
StackValue leftExpr;
boolean leftNullable = true;
JetExpression rightExpr; JetExpression rightExpr;
if (element instanceof JetCallExpression) { if (element instanceof JetCallExpression) {
receiver.put(AsmTypeConstants.OBJECT_TYPE, v); leftExpr = receiver;
JetCallExpression jetCallExpression = (JetCallExpression) element;
JetExpression calleeExpression = jetCallExpression.getCalleeExpression();
if (calleeExpression != null) {
JetType leftType = codegen.getBindingContext().get(BindingContext.EXPRESSION_TYPE, calleeExpression);
if (leftType != null) {
leftNullable = leftType.isNullable();
}
}
rightExpr = arguments.get(0); rightExpr = arguments.get(0);
} }
else { else {
JetExpression leftExpr = arguments.get(0); leftExpr = codegen.gen(arguments.get(0));
JetType leftType = codegen.getBindingContext().get(BindingContext.EXPRESSION_TYPE, leftExpr);
assert leftType != null;
leftNullable = leftType.isNullable();
codegen.gen(leftExpr).put(AsmTypeConstants.OBJECT_TYPE, v);
rightExpr = arguments.get(1); rightExpr = arguments.get(1);
} }
JetType rightType = codegen.getBindingContext().get(BindingContext.EXPRESSION_TYPE, rightExpr); leftExpr.put(AsmTypeConstants.OBJECT_TYPE, v);
codegen.gen(rightExpr).put(AsmTypeConstants.OBJECT_TYPE, v); codegen.gen(rightExpr).put(AsmTypeConstants.OBJECT_TYPE, v);
assert rightType != null; return genEqualsForExpressionsOnStack(v, JetTokens.EQEQ, AsmTypeConstants.OBJECT_TYPE, AsmTypeConstants.OBJECT_TYPE);
return genEqualsForExpressionsOnStack(v, JetTokens.EQEQ, AsmTypeConstants.OBJECT_TYPE, AsmTypeConstants.OBJECT_TYPE,
leftNullable,
rightType.isNullable());
} }
} }
+6 -1
View File
@@ -20,6 +20,7 @@ import jet.Function0;
import java.util.*; import java.util.*;
@SuppressWarnings("unused")
public class Intrinsics { public class Intrinsics {
private Intrinsics() { private Intrinsics() {
} }
@@ -76,7 +77,11 @@ public class Intrinsics {
public static int compare(int thisVal, int anotherVal) { public static int compare(int thisVal, int anotherVal) {
return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1)); return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
} }
public static boolean areEqual(Object first, Object second) {
return first == null ? second == null : first.equals(second);
}
public static <R> R stupidSync(Object lock, Function0<R> block) { public static <R> R stupidSync(Object lock, Function0<R> block) {
synchronized (lock) { synchronized (lock) {
return block.invoke(); return block.invoke();