From 85f9e2e47b5103a9ce7cf74911eaedd42c626581 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 20 Feb 2017 12:53:12 +0300 Subject: [PATCH] Fix CCE in generated code that happens when comparing boxed chars The problem was that when obtaining char from the wrapper, codegen used int as expected type that led to a ClassCastException: java.lang.Character cannot be cast to java.lang.Number The solution is using coercion to chars, it's still correct, because of implicit widening coercion in JVM from C to I #KT-15105 Fixed --- .../org/jetbrains/kotlin/codegen/AsmUtil.java | 1 + .../kotlin/codegen/intrinsics/CompareTo.kt | 2 +- .../codegen/box/binaryOp/compareBoxedChars.kt | 16 ++++++++++++++++ .../binaryOp/compareBoxedChars.txt | 5 +++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 6 ++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++++++ .../semantics/JsCodegenBoxTestGenerated.java | 6 ++++++ 7 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/codegen/box/binaryOp/compareBoxedChars.kt create mode 100644 compiler/testData/codegen/light-analysis/binaryOp/compareBoxedChars.txt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index d0bfd7759cd..fc383013e99 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -770,6 +770,7 @@ public class AsmUtil { if (left == Type.DOUBLE_TYPE || right == Type.DOUBLE_TYPE) return Type.DOUBLE_TYPE; if (left == Type.FLOAT_TYPE || right == Type.FLOAT_TYPE) return Type.FLOAT_TYPE; if (left == Type.LONG_TYPE || right == Type.LONG_TYPE) return Type.LONG_TYPE; + if (left == Type.CHAR_TYPE || right == Type.CHAR_TYPE) return Type.CHAR_TYPE; return Type.INT_TYPE; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/CompareTo.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/CompareTo.kt index a24c15e84f9..1d3cf1dfa09 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/CompareTo.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/CompareTo.kt @@ -25,7 +25,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter class CompareTo : IntrinsicMethod() { private fun genInvoke(type: Type?, v: InstructionAdapter) { when (type) { - Type.INT_TYPE -> v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "compare", "(II)I", false) + Type.INT_TYPE, Type.CHAR_TYPE -> v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "compare", "(II)I", false) Type.LONG_TYPE -> v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "compare", "(JJ)I", false) Type.FLOAT_TYPE -> v.invokestatic("java/lang/Float", "compare", "(FF)I", false) Type.DOUBLE_TYPE -> v.invokestatic("java/lang/Double", "compare", "(DD)I", false) diff --git a/compiler/testData/codegen/box/binaryOp/compareBoxedChars.kt b/compiler/testData/codegen/box/binaryOp/compareBoxedChars.kt new file mode 100644 index 00000000000..f472ebfc47d --- /dev/null +++ b/compiler/testData/codegen/box/binaryOp/compareBoxedChars.kt @@ -0,0 +1,16 @@ +fun id(x: T) = x + +fun box(): String { + if (id('a') > id('b')) return "fail 1" + if (id('a') >= id('b')) return "fail 2" + if (id('b') < id('a')) return "fail 3" + if (id('b') <= id('a')) return "fail 4" + + val x = id('a').compareTo('b') + if (x != -1) return "fail 5" + + val y = id('b').compareTo('a') + if (y != 1) return "fail 6" + + return "OK" +} diff --git a/compiler/testData/codegen/light-analysis/binaryOp/compareBoxedChars.txt b/compiler/testData/codegen/light-analysis/binaryOp/compareBoxedChars.txt new file mode 100644 index 00000000000..815bb99390f --- /dev/null +++ b/compiler/testData/codegen/light-analysis/binaryOp/compareBoxedChars.txt @@ -0,0 +1,5 @@ +@kotlin.Metadata +public final class CompareBoxedCharsKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method id(p0: java.lang.Object): java.lang.Object +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index db194bc194b..6c398ba09cd 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -785,6 +785,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("compareBoxedChars.kt") + public void testCompareBoxedChars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/compareBoxedChars.kt"); + doTest(fileName); + } + @TestMetadata("compareWithBoxedDouble.kt") public void testCompareWithBoxedDouble() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/compareWithBoxedDouble.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index f03a06917d4..66ae76a9a29 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -785,6 +785,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("compareBoxedChars.kt") + public void testCompareBoxedChars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/compareBoxedChars.kt"); + doTest(fileName); + } + @TestMetadata("compareWithBoxedDouble.kt") public void testCompareWithBoxedDouble() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/compareWithBoxedDouble.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 87bb476ace3..51f7ccad211 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -977,6 +977,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("compareBoxedChars.kt") + public void testCompareBoxedChars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/compareBoxedChars.kt"); + doTest(fileName); + } + @TestMetadata("compareWithBoxedDouble.kt") public void testCompareWithBoxedDouble() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/compareWithBoxedDouble.kt");