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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user