From 2cb28b6be172afbece5bde0e37f52c11870bc9d6 Mon Sep 17 00:00:00 2001 From: Alexander Korepanov Date: Wed, 5 Jul 2023 09:49:46 +0200 Subject: [PATCH] [JS IR] Add an explicit toString() call for concatenating Char values ^KT-59718 Fixed --- .../js/lower/JsStringConcatenationLowering.kt | 17 +++- .../testData/box/char/charNoBoxing.kt | 88 +++++++++++++++---- 2 files changed, 87 insertions(+), 18 deletions(-) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsStringConcatenationLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsStringConcatenationLowering.kt index 06eaa7a0867..df432b0aa16 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsStringConcatenationLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsStringConcatenationLowering.kt @@ -9,12 +9,15 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.overrides import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.jetbrains.kotlin.utils.filterIsInstanceAnd class JsStringConcatenationLowering(val context: CommonBackendContext) : FileLoweringPass { override fun lower(irFile: IrFile) { @@ -30,7 +33,9 @@ private class JsStringConcatenationTransformer(val context: CommonBackendContext // See KT-39891 if (this !is IrSimpleType) return false return when (classifier.signature) { - IdSignatureValues.any, IdSignatureValues.comparable, IdSignatureValues.number, IdSignatureValues._long -> true + IdSignatureValues.any, IdSignatureValues.comparable, IdSignatureValues.number, + IdSignatureValues._long, IdSignatureValues._char + -> true else -> false } } @@ -43,7 +48,15 @@ private class JsStringConcatenationTransformer(val context: CommonBackendContext extensionReceiver = this@explicitlyConvertedToString } } else { - JsIrBuilder.buildCall(context.ir.symbols.memberToString).apply { + val anyToStringMethodSymbol = context.ir.symbols.memberToString + val toStringMethodSymbol = type.classOrNull?.let { + val toStringMethods = it.owner.declarations.filterIsInstanceAnd { f -> + f.overrides(anyToStringMethodSymbol.owner) + } + toStringMethods.singleOrNull()?.symbol + } ?: anyToStringMethodSymbol + + JsIrBuilder.buildCall(toStringMethodSymbol).apply { dispatchReceiver = this@explicitlyConvertedToString } } diff --git a/js/js.translator/testData/box/char/charNoBoxing.kt b/js/js.translator/testData/box/char/charNoBoxing.kt index 8fbde528519..dd8859ca9a0 100644 --- a/js/js.translator/testData/box/char/charNoBoxing.kt +++ b/js/js.translator/testData/box/char/charNoBoxing.kt @@ -1,42 +1,98 @@ +// KJS_WITH_FULL_RUNTIME + fun getC(c: Char) = c inline fun getCInline(c: Char) = c -// CHECK_NEW_COUNT: function=testNonInline1 count=0 -fun testNonInline1(a: Char, b: Char) : Boolean { +// CHECK_NEW_COUNT: function=testNonInlineEquals1 count=0 +fun testNonInlineEquals1(a: Char, b: Char) : Boolean { return getC(a) == getC(b) } -// CHECK_NEW_COUNT: function=testNonInline2 count=0 -fun testNonInline2(a: Char, b: Char) : Boolean { +// CHECK_NEW_COUNT: function=testNonInlineEquals2 count=0 +fun testNonInlineEquals2(a: Char, b: Char) : Boolean { val a1 = getC(a) val b1 = getC(b) return a1 == b1 } -// CHECK_NEW_COUNT: function=testInline1 count=0 -fun testInline1(a: Char, b: Char) : Boolean { +// CHECK_NEW_COUNT: function=testInlineEquals1 count=0 +fun testInlineEquals1(a: Char, b: Char) : Boolean { return getCInline(a) == getCInline(b) } -// CHECK_NEW_COUNT: function=testInline2 count=0 -fun testInline2(a: Char, b: Char) : Boolean { +// CHECK_NEW_COUNT: function=testInlineEquals2 count=0 +fun testInlineEquals2(a: Char, b: Char) : Boolean { val a1 = getCInline(a) val b1 = getCInline(b) return a1 == b1 } +// CHECK_NEW_COUNT: function=testStringAppend1 count=0 +fun testStringAppend1(s1: String, b: Char, s2: String) : Boolean { + val s = s1 + b + return s == s2 +} + +// CHECK_NEW_COUNT: function=testStringAppend2 count=0 +fun testStringAppend2(a: Char, s1: String, s2: String) : Boolean { + val s = a + s1 + return s == s2 +} + +// CHECK_NEW_COUNT: function=testStringAppendInline1 count=0 +fun testStringAppendInline1(s1: String, b: Char, s2: String) : Boolean { + val s = s1 + getCInline(b) + return s == s2 +} + +// CHECK_NEW_COUNT: function=testStringAppendInline2 count=0 +fun testStringAppendInline2(a: Char, s1: String, s2: String) : Boolean { + val s = getCInline(a) + s1 + return s == s2 +} + +// CHECK_NEW_COUNT: function=testStringBuild count=0 +fun testStringBuild(s1: String, b: Char, s2: String) : Boolean { + val s = "$s1 $b " + return s == s2 +} + +// CHECK_NEW_COUNT: function=testStringBuildInline count=0 +fun testStringBuildInline(s1: String, b: Char, s2: String) : Boolean { + val s = "$s1 ${getCInline(b)} " + return s == s2 +} + fun box(): String { - if (!testNonInline1('a', 'a')) { - return "Fail testNonInline1" + if (!testNonInlineEquals1('a', 'a')) { + return "Fail testNonInlineEquals1" } - if (!testNonInline2('b', 'b')) { - return "Fail testNonInline2" + if (!testNonInlineEquals2('b', 'b')) { + return "Fail testNonInlineEquals2" } - if (!testInline1('c', 'c')) { - return "Fail testInline1" + if (!testInlineEquals1('c', 'c')) { + return "Fail testInlineEquals1" } - if (!testInline2('d', 'd')) { - return "Fail testInline2" + if (!testInlineEquals2('d', 'd')) { + return "Fail testInlineEquals2" + } + if (!testStringAppend1("hello", 'x', "hellox")) { + return "Fail testStringAppend1" + } + if (!testStringAppend2('x', "hello", "xhello")) { + return "Fail testStringAppend2" + } + if (!testStringAppendInline1("hello", 'x', "hellox")) { + return "Fail testStringAppendInline1" + } + if (!testStringAppendInline2('x', "hello", "xhello")) { + return "Fail testStringAppendInline2" + } + if (!testStringBuild("hello", 'x', "hello x ")) { + return "Fail testStringBuild" + } + if (!testStringBuildInline("hello", 'x', "hello x ")) { + return "Fail testStringBuildInline" } return "OK" }