JS: correct Double and Float conversions to Int (KT-8374 fixed)

This commit is contained in:
Anton Bannykh
2017-10-05 15:11:01 +03:00
parent a52d719943
commit b5d32f420d
13 changed files with 126 additions and 39 deletions
@@ -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"
}
@@ -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");
+7 -1
View File
@@ -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");
@@ -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)
}
@@ -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));
}
}
@@ -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
View File
@@ -1,4 +1,4 @@
// EXPECTED_REACHABLE_NODES: 1018
// EXPECTED_REACHABLE_NODES: 1126
// MODULE: lib
// FILE: a.kt
@@ -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