JS: correct Double and Float conversions to Int (KT-8374 fixed)
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
// WITH_RUNTIME
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
fun box(): String {
|
||||
|
||||
assertEquals(-2147483648, Float.NEGATIVE_INFINITY.toInt())
|
||||
assertEquals(-2147483648, (-Float.MAX_VALUE).toInt())
|
||||
assertEquals(-1, (-1.9f).toInt())
|
||||
assertEquals(0, (-0.0f).toInt())
|
||||
assertEquals(0, (-Float.MIN_VALUE).toInt())
|
||||
assertEquals(0, 0.0f.toInt())
|
||||
assertEquals(0, Float.NaN.toInt())
|
||||
assertEquals(0, Float.MIN_VALUE.toInt())
|
||||
assertEquals(0, 0.9f.toInt())
|
||||
assertEquals(2147483647, Float.MAX_VALUE.toInt())
|
||||
assertEquals(2147483647, Float.POSITIVE_INFINITY.toInt())
|
||||
|
||||
assertEquals(-2147483648, Double.NEGATIVE_INFINITY.toInt())
|
||||
assertEquals(-2147483648, (-Double.MAX_VALUE).toInt())
|
||||
assertEquals(-2147483648, (-2147483649.0).toInt())
|
||||
assertEquals(-2147483648, (-2147483648.0).toInt())
|
||||
assertEquals(-2147483647, (-2147483647.9).toInt())
|
||||
assertEquals(-1, (-1.9).toInt())
|
||||
assertEquals(0, (-0.0).toInt())
|
||||
assertEquals(0, (-Double.MIN_VALUE).toInt())
|
||||
assertEquals(0, 0.0.toInt())
|
||||
assertEquals(0, Double.NaN.toInt())
|
||||
assertEquals(0, Double.MIN_VALUE.toInt())
|
||||
assertEquals(0, 0.9.toInt())
|
||||
assertEquals(2147483646, 2147483646.9.toInt())
|
||||
assertEquals(2147483647, 2147483647.0.toInt())
|
||||
assertEquals(2147483647, 2147483648.0.toInt())
|
||||
assertEquals(2147483647, Double.MAX_VALUE.toInt())
|
||||
assertEquals(2147483647, Double.POSITIVE_INFINITY.toInt())
|
||||
|
||||
for (d in doubleArrayOf(Double.NEGATIVE_INFINITY, -Double.MAX_VALUE,
|
||||
-2147483649.0, -2147483648.0, -2147483647.0,
|
||||
-65536.0, -65535.0, -65534.0,
|
||||
-1.5, -Double.MIN_VALUE,
|
||||
-0.0, 0.0,
|
||||
Double.MIN_VALUE, 1.5,
|
||||
65534.0, 65535.0, 65536.0,
|
||||
2147483647.0, 2147483648.0, 2147483649.0,
|
||||
Double.MAX_VALUE, Double.POSITIVE_INFINITY)) {
|
||||
assertEquals(d.toInt().toByte(), d.toByte())
|
||||
assertEquals(d.toInt().toShort(), d.toShort())
|
||||
assertEquals(d.toInt().toChar(), d.toChar())
|
||||
|
||||
val f = d.toFloat()
|
||||
assertEquals(f.toInt().toByte(), f.toByte())
|
||||
assertEquals(f.toInt().toShort(), f.toShort())
|
||||
assertEquals(f.toInt().toChar(), f.toChar())
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+6
@@ -12812,6 +12812,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("conversions.kt")
|
||||
public void testConversions() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/conversions.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ea35963.kt")
|
||||
public void testEa35963() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/ea35963.kt");
|
||||
|
||||
@@ -12812,6 +12812,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("conversions.kt")
|
||||
public void testConversions() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/conversions.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ea35963.kt")
|
||||
public void testEa35963() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/ea35963.kt");
|
||||
|
||||
@@ -12812,6 +12812,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("conversions.kt")
|
||||
public void testConversions() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/conversions.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ea35963.kt")
|
||||
public void testEa35963() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/ea35963.kt");
|
||||
|
||||
@@ -31,7 +31,7 @@ Kotlin.numberToLong = function (a) {
|
||||
};
|
||||
|
||||
Kotlin.numberToInt = function (a) {
|
||||
return a instanceof Kotlin.Long ? a.toInt() : (a | 0);
|
||||
return a instanceof Kotlin.Long ? a.toInt() : Kotlin.doubleToInt(a);
|
||||
};
|
||||
|
||||
Kotlin.numberToShort = function (a) {
|
||||
@@ -50,6 +50,12 @@ Kotlin.numberToChar = function (a) {
|
||||
return Kotlin.toChar(Kotlin.numberToInt(a));
|
||||
};
|
||||
|
||||
Kotlin.doubleToInt = function(a) {
|
||||
if (a > 2147483647) return 2147483647;
|
||||
if (a < -2147483648) return -2147483648;
|
||||
return a | 0;
|
||||
};
|
||||
|
||||
Kotlin.toBoxedChar = function (a) {
|
||||
if (a == null) return a;
|
||||
if (a instanceof Kotlin.BoxedChar) return a;
|
||||
|
||||
@@ -13994,6 +13994,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("conversions.kt")
|
||||
public void testConversions() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/conversions.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ea35963.kt")
|
||||
public void testEa35963() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/ea35963.kt");
|
||||
|
||||
+18
-12
@@ -22,7 +22,10 @@ import org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*
|
||||
import org.jetbrains.kotlin.utils.identity
|
||||
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.invokeKotlinFunction
|
||||
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.toByte
|
||||
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.toChar
|
||||
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.toShort
|
||||
import java.util.function.Predicate
|
||||
|
||||
object NumberAndCharConversionFIF : CompositeFIF() {
|
||||
@@ -33,16 +36,18 @@ object NumberAndCharConversionFIF : CompositeFIF() {
|
||||
.or(pattern("Long.toLong"))
|
||||
.or(pattern("Char.toChar"))
|
||||
|
||||
private val convertOperations: Map<String, ConversionUnaryIntrinsic> =
|
||||
private val convertOperations: Map<String, FunctionIntrinsicWithReceiverComputed> =
|
||||
mapOf(
|
||||
"Float|Double.toInt" to ConversionUnaryIntrinsic(::toInt32),
|
||||
"Int|Float|Double.toShort" to ConversionUnaryIntrinsic(::toShort),
|
||||
"Short|Int|Float|Double.toByte" to ConversionUnaryIntrinsic(::toByte),
|
||||
"Float|Double.toInt" to ConversionUnaryIntrinsic { invokeKotlinFunction("numberToInt", it) },
|
||||
"Float|Double.toShort" to ConversionUnaryIntrinsic { toShort(invokeKotlinFunction("numberToInt", it)) },
|
||||
"Int.toShort" to ConversionUnaryIntrinsic { toShort(it) },
|
||||
"Float|Double.toByte" to ConversionUnaryIntrinsic { toByte(invokeKotlinFunction("numberToInt", it)) },
|
||||
"Short|Int.toByte" to ConversionUnaryIntrinsic { toByte(it) },
|
||||
|
||||
"Int|Short|Byte.toLong" to ConversionUnaryIntrinsic(::longFromInt),
|
||||
"Float|Double.toLong" to ConversionUnaryIntrinsic(::longFromNumber),
|
||||
"Int|Short|Byte.toLong" to ConversionUnaryIntrinsic { longFromInt(it) },
|
||||
"Float|Double.toLong" to ConversionUnaryIntrinsic { longFromNumber(it) },
|
||||
|
||||
"Char.toDouble|toFloat|toInt" to ConversionUnaryIntrinsic(::charToInt),
|
||||
"Char.toDouble|toFloat|toInt" to ConversionUnaryIntrinsic { charToInt(it) },
|
||||
"Char.toShort" to ConversionUnaryIntrinsic { toShort(charToInt(it)) },
|
||||
"Char.toByte" to ConversionUnaryIntrinsic { toByte(charToInt(it)) },
|
||||
"Char.toLong" to ConversionUnaryIntrinsic { longFromInt(charToInt(it)) },
|
||||
@@ -54,7 +59,8 @@ object NumberAndCharConversionFIF : CompositeFIF() {
|
||||
"Number.toFloat|toDouble" to ConversionUnaryIntrinsic { invokeKotlinFunction("numberToDouble", it) },
|
||||
"Number.toLong" to ConversionUnaryIntrinsic { invokeKotlinFunction("numberToLong", it) },
|
||||
|
||||
"Int|Short|Byte|Float|Double.toChar" to ConversionUnaryIntrinsic(::toChar),
|
||||
"Float|Double.toChar" to ConversionUnaryIntrinsic { toChar(invokeKotlinFunction("numberToInt", it)) },
|
||||
"Int|Short|Byte.toChar" to ConversionUnaryIntrinsic { toChar(it) },
|
||||
|
||||
"Long.toFloat|toDouble" to ConversionUnaryIntrinsic { invokeMethod(it, "toNumber") },
|
||||
"Long.toInt" to ConversionUnaryIntrinsic { invokeMethod(it, "toInt") },
|
||||
@@ -64,16 +70,16 @@ object NumberAndCharConversionFIF : CompositeFIF() {
|
||||
|
||||
)
|
||||
|
||||
class ConversionUnaryIntrinsic(val applyFun: (receiver: JsExpression) -> JsExpression) : FunctionIntrinsicWithReceiverComputed() {
|
||||
class ConversionUnaryIntrinsic(val applyFun: TranslationContext.(receiver: JsExpression) -> JsExpression) : FunctionIntrinsicWithReceiverComputed() {
|
||||
override fun apply(receiver: JsExpression?, arguments: List<JsExpression>, context: TranslationContext): JsExpression {
|
||||
assert(receiver != null)
|
||||
assert(arguments.isEmpty())
|
||||
return applyFun(receiver!!)
|
||||
return context.applyFun(receiver!!)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
add(USE_AS_IS, ConversionUnaryIntrinsic(identity()))
|
||||
add(USE_AS_IS, ConversionUnaryIntrinsic { it })
|
||||
for((stringPattern, intrinsic) in convertOperations) {
|
||||
add(pattern(stringPattern), intrinsic)
|
||||
}
|
||||
|
||||
+2
-1
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntri
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.RangeToIntrinsic;
|
||||
import org.jetbrains.kotlin.js.translate.operation.OperatorTable;
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.AstUtilsKt;
|
||||
import org.jetbrains.kotlin.lexer.KtToken;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
|
||||
@@ -283,7 +284,7 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
@NotNull
|
||||
@Override
|
||||
public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
|
||||
return JsAstUtils.toChar(functionIntrinsic.doApply(left, right, context));
|
||||
return AstUtilsKt.toChar(context, functionIntrinsic.doApply(left, right, context));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntri
|
||||
import org.jetbrains.kotlin.js.translate.operation.OperatorTable;
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.AstUtilsKt;
|
||||
import org.jetbrains.kotlin.lexer.KtToken;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
|
||||
@@ -99,7 +100,7 @@ public enum PrimitiveUnaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
@NotNull List<? extends JsExpression> arguments,
|
||||
@NotNull TranslationContext context
|
||||
) {
|
||||
return JsAstUtils.toShort(underlyingIntrinsic.apply(receiver, arguments, context));
|
||||
return AstUtilsKt.toShort(context, underlyingIntrinsic.apply(receiver, arguments, context));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +118,7 @@ public enum PrimitiveUnaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
@NotNull List<? extends JsExpression> arguments,
|
||||
@NotNull TranslationContext context
|
||||
) {
|
||||
return JsAstUtils.toByte(underlyingIntrinsic.apply(receiver, arguments, context));
|
||||
return AstUtilsKt.toByte(context, underlyingIntrinsic.apply(receiver, arguments, context));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
|
||||
import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind;
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer;
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt;
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -157,26 +156,6 @@ public final class JsAstUtils {
|
||||
return new JsInvocation(new JsNameRef("fromCharCode", new JsNameRef("String")), expression);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsExpression toShort(@NotNull JsExpression expression) {
|
||||
return invokeKotlinFunction(OperatorConventions.SHORT.getIdentifier(), expression);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsExpression toByte(@NotNull JsExpression expression) {
|
||||
return invokeKotlinFunction(OperatorConventions.BYTE.getIdentifier(), expression);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsExpression toLong(@NotNull JsExpression expression) {
|
||||
return invokeKotlinFunction(OperatorConventions.LONG.getIdentifier(), expression);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsExpression toChar(@NotNull JsExpression expression) {
|
||||
return invokeKotlinFunction(OperatorConventions.CHAR.getIdentifier(), expression);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsExpression compareTo(@NotNull JsExpression left, @NotNull JsExpression right) {
|
||||
return invokeKotlinFunction(OperatorNameConventions.COMPARE_TO.getIdentifier(), left, right);
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.js.translate.utils.jsAstUtils
|
||||
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
|
||||
fun JsFunction.addStatement(stmt: JsStatement) {
|
||||
body.statements.add(stmt)
|
||||
@@ -103,3 +105,15 @@ var JsConditional.then: JsExpression
|
||||
var JsConditional.otherwise: JsExpression
|
||||
get() = elseExpression
|
||||
set(value) { elseExpression = value }
|
||||
|
||||
// Extension functions below produce aliased invocations.
|
||||
fun TranslationContext.invokeKotlinFunction(functionName: String, vararg arguments: JsExpression)
|
||||
= JsInvocation(getReferenceToIntrinsic(functionName), arguments.toList())
|
||||
|
||||
fun TranslationContext.toByte(expression: JsExpression) = invokeKotlinFunction(OperatorConventions.BYTE.identifier, expression)
|
||||
|
||||
fun TranslationContext.toShort(expression: JsExpression) = invokeKotlinFunction(OperatorConventions.SHORT.identifier, expression)
|
||||
|
||||
fun TranslationContext.toChar(expression: JsExpression) = invokeKotlinFunction(OperatorConventions.CHAR.identifier, expression)
|
||||
|
||||
fun TranslationContext.toLong(expression: JsExpression) = invokeKotlinFunction(OperatorConventions.LONG.identifier, expression)
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1018
|
||||
// EXPECTED_REACHABLE_NODES: 1126
|
||||
|
||||
// MODULE: lib
|
||||
// FILE: a.kt
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1018
|
||||
// EXPECTED_REACHABLE_NODES: 1124
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
inline fun foo(x: String = "x", y: String = "y") = x + y
|
||||
|
||||
Reference in New Issue
Block a user