Support large strings in indy-with-constants concatenation
#KT-47917 Fixed
This commit is contained in:
@@ -121,25 +121,16 @@ class StringConcatGenerator(val mode: JvmStringConcat, val mv: InstructionAdapte
|
||||
false
|
||||
)
|
||||
|
||||
val templateBuilder = StringBuilder()
|
||||
items.forEach {
|
||||
when (it.itemType) {
|
||||
ItemType.PARAMETER ->
|
||||
templateBuilder.append("\u0001")
|
||||
ItemType.CONSTANT ->
|
||||
templateBuilder.append("\u0002")
|
||||
ItemType.INLINED_CONSTANT ->
|
||||
templateBuilder.append(it.value)
|
||||
}
|
||||
}
|
||||
val itemForGeneration = fitRestrictions(items)
|
||||
val templateBuilder = buildRecipe(itemForGeneration)
|
||||
|
||||
val specialSymbolsInTemplate = items.filter { it.itemType == ItemType.CONSTANT }.map { it.value }
|
||||
val specialSymbolsInTemplate = itemForGeneration.filter { it.itemType == ItemType.CONSTANT }.map { it.value }
|
||||
|
||||
mv.invokedynamic(
|
||||
"makeConcatWithConstants",
|
||||
Type.getMethodDescriptor(
|
||||
JAVA_STRING_TYPE,
|
||||
*items.filter { it.itemType == ItemType.PARAMETER }.map { it.type }.toTypedArray()
|
||||
*itemForGeneration.filter { it.itemType == ItemType.PARAMETER }.map { it.type }.toTypedArray()
|
||||
),
|
||||
bootstrap,
|
||||
arrayOf(templateBuilder.toString()) + specialSymbolsInTemplate
|
||||
@@ -172,6 +163,51 @@ class StringConcatGenerator(val mode: JvmStringConcat, val mv: InstructionAdapte
|
||||
items.add(Item.parameter(JAVA_STRING_TYPE))
|
||||
paramSlots = JAVA_STRING_TYPE.size
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun buildRecipe(itemForGeneration: ArrayList<Item>): StringBuilder {
|
||||
val templateBuilder = StringBuilder()
|
||||
itemForGeneration.forEach {
|
||||
when (it.itemType) {
|
||||
ItemType.PARAMETER ->
|
||||
templateBuilder.append("\u0001")
|
||||
ItemType.CONSTANT ->
|
||||
templateBuilder.append("\u0002")
|
||||
ItemType.INLINED_CONSTANT ->
|
||||
templateBuilder.append(it.value)
|
||||
}
|
||||
}
|
||||
return templateBuilder
|
||||
}
|
||||
|
||||
private fun fitRestrictions(items: List<Item>): ArrayList<Item> {
|
||||
val result = arrayListOf<Item>()
|
||||
items.forEach { item ->
|
||||
when (item.itemType) {
|
||||
//split INLINED_CONSTANT becomes split CONSTANT one
|
||||
ItemType.CONSTANT, ItemType.INLINED_CONSTANT -> splitStringConstant(item.value).forEach { part ->
|
||||
result.add(
|
||||
Item(
|
||||
item.type,
|
||||
ItemType.CONSTANT,
|
||||
part
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> result.add(item)
|
||||
}
|
||||
}
|
||||
|
||||
var recipe = buildrecipe(result)
|
||||
while (recipe.length >= MAX_CONST_STRING_PART_LIMIT) {
|
||||
val item = items.filter { it.itemType == ItemType.INLINED_CONSTANT }.maxByOrNull { it.value.length } ?: break
|
||||
//move longest INLINED_CONSTANT to CONSTANT
|
||||
item.itemType = ItemType.CONSTANT
|
||||
recipe = buildrecipe(result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -678,3 +678,26 @@ internal fun LabelNode.linkWithLabel(): LabelNode {
|
||||
}
|
||||
|
||||
fun linkedLabel(): Label = LabelNode().linkWithLabel().label
|
||||
|
||||
// Strings in constant pool contain at most 2^16-1 = 65535 bytes.
|
||||
// Non-BMP characters in UTF8 require at most 4 bytes.
|
||||
const val MAX_CONST_STRING_PART_LIMIT: Int = 65535 / 4
|
||||
|
||||
fun splitStringConstant(value: String): List<String> {
|
||||
val length = value.length
|
||||
|
||||
return if (length <= MAX_CONST_STRING_PART_LIMIT) {
|
||||
listOf(value)
|
||||
} else {
|
||||
// Split strings into parts, each of which satisfies JVM class file constant pool constraints.
|
||||
// Note that even if we split surrogate pairs between parts, they will be joined on concatenation.
|
||||
var i = 0
|
||||
val result = arrayListOf<String>()
|
||||
while (i < length) {
|
||||
val j = minOf(i + MAX_CONST_STRING_PART_LIMIT, length)
|
||||
result.add(value.substring(i, j))
|
||||
i += MAX_CONST_STRING_PART_LIMIT
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -41192,6 +41192,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/strings/kt42457_old.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt47917.kt")
|
||||
public void testKt47917() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/kt47917.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt5389_stringBuilderGet.kt")
|
||||
public void testKt5389_stringBuilderGet() throws Exception {
|
||||
@@ -41281,6 +41287,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
public void testStringPlusOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("surrogatePair.kt")
|
||||
public void testSurrogatePair() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/surrogatePair.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+5
-11
@@ -869,12 +869,9 @@ class ExpressionCodegen(
|
||||
private fun generateStringConstant(value: String) {
|
||||
val length = value.length
|
||||
|
||||
// Strings in constant pool contain at most 2^16-1 = 65535 bytes.
|
||||
// Non-BMP characters in UTF8 require at most 4 bytes.
|
||||
val maxPartLength = 65535 / 4
|
||||
|
||||
if (length <= maxPartLength) {
|
||||
mv.aconst(value)
|
||||
val splitted = splitStringConstant(value)
|
||||
if (splitted.size == 1) {
|
||||
mv.aconst(splitted.first())
|
||||
} else {
|
||||
// Split strings into parts, each of which satisfies JVM class file constant pool constraints.
|
||||
// Note that even if we split surrogate pairs between parts, they will be joined on concatenation.
|
||||
@@ -882,12 +879,9 @@ class ExpressionCodegen(
|
||||
mv.dup()
|
||||
mv.iconst(length)
|
||||
mv.invokespecial("java/lang/StringBuilder", "<init>", "(I)V", false)
|
||||
var i = 0
|
||||
while (i < length) {
|
||||
val j = minOf(i + maxPartLength, length)
|
||||
mv.aconst(value.substring(i, j))
|
||||
for (part in splitted) {
|
||||
mv.aconst(part)
|
||||
mv.invokevirtual("java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false)
|
||||
i += maxPartLength
|
||||
}
|
||||
mv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
// Will be executed on JDK 9, 11, 17
|
||||
fun test(s: String): String {
|
||||
return "\ud83c" + s + "\udf09";
|
||||
}
|
||||
|
||||
fun box() : String {
|
||||
return if (test("") == "\ud83c\udf09") "OK" else "fail: ${test("")}"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+32
File diff suppressed because one or more lines are too long
+12
@@ -41048,6 +41048,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/strings/kt42457_old.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt47917.kt")
|
||||
public void testKt47917() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/kt47917.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt5389_stringBuilderGet.kt")
|
||||
public void testKt5389_stringBuilderGet() throws Exception {
|
||||
@@ -41137,6 +41143,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
public void testStringPlusOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("surrogatePair.kt")
|
||||
public void testSurrogatePair() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/surrogatePair.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+6
@@ -114,6 +114,12 @@ public class BlackBoxModernJdkCodegenTestGenerated extends AbstractBlackBoxCodeg
|
||||
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava11/kt36984.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt47917_oldBackend.kt")
|
||||
public void testKt47917_oldBackend() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava11/kt47917_oldBackend.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("varHandle.kt")
|
||||
public void testVarHandle() throws Exception {
|
||||
|
||||
+12
@@ -41192,6 +41192,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/strings/kt42457_old.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt47917.kt")
|
||||
public void testKt47917() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/kt47917.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt5389_stringBuilderGet.kt")
|
||||
public void testKt5389_stringBuilderGet() throws Exception {
|
||||
@@ -41281,6 +41287,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
public void testStringPlusOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("surrogatePair.kt")
|
||||
public void testSurrogatePair() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/surrogatePair.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+24
@@ -114,6 +114,30 @@ public class IrBlackBoxModernJdkCodegenTestGenerated extends AbstractIrBlackBoxC
|
||||
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava11/kt36984.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt47917.kt")
|
||||
public void testKt47917() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava11/kt47917.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt47917_oldBackend.kt")
|
||||
public void testKt47917_oldBackend() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava11/kt47917_oldBackend.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt47917_russian.kt")
|
||||
public void testKt47917_russian() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava11/kt47917_russian.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt47917_surrogatePairs.kt")
|
||||
public void testKt47917_surrogatePairs() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxModernJdk/testsWithJava11/kt47917_surrogatePairs.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("varHandle.kt")
|
||||
public void testVarHandle() throws Exception {
|
||||
|
||||
+10
@@ -32910,6 +32910,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/strings/kt13213a.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt47917.kt")
|
||||
public void ignoreKt47917() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/kt47917.kt");
|
||||
}
|
||||
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||
}
|
||||
@@ -33047,6 +33052,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
public void testStringPlusOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("surrogatePair.kt")
|
||||
public void testSurrogatePair() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/surrogatePair.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/super")
|
||||
|
||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java
Generated
+10
@@ -27513,6 +27513,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
|
||||
runTest("compiler/testData/codegen/box/strings/kt3652.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt47917.kt")
|
||||
public void testKt47917() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/kt47917.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt5389_stringBuilderGet.kt")
|
||||
public void testKt5389_stringBuilderGet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/kt5389_stringBuilderGet.kt");
|
||||
@@ -27582,6 +27587,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
|
||||
public void testStringPlusOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("surrogatePair.kt")
|
||||
public void testSurrogatePair() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/surrogatePair.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/super")
|
||||
|
||||
Generated
+10
@@ -26919,6 +26919,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/strings/kt3652.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt47917.kt")
|
||||
public void testKt47917() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/kt47917.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt5389_stringBuilderGet.kt")
|
||||
public void testKt5389_stringBuilderGet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/kt5389_stringBuilderGet.kt");
|
||||
@@ -26988,6 +26993,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
public void testStringPlusOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("surrogatePair.kt")
|
||||
public void testSurrogatePair() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/surrogatePair.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/super")
|
||||
|
||||
Generated
+10
@@ -26859,6 +26859,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/strings/kt3652.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt47917.kt")
|
||||
public void testKt47917() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/kt47917.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt5389_stringBuilderGet.kt")
|
||||
public void testKt5389_stringBuilderGet() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/kt5389_stringBuilderGet.kt");
|
||||
@@ -26928,6 +26933,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
public void testStringPlusOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("surrogatePair.kt")
|
||||
public void testSurrogatePair() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/surrogatePair.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/super")
|
||||
|
||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java
Generated
+10
@@ -14949,6 +14949,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
||||
runTest("compiler/testData/codegen/box/strings/kt3652.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt47917.kt")
|
||||
public void testKt47917() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/kt47917.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt5956.kt")
|
||||
public void testKt5956() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/kt5956.kt");
|
||||
@@ -14998,6 +15003,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
||||
public void testStringPlusOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/stringPlusOverride.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("surrogatePair.kt")
|
||||
public void testSurrogatePair() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/strings/surrogatePair.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/super")
|
||||
|
||||
Reference in New Issue
Block a user