Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compilation fails with ~200 items in strings.xml (MethodTooLargeException) #4194

Closed
asapha opened this issue Jan 28, 2024 · 10 comments · Fixed by #4404
Closed

Compilation fails with ~200 items in strings.xml (MethodTooLargeException) #4194

asapha opened this issue Jan 28, 2024 · 10 comments · Fixed by #4404
Assignees
Labels
bug Something isn't working resources

Comments

@asapha
Copy link

asapha commented Jan 28, 2024

Description

I'm not sure what should be considered as a reasonable amount of strings in a strings.xml file but, when trying to replace moko-resources in an existing project, it failed to compile with this error:

exception: exception: java.lang.RuntimeException: Error generating class file components/resources/demo/shared/generated/resources/Res$string.class (compiled from [compose-multiplatform/components/resources/demo/shared/build/generated/compose/resourceGenerator/kotlin/components/resources/demo/shared/generated/resources/Res.kt]): Method too large: components/resources/demo/shared/generated/resources/Res$string.<clinit> ()V
exception: 	at org.jetbrains.kotlin.codegen.ClassFileFactory$OutputClassFile.asByteArray(ClassFileFactory.java:310)
exception: 	at org.jetbrains.kotlin.cli.common.output.OutputUtilsKt.writeAll(outputUtils.kt:33)
exception: 	at org.jetbrains.kotlin.cli.common.output.OutputUtilsKt.writeAllTo(outputUtils.kt:47)
exception: 	at org.jetbrains.kotlin.cli.common.output.OutputUtilsKt.writeAll(outputUtils.kt:52)
exception: 	at org.jetbrains.kotlin.cli.jvm.compiler.CliCompilerUtilsKt.writeOutput(cliCompilerUtils.kt:134)
exception: 	at org.jetbrains.kotlin.cli.jvm.compiler.CliCompilerUtilsKt$createOutputFilesFlushingCallbackIfPossible$1.invoke(cliCompilerUtils.kt:95)
exception: 	at org.jetbrains.kotlin.cli.jvm.compiler.CliCompilerUtilsKt$createOutputFilesFlushingCallbackIfPossible$1.invoke(cliCompilerUtils.kt:93)
exception: 	at org.jetbrains.kotlin.codegen.state.GenerationStateKt$GenerationStateEventCallback$1.invoke(GenerationState.kt:412)
exception: 	at org.jetbrains.kotlin.codegen.state.GenerationStateKt$GenerationStateEventCallback$1.invoke(GenerationState.kt:411)
exception: 	at org.jetbrains.kotlin.codegen.state.GenerationState.afterIndependentPart(GenerationState.kt:363)
exception: 	at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.invokeCodegen(JvmIrCodegenFactory.kt:366)
exception: 	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.runCodegen(KotlinToJVMBytecodeCompiler.kt:347)
exception: 	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:122)
exception: 	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:43)
exception: 	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:165)
exception: 	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:50)
exception: 	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:104)
exception: 	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:48)
exception: 	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101)
exception: 	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:79)
exception: 	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:43)
exception: 	at org.jetbrains.kotlin.cli.common.CLITool$Companion.doMainNoExit(CLITool.kt:180)
exception: 	at org.jetbrains.kotlin.cli.common.CLITool$Companion.doMainNoExit$default(CLITool.kt:175)
exception: 	at org.jetbrains.kotlin.cli.common.CLITool$Companion.doMain(CLITool.kt:167)
exception: 	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler$Companion.main(K2JVMCompiler.kt:250)
exception: 	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.main(K2JVMCompiler.kt)
exception: caused by: org.jetbrains.org.objectweb.asm.MethodTooLargeException: Method too large: components/resources/demo/shared/generated/resources/Res$string.<clinit> ()V
exception: 	at org.jetbrains.org.objectweb.asm.MethodWriter.computeMethodInfoSize(MethodWriter.java:2087)
exception: 	at org.jetbrains.org.objectweb.asm.ClassWriter.toByteArray(ClassWriter.java:489)
exception: 	at org.jetbrains.kotlin.codegen.ClassBuilderFactories$2.asBytes(ClassBuilderFactories.java:118)
exception: 	at org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory.asBytes(DelegatingClassBuilderFactory.kt:36)
exception: 	at org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory.asBytes(DelegatingClassBuilderFactory.kt:36)
exception: 	at org.jetbrains.kotlin.codegen.ClassFileFactory$ClassBuilderAndSourceFileList.asBytes(ClassFileFactory.java:343)
exception: 	at org.jetbrains.kotlin.codegen.ClassFileFactory$OutputClassFile.asByteArray(ClassFileFactory.java:307)
exception: 	... 25 more
exception: 

Repro

In order to reproduce it, you can add the following code to components/resources/demo/shared/build.gradle.kts

generateStringFiles task
val template = """
    <resources>
        <string name="app_name">Compose Resources App</string>
        <string name="hello">😊 Hello world!</string>
        <string name="multi_line">Lorem ipsum dolor sit amet,
            consectetur adipiscing elit.
            Donec eget turpis ac sem ultricies consequat.</string>
        <string name="str_template">Hello, %1${"$"}s! You have %2${"$"}d new messages.</string>
        <string-array name="str_arr">
            <item>item 1</item>
            <item>item 2</item>
            <item>item 3</item>
        </string-array>
        [ADDITIONAL_STRINGS]
    </resources>    
""".trimIndent()

