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

XML resource names with special chars not completely quoted/escaped by generateComposeResClass #4548

Closed
toasterofbread opened this issue Mar 26, 2024 · 1 comment · Fixed by #4901
Assignees
Labels
bug Something isn't working resources

Comments

@toasterofbread
Copy link

toasterofbread commented Mar 26, 2024

Describe the bug

I'm in the process of migrating my multiplatform application to use the resources system introduced in 1.6.0. Many of my XML string names include the $ character as a way of indicating that the following word will be replaced at runtime with a different value.

This character is illegal within an unquoted Kotlin name, but this isn't handled correctly in some cases by the resources generator and causes compilation to fail in several places per string.

Example

This string:

<resources>
    <string name="info_using_release_$x">Using release $x</string>
</resources>

Is incorrectly generated into Kotlin as:

public val `info_using_release_$x`: StringResource by 
    lazy { init_info_using_release_$x() } // Not quoted

...

@ExperimentalResourceApi
internal val Res.string.`info_using_release_$x`: StringResource
  get() = String0.info_using_release_$x // Not quoted

@ExperimentalResourceApi
private fun `init_info_using_release_$x`(): StringResource =
    org.jetbrains.compose.resources.StringResource(
  "string:info_using_release_$x", "info_using_release_$x", // '$' not escaped
    setOf(
      org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"),
    )
)

Affected platforms
All platforms (affects resources at compile-time)

Versions

  • Kotlin version: 1.9.22
  • Compose Multiplatform version: 1.6.1
  • OS version: Arch Linux
  • OS architecture: x86_64
  • JDK: openjdk 17.0.10

To Reproduce
Steps and/or the code snippet to reproduce the behavior:

  1. Create a Compose Multiplatform project using the resources system introduced in 1.6.0
  2. In the strings.xml file within src/commonMain/composeResources/values, create a string with a name that is illegal as a Kotlin variable name
  3. Build the project
  4. Resource generation succeeds, but compilation fails

Expected behavior
XML strings are accessible using the resources API even if names contain Kotlin-illegal characters.

Hacky workaround

Code
tasks.named("generateComposeResClass") {
    doLast {
        val directory_name: String =
            buildString {
                val group: String =
                    project.group.toString()
                        .lowercase()
                        .replace('.', '/')
                        .replace('-', '_')
                append(group)

                if (group.isNotEmpty()) {
                    append("/")
                }

                append(project.name.lowercase())
            }

        val directory: File =
            project.layout.buildDirectory
                .dir("generated/compose/resourceGenerator/kotlin/$directory_name/generated/resources")
                .get()
                .asFile

        for (file in directory.listFiles()) {
            if (!file.name.startsWith("String") || !file.name.endsWith(".kt")) {
                continue
            }

            val object_name: String = file.name.split(".", limit = 2).first()

            val lines: MutableList<String> = file.readLines().toMutableList()
            val i: MutableListIterator<String> = lines.listIterator()

            val quoted_names: MutableList<String> = mutableListOf()

            while (i.hasNext()) {
                val line: String = i.next().trim()
                if (line.startsWith("public val `") && line.endsWith("`: StringResource by")) {
                    quoted_names.add(line.substring(12, line.length - 20))

                    i.quoteNextLine(
                        opening = "lazy { ",
                        closing = "() }"
                    )
                }
                else if (line.startsWith("internal val Res.string.`") && line.endsWith("`: StringResource")) {
                    i.quoteNextLine(
                        opening = "get() = ${object_name}.",
                        closing = null
                    )
                }
            }

            var content: String = lines.joinToString("\n")

            for (name in quoted_names) {
                val escaped_name: String = name.replace("\$", "\\$")
                content = content.replace("$name\"", "$escaped_name\"")
            }

            file.writeText(content)
        }
    }
}

fun MutableListIterator<String>.quoteNextLine(opening: String, closing: String?) {
    if (!hasNext()) {
        return
    }

    val next_line: String = next()

    val opening_index: Int = next_line.indexOf(opening)
    if (opening_index == -1) {
        return
    }

    val closing_index: Int
    if (closing == null) {
        closing_index = next_line.length
    }
    else {
        closing_index = next_line.indexOf(closing)
        if (closing_index == -1) {
            return
        }
    }

    val new_line: StringBuilder = StringBuilder(next_line)
    new_line.insert(opening_index + opening.length, '`')
    new_line.insert(closing_index + 1, '`')

    set(new_line.toString())
}
@toasterofbread toasterofbread added bug Something isn't working submitted labels Mar 26, 2024
@pjBooms pjBooms added hover and removed hover labels Mar 27, 2024
terrakok added a commit that referenced this issue May 31, 2024
Fixes #4548

## Testing
Add compose resources with name such as "package", "is" or "item_$xxx"
and check that app compiles and works fine. Accessors should be properly
escaped

## Release Notes
### Fixes - Resources
- Fix resource accessors escaping. Now it is possible to use resources
with names: "package", "is", "item_$xxx" etc
@okushnikov
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working resources
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants