JS: don't use strict comparison (===) for enums when both left-hand side and right-hand side are nullable. See KT-12598
This commit is contained in:
@@ -1433,6 +1433,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNullUndefined.kt")
|
||||
public void testEqualsNullUndefined() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/enum/equalsNullUndefined.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("implementsComparable.kt")
|
||||
public void testImplementsComparable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/enum/implementsComparable.kt");
|
||||
|
||||
+2
-1
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
|
||||
object CompareToBOIF : BinaryOperationIntrinsicFactory {
|
||||
@@ -66,7 +67,7 @@ object CompareToBOIF : BinaryOperationIntrinsicFactory {
|
||||
|
||||
override fun getSupportTokens() = OperatorConventions.COMPARISON_OPERATIONS
|
||||
|
||||
override fun getIntrinsic(descriptor: FunctionDescriptor): BinaryOperationIntrinsic? {
|
||||
override fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic? {
|
||||
if (descriptor.isDynamic()) return CompareToIntrinsic
|
||||
|
||||
if (!KotlinBuiltIns.isBuiltIn(descriptor)) return null
|
||||
|
||||
+10
-5
@@ -33,13 +33,13 @@ import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.types.isDynamic
|
||||
import java.util.*
|
||||
|
||||
object EqualsBOIF : BinaryOperationIntrinsicFactory {
|
||||
|
||||
|
||||
private object EqualsIntrinsic : AbstractBinaryOperationIntrinsic() {
|
||||
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
|
||||
val isNegated = expression.isNegated()
|
||||
@@ -80,11 +80,11 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSupportTokens() = OperatorConventions.EQUALS_OPERATIONS
|
||||
override fun getSupportTokens() = OperatorConventions.EQUALS_OPERATIONS!!
|
||||
|
||||
override fun getIntrinsic(descriptor: FunctionDescriptor): BinaryOperationIntrinsic? =
|
||||
override fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic? =
|
||||
when {
|
||||
DescriptorUtils.isEnumClass(descriptor.containingDeclaration) -> EnumEqualsIntrinsic
|
||||
isEnumIntrinsicApplicable(descriptor, leftType, rightType) -> EnumEqualsIntrinsic
|
||||
|
||||
KotlinBuiltIns.isBuiltIn(descriptor) ||
|
||||
TopLevelFIF.EQUALS_IN_ANY.apply(descriptor) -> EqualsIntrinsic
|
||||
@@ -92,5 +92,10 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun isEnumIntrinsicApplicable(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): Boolean {
|
||||
return DescriptorUtils.isEnumClass(descriptor.containingDeclaration) && leftType != null && rightType != null &&
|
||||
!TypeUtils.isNullableType(leftType) && !TypeUtils.isNullableType(rightType)
|
||||
}
|
||||
|
||||
private fun KtBinaryExpression.isNegated() = getOperationToken(this) == KtTokens.EXCLEQ
|
||||
}
|
||||
|
||||
+2
-1
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.js.translate.operation.OperatorTable
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*
|
||||
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.utils.identity as ID
|
||||
|
||||
@@ -71,7 +72,7 @@ object LongCompareToBOIF : BinaryOperationIntrinsicFactory {
|
||||
|
||||
override fun getSupportTokens() = OperatorConventions.COMPARISON_OPERATIONS
|
||||
|
||||
override fun getIntrinsic(descriptor: FunctionDescriptor): BinaryOperationIntrinsic? {
|
||||
override fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic? {
|
||||
if (KotlinBuiltIns.isBuiltIn(descriptor)) {
|
||||
return when {
|
||||
FLOATING_POINT_COMPARE_TO_LONG_PATTERN.apply(descriptor) -> FLOATING_POINT_COMPARE_TO_LONG
|
||||
|
||||
+22
-19
@@ -16,15 +16,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.js.translate.intrinsic.operation
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression
|
||||
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken
|
||||
import gnu.trove.THashMap
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
interface BinaryOperationIntrinsic {
|
||||
|
||||
@@ -35,7 +35,7 @@ interface BinaryOperationIntrinsic {
|
||||
|
||||
class BinaryOperationIntrinsics {
|
||||
|
||||
private val intrinsicCache = THashMap<Pair<KtToken, FunctionDescriptor>, BinaryOperationIntrinsic>()
|
||||
private val intrinsicCache = mutableMapOf<IntrinsicKey, BinaryOperationIntrinsic>()
|
||||
|
||||
private val factories = listOf(LongCompareToBOIF, EqualsBOIF, CompareToBOIF)
|
||||
|
||||
@@ -46,22 +46,20 @@ class BinaryOperationIntrinsics {
|
||||
return NO_INTRINSIC
|
||||
}
|
||||
|
||||
return lookUpCache(token, descriptor) ?: computeAndCacheIntrinsic(token, descriptor)
|
||||
val leftType = expression.left?.let { context.bindingContext().getType(it) }
|
||||
val rightType = expression.right?.let { context.bindingContext().getType(it) }
|
||||
|
||||
val key = IntrinsicKey(token, descriptor, leftType, rightType)
|
||||
return intrinsicCache.getOrPut(key) { computeIntrinsic(token, descriptor, leftType, rightType) }
|
||||
}
|
||||
|
||||
private fun lookUpCache(token: KtToken, descriptor: FunctionDescriptor): BinaryOperationIntrinsic? =
|
||||
intrinsicCache.get(Pair(token, descriptor))
|
||||
|
||||
private fun computeAndCacheIntrinsic(token: KtToken, descriptor: FunctionDescriptor): BinaryOperationIntrinsic {
|
||||
val result = computeIntrinsic(token, descriptor)
|
||||
intrinsicCache.put(Pair(token, descriptor), result)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun computeIntrinsic(token: KtToken, descriptor: FunctionDescriptor): BinaryOperationIntrinsic {
|
||||
private fun computeIntrinsic(
|
||||
token: KtToken, descriptor: FunctionDescriptor,
|
||||
leftType: KotlinType?, rightType: KotlinType?
|
||||
): BinaryOperationIntrinsic {
|
||||
for (factory in factories) {
|
||||
if (factory.getSupportTokens().contains(token)) {
|
||||
val intrinsic = factory.getIntrinsic(descriptor)
|
||||
val intrinsic = factory.getIntrinsic(descriptor, leftType, rightType)
|
||||
if (intrinsic != null) {
|
||||
return intrinsic
|
||||
}
|
||||
@@ -71,11 +69,16 @@ class BinaryOperationIntrinsics {
|
||||
}
|
||||
}
|
||||
|
||||
private data class IntrinsicKey(
|
||||
val token: KtToken, val function: FunctionDescriptor,
|
||||
val leftType: KotlinType?, val rightType: KotlinType?
|
||||
)
|
||||
|
||||
interface BinaryOperationIntrinsicFactory {
|
||||
|
||||
fun getSupportTokens(): ImmutableSet<out KtToken>
|
||||
|
||||
fun getIntrinsic(descriptor: FunctionDescriptor): BinaryOperationIntrinsic?
|
||||
fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic?
|
||||
}
|
||||
|
||||
abstract class AbstractBinaryOperationIntrinsic : BinaryOperationIntrinsic {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
enum class A {
|
||||
X,
|
||||
Y
|
||||
}
|
||||
|
||||
fun getNull(): A? = null
|
||||
|
||||
fun getUndefined(): A? = js("void 0")
|
||||
|
||||
fun getX(): A? = A.X
|
||||
|
||||
fun <T : A?> testGenericEquals(a: T, b: T) = a == b
|
||||
|
||||
fun <T : A?> testGenericEqualsToNull(a: T) = a == null
|
||||
|
||||
fun <T : A> testGenericEqualsNotNull(a: T, b: T) = a == b
|
||||
|
||||
fun box(): String {
|
||||
if (getX() == getNull()) return "fail1"
|
||||
if (getX() == getUndefined()) return "fail2"
|
||||
if (getNull() != null) return "fail3"
|
||||
if (getUndefined() != null) return "fail4"
|
||||
if (getUndefined() != getNull()) return "fail5"
|
||||
if (A.X == getUndefined()) return "fail6"
|
||||
if (A.X == getNull()) return "fail6"
|
||||
if (A.X == null) return "fail7"
|
||||
if (A.X == A.Y) return "fail8"
|
||||
if (A.X != getX()) return "fail9"
|
||||
if (!testGenericEquals(getUndefined(), getNull())) return "fail10"
|
||||
if (testGenericEqualsNotNull(A.X, A.Y)) return "fail11"
|
||||
if (!testGenericEqualsNotNull(A.X, getX()!!)) return "fail12"
|
||||
if (!testGenericEqualsToNull(getNull())) return "fail13"
|
||||
if (!testGenericEqualsToNull(getUndefined())) return "fail14"
|
||||
if (testGenericEqualsToNull(getX())) return "fail15"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user