diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 17bf4f83a29..bb6e530051a 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -12046,6 +12046,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/dataClasses/changingVarParam.kt"); } + @Test + @TestMetadata("dataClassWithManyFields.kt") + public void testDataClassWithManyFields() throws Exception { + runTest("compiler/testData/codegen/box/dataClasses/dataClassWithManyFields.kt"); + } + @Test @TestMetadata("doubleParam.kt") public void testDoubleParam() throws Exception { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStringConcatenationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStringConcatenationLowering.kt index 0033b7f068d..0d9a92dbc6a 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStringConcatenationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStringConcatenationLowering.kt @@ -26,7 +26,7 @@ import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid -internal val jvmStringConcatenationLowering = makeIrFilePhase( +internal val jvmStringConcatenationLowering = makeIrFilePhase( { context: JvmBackendContext -> if (!context.state.runtimeStringConcat.isDynamic) JvmStringConcatenationLowering(context) @@ -95,6 +95,8 @@ private fun IrExpression.unwrapImplicitNotNull() = else this +private const val MAX_STRING_CONCAT_DEPTH = 23 + /** * This lowering pass replaces [IrStringConcatenation]s with StringBuilder appends. * @@ -108,7 +110,7 @@ private class JvmStringConcatenationLowering(val context: JvmBackendContext) : F private val stringBuilder = context.ir.symbols.stringBuilder.owner private val constructor = stringBuilder.constructors.single { - it.valueParameters.size == 0 + it.valueParameters.isEmpty() } private val toStringFunction = stringBuilder.toStringFunction @@ -152,22 +154,48 @@ private class JvmStringConcatenationLowering(val context: JvmBackendContext) : F putValueArgument(1, lowerInlineClassArgument(arguments[1]) ?: arguments[1]) } - else -> { - var stringBuilder = irCall(constructor) - for (arg in arguments) { - val argument = normalizeArgument(arg) - val appendFunction = typeToAppendFunction(argument.type) - stringBuilder = irCall(appendFunction).apply { - dispatchReceiver = stringBuilder - // Unwrapping IMPLICIT_NOTNULL is necessary for ALL arguments. There could be a call to `String.plus(Any?)` - // anywhere in the flattened IrStringConcatenation expression, e.g., `"foo" + (Java.platformString() + 123)`. - putValueArgument(0, lowerInlineClassArgument(argument) ?: argument.unwrapImplicitNotNull()) - } - } + arguments.size < MAX_STRING_CONCAT_DEPTH -> { irCall(toStringFunction).apply { - dispatchReceiver = stringBuilder + dispatchReceiver = appendWindow(arguments, irCall(constructor)) } } + + else -> { + // arguments.size >= MAX_STRING_CONCAT_DEPTH. Prevent SOE in ExpressionCodegen. + // Generates: + // { + // val tmp = StringBuilder() + // tmp.append(a0).append(a1) /* ... */ .append(a21).append(a22) + // /* ... repeat for each possibly partial MAX_STRING_CONCAT_DEPTH-element window ... */ + // tmp.toString() + // } + irBlock { + val tmpStringBuilder = irTemporary(irCall(constructor)) + val argsWindowed = + arguments.windowed( + size = MAX_STRING_CONCAT_DEPTH, + step = MAX_STRING_CONCAT_DEPTH, + partialWindows = true + ) + for (argsWindow in argsWindowed) { + +appendWindow(argsWindow, irGet(tmpStringBuilder)) + } + +irCall(toStringFunction).apply { dispatchReceiver = irGet(tmpStringBuilder) } + } + } + } + } + } + + private fun JvmIrBuilder.appendWindow(arguments: List, stringBuilder0: IrExpression): IrExpression { + return arguments.fold(stringBuilder0) { stringBuilder, arg -> + val argument = normalizeArgument(arg) + val appendFunction = typeToAppendFunction(argument.type) + irCall(appendFunction).apply { + dispatchReceiver = stringBuilder + // Unwrapping IMPLICIT_NOTNULL is necessary for ALL arguments. There could be a call to `String.plus(Any?)` + // anywhere in the flattened IrStringConcatenation expression, e.g., `"foo" + (Java.platformString() + 123)`. + putValueArgument(0, lowerInlineClassArgument(argument) ?: argument.unwrapImplicitNotNull()) } } } @@ -177,7 +205,8 @@ private class JvmStringConcatenationLowering(val context: JvmBackendContext) : F * This lowering pass lowers inline classes arguments of [IrStringConcatenation]. * Transformed [IrStringConcatenation] would be used as is in [ExpressionCodegen] for makeConcat/makeConcatWithConstants bytecode generation */ -private class JvmDynamicStringConcatenationLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() { +private class JvmDynamicStringConcatenationLowering(val context: JvmBackendContext) : FileLoweringPass, + IrElementTransformerVoidWithContext() { override fun lower(irFile: IrFile) = irFile.transformChildrenVoid() override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression { diff --git a/compiler/testData/codegen/box/dataClasses/dataClassWithManyFields.kt b/compiler/testData/codegen/box/dataClasses/dataClassWithManyFields.kt new file mode 100644 index 00000000000..913037437b7 --- /dev/null +++ b/compiler/testData/codegen/box/dataClasses/dataClassWithManyFields.kt @@ -0,0 +1,65 @@ +// IGNORE_BACKEND: WASM + +data class DC( + val d0: String = "", val d1: String = "", val d2: String = "", val d3: String = "", val d4: String = "", val d5: String = "", + val d6: String = "", val d7: String = "", val d8: String = "", val d9: String = "", val d10: String = "", val d11: String = "", + val d12: String = "", val d13: String = "", val d14: String = "", val d15: String = "", val d16: String = "", val d17: String = "", + val d18: String = "", val d19: String = "", val d20: String = "", val d21: String = "", val d22: String = "", val d23: String = "", + val d24: String = "", val d25: String = "", val d26: String = "", val d27: String = "", val d28: String = "", val d29: String = "", + val d30: String = "", val d31: String = "", val d32: String = "", val d33: String = "", val d34: String = "", val d35: String = "", + val d36: String = "", val d37: String = "", val d38: String = "", val d39: String = "", val d40: String = "", val d41: String = "", + val d42: String = "", val d43: String = "", val d44: String = "", val d45: String = "", val d46: String = "", val d47: String = "", + val d48: String = "", val d49: String = "", val d50: String = "", val d51: String = "", val d52: String = "", val d53: String = "", + val d54: String = "", val d55: String = "", val d56: String = "", val d57: String = "", val d58: String = "", val d59: String = "", + val d60: String = "", val d61: String = "", val d62: String = "", val d63: String = "", val d64: String = "", val d65: String = "", + val d66: String = "", val d67: String = "", val d68: String = "", val d69: String = "", val d70: String = "", val d71: String = "", + val d72: String = "", val d73: String = "", val d74: String = "", val d75: String = "", val d76: String = "", val d77: String = "", + val d78: String = "", val d79: String = "", val d80: String = "", val d81: String = "", val d82: String = "", val d83: String = "", + val d84: String = "", val d85: String = "", val d86: String = "", val d87: String = "", val d88: String = "", val d89: String = "", + val d90: String = "", val d91: String = "", val d92: String = "", val d93: String = "", val d94: String = "", val d95: String = "", + val d96: String = "", val d97: String = "", val d98: String = "", val d99: String = "", val d100: String = "", val d101: String = "", + val d102: String = "", val d103: String = "", val d104: String = "", val d105: String = "", val d106: String = "", val d107: String = "", + val d108: String = "", val d109: String = "", val d110: String = "", val d111: String = "", val d112: String = "", val d113: String = "", + val d114: String = "", val d115: String = "", val d116: String = "", val d117: String = "", val d118: String = "", val d119: String = "", + val d120: String = "", val d121: String = "", val d122: String = "", val d123: String = "", val d124: String = "", val d125: String = "", + val d126: String = "", val d127: String = "", val d128: String = "", val d129: String = "", val d130: String = "", val d131: String = "", + val d132: String = "", val d133: String = "", val d134: String = "", val d135: String = "", val d136: String = "", val d137: String = "", + val d138: String = "", val d139: String = "", val d140: String = "", val d141: String = "", val d142: String = "", val d143: String = "", + val d144: String = "", val d145: String = "", val d146: String = "", val d147: String = "", val d148: String = "", val d149: String = "", + val d150: String = "", val d151: String = "", val d152: String = "", val d153: String = "", val d154: String = "", val d155: String = "", + val d156: String = "", val d157: String = "", val d158: String = "", val d159: String = "", val d160: String = "", val d161: String = "", + val d162: String = "", val d163: String = "", val d164: String = "", val d165: String = "", val d166: String = "", val d167: String = "", + val d168: String = "", val d169: String = "", val d170: String = "", val d171: String = "", val d172: String = "", val d173: String = "", + val d174: String = "", val d175: String = "", val d176: String = "", val d177: String = "", val d178: String = "", val d179: String = "", + val d180: String = "", val d181: String = "", val d182: String = "", val d183: String = "", val d184: String = "", val d185: String = "", + val d186: String = "", val d187: String = "", val d188: String = "", val d189: String = "", val d190: String = "", val d191: String = "", + val d192: String = "", val d193: String = "", val d194: String = "", val d195: String = "", val d196: String = "", val d197: String = "", + val d198: String = "", val d199: String = "", val d200: String = "", val d201: String = "", val d202: String = "", val d203: String = "", + val d204: String = "", val d205: String = "", val d206: String = "", val d207: String = "", val d208: String = "", val d209: String = "", + val d210: String = "", val d211: String = "", val d212: String = "", val d213: String = "", val d214: String = "", val d215: String = "", + val d216: String = "", val d217: String = "", val d218: String = "", val d219: String = "", val d220: String = "", val d221: String = "", + val d222: String = "", val d223: String = "", val d224: String = "", val d225: String = "", val d226: String = "", val d227: String = "", + val d228: String = "", val d229: String = "", val d230: String = "", val d231: String = "", val d232: String = "", val d233: String = "", + val d234: String = "", val d235: String = "", val d236: String = "", val d237: String = "", val d238: String = "", val d239: String = "", + val d240: String = "" +) + +val golden = "DC(d0=OK, d1=, d2=, d3=, d4=, d5=, d6=, d7=, d8=, d9=, d10=, d11=, d12=, d13=, d14=, d15=, d16=, d17=, d18=, d19=, d20=, " + + "d21=, d22=, d23=, d24=, d25=, d26=, d27=, d28=, d29=, d30=, d31=, d32=, d33=, d34=, d35=, d36=, d37=, d38=, d39=, d40=, d41=, " + + "d42=, d43=, d44=, d45=, d46=, d47=, d48=, d49=, d50=, d51=, d52=, d53=, d54=, d55=, d56=, d57=, d58=, d59=, d60=, d61=, d62=, " + + "d63=, d64=, d65=, d66=, d67=, d68=, d69=, d70=, d71=, d72=, d73=, d74=, d75=, d76=, d77=, d78=, d79=, d80=, d81=, d82=, d83=, " + + "d84=, d85=, d86=, d87=, d88=, d89=, d90=, d91=, d92=, d93=, d94=, d95=, d96=, d97=, d98=, d99=, d100=, d101=, d102=, d103=, " + + "d104=, d105=, d106=, d107=, d108=, d109=, d110=, d111=, d112=, d113=, d114=, d115=, d116=, d117=, d118=, d119=, d120=, d121=, " + + "d122=, d123=, d124=, d125=, d126=, d127=, d128=, d129=, d130=, d131=, d132=, d133=, d134=, d135=, d136=, d137=, d138=, d139=, " + + "d140=, d141=, d142=, d143=, d144=, d145=, d146=, d147=, d148=, d149=, d150=, d151=, d152=, d153=, d154=, d155=, d156=, d157=, " + + "d158=, d159=, d160=, d161=, d162=, d163=, d164=, d165=, d166=, d167=, d168=, d169=, d170=, d171=, d172=, d173=, d174=, d175=, " + + "d176=, d177=, d178=, d179=, d180=, d181=, d182=, d183=, d184=, d185=, d186=, d187=, d188=, d189=, d190=, d191=, d192=, d193=, " + + "d194=, d195=, d196=, d197=, d198=, d199=, d200=, d201=, d202=, d203=, d204=, d205=, d206=, d207=, d208=, d209=, d210=, d211=, " + + "d212=, d213=, d214=, d215=, d216=, d217=, d218=, d219=, d220=, d221=, d222=, d223=, d224=, d225=, d226=, d227=, d228=, d229=, " + + "d230=, d231=, d232=, d233=, d234=, d235=, d236=, d237=, d238=, d239=, d240=)" + +fun box(): String { + val dc = DC("OK") + if (dc.toString() != golden) return "Failed: $dc" + return dc.d0 +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index cebf1b2889d..d1788fd4488 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -12046,6 +12046,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/dataClasses/changingVarParam.kt"); } + @Test + @TestMetadata("dataClassWithManyFields.kt") + public void testDataClassWithManyFields() throws Exception { + runTest("compiler/testData/codegen/box/dataClasses/dataClassWithManyFields.kt"); + } + @Test @TestMetadata("doubleParam.kt") public void testDoubleParam() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 7465e9b43f8..d9759abce7d 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -12046,6 +12046,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/dataClasses/changingVarParam.kt"); } + @Test + @TestMetadata("dataClassWithManyFields.kt") + public void testDataClassWithManyFields() throws Exception { + runTest("compiler/testData/codegen/box/dataClasses/dataClassWithManyFields.kt"); + } + @Test @TestMetadata("doubleParam.kt") public void testDoubleParam() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 4c1eaac601a..73bca3d67b6 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -9701,6 +9701,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/dataClasses/changingVarParam.kt"); } + @TestMetadata("dataClassWithManyFields.kt") + public void testDataClassWithManyFields() throws Exception { + runTest("compiler/testData/codegen/box/dataClasses/dataClassWithManyFields.kt"); + } + @TestMetadata("doubleParam.kt") public void testDoubleParam() throws Exception { runTest("compiler/testData/codegen/box/dataClasses/doubleParam.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java index 806f5cdb20f..042b6788701 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java @@ -8625,6 +8625,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes runTest("compiler/testData/codegen/box/dataClasses/changingVarParam.kt"); } + @TestMetadata("dataClassWithManyFields.kt") + public void testDataClassWithManyFields() throws Exception { + runTest("compiler/testData/codegen/box/dataClasses/dataClassWithManyFields.kt"); + } + @TestMetadata("doubleParam.kt") public void testDoubleParam() throws Exception { runTest("compiler/testData/codegen/box/dataClasses/doubleParam.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 3fc4bdc9d89..5e410811623 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -8031,6 +8031,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/dataClasses/changingVarParam.kt"); } + @TestMetadata("dataClassWithManyFields.kt") + public void testDataClassWithManyFields() throws Exception { + runTest("compiler/testData/codegen/box/dataClasses/dataClassWithManyFields.kt"); + } + @TestMetadata("doubleParam.kt") public void testDoubleParam() throws Exception { runTest("compiler/testData/codegen/box/dataClasses/doubleParam.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index ca9a1ec9050..a3f7741f532 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -8031,6 +8031,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/dataClasses/changingVarParam.kt"); } + @TestMetadata("dataClassWithManyFields.kt") + public void testDataClassWithManyFields() throws Exception { + runTest("compiler/testData/codegen/box/dataClasses/dataClassWithManyFields.kt"); + } + @TestMetadata("doubleParam.kt") public void testDoubleParam() throws Exception { runTest("compiler/testData/codegen/box/dataClasses/doubleParam.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index 4fbe7046032..072e43ee881 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -3644,6 +3644,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest runTest("compiler/testData/codegen/box/dataClasses/changingVarParam.kt"); } + @TestMetadata("dataClassWithManyFields.kt") + public void testDataClassWithManyFields() throws Exception { + runTest("compiler/testData/codegen/box/dataClasses/dataClassWithManyFields.kt"); + } + @TestMetadata("genericParam.kt") public void testGenericParam() throws Exception { runTest("compiler/testData/codegen/box/dataClasses/genericParam.kt");