diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StringConcatGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/StringConcatGenerator.kt index 6bfa5815c1f..335774d1b7c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StringConcatGenerator.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StringConcatGenerator.kt @@ -23,6 +23,7 @@ class StringConcatGenerator(val mode: JvmRuntimeStringConcat, val mv: Instructio private val template = StringBuilder("") private val paramTypes = arrayListOf() + private var justFlushed = false @JvmOverloads fun genStringBuilderConstructorIfNeded(swap: Boolean = false) { @@ -37,6 +38,7 @@ class StringConcatGenerator(val mode: JvmRuntimeStringConcat, val mv: Instructio @JvmOverloads fun putValueOrProcessConstant(stackValue: StackValue, type: Type = stackValue.type, kotlinType: KotlinType? = stackValue.kotlinType) { + justFlushed = false if (mode == JvmRuntimeStringConcat.ENABLE) { when (stackValue) { is StackValue.Constant -> { @@ -70,6 +72,7 @@ class StringConcatGenerator(val mode: JvmRuntimeStringConcat, val mv: Instructio false ) } else { + justFlushed = false paramTypes.add(type) template.append("\u0001") if (paramTypes.size == 200) { @@ -77,6 +80,7 @@ class StringConcatGenerator(val mode: JvmRuntimeStringConcat, val mv: Instructio // because of `StringConcatFactory` limitation add use it as new argument for further processing: // "The number of parameter slots in {@code concatType} is less than or equal to 200" genToString() + justFlushed = true } } } @@ -86,21 +90,38 @@ class StringConcatGenerator(val mode: JvmRuntimeStringConcat, val mv: Instructio mv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false) } else { //if state was flushed in `invokeAppend` do nothing - if (template.isEmpty() && paramTypes.size == 1 && paramTypes[0] == JAVA_STRING_TYPE) return - val bootstrap = Handle( - Opcodes.H_INVOKESTATIC, - "java/lang/invoke/StringConcatFactory", - "makeConcatWithConstants", - "(Ljava/lang/invoke/MethodHandles\$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;", - false - ) + if (justFlushed) return + if (mode == JvmRuntimeStringConcat.ENABLE) { + val bootstrap = Handle( + Opcodes.H_INVOKESTATIC, + "java/lang/invoke/StringConcatFactory", + "makeConcatWithConstants", + "(Ljava/lang/invoke/MethodHandles\$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;", + false + ) - mv.invokedynamic( - "makeConcatWithConstants", - Type.getMethodDescriptor(JAVA_STRING_TYPE, *paramTypes.toTypedArray()), - bootstrap, - arrayOf(template.toString()) - ) + mv.invokedynamic( + "makeConcatWithConstants", + Type.getMethodDescriptor(JAVA_STRING_TYPE, *paramTypes.toTypedArray()), + bootstrap, + arrayOf(template.toString()) + ) + } else { + val bootstrap = Handle( + Opcodes.H_INVOKESTATIC, + "java/lang/invoke/StringConcatFactory", + "makeConcat", + "(Ljava/lang/invoke/MethodHandles\$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", + false + ) + + mv.invokedynamic( + "makeConcat", + Type.getMethodDescriptor(JAVA_STRING_TYPE, *paramTypes.toTypedArray()), + bootstrap, + arrayOf() + ) + } template.clear() paramTypes.clear() paramTypes.add(JAVA_STRING_TYPE) diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBytecodeTextTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBytecodeTextTestGenerated.java index 48dbf02fed2..ca755f534a0 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBytecodeTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBytecodeTextTestGenerated.java @@ -4486,6 +4486,41 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/stringOperations/concat.kt"); } + @TestMetadata("concatDynamic.kt") + public void testConcatDynamic() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic.kt"); + } + + @TestMetadata("concatDynamic200.kt") + public void testConcatDynamic200() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic200.kt"); + } + + @TestMetadata("concatDynamic201.kt") + public void testConcatDynamic201() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic201.kt"); + } + + @TestMetadata("concatDynamicConstants.kt") + public void testConcatDynamicConstants() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicConstants.kt"); + } + + @TestMetadata("concatDynamicIndy.kt") + public void testConcatDynamicIndy() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicIndy.kt"); + } + + @TestMetadata("concatDynamicIndy201.kt") + public void testConcatDynamicIndy201() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicIndy201.kt"); + } + + @TestMetadata("concatNotDynamic.kt") + public void testConcatNotDynamic() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatNotDynamic.kt"); + } + @TestMetadata("constConcat.kt") public void testConstConcat() throws Exception { runTest("compiler/testData/codegen/bytecodeText/stringOperations/constConcat.kt"); diff --git a/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic200.kt b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic200.kt index e967c489bcf..24b8e54a992 100644 --- a/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic200.kt +++ b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic200.kt @@ -8,6 +8,7 @@ fun box() { z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + "some constant" + "some constant" + "some constant" + "some constant" + // constant in this mode are inlined in recipe z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + diff --git a/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicIndy.kt b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicIndy.kt new file mode 100644 index 00000000000..b112205dff2 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicIndy.kt @@ -0,0 +1,23 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=indy +// JVM_TARGET: 9 +class A + +inline fun test(s: (String) -> Unit) { + s("456") +} + +fun box(a: String, b: String?) { + val s = a + "1" + "2" + 3 + 4L + b + 5.0 + 6F + '7' + A() + true + false + 1u + + a.plus(b) + b?.plus(a) + val ref1 = a::plus + val ref2 = b::plus + + test("123"::plus) +} + +// 0 INVOKEDYNAMIC makeConcatWithConstants +// 6 INVOKEDYNAMIC makeConcat +// 0 append +// 0 stringPlus \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicIndy201.kt b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicIndy201.kt new file mode 100644 index 00000000000..45b9023422e --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicIndy201.kt @@ -0,0 +1,20 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=indy +// JVM_TARGET: 9 +fun box() { + val z = "0" + val result = z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + "some constant" + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z //200 + 1 constant + +} +// 2 INVOKEDYNAMIC makeConcat +// 0 append +// 0 stringPlus \ No newline at end of file diff --git a/compiler/testData/codegen/java9/box/concatDynamic.kt b/compiler/testData/codegen/java9/box/concatDynamic.kt new file mode 100644 index 00000000000..fd564f233f3 --- /dev/null +++ b/compiler/testData/codegen/java9/box/concatDynamic.kt @@ -0,0 +1,14 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable +// JVM_TARGET: 9 +fun box(): String { + val p = 3147483648u + val a = "_" + val b = "_" + val s = a + "1" + "2" + 3 + 4L + b + 5.0 + 6F + '7' + true + false + 3147483647u + p + + return if (s != "_1234_5.06.07truefalse31474836473147483648") "fail $s" else "OK" +} + +fun main() { + box().let { if (it != "OK") throw AssertionError(it) } +} diff --git a/compiler/testData/codegen/java9/box/concatDynamicIndy200.kt b/compiler/testData/codegen/java9/box/concatDynamicIndy200.kt new file mode 100644 index 00000000000..232e28bb268 --- /dev/null +++ b/compiler/testData/codegen/java9/box/concatDynamicIndy200.kt @@ -0,0 +1,22 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=indy +// JVM_TARGET: 9 +fun box(): String { + val z = "0" + val result = z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z //200 + + return if (result.length != 200) + "fail: ${result.length}" else "OK" +} + +fun main() { + box().let { if (it != "OK") throw AssertionError(it) } +} diff --git a/compiler/testData/codegen/java9/box/concatDynamicIndy201.kt b/compiler/testData/codegen/java9/box/concatDynamicIndy201.kt new file mode 100644 index 00000000000..66dce61bb0d --- /dev/null +++ b/compiler/testData/codegen/java9/box/concatDynamicIndy201.kt @@ -0,0 +1,23 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=indy +// JVM_TARGET: 9 +fun box(): String { + val z = "0" + val result = z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + //200 + z //201 + + return if (result.length != 201) + "fail: ${result.length}" else "OK" +} + +fun main() { + box().let { if (it != "OK") throw AssertionError(it) } +} diff --git a/compiler/testData/codegen/java9/box/concatDynamicInlineClasses.kt b/compiler/testData/codegen/java9/box/concatDynamicInlineClasses.kt new file mode 100644 index 00000000000..59c389d307e --- /dev/null +++ b/compiler/testData/codegen/java9/box/concatDynamicInlineClasses.kt @@ -0,0 +1,31 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable +// JVM_TARGET: 9 +inline class Str(val s: String) +inline class NStr(val s: String?) + +fun testStr(s: Str?) = "1$s$s" +fun testNStr(ns: NStr?) = "2$ns$ns" + +fun box(): String { + + val test1 = testStr(Str("0")) + if (test1 != "1Str(s=0)Str(s=0)") return "fail 1: $test1" + + val test2 = testStr(null) + if (test2 != "1nullnull") return "fail 2: $test2" + + val test3 = testNStr(NStr(null)) + if (test3 != "2NStr(s=null)NStr(s=null)") return "fail 3: $test3" + + val test4 = testNStr(NStr("0")) + if (test4 != "2NStr(s=0)NStr(s=0)") return "fail 4: $test4" + + val test5 = testNStr(null) + if (test5 != "2nullnull") return "fail 5: $test5" + + return "OK" +} + +fun main() { + box().let { if (it != "OK") throw AssertionError(it) } +} \ No newline at end of file diff --git a/compiler/testData/codegen/java9/box/concatDynamicWithInline.kt b/compiler/testData/codegen/java9/box/concatDynamicWithInline.kt new file mode 100644 index 00000000000..6a2df7c3d94 --- /dev/null +++ b/compiler/testData/codegen/java9/box/concatDynamicWithInline.kt @@ -0,0 +1,25 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable +// JVM_TARGET: 9 +inline fun test(crossinline s: (String) -> String): String { + var result = "1" + s("2") + "3" + 4 + { + "5" + s("6") + "7" + }() + + result += object { + fun run() = "8" + s("9") + "10" + }.run() + + return result +} + +fun box(): String { + val result = test { it } + if (result != "12345678910") return "fail 1: $result" + + val result2 = test { it + "_" } + return if (result2 != "12_3456_789_10") "fail 2: $result2" else "OK" +} + +fun main() { + box().let { if (it != "OK") throw AssertionError(it) } +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 8b9a2814c57..d8e508499f3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -4538,6 +4538,16 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicConstants.kt"); } + @TestMetadata("concatDynamicIndy.kt") + public void testConcatDynamicIndy() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicIndy.kt"); + } + + @TestMetadata("concatDynamicIndy201.kt") + public void testConcatDynamicIndy201() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicIndy201.kt"); + } + @TestMetadata("concatNotDynamic.kt") public void testConcatNotDynamic() throws Exception { runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatNotDynamic.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/Java9CodegenTest.kt b/compiler/tests/org/jetbrains/kotlin/codegen/Java9CodegenTest.kt index 70e773fe5de..d4fd2582610 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/Java9CodegenTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/codegen/Java9CodegenTest.kt @@ -52,6 +52,11 @@ class Java9CodegenTest : AbstractBlackBoxCodegenTest() { blackBox(true) } + fun testConcatDynamic() { + loadFile() + blackBox(true) + } + fun testConcatDynamic200() { loadFile() blackBox(true) @@ -61,4 +66,24 @@ class Java9CodegenTest : AbstractBlackBoxCodegenTest() { loadFile() blackBox(true) } + + fun testConcatDynamicIndy200() { + loadFile() + blackBox(true) + } + + fun testConcatDynamicIndy201() { + loadFile() + blackBox(true) + } + + fun testConcatDynamicWithInline() { + loadFile() + blackBox(true) + } + + fun testConcatDynamicInlineClasses() { + loadFile() + blackBox(true) + } } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java index ad01b4a6a42..e1de45453b3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java @@ -4506,6 +4506,16 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicConstants.kt"); } + @TestMetadata("concatDynamicIndy.kt") + public void testConcatDynamicIndy() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicIndy.kt"); + } + + @TestMetadata("concatDynamicIndy201.kt") + public void testConcatDynamicIndy201() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicIndy201.kt"); + } + @TestMetadata("concatNotDynamic.kt") public void testConcatNotDynamic() throws Exception { runTest("compiler/testData/codegen/bytecodeText/stringOperations/concatNotDynamic.kt");