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

Relocation breaks inner classes which contains '$' in name #792

Closed
Him188 opened this issue Sep 12, 2022 · 0 comments · Fixed by #793
Closed

Relocation breaks inner classes which contains '$' in name #792

Him188 opened this issue Sep 12, 2022 · 0 comments · Fixed by #793
Milestone

Comments

@Him188
Copy link
Contributor

Him188 commented Sep 12, 2022

The following Kotlin code is compiled with an inner class named $serializer by the Kotlin compiler.

package foo
@Serializable // This annotation instructs the compiler to generate an extra inner class named `$serializer`
enum class MyEnum {
    VALUE1
}

The qualified name of the inner class will be foo.MyEnum$$serializer.

If relocation is enabled, shadow runs RelocatorRemapper on the class. In org.objectweb.asm.commons.Remapper, it maps inner class names by substring-ing the name after the last $. So the resultant qualified name for the example will be foo.MyEnum$serializer (Note one $ is missing). This breaks Kotlin's class metadata and will cause AssertionError in runtime. See relevant issues like mamoe/mirai#2230.


Here's the org.objectweb.asm.commons.Remapper.mapInnerClassName for your reference.

  /**
   * Maps an inner class name to its new name. The default implementation of this method provides a
   * strategy that will work for inner classes produced by Java, but not necessarily other
   * languages. Subclasses can override.
   *
   * @param name the fully-qualified internal name of the inner class.
   * @param ownerName the internal name of the owner class of the inner class.
   * @param innerName the internal name of the inner class.
   * @return the new inner name of the inner class.
   */
  public String mapInnerClassName(
      final String name, final String ownerName, final String innerName) {
    final String remappedInnerName = this.mapType(name);
    if (remappedInnerName.contains("$")) {
      int index = remappedInnerName.lastIndexOf('$') + 1;
      while (index < remappedInnerName.length()
          && Character.isDigit(remappedInnerName.charAt(index))) {
        index++;
      }
      return remappedInnerName.substring(index);
    } else {
      return innerName;
    }
  }

Shadow Version

7.1.2

Gradle Version

7.3.0

Expected Behavior

Code runs successfully.

Actual Behavior

Gradle Build Script(s)

Content of Shadow JAR (jar tf <jar file> - post link to GIST if too long)

@Him188 Him188 changed the title Relocation breaks serializers compiled by kotlinx-serialization and cause AssertionError in runtime Relocation breaks inner classes which contains '$' in name Sep 13, 2022
@johnrengelman johnrengelman modified the milestones: 8.0, Next, 8.1.0 Feb 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants