JVM: Do not use equals-impl0 methods generated by older compiler versions
This commit is contained in:
committed by
Alexander Udalov
parent
b85b2d9af8
commit
2e53e36fd5
@@ -3513,10 +3513,13 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
Label endLabel = new Label();
|
||||
boolean flipComparison = opToken == KtTokens.EXCLEQ || opToken == KtTokens.EXCLEQEQEQ;
|
||||
|
||||
// Don't call equals-impl0 if the class file version is too low to support it.
|
||||
boolean useUnboxedEquals = leftIsUnboxed && JvmCodegenUtil.typeHasSpecializedInlineClassEquality(leftKotlinType, state);
|
||||
|
||||
leftValue.put(leftType, leftKotlinType, v);
|
||||
//noinspection SuspiciousNameCombination
|
||||
Type afterTopType = leftType;
|
||||
if (!leftIsUnboxed) {
|
||||
if (!useUnboxedEquals) {
|
||||
StackValue.coerce(leftType, leftKotlinType, OBJECT_TYPE, nullableAnyType, v);
|
||||
afterTopType = OBJECT_TYPE;
|
||||
}
|
||||
@@ -3524,12 +3527,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
rightValue.put(rightType, rightKotlinType, v);
|
||||
//noinspection SuspiciousNameCombination
|
||||
Type topType = rightType;
|
||||
if (!leftIsUnboxed || !rightIsUnboxed) {
|
||||
if (!useUnboxedEquals || !rightIsUnboxed) {
|
||||
StackValue.coerce(rightType, rightKotlinType, OBJECT_TYPE, nullableAnyType, v);
|
||||
topType = OBJECT_TYPE;
|
||||
}
|
||||
|
||||
if (leftIsUnboxed) {
|
||||
if (useUnboxedEquals) {
|
||||
String className = typeMapper.mapTypeAsDeclaration(leftKotlinType).getInternalName();
|
||||
// Nullable inline class wrappers around non-nullable types are unboxed, yet
|
||||
// equals-impl expects a non-nullable first argument and equals-impl0 expects
|
||||
@@ -3568,12 +3571,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
v.goTo(endLabel);
|
||||
v.visitLabel(nonNullLabel);
|
||||
}
|
||||
String descriptor = Type.getMethodType(Type.BOOLEAN_TYPE, leftType, leftType).toString();
|
||||
String descriptor = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, leftType, leftType);
|
||||
v.invokestatic(className, InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME.asString(), descriptor, false);
|
||||
} else {
|
||||
// equals-impl expects a non-nullable first argument, yet `left` may be unboxed even if
|
||||
// it is nullable when it is a wrapper around a non-nullable reference type.
|
||||
String descriptor = Type.getMethodType(Type.BOOLEAN_TYPE, leftType, OBJECT_TYPE).toString();
|
||||
String descriptor = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, leftType, OBJECT_TYPE);
|
||||
v.invokestatic(className, "equals-impl", descriptor, false);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.codegen.context.MethodContext;
|
||||
import org.jetbrains.kotlin.codegen.context.RootContext;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.config.ApiVersion;
|
||||
import org.jetbrains.kotlin.config.JvmAnalysisFlags;
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
@@ -42,6 +43,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver;
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||
|
||||
@@ -400,4 +402,24 @@ public class JvmCodegenUtil {
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
// Before metadata version 1.1.16 we did not generate equals-impl0 methods correctly.
|
||||
// The method is still present on all inline classes, but the implementation always throws
|
||||
// a NullPointerException.
|
||||
public static boolean typeHasSpecializedInlineClassEquality(@NotNull KotlinType type, @NotNull GenerationState state) {
|
||||
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
|
||||
if (!(descriptor instanceof DeserializedClassDescriptor))
|
||||
return true;
|
||||
|
||||
DeserializedClassDescriptor classDescriptor = (DeserializedClassDescriptor) descriptor;
|
||||
|
||||
// The Result class is the only inline class in the standard library without special rules for equality.
|
||||
// We only call Result.equals-impl0 if we are compiling for Kotlin 1.4 or later. Otherwise, the code
|
||||
// might well be running against an older version of the standard library.
|
||||
if (DescriptorUtils.getFqNameSafe(classDescriptor).equals(DescriptorUtils.RESULT_FQ_NAME)) {
|
||||
return state.getLanguageVersionSettings().getApiVersion().compareTo(ApiVersion.KOTLIN_1_4) >= 0;
|
||||
} else {
|
||||
return ((DeserializedClassDescriptor) descriptor).getMetadataVersion().isAtLeast(1, 1, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user