diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/WhenTest.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/WhenTest.java index 6bbdee65b4b..5c1d72d47c1 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/WhenTest.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/WhenTest.java @@ -137,4 +137,8 @@ public final class WhenTest extends AbstractExpressionTest { public void testEmpty() throws Exception { checkFooBoxIsOk(); } + + public void testWhenEqualsPattern() throws Exception { + checkFooBoxIsOk(); + } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java index bff340275fa..f9426451bf0 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java @@ -20,6 +20,8 @@ import com.google.dart.compiler.backend.js.ast.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.KtNodeTypes; +import org.jetbrains.kotlin.builtins.KotlinBuiltIns; +import org.jetbrains.kotlin.builtins.PrimitiveType; import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; @@ -31,7 +33,10 @@ import org.jetbrains.kotlin.js.translate.context.TemporaryVariable; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; import org.jetbrains.kotlin.js.translate.general.Translation; +import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF; +import org.jetbrains.kotlin.js.translate.utils.BindingUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; +import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS; @@ -39,10 +44,13 @@ import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtIsExpression; import org.jetbrains.kotlin.psi.KtTypeReference; import org.jetbrains.kotlin.resolve.DescriptorUtils; +import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.types.DynamicTypesKt; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt; +import java.util.Collections; + import static org.jetbrains.kotlin.builtins.FunctionTypesKt.isFunctionTypeOrSubtype; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isAnyOrNullableAny; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isArray; @@ -235,9 +243,43 @@ public final class PatternTranslator extends AbstractTranslator { } @NotNull - public JsExpression translateExpressionPattern(@NotNull JsExpression expressionToMatch, @NotNull KtExpression patternExpression) { + public JsExpression translateExpressionPattern( + @NotNull KotlinType type, + @NotNull JsExpression expressionToMatch, + @NotNull KtExpression patternExpression + ) { JsExpression expressionToMatchAgainst = translateExpressionForExpressionPattern(patternExpression); - return equality(expressionToMatch, expressionToMatchAgainst); + KotlinType patternType = BindingUtils.getTypeForExpression(bindingContext(), patternExpression); + + EqualityType matchEquality = equalityType(type); + EqualityType patternEquality = equalityType(patternType); + + if (matchEquality == EqualityType.PRIMITIVE && patternEquality == EqualityType.PRIMITIVE) { + return equality(expressionToMatch, expressionToMatchAgainst); + } + else if (expressionToMatchAgainst == JsLiteral.NULL) { + return TranslationUtils.nullCheck(expressionToMatch, false); + } + else { + return TopLevelFIF.KOTLIN_EQUALS.apply(expressionToMatch, Collections.singletonList(expressionToMatchAgainst), context()); + } + } + + @NotNull + private static EqualityType equalityType(@NotNull KotlinType type) { + DeclarationDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); + if (!(descriptor instanceof ClassDescriptor)) return EqualityType.GENERAL; + + PrimitiveType primitive = KotlinBuiltIns.getPrimitiveTypeByFqName(DescriptorUtilsKt.getFqNameUnsafe(descriptor)); + if (primitive == null) return EqualityType.GENERAL; + + return primitive == PrimitiveType.LONG ? EqualityType.LONG : EqualityType.PRIMITIVE; + } + + private enum EqualityType { + PRIMITIVE, + LONG, + GENERAL } @NotNull diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/WhenTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/WhenTranslator.java index 4805272060b..5adee1c22f9 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/WhenTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/WhenTranslator.java @@ -200,7 +200,11 @@ public final class WhenTranslator extends AbstractTranslator { return Translation.patternTranslator(context).translateExpressionForExpressionPattern(patternExpression); } else { - return Translation.patternTranslator(context).translateExpressionPattern(expressionToMatch, patternExpression); + KtExpression subject = whenExpression.getSubjectExpression(); + assert subject != null : "Subject must be non-null since expressionToMatch is non-null: " + + PsiUtilsKt.getTextWithLocation(condition); + KotlinType type = BindingUtils.getTypeForExpression(bindingContext(), whenExpression.getSubjectExpression()); + return Translation.patternTranslator(context).translateExpressionPattern(type, expressionToMatch, patternExpression); } } diff --git a/js/js.translator/testData/expression/when/cases/whenEqualsPattern.kt b/js/js.translator/testData/expression/when/cases/whenEqualsPattern.kt new file mode 100644 index 00000000000..0fae7552fc5 --- /dev/null +++ b/js/js.translator/testData/expression/when/cases/whenEqualsPattern.kt @@ -0,0 +1,87 @@ +package foo + +data class A(val bar: Int) + +@native class B + +fun makeB(): B = js("new Object();") + +fun intAgainstInt(x: Int) = when (x) { + 1 -> "a" + 2 -> "b" + else -> "*" +} + +fun intAgainstNullableInt(x: Int?) = when (x) { + 1 -> "a" + 2 -> "b" + null -> "c" + else -> "*" +} + +fun anyAgainstInt(x: Any?) = when (x) { + 1 -> "a" + 2 -> "b" + else -> "*" +} + +fun longAgainstLong(x: Long) = when (x) { + 1L -> "a" + 2L -> "b" + else -> "*" +} + +fun anyAgainstLong(x: Any?) = when (x) { + 1L -> "a" + 2L -> "b" + null -> "c" + else -> "*" +} + +fun anyAgainstAny(x: Any) = when (x) { + A(1) -> "a" + 1 -> "b" + else -> "*" +} + +fun dynamicAgainstPattern(x: dynamic) = when(x) { + 1 -> "a" + "2" -> "b" + else -> "*" +} + +fun box(): String { + assertEquals("a", intAgainstInt(1)) + assertEquals("b", intAgainstInt(2)) + assertEquals("*", intAgainstInt(23)) + + assertEquals("a", intAgainstNullableInt(1)) + assertEquals("b", intAgainstNullableInt(2)) + assertEquals("c", intAgainstNullableInt(null)) + assertEquals("*", intAgainstNullableInt(23)) + + assertEquals("a", anyAgainstInt(1)) + assertEquals("b", anyAgainstInt(2)) + assertEquals("*", anyAgainstInt(A(23))) + + assertEquals("a", longAgainstLong(1)) + assertEquals("b", longAgainstLong(2)) + assertEquals("*", longAgainstLong(23)) + + assertEquals("a", anyAgainstLong(1L)) + assertEquals("b", anyAgainstLong(2L)) + assertEquals("c", anyAgainstLong(null)) + assertEquals("*", anyAgainstLong(A(23))) + + assertEquals("a", anyAgainstAny(A(1))) + assertEquals("b", anyAgainstAny(1)) + assertEquals("*", anyAgainstAny(listOf(1))) + + assertEquals("a", dynamicAgainstPattern(1)) + assertEquals("a", dynamicAgainstPattern(js("1"))) + assertEquals("b", dynamicAgainstPattern("2")) + assertEquals("b", dynamicAgainstPattern(js("'2'"))) + assertEquals("*", dynamicAgainstPattern(js("{}"))) + + return "OK" +} \ No newline at end of file