JS: fix double compareTo behaviour for NaN and +-0 (KT-22723)

This commit is contained in:
Anton Bannykh
2017-12-27 21:18:05 +03:00
parent 12c01ef80a
commit ed80252ba8
49 changed files with 260 additions and 307 deletions
@@ -151,6 +151,12 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
)
var useExperimental: Array<String>? by FreezableVar(null)
@Argument(
value = "-Xproper-ieee754-comparisons",
description = "Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types"
)
var properIeee754Comparisons by FreezableVar(false)
open fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> {
return HashMap<AnalysisFlag<*>, Any>().apply {
put(AnalysisFlag.skipMetadataVersionCheck, skipMetadataVersionCheck)
@@ -224,12 +224,6 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
)
var noExceptionOnExplicitEqualsForBoxedNull by FreezableVar(false)
@Argument(
value = "-Xproper-ieee754-comparisons",
description = "Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types"
)
var properIeee754Comparisons by FreezableVar(false)
// Paths to output directories for friend modules.
var friendPaths: Array<String>? by FreezableVar(null)
@@ -226,6 +226,9 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
extraLanguageFeatures.put(LanguageFeature.ReadDeserializedContracts, LanguageFeature.State.ENABLED);
}
if (arguments.getProperIeee754Comparisons()) {
extraLanguageFeatures.put(LanguageFeature.ProperIeee754Comparisons, LanguageFeature.State.ENABLED);
}
setupPlatformSpecificLanguageFeatureSettings(extraLanguageFeatures, arguments);
@@ -235,10 +235,6 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
extraLanguageFeatures[LanguageFeature.StrictJavaNullabilityAssertions] = LanguageFeature.State.ENABLED
}
if (commandLineArguments.properIeee754Comparisons) {
extraLanguageFeatures[LanguageFeature.ProperIeee754Comparisons] = LanguageFeature.State.ENABLED
}
super.setupPlatformSpecificLanguageFeatureSettings(extraLanguageFeatures, commandLineArguments)
}
+1
View File
@@ -15,6 +15,7 @@ where advanced options include:
-Xno-check-actual Do not check presence of 'actual' modifier in multi-platform projects
-Xno-inline Disable method inlining
-Xplugin=<path> Load plugins from the given classpath
-Xproper-ieee754-comparisons Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types
-Xread-deserialized-contracts Enable reading of contracts from metadata
-Xrepeat=<count> Repeat compilation (for performance analysis)
-Xreport-output-files Report source to output files mapping
+1 -1
View File
@@ -28,7 +28,6 @@ where advanced options include:
-Xno-optimize Disable optimizations
-Xno-param-assertions Don't generate not-null assertions on parameters of methods accessible from Java
-Xno-receiver-assertions Don't generate not-null assertion for extension receiver arguments of platform types
-Xproper-ieee754-comparisons Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types
-Xreport-perf Report detailed performance statistics
-Xscript-resolver-environment=<key=value[,]>
Script resolver environment in key-value pairs (the value could be quoted and escaped)
@@ -53,6 +52,7 @@ where advanced options include:
-Xno-check-actual Do not check presence of 'actual' modifier in multi-platform projects
-Xno-inline Disable method inlining
-Xplugin=<path> Load plugins from the given classpath
-Xproper-ieee754-comparisons Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types
-Xread-deserialized-contracts Enable reading of contracts from metadata
-Xrepeat=<count> Repeat compilation (for performance analysis)
-Xreport-output-files Report source to output files mapping
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS, NATIVE
// IGNORE_BACKEND: NATIVE
inline fun ltx(a: Comparable<Any>, b: Any) = a < b
inline fun lex(a: Comparable<Any>, b: Any) = a <= b
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS
// IGNORE_BACKEND: NATIVE
fun equals1(a: Double, b: Double) = a.equals(b)
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JS
val minus: Any = -0.0
fun box(): String {
@@ -1,5 +1,4 @@
// !LANGUAGE: +ProperIeee754Comparisons
// IGNORE_BACKEND: JS
val minus: Any = -0.0
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS
fun box(): String {
if ((-0.0 as Comparable<Double>) >= 0.0) return "fail 0"
if ((-0.0F as Comparable<Float>) >= 0.0F) return "fail 1"
@@ -0,0 +1,62 @@
fun box(): String {
if (0.toByte().compareTo(-0.0) != 1) return "fail 1.1"
if (0.toByte().compareTo(-0.0F) != 1) return "fail 1.2"
if (0.toByte().compareTo(Double.NaN) != -1) return "fail 1.3"
if (0.toByte().compareTo(Float.NaN) != -1) return "fail 1.4"
if (0.toShort().compareTo(-0.0) != 1) return "fail 2.1"
if (0.toShort().compareTo(-0.0F) != 1) return "fail 2.2"
if (0.toShort().compareTo(Double.NaN) != -1) return "fail 2.3"
if (0.toShort().compareTo(Float.NaN) != -1) return "fail 2.4"
if (0.compareTo(-0.0) != 1) return "fail 3.1"
if (0.compareTo(-0.0F) != 1) return "fail 3.2"
if (0.compareTo(Double.NaN) != -1) return "fail 3.3"
if (0.compareTo(Float.NaN) != -1) return "fail 3.4"
if (0.0F.compareTo(-0.0) != 1) return "fail 4.1"
if (0.0F.compareTo(-0.0F) != 1) return "fail 4.2"
if (0.0F.compareTo(Double.NaN) != -1) return "fail 4.3"
if (0.0F.compareTo(Float.NaN) != -1) return "fail 4.4"
if (0.0.compareTo(-0.0) != 1) return "fail 5.1"
if (0.0.compareTo(-0.0F) != 1) return "fail 5.2"
if (0.0.compareTo(Double.NaN) != -1) return "fail 5.3"
if (0.0.compareTo(Float.NaN) != -1) return "fail 5.4"
if (0L.compareTo(-0.0) != 1) return "fail 6.1"
if (0L.compareTo(-0.0F) != 1) return "fail 6.2"
if (0L.compareTo(Double.NaN) != -1) return "fail 6.3"
if (0L.compareTo(Float.NaN) != -1) return "fail 6.4"
if ((-0.0).compareTo(0.toByte()) != -1) return "fail 7.1"
if ((-0.0).compareTo(0.toShort()) != -1) return "fail 7.2"
if ((-0.0).compareTo(0) != -1) return "fail 7.3"
if ((-0.0).compareTo(0.0F) != -1) return "fail 7.4"
if ((-0.0).compareTo(0.0) != -1) return "fail 7.5"
if ((-0.0).compareTo(0L) != -1) return "fail 7.6"
if ((-0.0F).compareTo(0.toByte()) != -1) return "fail 8.1"
if ((-0.0F).compareTo(0.toShort()) != -1) return "fail 8.2"
if ((-0.0F).compareTo(0) != -1) return "fail 8.3"
if ((-0.0F).compareTo(0.0F) != -1) return "fail 8.4"
if ((-0.0F).compareTo(0.0) != -1) return "fail 8.5"
if ((-0.0F).compareTo(0L) != -1) return "fail 8.6"
if (Double.NaN.compareTo(0.toByte()) != 1) return "fail 9.1"
if (Double.NaN.compareTo(0.toShort()) != 1) return "fail 9.2"
if (Double.NaN.compareTo(0) != 1) return "fail 9.3"
if (Double.NaN.compareTo(0.0F) != 1) return "fail 9.4"
if (Double.NaN.compareTo(0.0) != 1) return "fail 9.5"
if (Double.NaN.compareTo(0L) != 1) return "fail 9.6"
if (Float.NaN.compareTo(0.toByte()) != 1) return "fail 10.1"
if (Float.NaN.compareTo(0.toShort()) != 1) return "fail 10.2"
if (Float.NaN.compareTo(0) != 1) return "fail 10.3"
if (Float.NaN.compareTo(0.0F) != 1) return "fail 10.4"
if (Float.NaN.compareTo(0.0) != 1) return "fail 10.5"
if (Float.NaN.compareTo(0L) != 1) return "fail 10.6"
return "OK"
}
-1
View File
@@ -1,5 +1,4 @@
// !LANGUAGE: -ProperIeee754Comparisons
// IGNORE_BACKEND: JS
fun equals1(a: Double, b: Double) = a == b
-1
View File
@@ -1,5 +1,4 @@
// !LANGUAGE: -ProperIeee754Comparisons
// IGNORE_BACKEND: JS
fun equals1(a: Float, b: Float) = a == b
-1
View File
@@ -1,6 +1,5 @@
// !LANGUAGE: -ProperIeee754Comparisons
// WITH_RUNTIME
// IGNORE_BACKEND: JS
import kotlin.test.*
@@ -1,5 +1,4 @@
// !LANGUAGE: -ProperIeee754Comparisons
// IGNORE_BACKEND: JS
fun equals1(a: Double, b: Double?) = a == b
@@ -1,5 +1,4 @@
// !LANGUAGE: -ProperIeee754Comparisons
// IGNORE_BACKEND: JS
fun equals1(a: Float, b: Float?) = a == b
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS
fun less1(a: Double, b: Double) = a.compareTo(b) == -1
fun less2(a: Double?, b: Double?) = a!!.compareTo(b!!) == -1
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JS
fun equals1(a: Double, b: Double) = a.equals(b)
fun equals2(a: Double?, b: Double?) = a!!.equals(b!!)
@@ -1,5 +1,4 @@
// !LANGUAGE: -ProperIeee754Comparisons
// IGNORE_BACKEND: JS
fun greater1(a: Double, b: Double) = a > b
-1
View File
@@ -1,5 +1,4 @@
// !LANGUAGE: -ProperIeee754Comparisons
// IGNORE_BACKEND: JS
fun greater1(a: Float, b: Float) = a > b
-2
View File
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JS
inline fun less(a: Comparable<Double>, b: Double): Boolean {
return a < b
}
-1
View File
@@ -1,5 +1,4 @@
// !LANGUAGE: -ProperIeee754Comparisons
// IGNORE_BACKEND: JS
fun less1(a: Double, b: Double) = a < b
-1
View File
@@ -1,5 +1,4 @@
// !LANGUAGE: -ProperIeee754Comparisons
// IGNORE_BACKEND: JS
fun less1(a: Float, b: Float) = a < b
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS
fun box(): String {
val plusZero: Double? = 0.0
val minusZero: Double = -0.0
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS
fun box(): String {
val zero: Any = 0.0
val floatZero: Any = -0.0F
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JS
fun eqDI(x: Any?, y: Any?) = x is Double? && y is Int? && x == y
fun eqDL(x: Any?, y: Any?) = x is Double? && y is Long? && x == y
fun eqID(x: Any?, y: Any?) = x is Int? && y is Double? && x == y
@@ -1,5 +1,4 @@
// !LANGUAGE: +ProperIeee754Comparisons
// IGNORE_BACKEND: JS
fun eqDI(x: Any?, y: Any?) = x is Double? && y is Int? && x == y
fun eqDL(x: Any?, y: Any?) = x is Double? && y is Long? && x == y
-1
View File
@@ -1,5 +1,4 @@
// !LANGUAGE: -ProperIeee754Comparisons
// IGNORE_BACKEND: JS
fun box(): String {
val plusZero: Any = 0.0
-1
View File
@@ -1,5 +1,4 @@
// LANGUAGE_VERSION: 1.0
// IGNORE_BACKEND: JS
fun box(): String {
val plusZero: Any = 0.0
@@ -1,5 +1,4 @@
// !LANGUAGE: -ProperIeee754Comparisons
// IGNORE_BACKEND: JS
fun box(): String {
val plusZero: Any = 0.0
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS
// WITH_RUNTIME
val DOUBLE_RANGE = 0.0 .. -0.0
@@ -10125,6 +10125,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
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");
@@ -10125,6 +10125,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
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");
@@ -10125,6 +10125,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
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");
@@ -946,6 +946,10 @@ public abstract class KotlinBuiltIns {
return isConstructedFromGivenClassAndNotNullable(type, FQ_NAMES._long);
}
public static boolean isLongOrNullableLong(@NotNull KotlinType type) {
return isConstructedFromGivenClass(type, FQ_NAMES._long);
}
public static boolean isShort(@NotNull KotlinType type) {
return isConstructedFromGivenClassAndNotNullable(type, FQ_NAMES._short);
}
@@ -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")
@@ -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)
}
}
@@ -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),
@@ -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;
@@ -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
}
}
}
@@ -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() {
@@ -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
}
}
+1 -1
View File
@@ -130,5 +130,5 @@ Kotlin.arrayDeepHashCode = function (arr) {
};
Kotlin.primitiveArraySort = function (array) {
array.sort(Kotlin.primitiveCompareTo)
array.sort(Kotlin.doubleCompareTo)
};
+4
View File
@@ -31,6 +31,10 @@ Kotlin.equals = function (obj1, obj2) {
return obj1.equals(obj2);
}
if (typeof obj1 === "number" && typeof obj2 === "number") {
return obj1 === obj2 && (obj1 !== 0 || 1 / obj1 === 1 / obj2)
}
return obj1 === obj2;
};
+20 -7
View File
@@ -16,14 +16,13 @@
Kotlin.compareTo = function (a, b) {
var typeA = typeof a;
var typeB = typeof a;
if (Kotlin.isChar(a) && typeB === "number") {
return Kotlin.primitiveCompareTo(a.charCodeAt(0), b);
if (typeA === "number") {
if (typeof b === "number") {
return Kotlin.doubleCompareTo(a, b);
}
return Kotlin.primitiveCompareTo(a, b);
}
if (typeA === "number" && Kotlin.isChar(b)) {
return Kotlin.primitiveCompareTo(a, b.charCodeAt(0));
}
if (typeA === "number" || typeA === "string" || typeA === "boolean") {
if (typeA === "string" || typeA === "boolean") {
return Kotlin.primitiveCompareTo(a, b);
}
return a.compareTo_11rb$(b);
@@ -33,6 +32,20 @@ Kotlin.primitiveCompareTo = function (a, b) {
return a < b ? -1 : a > b ? 1 : 0;
};
Kotlin.doubleCompareTo = function (a, b) {
if (a < b) return -1;
if (a > b) return 1;
if (a === b) {
if (a !== 0) return 0;
var ia = 1 / a;
return ia === 1 / b ? 0 : (ia < 0 ? -1 : 1);
}
return a !== a ? (b !== b ? 0 : 1) : -1
};
Kotlin.charInc = function (value) {
return Kotlin.toChar(value+1);
};
@@ -104,7 +104,6 @@ class NaNPropagationTest {
}
@JvmVersion
class NaNTotalOrderTest {
private fun <T : Comparable<T>> totalOrderMinOf2(f2t: (T, T) -> T, function: String) {