val generateStringFiles = tasks.register("generateStringFiles") {
    val numberOfLanguages = 15
    val numberOfStrings = 200
    val langs = Locale.getAvailableLocales()
        .map { it.language }
        .filter { it.count() == 2 }
        .sorted()
        .distinct()
        .take(numberOfLanguages)
        .toList()

    val resourcesFolder = project.file("src/commonMain/composeResources")

    doLast {
        // THIS REMOVES THE `values` FOLDER IN `composeResources`
        // THIS REMOVES THE `values` FOLDER IN `composeResources`
        // Necessary when reducing the number of languages.
        resourcesFolder.listFiles()?.filter { it.name.startsWith("values") }?.forEach {
            it.deleteRecursively()
        }

        langs.forEachIndexed { langIndex, lang ->
            val additionalStrings =
                (0 until numberOfStrings).joinToString(System.lineSeparator()) { index ->
                    """
                    <string name="string_${index.toString().padStart(4, '0')}">String $index in lang $lang</string>
                    """.trimIndent()
                }

            val langFile = if (langIndex == 0) {
                File(resourcesFolder, "values/strings.xml")
            } else {
                File(resourcesFolder, "values-$lang/strings.xml")
            }
            langFile.parentFile.mkdirs()
            langFile.writeText(template.replace("[ADDITIONAL_STRINGS]", additionalStrings))
        }
    }
}

tasks.named("generateComposeResClass") {
    dependsOn(generateStringFiles)
}

It'll create multiple values-XXX/strings.xml files, each filled with the requested amount of strings when you run ./gradlew :resources:demo:desktopApp:run.
With the default values (200 strings, 15 languages), the Res.kt file will have 14k lines.

Tested with compose.version=1.6.0-beta01

Workaround

Maintain different string files in multiple modules.

@asapha asapha added enhancement New feature or request submitted labels Jan 28, 2024
@tipapro
Copy link

tipapro commented Jan 29, 2024

The same problem occurs if we use a huge icon pack. This pack is for example.

@terrakok terrakok self-assigned this Jan 29, 2024
@terrakok terrakok added bug Something isn't working and removed enhancement New feature or request submitted labels Jan 29, 2024
@terrakok
Copy link
Member

#4205

@asapha
Copy link
Author

asapha commented Feb 5, 2024

Fixed for me, thanks!

@tipapro I'm closing the issue since it was specific to strings. I think you should open another one if the problem persists for your use case.

@asapha asapha closed this as completed Feb 5, 2024
@asapha asapha changed the title Compilation fails with MethodTooLargeException due to the generated Res class Compilation fails with ~200 items in strings.xml (MethodTooLargeException) Feb 5, 2024
@asapha
Copy link
Author

asapha commented Feb 29, 2024

Hello @terrakok, the error is back on 1.6.0 with the same repro as above.
I'm re-opening this issue, let me know if you'd prefer me to create a new one.

