JS: fix double compareTo behaviour for NaN and +-0 (KT-22723)
This commit is contained in:
+31
-175
@@ -1122,13 +1122,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("boxedRealsCmp.kt")
|
||||
public void testBoxedRealsCmp() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedRealsCmp.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("casts.kt")
|
||||
@@ -1152,13 +1146,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("explicitEqualsOnDouble.kt")
|
||||
public void testExplicitEqualsOnDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/explicitEqualsOnDouble.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fold.kt")
|
||||
@@ -11028,25 +11016,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("asComparableToDouble.kt")
|
||||
public void testAsComparableToDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/asComparableToDouble.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("asComparableToDouble_properIeeeComparisons.kt")
|
||||
public void testAsComparableToDouble_properIeeeComparisons() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/asComparableToDouble_properIeeeComparisons.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("comparableToTWithT_properIeeeComparisons.kt")
|
||||
@@ -11058,13 +11034,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("comparableTypeCast.kt")
|
||||
public void testComparableTypeCast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/comparableTypeCast.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("dataClass.kt")
|
||||
@@ -11073,16 +11043,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("differentTypesComparison.kt")
|
||||
public void testDifferentTypesComparison() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/differentTypesComparison.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsDouble.kt")
|
||||
public void testEqualsDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsDouble.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsDouble_properIeeeComparisons.kt")
|
||||
@@ -11094,13 +11064,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("equalsFloat.kt")
|
||||
public void testEqualsFloat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsFloat.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsFloat_properIeeeComparisons.kt")
|
||||
@@ -11112,13 +11076,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("equalsNaN.kt")
|
||||
public void testEqualsNaN() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsNaN.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNaN_properIeeeComparisons.kt")
|
||||
@@ -11130,13 +11088,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("equalsNullableDouble.kt")
|
||||
public void testEqualsNullableDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsNullableDouble.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNullableDouble_properIeeeComparisons.kt")
|
||||
@@ -11148,13 +11100,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("equalsNullableFloat.kt")
|
||||
public void testEqualsNullableFloat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsNullableFloat.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNullableFloat_properIeeeComparisons.kt")
|
||||
@@ -11166,25 +11112,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("explicitCompareCall.kt")
|
||||
public void testExplicitCompareCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/explicitCompareCall.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("explicitEqualsCall.kt")
|
||||
public void testExplicitEqualsCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/explicitEqualsCall.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("generic.kt")
|
||||
@@ -11196,13 +11130,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("greaterDouble.kt")
|
||||
public void testGreaterDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/greaterDouble.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("greaterDouble_properIeeeComparisons.kt")
|
||||
@@ -11214,13 +11142,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("greaterFloat.kt")
|
||||
public void testGreaterFloat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/greaterFloat.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("greaterFloat_properIeeeComparisons.kt")
|
||||
@@ -11232,25 +11154,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("inline.kt")
|
||||
public void testInline() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/inline.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lessDouble.kt")
|
||||
public void testLessDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/lessDouble.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lessDouble_properIeeeComparisons.kt")
|
||||
@@ -11262,13 +11172,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("lessFloat.kt")
|
||||
public void testLessFloat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/lessFloat.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lessFloat_properIeeeComparisons.kt")
|
||||
@@ -11346,13 +11250,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("safeCall.kt")
|
||||
public void testSafeCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/safeCall.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCastOnWhenSubjectAfterCheckInBranch_properIeeeComparisons.kt")
|
||||
@@ -11364,37 +11262,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("smartCastToDifferentTypes.kt")
|
||||
public void testSmartCastToDifferentTypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypes.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCastToDifferentTypesWithNumericPromotion.kt")
|
||||
public void testSmartCastToDifferentTypesWithNumericPromotion() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesWithNumericPromotion.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCastToDifferentTypesWithNumericPromotion_properIeeeComparisons.kt")
|
||||
public void testSmartCastToDifferentTypesWithNumericPromotion_properIeeeComparisons() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesWithNumericPromotion_properIeeeComparisons.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCastToDifferentTypes_properIeeeComparisons.kt")
|
||||
@@ -11412,25 +11292,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("when.kt")
|
||||
public void testWhen() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/when.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("when10.kt")
|
||||
public void testWhen10() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/when10.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("when10_properIeeeComparisons.kt")
|
||||
@@ -11442,13 +11310,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("whenNoSubject.kt")
|
||||
public void testWhenNoSubject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/whenNoSubject.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("whenNoSubject_properIeeeComparisons.kt")
|
||||
@@ -16823,13 +16685,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("inDoubleRangeLiteralVsComparableRangeLiteral.kt")
|
||||
public void testInDoubleRangeLiteralVsComparableRangeLiteral() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inDoubleRangeLiteralVsComparableRangeLiteral.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inExtensionRange.kt")
|
||||
|
||||
+16
-8
@@ -34,16 +34,14 @@ import org.jetbrains.kotlin.js.translate.general.Translation;
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.ArrayFIF;
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF;
|
||||
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator;
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
|
||||
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.js.translate.utils.*;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS;
|
||||
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.checkers.PrimitiveNumericComparisonInfo;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -272,16 +270,26 @@ public final class PatternTranslator extends AbstractTranslator {
|
||||
|
||||
@NotNull
|
||||
public JsExpression translateExpressionPattern(
|
||||
@NotNull KotlinType type,
|
||||
@NotNull KtExpression subjectExpression,
|
||||
@NotNull JsExpression expressionToMatch,
|
||||
@NotNull KtExpression patternExpression
|
||||
) {
|
||||
JsExpression expressionToMatchAgainst = translateExpressionForExpressionPattern(patternExpression);
|
||||
KotlinType patternType = BindingUtils.getTypeForExpression(bindingContext(), patternExpression);
|
||||
PrimitiveNumericComparisonInfo ieeeInfo = UtilsKt.getPrimitiveNumericComparisonInfo(context(), patternExpression);
|
||||
|
||||
EqualityType matchEquality = equalityType(type);
|
||||
KotlinType subjectType, patternType;
|
||||
if (ieeeInfo != null) {
|
||||
subjectType = ieeeInfo.getLeftType();
|
||||
patternType = ieeeInfo.getRightType();
|
||||
} else {
|
||||
subjectType = UtilsKt.getPrecisePrimitiveTypeNotNull(context(), subjectExpression);
|
||||
patternType = UtilsKt.getPrecisePrimitiveTypeNotNull(context(), patternExpression);
|
||||
}
|
||||
|
||||
EqualityType matchEquality = equalityType(subjectType);
|
||||
EqualityType patternEquality = equalityType(patternType);
|
||||
|
||||
JsExpression expressionToMatchAgainst = translateExpressionForExpressionPattern(patternExpression);
|
||||
|
||||
if (matchEquality == EqualityType.PRIMITIVE && patternEquality == EqualityType.PRIMITIVE) {
|
||||
return equality(expressionToMatch, expressionToMatchAgainst);
|
||||
}
|
||||
|
||||
@@ -326,10 +326,8 @@ private constructor(private val whenExpression: KtWhenExpression, context: Trans
|
||||
val patternTranslator = Translation.patternTranslator(context)
|
||||
return if (expressionToMatch == null) {
|
||||
patternTranslator.translateExpressionForExpressionPattern(patternExpression)
|
||||
}
|
||||
else {
|
||||
val type = bindingContext().getType(whenExpression.subjectExpression!!)!!
|
||||
patternTranslator.translateExpressionPattern(type, expressionToMatch, patternExpression)
|
||||
} else {
|
||||
patternTranslator.translateExpressionPattern(whenExpression.subjectExpression!!, expressionToMatch, patternExpression)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ object LongOperationFIF : FunctionIntrinsicFactory {
|
||||
|
||||
private val floatBinaryIntrinsics: Map<String, BaseBinaryIntrinsic> =
|
||||
mapOf(
|
||||
"compareTo" to BaseBinaryIntrinsic(::primitiveCompareTo),
|
||||
"compareTo" to BaseBinaryIntrinsic(::compareTo),
|
||||
"plus" to BaseBinaryIntrinsic(::sum),
|
||||
"minus" to BaseBinaryIntrinsic(::subtract),
|
||||
"times" to BaseBinaryIntrinsic(::mul),
|
||||
|
||||
+7
-2
@@ -110,8 +110,11 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
private static final DescriptorPredicate PRIMITIVE_NUMBERS_BINARY_OPERATIONS =
|
||||
pattern(NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS, BINARY_OPERATIONS);
|
||||
|
||||
private static final DescriptorPredicate PRIMITIVE_INTEGRAL_NUMBERS_COMPARE_TO_INTEGRAL_OPERATIONS =
|
||||
pattern("Byte|Short|Int.compareTo(Byte|Short|Int)");
|
||||
private static final DescriptorPredicate PRIMITIVE_NUMBERS_COMPARE_TO_OPERATIONS =
|
||||
pattern(NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS, "compareTo");
|
||||
|
||||
private static final Predicate<FunctionDescriptor> INT_WITH_BIT_OPERATIONS = pattern("Int.or|and|xor|shl|shr|ushr")
|
||||
.or(pattern("Short|Byte.or|and|xor"));
|
||||
private static final DescriptorPredicate BOOLEAN_OPERATIONS = pattern("Boolean.or|and|xor");
|
||||
@@ -140,10 +143,12 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
return new RangeToIntrinsic(descriptor);
|
||||
}
|
||||
|
||||
if (PRIMITIVE_NUMBERS_COMPARE_TO_OPERATIONS.test(descriptor)) {
|
||||
if (PRIMITIVE_INTEGRAL_NUMBERS_COMPARE_TO_INTEGRAL_OPERATIONS.test(descriptor)) {
|
||||
return PRIMITIVE_NUMBER_COMPARE_TO_INTRINSIC;
|
||||
}
|
||||
|
||||
if (PRIMITIVE_NUMBERS_COMPARE_TO_OPERATIONS.test(descriptor)) {
|
||||
return BUILTINS_COMPARE_TO_INTRINSIC;
|
||||
}
|
||||
|
||||
if (KotlinBuiltIns.isBuiltIn(descriptor) && descriptor.getName().equals(OperatorNameConventions.COMPARE_TO)) {
|
||||
return BUILTINS_COMPARE_TO_INTRINSIC;
|
||||
|
||||
+8
-13
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperation
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsIntLiteral
|
||||
import org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.operation.OperatorTable
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
@@ -33,10 +32,6 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
|
||||
object CompareToBOIF : BinaryOperationIntrinsicFactory {
|
||||
val COMPARE_TO_CHAR = pattern("Int|Short|Byte|Double|Float.compareTo(Char)")
|
||||
val CHAR_COMPARE_TO = pattern("Char.compareTo(Int|Short|Byte|Double|Float)")
|
||||
val PRIMITIVE_COMPARE_TO = pattern("Int|Short|Byte|Double|Float|Char|String|Boolean.compareTo")
|
||||
|
||||
private object CompareToIntrinsic : AbstractBinaryOperationIntrinsic() {
|
||||
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
|
||||
val operator = OperatorTable.getBinaryOperator(getOperationToken(expression))
|
||||
@@ -71,17 +66,17 @@ object CompareToBOIF : BinaryOperationIntrinsicFactory {
|
||||
override fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic? {
|
||||
if (descriptor.isDynamic()) return CompareToIntrinsic
|
||||
|
||||
if (leftType == null || rightType == null) return null
|
||||
|
||||
if (!KotlinBuiltIns.isBuiltIn(descriptor)) return null
|
||||
|
||||
// Types may be nullable if properIeeeComparisons are switched off, e.g. fun foo(a: Double?) = a != null && a < 0.0
|
||||
return when {
|
||||
COMPARE_TO_CHAR.test(descriptor) ->
|
||||
CompareToCharIntrinsic
|
||||
CHAR_COMPARE_TO.test(descriptor) ->
|
||||
CompareCharToPrimitiveIntrinsic
|
||||
PRIMITIVE_COMPARE_TO.test(descriptor) ->
|
||||
CompareToIntrinsic
|
||||
else ->
|
||||
CompareToFunctionIntrinsic
|
||||
KotlinBuiltIns.isCharOrNullableChar(rightType) -> CompareToCharIntrinsic
|
||||
KotlinBuiltIns.isCharOrNullableChar(leftType) -> CompareCharToPrimitiveIntrinsic
|
||||
KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(leftType) &&
|
||||
KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(rightType) -> CompareToIntrinsic
|
||||
else -> CompareToFunctionIntrinsic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
-49
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.js.translate.intrinsic.operation
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperation
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator
|
||||
@@ -31,11 +30,8 @@ import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
@@ -46,9 +42,8 @@ import java.util.*
|
||||
object EqualsBOIF : BinaryOperationIntrinsicFactory {
|
||||
private object EqualsIntrinsic : AbstractBinaryOperationIntrinsic() {
|
||||
|
||||
// Primitives with no boxing or tricky NaN behavior, represented by a Number at runtime.
|
||||
// Whenever the left side of equality is of this type, equality could be tested via '==='.
|
||||
private val SIMPLE_PRIMITIVES = EnumSet.of(PrimitiveType.BYTE, PrimitiveType.SHORT, PrimitiveType.INT)
|
||||
private val JS_NUMBER_PRIMITIVES =
|
||||
EnumSet.of(PrimitiveType.BYTE, PrimitiveType.SHORT, PrimitiveType.INT, PrimitiveType.DOUBLE, PrimitiveType.FLOAT)
|
||||
|
||||
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
|
||||
val isNegated = expression.isNegated()
|
||||
@@ -60,25 +55,38 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
|
||||
return TranslationUtils.nullCheck(coercedSubject, isNegated)
|
||||
}
|
||||
|
||||
val ktLeft = checkNotNull(expression.left) { "No left-hand side: " + expression.text }
|
||||
val ktRight = checkNotNull(expression.right) { "No right-hand side: " + expression.text }
|
||||
val leftKotlinType = getRefinedType(ktLeft, context)
|
||||
val rightKotlinType = getRefinedType(ktRight, context)
|
||||
val (leftKotlinType, rightKotlinType) = binaryOperationTypes(expression, context)
|
||||
|
||||
val leftType = leftKotlinType?.let { KotlinBuiltIns.getPrimitiveType(it) }
|
||||
val rightType = rightKotlinType?.let { KotlinBuiltIns.getPrimitiveType(it) }
|
||||
|
||||
if (leftType != null && (leftType in SIMPLE_PRIMITIVES || leftType == rightType && leftType != PrimitiveType.LONG)) {
|
||||
if (leftType != null && rightType != null && (
|
||||
leftType in JS_NUMBER_PRIMITIVES && rightType in JS_NUMBER_PRIMITIVES ||
|
||||
leftType in JS_NUMBER_PRIMITIVES && rightType == PrimitiveType.LONG ||
|
||||
leftType == PrimitiveType.LONG && rightType in JS_NUMBER_PRIMITIVES ||
|
||||
leftType == PrimitiveType.BOOLEAN && rightType == PrimitiveType.BOOLEAN ||
|
||||
leftType == PrimitiveType.CHAR && rightType == PrimitiveType.CHAR
|
||||
)) {
|
||||
val useEq = leftType == PrimitiveType.LONG || rightType == PrimitiveType.LONG
|
||||
|
||||
val operator = when {
|
||||
useEq && isNegated -> JsBinaryOperator.NEQ
|
||||
useEq && !isNegated -> JsBinaryOperator.EQ
|
||||
!useEq && isNegated -> JsBinaryOperator.REF_NEQ
|
||||
else /* !useEq && !isNegated */ -> JsBinaryOperator.REF_EQ
|
||||
}
|
||||
|
||||
val coercedLeft = TranslationUtils.coerce(context, left, leftKotlinType)
|
||||
val coercedRight = TranslationUtils.coerce(context, right, rightKotlinType!!)
|
||||
return JsBinaryOperation(if (isNegated) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ, coercedLeft, coercedRight)
|
||||
val coercedRight = TranslationUtils.coerce(context, right, rightKotlinType)
|
||||
return JsBinaryOperation(operator, coercedLeft, coercedRight)
|
||||
}
|
||||
|
||||
val resolvedCall = expression.getResolvedCall(context.bindingContext())
|
||||
val appliedToDynamic =
|
||||
resolvedCall != null &&
|
||||
with(resolvedCall.dispatchReceiver) {
|
||||
if (this != null) type.isDynamic() else false
|
||||
}
|
||||
resolvedCall != null &&
|
||||
with(resolvedCall.dispatchReceiver) {
|
||||
if (this != null) type.isDynamic() else false
|
||||
}
|
||||
|
||||
if (appliedToDynamic) {
|
||||
return JsBinaryOperation(if (isNegated) JsBinaryOperator.NEQ else JsBinaryOperator.EQ, left, right)
|
||||
@@ -89,37 +97,6 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
|
||||
val result = TopLevelFIF.KOTLIN_EQUALS.apply(coercedLeft, listOf(coercedRight), context)
|
||||
return if (isNegated) JsAstUtils.not(result) else result
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get as precise statically known primitive type as possible - taking smart-casts and generic supertypes into account.
|
||||
* This is needed to be compatible with JVM NaN behaviour, in particular the following two cases.
|
||||
*
|
||||
* // Smart-casts
|
||||
* val a: Any = Double.NaN
|
||||
* println(a == a) // true
|
||||
* if (a is Double) {
|
||||
* println(a == a) // false
|
||||
* }
|
||||
*
|
||||
* // Generics with Double super-type
|
||||
* fun <T: Double> foo(v: T) = println(v == v)
|
||||
* foo(Double.NaN) // false
|
||||
*
|
||||
* Also see org/jetbrains/kotlin/codegen/codegenUtil.kt#calcTypeForIEEE754ArithmeticIfNeeded
|
||||
*/
|
||||
private fun getRefinedType(expression: KtExpression, context: TranslationContext): KotlinType? {
|
||||
val bindingContext = context.bindingContext()
|
||||
val descriptor = context.declarationDescriptor ?: context.currentModule
|
||||
val ktType = bindingContext.getType(expression) ?: return null
|
||||
|
||||
val dataFlow = DataFlowValueFactoryImpl().createDataFlowValue(expression, ktType, bindingContext, descriptor)
|
||||
val isPrimitiveFn = KotlinBuiltIns::isPrimitiveTypeOrNullablePrimitiveType
|
||||
|
||||
val languageVersionSettings = context.config.configuration.languageVersionSettings
|
||||
return bindingContext.getDataFlowInfoBefore(expression).getStableTypes(dataFlow, languageVersionSettings).find(isPrimitiveFn) ?: // Smart-casts
|
||||
TypeUtils.getAllSupertypes(ktType).find(isPrimitiveFn) ?: // Generic super-types
|
||||
ktType // Default
|
||||
}
|
||||
}
|
||||
|
||||
object EnumEqualsIntrinsic : AbstractBinaryOperationIntrinsic() {
|
||||
|
||||
+12
-2
@@ -21,6 +21,8 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
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 org.jetbrains.kotlin.js.translate.utils.getPrecisePrimitiveType
|
||||
import org.jetbrains.kotlin.js.translate.utils.getPrimitiveNumericComparisonInfo
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -45,8 +47,7 @@ class BinaryOperationIntrinsics {
|
||||
return NO_INTRINSIC
|
||||
}
|
||||
|
||||
val leftType = expression.left?.let { context.bindingContext().getType(it) }
|
||||
val rightType = expression.right?.let { context.bindingContext().getType(it) }
|
||||
val (leftType, rightType) = binaryOperationTypes(expression, context)
|
||||
|
||||
val key = IntrinsicKey(token, descriptor, leftType, rightType)
|
||||
return intrinsicCache.getOrPut(key) { computeIntrinsic(token, descriptor, leftType, rightType) }
|
||||
@@ -68,6 +69,15 @@ class BinaryOperationIntrinsics {
|
||||
}
|
||||
}
|
||||
|
||||
// Takes into account smart-casts (needed for IEEE 754 comparisons)
|
||||
fun binaryOperationTypes(expression: KtBinaryExpression, context: TranslationContext): Pair<KotlinType?, KotlinType?> {
|
||||
val info = context.getPrimitiveNumericComparisonInfo(expression)
|
||||
if (info != null) {
|
||||
return info.leftType to info.rightType
|
||||
}
|
||||
return expression.left?.let { context.getPrecisePrimitiveType(it) } to expression.right?.let { context.getPrecisePrimitiveType(it) }
|
||||
}
|
||||
|
||||
private data class IntrinsicKey(
|
||||
val token: KtToken, val function: FunctionDescriptor,
|
||||
val leftType: KotlinType?, val rightType: KotlinType?
|
||||
|
||||
@@ -19,6 +19,9 @@ package org.jetbrains.kotlin.js.translate.utils
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.backend.common.COROUTINE_SUSPENDED_NAME
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
@@ -31,15 +34,14 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed
|
||||
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction
|
||||
import org.jetbrains.kotlin.psi.KtBlockExpression
|
||||
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
||||
import org.jetbrains.kotlin.psi.KtFunctionLiteral
|
||||
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasOrInheritsParametersWithDefaultValue
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
|
||||
fun generateDelegateCall(
|
||||
classDescriptor: ClassDescriptor,
|
||||
@@ -242,4 +244,34 @@ fun TranslationContext.createCoroutineResult(resolvedCall: ResolvedCall<*>): JsE
|
||||
coroutineResult = true
|
||||
synthetic = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get precise statically known primitive type. Takes generic supertypes into account. Doesn't handle smart-casts.
|
||||
* This is needed to be compatible with JVM NaN behaviour:
|
||||
*
|
||||
* // Generics with Double super-type
|
||||
* fun <T: Double> foo(v: T) = println(v == v)
|
||||
* foo(Double.NaN) // false
|
||||
*
|
||||
* Also see org/jetbrains/kotlin/codegen/codegenUtil.kt#calcTypeForIEEE754ArithmeticIfNeeded
|
||||
*/
|
||||
fun TranslationContext.getPrecisePrimitiveType(expression: KtExpression): KotlinType? {
|
||||
val bindingContext = bindingContext()
|
||||
val ktType = bindingContext.getType(expression) ?: return null
|
||||
|
||||
return TypeUtils.getAllSupertypes(ktType).find(KotlinBuiltIns::isPrimitiveTypeOrNullablePrimitiveType) ?: ktType
|
||||
}
|
||||
|
||||
fun TranslationContext.getPrecisePrimitiveTypeNotNull(expression: KtExpression): KotlinType {
|
||||
return getPrecisePrimitiveType(expression) ?: throw IllegalStateException("Type must be not null for " + expression)
|
||||
}
|
||||
|
||||
fun TranslationContext.getPrimitiveNumericComparisonInfo(expression: KtExpression) =
|
||||
config.configuration.languageVersionSettings.let {
|
||||
if (it.supportsFeature(LanguageFeature.ProperIeee754Comparisons)) {
|
||||
bindingContext().get(BindingContext.PRIMITIVE_NUMERIC_COMPARISON_INFO, expression)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user