48

Name Mangling in Kotlin - Nicola Corti - Medium

 4 years ago
source link: https://medium.com/@cortinico/name-mangling-in-kotlin-7d0e2a7a173a
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Image for post
Image for post
Sarajevo — Bosnia and Erzigovina

Name Mangling in Kotlin

Mangle

Verb
/ˈmæŋ.ɡəl/

To destroy something by twisting it with force or tearing it into pieces so that its original form is completely changed.
Cambridge Dictionary

If you’ve played a bit around with Kotlin, chances are that you faced name mangling during your development.

Name mangling is a technique used by the Kotlin Compiler to alter the name of identifiers (e.g. function or variable names). This technique can be used to make identifiers harder to access in the bytecode.

I discovered name mangling while preparing the release v3.1.0 of Chucker. Before releasing a new version of a library, I generally inspect the API surface to make sure I'm not introducing any unintended change to the API with a tool like japicmp.

This time I noticed that I was removing a method and adding a new one, and that was not expected, as it will result in a breaking change 🤨.

This was due to name mangling. Let’s see two scenarios where name mangling is used inside the Kotlin compiler: inline classes and the internal modifier.

Inline Classes

Inline Classes have been introduced as experimental in Kotlin 1.3. They are a great tool to easily create wrap types, without introducing runtime overheads due to type wrapping/unwrapping.

An example could be a having Username/ Password inline classes to wrap String values.

So for Example, your login function:

could be improved using inline classes in:

Inline classes provide type safety at compile type, so you’re sure you’re not mixing up username and passwords of your users.

To overcome wrapping/unwrapping overhead, the Kotlin compiler is generating bytecode that uses the underlying type of the inline class.

This could lead to problematic scenarios. For example, if you happen to have a method overload with an inline class:

The Kotlin compiler would have to generate two functions with the same signature.

To avoid such scenarios, the compiler is mangling the name of functions that are using inline classes by adding an hashcode to the function name.

So the previous validate(Password) function will be compile to a validate-<hashcode>(String) to avoid the signature clash with the validate(String) function. It can easily be seen using the Show Kotlin Bytecode feature of IntelliJ:

It’s interesting to note that the - char introduced by the mangling is an invalid character in Java.

This is making the function that accepts an inline class impossible to use from Java.

This is actually by design, but it’s worth noting when creating APIs that should be consumed by Java users.

Internal Modifier

Kotlin gave us a new visibility modifier: internal. If you're not familiar with this modifier: it restricts the visibility of a declaration to the same module.

The internal modifier is handy when dealing with modularized projects, since it allows to easily contain the exposed API. Unfortunately, there is no support for the internal modifier neither in Java nor in the bytecode.

internal declarations in Kotlin are compiled to public declarations in Java.

In other words: your internal function, classes, interfaces, variables etc. are accessible by Java classes outside of your module.

As you can imagine, this is not great, especially if you’re migrating a codebase from Java to Kotlin and you’re making an extensive use of the internal keyword.

Luckily, the Kotlin compiler is using name mangling on internal identifiers to make them harder to call from Java. Names are rewritten appending the module name to them. So for example the following code:

will compile to this equivalent Java code:

so you could potentially call deleteUsers in this way in Java:

Having the $library token inside the identifier, should warn developers that this is an internal method and should not be used.

Please note that this is also true when you’re developing a Kotlin library, that could be consumed by Java projects. In other words: your internal methods will appear to the Java user of your library in their IDE auto-complete.

If you happen to use IntelliJ, there is a built-in code inspection that will raise a warning for usages of Kotlin internal declarations (that could potentially be suppressed with a @SuppressWarnings("KotlinInternalInJava")).

Image for post
Image for post

The Chucker Case

Chucker was recently rewritten in Kotlin. We leveraged the internal keyword extensively to limit the visibility of our classes/methods.

One of our users on Github, reported a build failure with his Gradle build when using our library:

Execution failed for task ':app:mergeDebugJavaResource'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
> More than one file was found with OS independent path 'META-INF/library_release.kotlin_module'

This failure is caused by a name clash with the .kotlin_module file. For every Kotlin module, the Kotlin compiler is generating META-INF/<module name>.kotlin_module file to store metadata from the compilation (more info on kotlin_module file here).

The gradle module of Chucker is called library. As you can image, other libraries are also using the same module name. This is causing the More than one file was found error previously mentioned.

The fix was to pass the -module-name compiler flag, to change the name of our module at compile time:

This will make sure that our .kotlin_module is not clashing with others'.

The side effect of adding this compiler flag was also that signatures of our internal methods changed. So for example the method:

changed his signature from:

as it was correctly spotted by japicmp:

Image for post
Image for post

This was nothing major to worry about but rather something interesting to be aware of. I honestly don’t expect users of our library to accidentally access internal methods (and if they do, it’s on their own risk).

Conclusions

Name mangling is often used by the Kotlin compiler to address Java interoperability with Kotlin. Specifically it’s used to:

  • Prevent methods that are using inline classes from being used from Java.
  • Making internal declarations harder to accidentally being used from Java.

I hope you enjoyed this post and if you want to talk more about Kotlin, feel free to reach out to me as @cortinico on Twitter.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK