Skip to content

Commit 7b375de

Browse files
authored
Kotlin: explicit accessors for static properties (#1782)
---------- Motivation ---------- Java generator in Gluecodium generates getter/setter methods for static properties. When such Java code is accessed from Kotlin the methods should be represented as Kotlin synthetic properties (according to the documentation of Kotlin language). However, it turns out that such static properties can be also accessed via getter and setter static functions. Such usage was identified to be working. Therefore, in order to allow seamless replacement of Java generated code with Kotlin generated code for Kotlin apps the Kotlin generated code must also allow getting/setting static properties via getter/setter functions. ---------- Implemented solution ---------- Kotlin generator produces additional extension methods for companion object to allow getting and setting static properties. The generated methods are annotated as JvmSynthetic to ensure that they are not visible in Java (because such feature is Kotlin-specific). Moreover, the value passed to file:JvmName() is adjusted to avoid compilation problems. That name is used only when we have global functions or properties. Such entities are compiled to the class with specified name. Because we used the same name as the element defined for given file, when extensions are defined we would end up with two classes with the same name -- this was adjusted. Signed-off-by: Patryk Wrobel <[email protected]>
1 parent 0ce8de3 commit 7b375de

File tree

218 files changed

+412
-214
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

218 files changed

+412
-214
lines changed

.github/workflows/functional-tests.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ jobs:
283283
version: 1.11.0
284284
- name: Build and run functional tests
285285
run: |
286+
export MACOSX_DEPLOYMENT_TARGET=$(sw_vers -productVersion)
287+
export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"
286288
./scripts/build-swift-functional --buildGluecodium
287289
working-directory: functional-tests
288290
- name: Removing build folder
@@ -291,6 +293,8 @@ jobs:
291293
working-directory: functional-tests
292294
- name: Build and run namerules tests
293295
run: |
296+
export MACOSX_DEPLOYMENT_TARGET=$(sw_vers -productVersion)
297+
export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"
294298
./scripts/build-swift-namerules --publish
295299
working-directory: functional-tests
296300
- name: Build examples

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44
### Features:
55
* Kotlin: interfaces are now generated as functional interfaces whenever it is possible.
6+
* Kotlin: static properties of classes can be now accessed also via static getter and setter generated as extension function.
67

78
## 13.19.0
89
Release date 2025-08-11

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ class AttributesTest {
5959
assertEquals("fooBar", Attributes.staticAttribute)
6060
}
6161

62+
@org.junit.Test
63+
fun setGetStaticAttributeViaExplicitFunction() {
64+
Attributes.setStaticAttribute("fooBar")
65+
assertEquals("fooBar", Attributes.getStaticAttribute())
66+
}
67+
6268
@org.junit.Test
6369
fun getCachedProperty() {
6470
val instance: CachedProperties = CachedProperties()
@@ -83,6 +89,7 @@ class AttributesTest {
8389

8490
val result1: ByteArray = CachedProperties.staticCachedProperty
8591
val result2: ByteArray = CachedProperties.staticCachedProperty
92+
val result3: ByteArray = CachedProperties.getStaticCachedProperty()
8693

8794
assertEquals(1, CachedProperties.staticCallCount)
8895

@@ -93,5 +100,8 @@ class AttributesTest {
93100

94101
assertEquals(result1, result2)
95102
assertTrue(result1 === result2)
103+
104+
assertEquals(result1, result3)
105+
assertTrue(result1 === result3)
96106
}
97107
}

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import com.here.gluecodium.model.lime.LimeField
2828
import com.here.gluecodium.model.lime.LimeInterface
2929
import com.here.gluecodium.model.lime.LimeLambda
3030
import com.here.gluecodium.model.lime.LimeNamedElement
31+
import com.here.gluecodium.model.lime.LimeProperty
32+
import com.here.gluecodium.model.lime.LimeType
3133

3234
internal class KotlinVisibilityResolver(private val referenceMap: Map<String, LimeElement>) : NameResolver {
3335
override fun resolveName(element: Any): String {
@@ -60,4 +62,33 @@ internal class KotlinVisibilityResolver(private val referenceMap: Map<String, Li
6062
val parent: LimeElement? = referenceMap[element.path.parent.toString()]
6163
return parent != null && (parent is LimeInterface || parent is LimeLambda)
6264
}
65+
66+
override fun resolveGetterName(element: Any) = resolveVisibilityForPropertyExtension(element)
67+
68+
override fun resolveSetterName(element: Any) = resolveVisibilityForPropertyExtension(element)
69+
70+
// Extension method for static properties require explicit 'internal' keyword even when property is nested
71+
// inside hierarchy of internal nested types. Therefore, we need to iterate over parent types and check if
72+
// any of them is internal.
73+
private fun resolveVisibilityForPropertyExtension(property: Any): String {
74+
if (property !is LimeProperty) {
75+
return ""
76+
}
77+
78+
if (isInternal(property)) {
79+
return "internal "
80+
}
81+
82+
val parentElement = referenceMap[property.path.parent.toString()] as LimeNamedElement? ?: return ""
83+
if (isInternal(parentElement)) {
84+
return "internal "
85+
}
86+
87+
val isEachParentPublic =
88+
generateSequence(parentElement) {
89+
referenceMap[it.path.parent.toString()] as? LimeType
90+
}.none { isInternal(it) }
91+
92+
return if (isEachParentPublic) "" else "internal "
93+
}
6394
}

gluecodium/src/main/resources/templates/kotlin/KotlinClass.mustache

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,16 @@
155155
}}{{/thisConstructor}}{{!!
156156
}}{{/kotlinConstructorExceptions}}{{!!
157157
158-
}}
158+
}}
159+
160+
{{#ifPredicate "hasStaticProperties"}}
161+
{{#set classElement=this}}
162+
{{#properties}}
163+
{{#if isStatic}}
164+
{{#set property=this isCached=attributes.cached}}{{#property}}
165+
{{>kotlin/KotlinStaticPropertyExtension}}
166+
{{/property}}{{/set}}
167+
{{/if}}
168+
{{/properties}}
169+
{{/set}}
170+
{{/ifPredicate}}

gluecodium/src/main/resources/templates/kotlin/KotlinFile.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*
2424
*/
2525

26-
{{#modelName}}@file:JvmName("{{modelName}}"){{/modelName}}
26+
{{#modelName}}@file:JvmName("{{modelName}}Extensions"){{/modelName}}
2727

2828
package {{#package}}{{this}}{{#if iter.hasNext}}.{{/if}}{{/package}}
2929

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{{!!
2+
!
3+
! Copyright (C) 2016-2025 HERE Europe B.V.
4+
!
5+
! Licensed under the Apache License, Version 2.0 (the "License");
6+
! you may not use this file except in compliance with the License.
7+
! You may obtain a copy of the License at
8+
!
9+
! http://www.apache.org/licenses/LICENSE-2.0
10+
!
11+
! Unless required by applicable law or agreed to in writing, software
12+
! distributed under the License is distributed on an "AS IS" BASIS,
13+
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
! See the License for the specific language governing permissions and
15+
! limitations under the License.
16+
!
17+
! SPDX-License-Identifier: Apache-2.0
18+
! License-Filename: LICENSE
19+
!
20+
!}}
21+
{{>kotlin/KotlinDocComment}}{{>kotlin/KotlinAttributes}}
22+
@JvmSynthetic
23+
{{resolveName this "visibility" "getter"}}fun {{resolveName classElement}}.Companion.{{resolveName this "" "getter"}}(): {{resolveName typeRef}} = {{resolveName classElement}}.{{resolveName this}}{{!!
24+
}}{{#if setter}}
25+
26+
{{>kotlin/KotlinDocComment}}{{>kotlin/KotlinAttributes}}
27+
@JvmSynthetic
28+
{{resolveName this "visibility" "setter"}}fun {{resolveName classElement}}.Companion.{{resolveName this "" "setter"}}(value: {{resolveName typeRef}}) {
29+
{{resolveName classElement}}.{{resolveName this}} = value
30+
}
31+
32+
{{/if}}

gluecodium/src/test/resources/smoke/attributes/output/android-kotlin/com/example/smoke/AttributesClass.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
*/
55

6-
@file:JvmName("AttributesClass")
6+
@file:JvmName("AttributesClassExtensions")
77

88
package com.example.smoke
99

gluecodium/src/test/resources/smoke/attributes/output/android-kotlin/com/example/smoke/AttributesCrashException.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
*/
55

6-
@file:JvmName("AttributesCrash")
6+
@file:JvmName("AttributesCrashExtensions")
77

88
package com.example.smoke
99

gluecodium/src/test/resources/smoke/attributes/output/android-kotlin/com/example/smoke/AttributesEnum.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
*/
55

6-
@file:JvmName("AttributesEnum")
6+
@file:JvmName("AttributesEnumExtensions")
77

88
package com.example.smoke
99

0 commit comments

Comments
 (0)