Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Xamarin.Android.Tools.Bytecode] Kotlin internal prop visibility (#1151)
Fixes: #1139 Context: https://jetbrains.gitbooks.io/kotlin-reference-for-kindle/content/properties.html Kotlin does not allow classes to explicitly contain fields: > Classes in Kotlin cannot have fields. They can *implicitly* contain fields, but not explicitly. Syntax that *look like* a field to those who don't know Kotlin: /* partial */ class EmojiPickerView { private var onEmojiPickedListener: Consumer<EmojiViewItem>? = null } are in fact *properties*, which may or may not involve a compiler- generated backing field. When a property is declared in Kotlin, Java `get*` and/or `set*` methods are generated as needed. In the case where the property is `internal` -- which is not supported by Java -- `public` getters or setters are generated. We wish to "hide" these as they are not intended to be part of the public API. That is, they would not be callable by Kotlin code. However, consider these code fragments from [`EmojiPickerView.kt`][0]: /* partial */ class EmojiPickerView { // Line 100 private var onEmojiPickedListener: Consumer<EmojiViewItem>? = null // Lines 317-319 fun setOnEmojiPickedListener(onEmojiPickedListener: Consumer<EmojiViewItem>?) { this.onEmojiPickedListener = onEmojiPickedListener } } Default member visibility in Kotlin is `public`, so this declares a private `onEmojiPickedListener` property and a public `setOnEmojiPickedListener()` method. However, we were incorrectly determining visibility (678c4bd): we treated `onEmojiPickedListener` and `setOnEmojiPickedListener()` as if they were part of the same property. As part of this association, we saw that `onEmojiPickedListener` was private, and marked `setOnEmojiPickedListener()` as private to follow suit. This was incorrect, because `private` properties do not have setters *at all*, so we should not attempt to hide anything for `private` properties, only for `internal` properties. With that incorrect association broken, that allows `setOnEmojiPickedListener()` to be considered separately, and found to have `public` visibility. For example, this Kotlin code: private var type = 0 internal var itype = 0 generates Java code equivalent to: private int type; private int itype; public final int getItype$main() { return this.itype; } public final void setItype$main(int <set-?>) { this.itype = <set-?>; Additionally, when matching `internal` properties to their getters or setters, the generated name differs from the names given to `public` getters and setters. // Kotlin: public var type = 0 // Java: public final int getType () { ... } // Kotlin: internal var type = 0 // Java: public final int getType$main () { ... } Fix this scenario: * Do not attempt to hide getters/setters for `private` Kotlin properties. * Improve matching of generated names for `internal` Kotlin properties. Additionally, our existing unit test in `NameShadowing.kt` was written incorrectly: // Incorrect, return type of a function fun setType(type: Int) = { println (type); } // Correct, return type of void fun setType(type: Int) { println (type); } The fixed `NameShadowing.kt` unit test fails without the other changes here. Additionally, add several more unit test cases to cover `internal` properties and mangled getter/setter names. Additionally, some enum values in `KotlinPropertyFlags` were specified incorrectly which has been fixed. [0]: https://github.com/androidx/androidx/blob/0d655214d339e006f4e13a85f55c78770c885f2e/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt
- Loading branch information