Skip to content

Commit e1a418b

Browse files
authored
Kotlin: generate static properties as get/set functions (#1800)
---------- Motivation ---------- During internal testing I found that 'static internal properties' do not work correctly. Basically, the combination of 'internal + JvmStatic property + JvmName for get/set' sometimes is not correctly resolved in runtime. Moreover, I identified the gap related to internal static properties and functions in interfaces. Even though companion object of interface allows usage of internal keyword, we cannot use JvmStatic. Therefore, such usage must be flagged during validation. ---------- Solution ---------- 1. This commit brings reproducer of runtime problems in functional tests. 2. It also provides a fix for such problems. 3. Validation of internal elements in LimeInterface for Kotlin is adjusted. A note before showing the design decision: - in the past the static properties were implemented in interfaces as get/set functions pair, because of similar problems - moreover, we had to provide extension functions for get/set of static properties in classes to preserve compatibility with Java Because of that and the problem stated in 'Motivation' section, the static properties in classes are also handled using get/set function pair. This way internal static properties work as expected. This commit additionally removes the generation of extension functions because they are not needed now. Furthermore, validation of LimeInterface in Kotlin is adjusted to disallow static internal properties and functions. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
1 parent 7f4d031 commit e1a418b

31 files changed

Lines changed: 272 additions & 199 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* Kotlin: minimize the usage of 'JvmSuppressWildcards' annotation for collection parameters. This annotation was used for all generated collection types. It needs to be used only when collection type holds enumeration or open type e.g.: 'open class', 'interface' or 'fun interface'.
88
* Kotlin: raise error when '@Internal' elements are defined in interfaces. Kotlin language does not allow usage of 'internal' keyword in interfaces. We need to raise an error, because there is no valid way of generating such code.
99
* Kotlin: make the 'Impl' classes private. Such classes are used to represent C++ implementation of interface/lambda in Kotlin and they should not be accessed by the users. Only JNI layer should access such types.
10+
* Kotlin: always handle static properties as get/set pair. Moreover, disallow internal static properties/functions in LimeInterface elements.
1011

1112
## 14.0.0
1213
Release date 2025-12-01

functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/AttributesTest.kt

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,6 @@ class AttributesTest {
5555

5656
@org.junit.Test
5757
fun setGetStaticAttribute() {
58-
Attributes.staticAttribute = "fooBar"
59-
assertEquals("fooBar", Attributes.staticAttribute)
60-
}
61-
62-
@org.junit.Test
63-
fun setGetStaticAttributeViaExplicitFunction() {
6458
Attributes.setStaticAttribute("fooBar")
6559
assertEquals("fooBar", Attributes.getStaticAttribute())
6660
}
@@ -85,13 +79,13 @@ class AttributesTest {
8579

8680
@org.junit.Test
8781
fun getStaticCachedProperty() {
88-
assertEquals(0, CachedProperties.staticCallCount)
82+
assertEquals(0, CachedProperties.getStaticCallCount())
8983

90-
val result1: ByteArray = CachedProperties.staticCachedProperty
91-
val result2: ByteArray = CachedProperties.staticCachedProperty
84+
val result1: ByteArray = CachedProperties.getStaticCachedProperty()
85+
val result2: ByteArray = CachedProperties.getStaticCachedProperty()
9286
val result3: ByteArray = CachedProperties.getStaticCachedProperty()
9387

94-
assertEquals(1, CachedProperties.staticCallCount)
88+
assertEquals(1, CachedProperties.getStaticCallCount())
9589

9690
assertEquals(3, result1.size)
9791
assertEquals(0, result1[0].toInt())

functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/DatesTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ class DatesTest {
3737
@org.junit.Test
3838
fun dateAttributeRoundTrip() {
3939
val date = Date()
40-
Dates.dateAttribute = date
40+
Dates.setDateAttribute(date)
4141

42-
assertEquals(date, Dates.dateAttribute)
42+
assertEquals(date, Dates.getDateAttribute())
4343
}
4444

4545
@org.junit.Test

functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/LambdasTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ class LambdasTest {
114114

115115
@org.junit.Test
116116
fun setGetLambdaProperty() {
117-
Lambdas.realConcatenator = Lambdas.getConcatenator(">.<")
118-
val result: String = Lambdas.realConcatenator.apply("foo", "bar")
117+
Lambdas.setRealConcatenator(Lambdas.getConcatenator(">.<"))
118+
val result: String = Lambdas.getRealConcatenator().apply("foo", "bar")
119119

120120
assertEquals("foo>.<bar", result)
121121
}

functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/LocalesTest.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,32 +65,32 @@ class LocalesTest {
6565
@org.junit.Test
6666
fun localePropertyRoundTrip() {
6767
val locale: Locale = Locale.getDefault()
68-
Locales.localeProperty = locale
68+
Locales.setLocaleProperty(locale)
6969

70-
assertEquals(locale, Locales.localeProperty)
70+
assertEquals(locale, Locales.getLocaleProperty())
7171
}
7272

7373
@org.junit.Test
7474
fun localeWithMalformedLanguage() {
7575
assertThrows(IllformedLocaleException::class.java) {
7676
@Suppress("UNUSED_VARIABLE")
77-
val locale = Locales.localeWithMalformedLanguage
77+
val locale = Locales.getLocaleWithMalformedLanguage()
7878
}
7979
}
8080

8181
@org.junit.Test
8282
fun localeWithMalformedCountry() {
8383
assertThrows(IllformedLocaleException::class.java) {
8484
@Suppress("UNUSED_VARIABLE")
85-
val locale = Locales.localeWithMalformedCountry
85+
val locale = Locales.getLocaleWithMalformedCountry()
8686
}
8787
}
8888

8989
@org.junit.Test
9090
fun localeWithMalformedScript() {
9191
assertThrows(IllformedLocaleException::class.java) {
9292
@Suppress("UNUSED_VARIABLE")
93-
val locale = Locales.localeWithMalformedScript
93+
val locale = Locales.getLocaleWithMalformedScript()
9494
}
9595
}
9696

functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/OptimizedListTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class OptimizedListTest {
5555

5656
@org.junit.Test
5757
fun optimizedListFromProperty() {
58-
val result: List<UnreasonablyLazyClass> = UseOptimizedList.lazyOnes
58+
val result: List<UnreasonablyLazyClass> = UseOptimizedList.getLazyOnes()
5959

6060
assertEquals(2, result.size)
6161
assertNotNull(result[0])
@@ -64,7 +64,8 @@ class OptimizedListTest {
6464

6565
@org.junit.Test
6666
fun optimizedListIteratorFromProperty() {
67-
val result: Iterator<UnreasonablyLazyClass> = UseOptimizedList.lazyOnes.iterator()
67+
val lazyOnes = UseOptimizedList.getLazyOnes()
68+
val result: Iterator<UnreasonablyLazyClass> = lazyOnes.iterator()
6869

6970
assertTrue(result.hasNext())
7071
assertNotNull(result.next())

functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/VisibilityAttributeTest.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,9 @@ class VisibilityAttributeTest {
199199
fun checkInternalConst() {
200200
assertEquals(11, SomeClassWithInternalMembers.INTERNAL_CONSTANT)
201201
}
202-
}
202+
203+
@org.junit.Test
204+
fun checkInternalStaticProperty() {
205+
assertEquals("foo", InternalAttributeClassWithStaticProperty.getFooBar())
206+
}
207+
}

functional-tests/functional/input/lime/VisibilityAttribute.lime

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct PublicStructWithNonDefaultInternalAttributeField {
3434

3535
@Internal
3636
class InternalAttributeClassWithStaticProperty {
37+
@Internal
3738
static property fooBar: String
3839
}
3940

functional-tests/functional/input/src/cpp/VisibilityAttribute.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "test/InternalAttributeClassWithFunctions.h"
2222
#include "test/InternalAttributeClassWithStaticProperty.h"
23+
#include "test/InternalAttributeInterfaceParent.h"
2324
#include "test/SomeClassWithInternalMembers.h"
2425
#include "test/SomeDerivedInternalClass.h"
2526
#include "test/SomeInternalClassWithMembers.h"

gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinInterfacesValidator.kt

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ internal class KotlinInterfacesValidator(private val logger: LimeLogger) {
3737
* For LIME elements annotated with '@Internal' we must generate 'internal' keyword for visibility.
3838
* This would not compile. Therefore, it must be disallowed and the user must know that direct non-static
3939
* elements and types defined in interface cannot be internal.
40+
* Moreover, we cannot use 'JvmStatic' compatibility annotation for non-public members of interfaces.
41+
* Therefore, all static functions and properties must also be taken into account.
4042
*/
4143
private fun validateDirectInternalMembers(limeInterface: LimeInterface): Boolean {
4244
val elementsToValidate: List<LimeNamedElement> =
43-
getDirectNestedTypes(limeInterface) +
44-
getNonStaticFunctions(limeInterface) +
45-
getNonStaticProperties(limeInterface)
45+
getDirectNestedTypes(limeInterface) + limeInterface.functions + limeInterface.properties
4646

4747
val containsDirectInternalElement =
4848
elementsToValidate.map {
@@ -62,12 +62,4 @@ internal class KotlinInterfacesValidator(private val logger: LimeLogger) {
6262
return limeInterface.structs + limeInterface.enumerations + limeInterface.typeAliases +
6363
limeInterface.exceptions + limeInterface.classes + limeInterface.interfaces + limeInterface.lambdas
6464
}
65-
66-
private fun getNonStaticFunctions(limeInterface: LimeInterface): List<LimeNamedElement> {
67-
return limeInterface.functions.filter { !it.isStatic }
68-
}
69-
70-
private fun getNonStaticProperties(limeInterface: LimeInterface): List<LimeNamedElement> {
71-
return limeInterface.properties.filter { !it.isStatic }
72-
}
7365
}

0 commit comments

Comments
 (0)