Support large strings in indy-with-constants concatenation

#KT-47917 Fixed
This commit is contained in:
Mikhael Bogdanov
2021-09-01 12:57:15 +02:00
committed by Space
parent d2d3708827
commit 19474ee30f
19 changed files with 314 additions and 24 deletions
@@ -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
}
}
@@ -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
@@ -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
File diff suppressed because one or more lines are too long
@@ -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
@@ -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 {
@@ -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
@@ -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 {
@@ -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")
@@ -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")
@@ -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")
@@ -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")
@@ -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")