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);
}
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) {
v.iconst(1);
v.xor(Type.INT_TYPE);
@@ -448,9 +388,7 @@ public class AsmUtil {
InstructionAdapter v,
IElementType opToken,
Type leftType,
Type rightType,
boolean leftNullable,
boolean rightNullable
Type rightType
) {
if ((isNumberPrimitive(leftType) || leftType.getSort() == Type.BOOLEAN) && leftType == rightType) {
return StackValue.cmp(opToken, leftType);
@@ -460,7 +398,13 @@ public class AsmUtil {
return StackValue.cmp(opToken, leftType);
}
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) {
JetType leftJetType = bindingContext.get(BindingContext.EXPRESSION_TYPE, left);
assert leftJetType != null;
Type leftType = asmType(leftJetType);
JetType rightJetType = bindingContext.get(BindingContext.EXPRESSION_TYPE, right);
assert rightJetType != null;
Type rightType = asmType(rightJetType);
Type leftType = expressionType(left);
Type rightType = expressionType(right);
if (leftType.equals(JET_NOTHING_TYPE)) {
return genCmpWithNull(right, rightType, opToken);
}
@@ -2418,13 +2415,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
gen(right, rightType);
}
if (isPrimitive(leftType)) // both are primitive
{
return genEqualsForExpressionsOnStack(v, opToken, leftType, rightType, false, false);
}
return
genEqualsForExpressionsOnStack(v, opToken, leftType, rightType, leftJetType.isNullable(), rightJetType.isNullable());
return genEqualsForExpressionsOnStack(v, opToken, leftType, rightType);
}
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());
}
private StackValue generateExpressionMatch(
StackValue expressionToMatch,
JetExpression patternExpression,
boolean expressionToMatchIsNullable
) {
private StackValue generateExpressionMatch(StackValue expressionToMatch, JetExpression patternExpression) {
if (expressionToMatch != null) {
Type subjectType = expressionToMatch.type;
expressionToMatch.dupReceiver(v);
expressionToMatch.put(subjectType, v);
boolean patternIsNullable = false;
JetType condJetType = bindingContext.get(BindingContext.EXPRESSION_TYPE, patternExpression);
Type condType;
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 {
condType = OBJECT_TYPE;
patternIsNullable = condJetType != null && condJetType.isNullable();
}
gen(patternExpression, condType);
return genEqualsForExpressionsOnStack(v, JetTokens.EQEQ, subjectType, condType, expressionToMatchIsNullable,
patternIsNullable);
return genEqualsForExpressionsOnStack(v, JetTokens.EQEQ, subjectType, condType);
}
else {
return gen(patternExpression);
@@ -3394,9 +3378,7 @@ The "returned" value of try expression with no finally is either the last expres
if (!whenEntry.isElse()) {
final JetWhenCondition[] conditions = whenEntry.getConditions();
for (int i = 0; i < conditions.length; i++) {
StackValue conditionValue = generateWhenCondition(subjectType, subjectLocal,
subjectJetType != null && subjectJetType.isNullable(),
conditions[i], nextCondition);
StackValue conditionValue = generateWhenCondition(subjectType, subjectLocal, conditions[i]);
conditionValue.condJump(nextCondition, true, v);
if (i < conditions.length - 1) {
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);
}
private StackValue generateWhenCondition(
Type subjectType, int subjectLocal, boolean subjectIsNullable,
JetWhenCondition condition, @Nullable Label nextEntry
) {
private StackValue generateWhenCondition(Type subjectType, int subjectLocal, JetWhenCondition condition) {
if (condition instanceof JetWhenConditionInRange) {
JetWhenConditionInRange conditionInRange = (JetWhenConditionInRange) condition;
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) {
JetExpression patternExpression = ((JetWhenConditionWithExpression) condition).getExpression();
return generateExpressionMatch(match, patternExpression, subjectIsNullable);
return generateExpressionMatch(match, patternExpression);
}
else {
throw new UnsupportedOperationException("unsupported kind of when condition");
@@ -458,8 +458,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
iv.store(2, AsmTypeConstants.OBJECT_TYPE);
for (PropertyDescriptor propertyDescriptor : properties) {
final JetType type = propertyDescriptor.getType();
final Type asmType = typeMapper.mapType(type);
Type asmType = typeMapper.mapType(propertyDescriptor.getType());
genPropertyOnStack(iv, propertyDescriptor, 0);
genPropertyOnStack(iv, propertyDescriptor, 2);
@@ -474,8 +473,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
}
}
else {
final StackValue value =
genEqualsForExpressionsOnStack(iv, JetTokens.EQEQ, asmType, asmType, type.isNullable(), type.isNullable());
StackValue value = genEqualsForExpressionsOnStack(iv, JetTokens.EQEQ, asmType, asmType);
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.lang.psi.JetCallExpression;
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.types.JetType;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.List;
@@ -45,36 +43,20 @@ public class Equals implements IntrinsicMethod {
StackValue receiver,
@NotNull GenerationState state
) {
boolean leftNullable = true;
StackValue leftExpr;
JetExpression rightExpr;
if (element instanceof JetCallExpression) {
receiver.put(AsmTypeConstants.OBJECT_TYPE, v);
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();
}
}
leftExpr = receiver;
rightExpr = arguments.get(0);
}
else {
JetExpression leftExpr = 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);
leftExpr = codegen.gen(arguments.get(0));
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);
assert rightType != null;
return genEqualsForExpressionsOnStack(v, JetTokens.EQEQ, AsmTypeConstants.OBJECT_TYPE, AsmTypeConstants.OBJECT_TYPE,
leftNullable,
rightType.isNullable());
return genEqualsForExpressionsOnStack(v, JetTokens.EQEQ, AsmTypeConstants.OBJECT_TYPE, AsmTypeConstants.OBJECT_TYPE);
}
}
+6 -1
View File
@@ -20,6 +20,7 @@ import jet.Function0;
import java.util.*;
@SuppressWarnings("unused")
public class Intrinsics {
private Intrinsics() {
}
@@ -76,7 +77,11 @@ public class Intrinsics {
public static int compare(int thisVal, int anotherVal) {
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) {
synchronized (lock) {
return block.invoke();