exception: exception: java.lang.RuntimeException: Error generating class file components/resources/demo/shared/generated/resources/String0.class (compiled from [/Users/azef/dev/kotlin/playground/compose-multiplatform/components/resources/demo/shared/build/generated/compose/resourceGenerator/kotlin/components/resources/demo/shared/generated/resources/String0.kt]): Method too large: components/resources/demo/shared/generated/resources/String0.<clinit> ()V
exception: 	at org.jetbrains.kotlin.codegen.ClassFileFactory$OutputClassFile.asByteArray(ClassFileFactory.java:310)
exception: 	at org.jetbrains.kotlin.cli.common.output.OutputUtilsKt.writeAll(outputUtils.kt:33)
exception: 	at org.jetbrains.kotlin.cli.common.output.OutputUtilsKt.writeAllTo(outputUtils.kt:47)
exception: 	at org.jetbrains.kotlin.cli.common.output.OutputUtilsKt.writeAll(outputUtils.kt:52)
exception: 	at org.jetbrains.kotlin.cli.jvm.compiler.CliCompilerUtilsKt.writeOutput(cliCompilerUtils.kt:134)
exception: 	at org.jetbrains.kotlin.cli.jvm.compiler.CliCompilerUtilsKt$createOutputFilesFlushingCallbackIfPossible$1.invoke(cliCompilerUtils.kt:95)
exception: 	at org.jetbrains.kotlin.cli.jvm.compiler.CliCompilerUtilsKt$createOutputFilesFlushingCallbackIfPossible$1.invoke(cliCompilerUtils.kt:93)
exception: 	at org.jetbrains.kotlin.codegen.state.GenerationStateKt$GenerationStateEventCallback$1.invoke(GenerationState.kt:412)
exception: 	at org.jetbrains.kotlin.codegen.state.GenerationStateKt$GenerationStateEventCallback$1.invoke(GenerationState.kt:411)
exception: 	at org.jetbrains.kotlin.codegen.state.GenerationState.afterIndependentPart(GenerationState.kt:363)
exception: 	at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.invokeCodegen(JvmIrCodegenFactory.kt:366)
exception: 	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.runCodegen(KotlinToJVMBytecodeCompiler.kt:347)
exception: 	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:122)
exception: 	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:43)
exception: 	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:165)
exception: 	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:50)
exception: 	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:104)
exception: 	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:48)
exception: 	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101)
exception: 	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:79)
exception: 	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:43)
exception: 	at org.jetbrains.kotlin.cli.common.CLITool$Companion.doMainNoExit(CLITool.kt:180)
exception: 	at org.jetbrains.kotlin.cli.common.CLITool$Companion.doMainNoExit$default(CLITool.kt:175)
exception: 	at org.jetbrains.kotlin.cli.common.CLITool$Companion.doMain(CLITool.kt:167)
exception: 	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler$Companion.main(K2JVMCompiler.kt:250)
exception: 	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.main(K2JVMCompiler.kt)
exception: caused by: org.jetbrains.org.objectweb.asm.MethodTooLargeException: Method too large: components/resources/demo/shared/generated/resources/String0.<clinit> ()V
exception: 	at org.jetbrains.org.objectweb.asm.MethodWriter.computeMethodInfoSize(MethodWriter.java:2087)
exception: 	at org.jetbrains.org.objectweb.asm.ClassWriter.toByteArray(ClassWriter.java:489)
exception: 	at org.jetbrains.kotlin.codegen.ClassBuilderFactories$2.asBytes(ClassBuilderFactories.java:118)
exception: 	at org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory.asBytes(DelegatingClassBuilderFactory.kt:36)
exception: 	at org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory.asBytes(DelegatingClassBuilderFactory.kt:36)
exception: 	at org.jetbrains.kotlin.codegen.ClassFileFactory$ClassBuilderAndSourceFileList.asBytes(ClassFileFactory.java:343)
exception: 	at org.jetbrains.kotlin.codegen.ClassFileFactory$OutputClassFile.asByteArray(ClassFileFactory.java:307)
exception: 	... 25 more
exception: 

@asapha asapha reopened this Feb 29, 2024
@mlilienberg
Copy link

I had a similar issue with RC version of compose 1.6.0. The Res file failed to be generated when adding exactly 2048 strings. with 2047 the file was generated. The 2048 strings could be placed in one localization or the combined total of multiple localisations. After upgrading to final 1.6.0 this issue was gone.

@terrakok
Copy link
Member

terrakok commented Mar 1, 2024

@asapha Hi! It looks strange. there is even a test for the case:

val generateResourceFiles = tasks.register("generateResourceFiles") {

could you provide a small reproducer?

@asapha
Copy link
Author

asapha commented Mar 1, 2024

@terrakok I did a git pull on the compose repo, updated the gradle properties file with 1.6.0, and did the same repro steps as above.

I'll see if the test fails on my end.

@asapha
Copy link
Author

asapha commented Mar 1, 2024

@terrakok We can make the test fail if we add back qualifiers into the mix.

You can replace gradle-plugins/compose/src/test/test-projects/misc/hugeResources/build.gradle.kts with this to repro:

import java.util.Locale

plugins {
    kotlin("multiplatform")
    id("org.jetbrains.compose")
}

group = "app.group"

kotlin {
    jvm("desktop")

    sourceSets {
        commonMain {
            dependencies {
                implementation(compose.runtime)
                implementation(compose.material)
                implementation(compose.components.resources)
            }
        }
    }
}

val generateResourceFiles = tasks.register("generateResourceFiles") {
    val resourcesFolder = project.file("src/commonMain/composeResources")
    val count = 25_000
    val numberOfLanguages = 5

    val langs = Locale.getAvailableLocales()
        .map { it.language }
        .filter { it.count() == 2 }
        .sorted()
        .distinct()
        .take(numberOfLanguages)
        .toList()

    doLast {
        langs.forEachIndexed { langIndex, lang ->
            val txt = buildString {
                appendLine("<resources>")
                repeat(count) {
                    appendLine("    <string name=\"str_${it}\">str_${it}</string>")
                }
                appendLine("</resources>")
            }

            val fileName = if (langIndex == 0) {
                "values/strings.xml"
            } else {
                "values-$lang/strings.xml"
            }

            File(resourcesFolder, fileName).apply {
                parentFile.mkdirs()
                writeText(txt)
            }
        }
    }
    doLast {
        repeat(count) {
            File(resourcesFolder, "drawable/icon_$it.xml").apply {
                parentFile.mkdirs()
                createNewFile() //empty file
            }
        }
    }
}

tasks.named("generateComposeResClass") {
    dependsOn(generateResourceFiles)
}

I tested with val count = 1000. I guess the qualifiers could also be added to the drawable part.

@terrakok
Copy link
Member

terrakok commented Mar 1, 2024

thank you! will fix it today

@okushnikov
Copy link
Collaborator

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

@JetBrains JetBrains locked and limited conversation to collaborators Dec 18, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working resources
Projects
None yet
6 participants