From 6bb80d6ba8b7dd2c069c66cc9b8be5b0ff2338e5 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Wed, 28 Jan 2026 09:23:12 +0100 Subject: [PATCH 01/23] kotlin: encapsulate the logic to check internal nested type in function This is not a functional change. The logic is preserved. All tests work in the identical way. This change introduces a new function, which will be used later for all types. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../kotlin/KotlinVisibilityResolver.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt index 2032e5c2aa..04a669045a 100644 --- a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt +++ b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt @@ -71,17 +71,17 @@ internal class KotlinVisibilityResolver(private val referenceMap: Map Date: Wed, 28 Jan 2026 09:30:11 +0100 Subject: [PATCH 02/23] kotlin: simplify 'resolveName()' method of visibility resolver The logic was simplified to use 'return when' statement. This improves readability and prepares for further changes. This is not a functional change. The code works as previously. All tests pass. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../kotlin/KotlinVisibilityResolver.kt | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt index 04a669045a..5e8acefeae 100644 --- a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt +++ b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt @@ -33,18 +33,15 @@ import com.here.gluecodium.model.lime.LimeType internal class KotlinVisibilityResolver(private val referenceMap: Map) : NameResolver { override fun resolveName(element: Any): String { - val namedElement = element as? LimeNamedElement - if (namedElement != null) { - return if (!isInternal(element)) { - "" - } else if (!isDirectKotlinInterfaceChild(element)) { - "internal " - } else { - "" - } - } else { + if (element !is LimeNamedElement) { throw GluecodiumExecutionException("Unsupported element type ${element.javaClass.name}") } + + return when { + isDirectKotlinInterfaceChild(element) -> "" + isInternal(element) -> "internal " + else -> "" + } } private fun isInternal(element: LimeNamedElement): Boolean { From 6dcbe5e01f03041c2f8986d9b6fd98fcfb22bcff Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Wed, 28 Jan 2026 18:37:58 +0100 Subject: [PATCH 03/23] kotlin: ensure that elements nested in internals are also explicitly internal This change ensures that all elements nested in internal types hierarchy are internal and annotated with JvmSyntetic. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../kotlin/KotlinVisibilityResolver.kt | 2 +- .../templates/kotlin/KotlinFunction.mustache | 15 ++++++++++++++- .../templates/kotlin/KotlinProperty.mustache | 18 ++++++++++++++---- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt index 5e8acefeae..96f65d583f 100644 --- a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt +++ b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt @@ -39,7 +39,7 @@ internal class KotlinVisibilityResolver(private val referenceMap: Map "" - isInternal(element) -> "internal " + isInternal(element) || isInInternalTypesHierarchy(element) -> "internal " else -> "" } } diff --git a/gluecodium/src/main/resources/templates/kotlin/KotlinFunction.mustache b/gluecodium/src/main/resources/templates/kotlin/KotlinFunction.mustache index 54d18e45fc..82f4413db5 100644 --- a/gluecodium/src/main/resources/templates/kotlin/KotlinFunction.mustache +++ b/gluecodium/src/main/resources/templates/kotlin/KotlinFunction.mustache @@ -21,8 +21,21 @@ {{#unless this.isConstructor}}{{>kotlin/KotlinMethodComment}}{{>kotlin/KotlinAttributes}}{{/unless}} {{#thrownType}}@Throws({{resolveName typeRef}}::class){{/thrownType}} {{#if isStatic}}@JvmStatic {{/if}}{{!! + + 1. If function has internal visibility then hide it from java. }}{{#resolveName "visibility"}}{{#unless this.isEmpty}}@JvmSynthetic {{/unless}}{{/resolveName}}{{!! -}}{{#unless override}}{{#unless isInterface}}{{#ifPredicate "isInternal"}}@JvmName("{{resolveName}}") {{/ifPredicate}}{{/unless}}{{/unless}}{{!! + +}}{{#unless override}}{{#unless isInterface}}{{!! + + 2. If function can use internal keyword, then set JvmName() to properly resolve JNI symbol. +}}{{#set thisFunction=this}}{{#thisFunction}}{{!! + }}{{#resolveName "visibility"}}{{#unless this.isEmpty}}{{!! + }}@JvmName("{{resolveName thisFunction}}") {{!! + }}{{/unless}}{{/resolveName}}{{!! +}}{{/thisFunction}}{{/set}}{{!! + +}}{{/unless}}{{/unless}}{{!! + }}{{#if override}}override {{/if}}{{!! }}{{#unless isInterface}}{{!! }}{{#unless this.isConstructor}}{{resolveName "visibility"}}{{/unless}}{{!! diff --git a/gluecodium/src/main/resources/templates/kotlin/KotlinProperty.mustache b/gluecodium/src/main/resources/templates/kotlin/KotlinProperty.mustache index ae36c16655..5305f4b73c 100644 --- a/gluecodium/src/main/resources/templates/kotlin/KotlinProperty.mustache +++ b/gluecodium/src/main/resources/templates/kotlin/KotlinProperty.mustache @@ -28,13 +28,23 @@ {{#unless isCached}}{{#set thisProperty=this}}{{#thisProperty}} {{#unless isInterface}}{{!! }}{{#unless override}}{{!! -}}{{#resolveName thisProperty "visibility" ""}}{{#unless this.isEmpty}}@JvmSynthetic {{/unless}}{{/resolveName}}{{!! -}}{{#ifPredicate thisProperty "propertyGetterRequiresJvmName"}}@JvmName("{{resolveName thisProperty "" "getter"}}") {{/ifPredicate}}{{/unless}}{{!! + +}}{{#resolveName thisProperty "visibility" ""}}{{!! + }}{{#unless this.isEmpty}}@JvmSynthetic @JvmName("{{resolveName thisProperty "" "getter"}}") {{/unless}}{{!! + }}{{#if this.isEmpty}}{{#ifPredicate thisProperty "propertyGetterRequiresJvmName"}}@JvmName("{{resolveName thisProperty "" "getter"}}") {{/ifPredicate}}{{/if}}{{!! +}}{{/resolveName}}{{!! + +}}{{/unless}}{{!! }}external {{/unless}}get{{#setter}} {{#unless isInterface}}{{!! }}{{#unless override}}{{!! -}}{{#resolveName thisProperty "visibility" ""}}{{#unless this.isEmpty}}@JvmSynthetic {{/unless}}{{/resolveName}}{{!! -}}{{#ifPredicate thisProperty "propertySetterRequiresJvmName"}}@JvmName("{{resolveName thisProperty "" "setter"}}") {{/ifPredicate}}{{/unless}}{{!! + +}}{{#resolveName thisProperty "visibility" ""}}{{!! + }}{{#unless this.isEmpty}}@JvmSynthetic @JvmName("{{resolveName thisProperty "" "setter"}}") {{/unless}}{{!! + }}{{#if this.isEmpty}}{{#ifPredicate thisProperty "propertySetterRequiresJvmName"}}@JvmName("{{resolveName thisProperty "" "setter"}}") {{/ifPredicate}}{{/if}}{{!! +}}{{/resolveName}}{{!! + +}}{{/unless}}{{!! }}external {{/unless}}set{{/setter}} {{/thisProperty}}{{/set}}{{/unless}} {{#if isCached}}{{#set thisProperty=this}}{{#thisProperty}} From a51bf11f0a3c6ffba34e657023106690dd7de633 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Thu, 19 Feb 2026 12:51:35 +0100 Subject: [PATCH 04/23] smoke-tests: showcase nested internal elements All elements nested in internal types hierarchy are internal. This does not apply to direct members of interfaces. According to Kotlin rules such types cannot be internal. However, such direct nested elements like functions and properties should use 'JvmSynthetic'. This will be fixed in the next commit. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../android-kotlin/com/example/smoke/Annotations.kt | 3 +-- .../com/example/smoke/OuterInternalStruct.kt | 2 +- .../com/example/smoke/OuterPublicClazz.kt | 4 +--- .../com/example/smoke/InternalClassWithComments.kt | 3 ++- .../android-kotlin/com/example/smoke/InternalClass.kt | 3 ++- .../com/example/smoke/InternalClassWithFunctions.kt | 11 +++++------ .../com/example/smoke/InternalInterface.kt | 8 ++++---- .../android-kotlin/com/example/smoke/PublicClass.kt | 3 +-- .../com/example/smoke/PublicTypeCollection.kt | 3 ++- 9 files changed, 19 insertions(+), 21 deletions(-) diff --git a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/Annotations.kt b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/Annotations.kt index 31353f7fd7..a5b3707af0 100644 --- a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/Annotations.kt +++ b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/Annotations.kt @@ -29,7 +29,7 @@ internal class Annotations : NativeBase { - external fun testOptional(self: Annotations) : Boolean? + @JvmSynthetic @JvmName("testOptional") internal external fun testOptional(self: Annotations) : Boolean? @@ -39,4 +39,3 @@ internal class Annotations : NativeBase { } } - diff --git a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalStruct.kt b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalStruct.kt index a147be61c6..2a6785b2ec 100644 --- a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalStruct.kt +++ b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalStruct.kt @@ -12,7 +12,7 @@ import com.example.MySmokeTestsInternalApi @MySmokeTestsInternalApi internal class OuterInternalStruct { - @JvmField var someField: Int + @JvmField @JvmSynthetic internal var someField: Int diff --git a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterPublicClazz.kt b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterPublicClazz.kt index f23bc21f77..7ebfbf988f 100644 --- a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterPublicClazz.kt +++ b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterPublicClazz.kt @@ -31,7 +31,7 @@ class OuterPublicClazz : NativeBase { - external fun someFunction() : Boolean + @JvmSynthetic @JvmName("someFunction") internal external fun someFunction() : Boolean @@ -43,7 +43,6 @@ class OuterPublicClazz : NativeBase { - /** * For internal use only. * @suppress @@ -64,4 +63,3 @@ class OuterPublicClazz : NativeBase { } } - diff --git a/gluecodium/src/test/resources/smoke/comments/output/android-kotlin/com/example/smoke/InternalClassWithComments.kt b/gluecodium/src/test/resources/smoke/comments/output/android-kotlin/com/example/smoke/InternalClassWithComments.kt index 55a3415767..949cdb87db 100644 --- a/gluecodium/src/test/resources/smoke/comments/output/android-kotlin/com/example/smoke/InternalClassWithComments.kt +++ b/gluecodium/src/test/resources/smoke/comments/output/android-kotlin/com/example/smoke/InternalClassWithComments.kt @@ -5,6 +5,7 @@ @file:JvmName("InternalClassWithCommentsExtensions") + package com.example.smoke import com.example.NativeBase @@ -31,7 +32,7 @@ internal class InternalClassWithComments : NativeBase { * This is definitely internal */ - external fun doNothing() : Unit + @JvmSynthetic @JvmName("doNothing") internal external fun doNothing() : Unit diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClass.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClass.kt index d63db72d91..b721a65240 100644 --- a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClass.kt +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClass.kt @@ -5,6 +5,7 @@ @file:JvmName("InternalClassExtensions") + package com.example.smoke import com.example.NativeBase @@ -26,7 +27,7 @@ internal class InternalClass : NativeBase { - external fun fooBar() : Unit + @JvmSynthetic @JvmName("fooBar") internal external fun fooBar() : Unit diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassWithFunctions.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassWithFunctions.kt index d36ae91fe9..5d54f76899 100644 --- a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassWithFunctions.kt +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassWithFunctions.kt @@ -15,12 +15,12 @@ internal class InternalClassWithFunctions : NativeBase { - constructor() : this(make(), null as Any?) { + internal constructor() : this(make(), null as Any?) { cacheThisInstance(); } - constructor(foo: String) : this(make(foo), null as Any?) { + internal constructor(foo: String) : this(make(foo), null as Any?) { cacheThisInstance(); } @@ -38,7 +38,7 @@ internal class InternalClassWithFunctions : NativeBase { - external fun fooBar() : Unit + @JvmSynthetic @JvmName("fooBar") internal external fun fooBar() : Unit @@ -46,10 +46,9 @@ internal class InternalClassWithFunctions : NativeBase { companion object { @JvmStatic private external fun disposeNativeHandle(nativeHandle: Long) - @JvmStatic private external fun make() : Long + @JvmStatic @JvmSynthetic @JvmName("make") private external fun make() : Long - @JvmStatic private external fun make(foo: String) : Long + @JvmStatic @JvmSynthetic @JvmName("make") private external fun make(foo: String) : Long } } - diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterface.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterface.kt index 0dfb799d10..591cca140d 100644 --- a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterface.kt +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterface.kt @@ -20,13 +20,13 @@ internal fun interface InternalInterface { companion object { - @JvmStatic fun getSomePropertyOfInternalInterface() : String { + @JvmStatic @JvmSynthetic fun getSomePropertyOfInternalInterface() : String { return InternalInterfaceImpl.getSomePropertyOfInternalInterface() } - @JvmStatic fun setSomePropertyOfInternalInterface(value: String) : Unit { + @JvmStatic @JvmSynthetic fun setSomePropertyOfInternalInterface(value: String) : Unit { InternalInterfaceImpl.setSomePropertyOfInternalInterface(value) } @@ -53,8 +53,8 @@ private class InternalInterfaceImpl : NativeBase, InternalInterface { @JvmStatic private external fun disposeNativeHandle(nativeHandle: Long) - @JvmStatic external fun getSomePropertyOfInternalInterface() : String + @JvmStatic @JvmSynthetic @JvmName("getSomePropertyOfInternalInterface") internal external fun getSomePropertyOfInternalInterface() : String - @JvmStatic external fun setSomePropertyOfInternalInterface(value: String) : Unit + @JvmStatic @JvmSynthetic @JvmName("setSomePropertyOfInternalInterface") internal external fun setSomePropertyOfInternalInterface(value: String) : Unit } } diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/PublicClass.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/PublicClass.kt index ee77a3ec7f..a91d294a6e 100644 --- a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/PublicClass.kt +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/PublicClass.kt @@ -17,7 +17,7 @@ class PublicClass : NativeBase { BAR(1); } internal class InternalStruct { - @JvmField var stringField: String + @JvmField @JvmSynthetic internal var stringField: String @@ -92,4 +92,3 @@ class PublicClass : NativeBase { } } - diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/PublicTypeCollection.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/PublicTypeCollection.kt index ee589c4acb..12a155c20e 100644 --- a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/PublicTypeCollection.kt +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/PublicTypeCollection.kt @@ -5,6 +5,7 @@ @file:JvmName("PublicTypeCollectionExtensions") + package com.example.smoke @@ -23,7 +24,7 @@ class PublicTypeCollection { - external fun fooBar() : Unit + @JvmSynthetic @JvmName("fooBar") internal external fun fooBar() : Unit } From 2ce3d4377a3495aa58470f4a06165f082e4a1c1d Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Thu, 19 Feb 2026 12:56:45 +0100 Subject: [PATCH 05/23] functional-tests: remove java interoperability cases that fail to compile The previous changes ensure that more elements are not accessible in Java. The following errors were visible -- the test cases were removed, because with the new handling of nested internal the compilation error was expected. ``` > Task :functional:compileReleaseUnitTestJavaWithJavac FAILED /functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:57: error: cannot find symbol SomeInternalClassWithMembers someObject = SomeInternalClassWithMembers.create(); ^ symbol: method create() location: class SomeInternalClassWithMembers /functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:58: error: cannot find symbol assertEquals(987, someObject.someFunction()); ^ symbol: method someFunction() location: variable someObject of type SomeInternalClassWithMembers /functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:63: error: cannot find symbol assertEquals(765, SomeInternalClassWithMembers.someStaticFunction()); ^ symbol: method someStaticFunction() location: class SomeInternalClassWithMembers /functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:68: error: cannot find symbol SomeInternalStructWithMembers someObject = SomeInternalStructWithMembers.create(); ^ symbol: method create() location: class SomeInternalStructWithMembers /functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:69: error: cannot find symbol assertEquals(123, someObject.someInteger); ^ symbol: variable someInteger location: variable someObject of type SomeInternalStructWithMembers /functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:70: error: cannot find symbol assertEquals(456, someObject.someLong); ^ symbol: variable someLong location: variable someObject of type SomeInternalStructWithMembers /functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:75: error: cannot find symbol SomeInternalStructWithMembers someObject = SomeInternalStructWithMembers.create(); ^ symbol: method create() location: class SomeInternalStructWithMembers /functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:76: error: cannot find symbol assertEquals(32, someObject.someFunction()); ^ symbol: method someFunction() location: variable someObject of type SomeInternalStructWithMembers ``` Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../android/test/VisibilityAttributeTest.java | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java b/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java index 95557b5fe9..c51ca997e5 100644 --- a/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java +++ b/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java @@ -52,30 +52,6 @@ public void internalConstructorOfClassCanBeCalled() { SomeClassWithInternalMembers someObject = new SomeClassWithInternalMembers(); } - @Test - public void functionOfInternalClassCanBeCalled() { - SomeInternalClassWithMembers someObject = SomeInternalClassWithMembers.create(); - assertEquals(987, someObject.someFunction()); - } - - @Test - public void staticFunctionOfInternalClassCanBeCalled() { - assertEquals(765, SomeInternalClassWithMembers.someStaticFunction()); - } - - @Test - public void fieldOfInternalStructCanBeAccessed() { - SomeInternalStructWithMembers someObject = SomeInternalStructWithMembers.create(); - assertEquals(123, someObject.someInteger); - assertEquals(456, someObject.someLong); - } - - @Test - public void functionOfInternalStructCanBeCalled() { - SomeInternalStructWithMembers someObject = SomeInternalStructWithMembers.create(); - assertEquals(32, someObject.someFunction()); - } - @Test public void internalFieldOfStructCanBeAccessed() { SomeStructWithInternalMembers someObject = new SomeStructWithInternalMembers(21); From e8f16cf244daa0688be0e228f3c46ff2ff7f1980 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Wed, 28 Jan 2026 18:51:05 +0100 Subject: [PATCH 06/23] kotlin: remove unused 'isInternal' predicate The logic will be handled in dedicated classes for resolving synthetic annotation and visibility. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../gluecodium/generator/kotlin/KotlinGeneratorPredicates.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinGeneratorPredicates.kt b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinGeneratorPredicates.kt index 37fa6e7d4f..2f8f72e52f 100644 --- a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinGeneratorPredicates.kt +++ b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinGeneratorPredicates.kt @@ -29,7 +29,6 @@ import com.here.gluecodium.model.lime.LimeExternalDescriptor import com.here.gluecodium.model.lime.LimeFunction import com.here.gluecodium.model.lime.LimeInterface import com.here.gluecodium.model.lime.LimeLambda -import com.here.gluecodium.model.lime.LimeNamedElement import com.here.gluecodium.model.lime.LimeProperty import com.here.gluecodium.model.lime.LimeStruct @@ -46,7 +45,6 @@ internal object KotlinGeneratorPredicates { "hasStaticProperties" to this::hasStaticProperties, "isExceptionSameForCtorAndHookFun" to this::isExceptionSameForCtorAndHookFun, "isFunctionalInterface" to this::isFunctionalInterface, - "isInternal" to this::isInternal, "propertyGetterRequiresJvmName" to this::propertyGetterRequiresJvmName, "propertySetterRequiresJvmName" to this::propertySetterRequiresJvmName, "needsAllFieldsConstructor" to this::needsAllFieldsConstructor, @@ -156,7 +154,4 @@ internal object KotlinGeneratorPredicates { CommonGeneratorPredicates.isInternal(property, LimeAttributeType.KOTLIN) || (property.setter?.attributes?.have(LimeAttributeType.KOTLIN, LimeAttributeValueType.NAME)) ?: false ) - - private fun isInternal(element: Any) = - element is LimeNamedElement && CommonGeneratorPredicates.isInternal(element, LimeAttributeType.KOTLIN) } From 9b40a40b9ea70d56770393b08d984b3c790aadf8 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Wed, 28 Jan 2026 19:10:25 +0100 Subject: [PATCH 07/23] kotlin: encapsulate the logic for conceptually internal types Some elements cannot use internal keyword even though conceptually they are internal. A good example are direct children elements of interfaces. Kotlin interface disallows internal elements as the direct children elements. Even though such children elements are not visible when accessed from Kotlin (because interface is internal), in Java they are accessible. To remove them from Java we will need to annotate them as synthetic via annotation. We need to have a way to nicely check whether element should be annotated as synthetic instead of having logic in mustache template. This change introduces new class, which checks for the conceptual internal visibility. The new class is applied in KotlinVisibilityResolver. No changes in smoke tests are present -- the logic is preserved. In next commits the new class will be used to create synthetic resolver. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../KotlinInternalVisibilityPredicate.kt | 68 +++++++++++++++++++ .../kotlin/KotlinVisibilityResolver.kt | 36 ++-------- 2 files changed, 74 insertions(+), 30 deletions(-) create mode 100644 gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinInternalVisibilityPredicate.kt diff --git a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinInternalVisibilityPredicate.kt b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinInternalVisibilityPredicate.kt new file mode 100644 index 0000000000..99e02f3cd3 --- /dev/null +++ b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinInternalVisibilityPredicate.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016-2026 HERE Europe B.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * License-Filename: LICENSE + */ + +package com.here.gluecodium.generator.kotlin + +import com.here.gluecodium.cli.GluecodiumExecutionException +import com.here.gluecodium.generator.common.CommonGeneratorPredicates +import com.here.gluecodium.model.lime.LimeAttributeType.KOTLIN +import com.here.gluecodium.model.lime.LimeElement +import com.here.gluecodium.model.lime.LimeField +import com.here.gluecodium.model.lime.LimeNamedElement +import com.here.gluecodium.model.lime.LimeType + +internal class KotlinInternalVisibilityPredicate(private val referenceMap: Map) { + // Returns true when the element is conceptually internal. + // Some elements cannot use internal keyword (e.g. the ones which are children of Kotlin interface). + // In such cases we still want to have information about 'conceptual' internal visibility of such + // elements to annotate them with 'JvmSynthetic' --> to hide them when Kotlin is accessed from Java. + // + // The children of such interface will not be visible in Kotlin, but without JvmSynthetic they would + // be visible in Java. + fun isConceptuallyInternal(element: Any): Boolean { + if (element !is LimeNamedElement) { + throw GluecodiumExecutionException("Unsupported element type ${element.javaClass.name}") + } + + return isInternal(element) || isInInternalTypesHierarchy(element) + } + + private fun isInternal(element: LimeNamedElement): Boolean { + var internal = CommonGeneratorPredicates.isInternal(element, KOTLIN) + if (!internal && element is LimeField) { + internal = CommonGeneratorPredicates.isInternal(element.typeRef.type, KOTLIN) + } + + return internal + } + + private fun isInInternalTypesHierarchy(element: LimeNamedElement): Boolean { + val parentElement = referenceMap[element.path.parent.toString()] as LimeNamedElement? ?: return false + if (isInternal(parentElement)) { + return true + } + + val isEachParentPublic = + generateSequence(parentElement) { + referenceMap[it.path.parent.toString()] as? LimeType + }.none { isInternal(it) } + + return !isEachParentPublic + } +} diff --git a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt index 96f65d583f..2c997e3353 100644 --- a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt +++ b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt @@ -20,18 +20,17 @@ package com.here.gluecodium.generator.kotlin import com.here.gluecodium.cli.GluecodiumExecutionException -import com.here.gluecodium.generator.common.CommonGeneratorPredicates import com.here.gluecodium.generator.common.NameResolver -import com.here.gluecodium.model.lime.LimeAttributeType.KOTLIN import com.here.gluecodium.model.lime.LimeElement -import com.here.gluecodium.model.lime.LimeField import com.here.gluecodium.model.lime.LimeInterface import com.here.gluecodium.model.lime.LimeLambda import com.here.gluecodium.model.lime.LimeNamedElement import com.here.gluecodium.model.lime.LimeProperty -import com.here.gluecodium.model.lime.LimeType -internal class KotlinVisibilityResolver(private val referenceMap: Map) : NameResolver { +internal class KotlinVisibilityResolver( + private val referenceMap: Map, + private val predicate: KotlinInternalVisibilityPredicate = KotlinInternalVisibilityPredicate(referenceMap), +) : NameResolver { override fun resolveName(element: Any): String { if (element !is LimeNamedElement) { throw GluecodiumExecutionException("Unsupported element type ${element.javaClass.name}") @@ -39,20 +38,11 @@ internal class KotlinVisibilityResolver(private val referenceMap: Map "" - isInternal(element) || isInInternalTypesHierarchy(element) -> "internal " + predicate.isConceptuallyInternal(element) -> "internal " else -> "" } } - private fun isInternal(element: LimeNamedElement): Boolean { - var internal = CommonGeneratorPredicates.isInternal(element, KOTLIN) - if (!internal && element is LimeField) { - internal = CommonGeneratorPredicates.isInternal(element.typeRef.type, KOTLIN) - } - - return internal - } - // Elements which are direct children of 'interface' or 'fun interface' cannot use 'internal' keyword in Kotlin. // 'fun interface' is used for LimeLambda private fun isDirectKotlinInterfaceChild(element: LimeNamedElement): Boolean { @@ -68,24 +58,10 @@ internal class KotlinVisibilityResolver(private val referenceMap: Map Date: Thu, 19 Feb 2026 13:05:58 +0100 Subject: [PATCH 08/23] kotlin: remove unused functions from visibility resolver In the past we had special logic for properties getters/setters when resolving visibility for extensions. The extensions have been removed and the special logic is a dead code. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../generator/kotlin/KotlinVisibilityResolver.kt | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt index 2c997e3353..0bf4b0358b 100644 --- a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt +++ b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinVisibilityResolver.kt @@ -25,7 +25,6 @@ import com.here.gluecodium.model.lime.LimeElement import com.here.gluecodium.model.lime.LimeInterface import com.here.gluecodium.model.lime.LimeLambda import com.here.gluecodium.model.lime.LimeNamedElement -import com.here.gluecodium.model.lime.LimeProperty internal class KotlinVisibilityResolver( private val referenceMap: Map, @@ -49,19 +48,4 @@ internal class KotlinVisibilityResolver( val parent: LimeElement? = referenceMap[element.path.parent.toString()] return parent != null && (parent is LimeInterface || parent is LimeLambda) } - - override fun resolveGetterName(element: Any) = resolveVisibilityForPropertyExtension(element) - - override fun resolveSetterName(element: Any) = resolveVisibilityForPropertyExtension(element) - - // Extension method for static properties require explicit 'internal' keyword even when property is nested - // inside hierarchy of internal nested types. Therefore, we need to iterate over parent types and check if - // any of them is internal. - private fun resolveVisibilityForPropertyExtension(property: Any): String { - return if ((property is LimeProperty) && predicate.isConceptuallyInternal(property)) { - "internal " - } else { - "" - } - } } From 7ee642ae4b614cde80a0cb153ccbd0acdfd6379b Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Wed, 28 Jan 2026 19:43:38 +0100 Subject: [PATCH 09/23] kotlin: resolve JvmSynthetic via synthetic resolver This allows to minimize the logic in mustache files. Moreover, the JvmSynthetic is used for elements defined in internal interfaces and internal lambda. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../generator/kotlin/KotlinGenerator.kt | 5 ++- .../kotlin/KotlinSyntheticResolver.kt | 41 +++++++++++++++++++ .../templates/kotlin/KotlinConstant.mustache | 2 +- .../kotlin/KotlinEnumeration.mustache | 6 +-- .../kotlin/KotlinEnumerator.mustache | 6 +-- .../templates/kotlin/KotlinFunction.mustache | 2 +- .../templates/kotlin/KotlinProperty.mustache | 10 ++--- .../templates/kotlin/KotlinStruct.mustache | 2 +- 8 files changed, 55 insertions(+), 19 deletions(-) create mode 100644 gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinSyntheticResolver.kt diff --git a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinGenerator.kt b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinGenerator.kt index c5ceeccda4..8b6fbfdac7 100644 --- a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinGenerator.kt +++ b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinGenerator.kt @@ -129,6 +129,7 @@ internal class KotlinGenerator : Generator { basePackages = basePackages, ) + val syntheticResolver = KotlinSyntheticResolver(limeModel.referenceMap) val visibilityResolver = KotlinVisibilityResolver(limeModel.referenceMap) val internalApiAnnotation = if (requireOptInAnnotation != null) internalApiAnnotationName!! else null @@ -152,6 +153,7 @@ internal class KotlinGenerator : Generator { it, nameResolver, visibilityResolver, + syntheticResolver, importResolver, importCollector, internalApiAnnotation, @@ -238,6 +240,7 @@ internal class KotlinGenerator : Generator { limeElement: LimeNamedElement, nameResolver: KotlinNameResolver, visibilityResolver: KotlinVisibilityResolver, + syntheticResolver: KotlinSyntheticResolver, importResolver: KotlinImportResolver, importCollector: KotlinImportCollector, internalApiAnnotation: String?, @@ -274,7 +277,7 @@ internal class KotlinGenerator : Generator { "internalApiAnnotationClassPath" to internalApiAnnotationClassPath, ) - val nameResolvers = mapOf("" to nameResolver, "visibility" to visibilityResolver) + val nameResolvers = mapOf("" to nameResolver, "visibility" to visibilityResolver, "synthetic" to syntheticResolver) val mainContent = TemplateEngine.render("kotlin/KotlinFile", templateData, nameResolvers, KotlinGeneratorPredicates.predicates) diff --git a/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinSyntheticResolver.kt b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinSyntheticResolver.kt new file mode 100644 index 0000000000..c470fb3407 --- /dev/null +++ b/gluecodium/src/main/java/com/here/gluecodium/generator/kotlin/KotlinSyntheticResolver.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016-2026 HERE Europe B.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * License-Filename: LICENSE + */ + +package com.here.gluecodium.generator.kotlin + +import com.here.gluecodium.cli.GluecodiumExecutionException +import com.here.gluecodium.generator.common.NameResolver +import com.here.gluecodium.model.lime.LimeElement +import com.here.gluecodium.model.lime.LimeNamedElement + +internal class KotlinSyntheticResolver( + private val referenceMap: Map, + private val predicate: KotlinInternalVisibilityPredicate = KotlinInternalVisibilityPredicate(referenceMap), +) : NameResolver { + override fun resolveName(element: Any): String { + if (element !is LimeNamedElement) { + throw GluecodiumExecutionException("Unsupported element type ${element.javaClass.name}") + } + + return when { + predicate.isConceptuallyInternal(element) -> "@JvmSynthetic " + else -> "" + } + } +} diff --git a/gluecodium/src/main/resources/templates/kotlin/KotlinConstant.mustache b/gluecodium/src/main/resources/templates/kotlin/KotlinConstant.mustache index f0eacf851f..a24f655b00 100644 --- a/gluecodium/src/main/resources/templates/kotlin/KotlinConstant.mustache +++ b/gluecodium/src/main/resources/templates/kotlin/KotlinConstant.mustache @@ -19,5 +19,5 @@ ! !}} {{>kotlin/KotlinDocComment}}{{>kotlin/KotlinAttributes}} -{{#resolveName "visibility"}}{{#unless this.isEmpty}}@JvmSynthetic {{this}}{{/unless}}{{/resolveName}}{{!! +{{resolveName "synthetic"}}{{resolveName "visibility"}}{{!! }}@JvmField final val {{resolveName}}: {{resolveName typeRef}} = {{resolveName value}} \ No newline at end of file diff --git a/gluecodium/src/main/resources/templates/kotlin/KotlinEnumeration.mustache b/gluecodium/src/main/resources/templates/kotlin/KotlinEnumeration.mustache index ede99aa88f..0ce76235ea 100644 --- a/gluecodium/src/main/resources/templates/kotlin/KotlinEnumeration.mustache +++ b/gluecodium/src/main/resources/templates/kotlin/KotlinEnumeration.mustache @@ -24,11 +24,7 @@ {{/unless}}{{/internalApiAnnotation}}internal {{!! }}{{/if}}{{!! }}{{#unless external.kotlin.converter}}{{>kotlin/KotlinTypeVisibility}}{{/unless}}{{!! -}}enum class {{resolveName}}(@JvmField {{!! -}}{{#resolveName "visibility"}}{{!! -}}{{#unless this.isEmpty}}@JvmSynthetic {{/unless}}{{!! -}}{{this}}{{!! -}}{{/resolveName}}val value: Int) { +}}enum class {{resolveName}}(@JvmField {{resolveName "synthetic"}}{{resolveName "visibility"}}val value: Int) { {{joinPartial uniqueEnumerators "kotlin/KotlinEnumerator" ", "}}{{#if uniqueEnumerators}};{{/if}}{{#if aliasEnumerators}} diff --git a/gluecodium/src/main/resources/templates/kotlin/KotlinEnumerator.mustache b/gluecodium/src/main/resources/templates/kotlin/KotlinEnumerator.mustache index 5f18233a53..de1564ca44 100644 --- a/gluecodium/src/main/resources/templates/kotlin/KotlinEnumerator.mustache +++ b/gluecodium/src/main/resources/templates/kotlin/KotlinEnumerator.mustache @@ -21,10 +21,6 @@ {{#if comment}} {{prefixPartial "kotlin/KotlinDocComment" " "}}{{/if}}{{!! }}{{prefixPartial "kotlin/KotlinAttributes" " "}}{{!! -}}{{#if isAlias}} @JvmField {{!! -}}{{#resolveName "visibility"}}{{!! -}}{{#unless this.isEmpty}}@JvmSynthetic {{/unless}}{{!! -}}{{this}}{{!! -}}{{/resolveName}}val {{resolveName}}{{!! +}}{{#if isAlias}} @JvmField {{resolveName "synthetic"}}{{resolveName "visibility"}}val {{resolveName}}{{!! }} = {{resolveName value}}{{/if}}{{!! }}{{#unless isAlias}} {{resolveName}}({{resolveName value}}){{/unless}} \ No newline at end of file diff --git a/gluecodium/src/main/resources/templates/kotlin/KotlinFunction.mustache b/gluecodium/src/main/resources/templates/kotlin/KotlinFunction.mustache index 82f4413db5..496404acf8 100644 --- a/gluecodium/src/main/resources/templates/kotlin/KotlinFunction.mustache +++ b/gluecodium/src/main/resources/templates/kotlin/KotlinFunction.mustache @@ -23,7 +23,7 @@ {{#if isStatic}}@JvmStatic {{/if}}{{!! 1. If function has internal visibility then hide it from java. -}}{{#resolveName "visibility"}}{{#unless this.isEmpty}}@JvmSynthetic {{/unless}}{{/resolveName}}{{!! +}}{{resolveName "synthetic"}}{{!! }}{{#unless override}}{{#unless isInterface}}{{!! diff --git a/gluecodium/src/main/resources/templates/kotlin/KotlinProperty.mustache b/gluecodium/src/main/resources/templates/kotlin/KotlinProperty.mustache index 5305f4b73c..78489c72b5 100644 --- a/gluecodium/src/main/resources/templates/kotlin/KotlinProperty.mustache +++ b/gluecodium/src/main/resources/templates/kotlin/KotlinProperty.mustache @@ -29,8 +29,8 @@ {{#unless isInterface}}{{!! }}{{#unless override}}{{!! -}}{{#resolveName thisProperty "visibility" ""}}{{!! - }}{{#unless this.isEmpty}}@JvmSynthetic @JvmName("{{resolveName thisProperty "" "getter"}}") {{/unless}}{{!! +}}{{resolveName thisProperty "synthetic"}}{{#resolveName thisProperty "visibility" ""}}{{!! + }}{{#unless this.isEmpty}}@JvmName("{{resolveName thisProperty "" "getter"}}") {{/unless}}{{!! }}{{#if this.isEmpty}}{{#ifPredicate thisProperty "propertyGetterRequiresJvmName"}}@JvmName("{{resolveName thisProperty "" "getter"}}") {{/ifPredicate}}{{/if}}{{!! }}{{/resolveName}}{{!! @@ -39,8 +39,8 @@ {{#unless isInterface}}{{!! }}{{#unless override}}{{!! -}}{{#resolveName thisProperty "visibility" ""}}{{!! - }}{{#unless this.isEmpty}}@JvmSynthetic @JvmName("{{resolveName thisProperty "" "setter"}}") {{/unless}}{{!! +}}{{resolveName thisProperty "synthetic"}}{{#resolveName thisProperty "visibility" ""}}{{!! + }}{{#unless this.isEmpty}}@JvmName("{{resolveName thisProperty "" "setter"}}") {{/unless}}{{!! }}{{#if this.isEmpty}}{{#ifPredicate thisProperty "propertySetterRequiresJvmName"}}@JvmName("{{resolveName thisProperty "" "setter"}}") {{/ifPredicate}}{{/if}}{{!! }}{{/resolveName}}{{!! @@ -48,7 +48,7 @@ }}external {{/unless}}set{{/setter}} {{/thisProperty}}{{/set}}{{/unless}} {{#if isCached}}{{#set thisProperty=this}}{{#thisProperty}} - {{#resolveName thisProperty "visibility" ""}}{{#unless this.isEmpty}}@JvmSynthetic {{/unless}}{{/resolveName}}{{!! + {{resolveName thisProperty "synthetic" ""}}{{!! }}get() { if (!is_cached_{{resolveName}}) { cache_{{resolveName}} = {{resolveName this "" "getter"}}_private() diff --git a/gluecodium/src/main/resources/templates/kotlin/KotlinStruct.mustache b/gluecodium/src/main/resources/templates/kotlin/KotlinStruct.mustache index 16bd4ff7a0..6289213aaf 100644 --- a/gluecodium/src/main/resources/templates/kotlin/KotlinStruct.mustache +++ b/gluecodium/src/main/resources/templates/kotlin/KotlinStruct.mustache @@ -32,7 +32,7 @@ {{#fields}} {{prefixPartial "kotlin/KotlinDocComment" " "}}{{prefixPartial "kotlin/KotlinAttributes" " "}}{{!! }} @JvmField {{!! -}}{{#resolveName "visibility"}}{{#unless this.isEmpty}}@JvmSynthetic {{this}}{{/unless}}{{/resolveName}}{{!! +}}{{resolveName "synthetic"}}{{resolveName "visibility"}}{{!! }}{{#unless isImmutable}}var {{/unless}}{{!! }}{{#if isImmutable}}val {{/if}}{{!! }}{{resolveName}}: {{resolveName typeRef}}{{!! From 3764b6d7cca8367702fb883f2eec10467415b646 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Thu, 19 Feb 2026 13:36:18 +0100 Subject: [PATCH 10/23] smoke tests: show usage of JvmSynthetic for functions in interfaces Still, the synthetic annotation is missing for properties. This will be adjusted in the next commit. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../com/example/smoke/OuterInternalInterface.kt | 4 ++-- .../android-kotlin/com/example/smoke/OuterInternalLambda.kt | 4 ++-- .../android-kotlin/com/example/smoke/InternalClassInherits.kt | 3 ++- .../android-kotlin/com/example/smoke/InternalInterface.kt | 4 ++-- .../com/example/smoke/InternalInterfaceParent.kt | 4 ++-- .../output/android-kotlin/com/example/smoke/InternalLambda.kt | 4 ++-- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalInterface.kt b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalInterface.kt index 48313e65b6..d3c5d9f414 100644 --- a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalInterface.kt +++ b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalInterface.kt @@ -16,7 +16,7 @@ internal fun interface OuterInternalInterface { - fun someFunction() : Int + @JvmSynthetic fun someFunction() : Int } @@ -33,7 +33,7 @@ private class OuterInternalInterfaceImpl : NativeBase, OuterInternalInterface { - override external fun someFunction() : Int + @JvmSynthetic override external fun someFunction() : Int diff --git a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalLambda.kt b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalLambda.kt index 2fee46637a..1c452aba53 100644 --- a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalLambda.kt +++ b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalLambda.kt @@ -15,7 +15,7 @@ import com.example.NativeBase internal fun interface OuterInternalLambda { - fun apply(p0: String) : Int + @JvmSynthetic fun apply(p0: String) : Int } /** @@ -30,7 +30,7 @@ private class OuterInternalLambdaImpl : NativeBase, OuterInternalLambda { - override external fun apply(p0: String) : Int + @JvmSynthetic override external fun apply(p0: String) : Int diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassInherits.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassInherits.kt index 01dffd47a4..25a244eebf 100644 --- a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassInherits.kt +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassInherits.kt @@ -5,6 +5,7 @@ @file:JvmName("InternalClassInheritsExtensions") + package com.example.smoke import com.example.NativeBase @@ -28,7 +29,7 @@ internal class InternalClassInherits : NativeBase, InternalInterfaceParent { - override external fun fooBar() : Unit + @JvmSynthetic override external fun fooBar() : Unit override var prop: String external get external set diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterface.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterface.kt index 591cca140d..ad0b6eeae9 100644 --- a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterface.kt +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterface.kt @@ -14,7 +14,7 @@ internal fun interface InternalInterface { - fun fooBar() : Unit + @JvmSynthetic fun fooBar() : Unit companion object { @@ -45,7 +45,7 @@ private class InternalInterfaceImpl : NativeBase, InternalInterface { - override external fun fooBar() : Unit + @JvmSynthetic override external fun fooBar() : Unit diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterfaceParent.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterfaceParent.kt index 14a95b677f..a947124cb8 100644 --- a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterfaceParent.kt +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterfaceParent.kt @@ -14,7 +14,7 @@ internal interface InternalInterfaceParent { - fun fooBar() : Unit + @JvmSynthetic fun fooBar() : Unit var prop: String get @@ -35,7 +35,7 @@ private class InternalInterfaceParentImpl : NativeBase, InternalInterfaceParent - override external fun fooBar() : Unit + @JvmSynthetic override external fun fooBar() : Unit override var prop: String external get diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalLambda.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalLambda.kt index 44c274d169..b641296ab8 100644 --- a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalLambda.kt +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalLambda.kt @@ -13,7 +13,7 @@ import com.example.NativeBase internal fun interface InternalLambda { - fun apply() : Unit + @JvmSynthetic fun apply() : Unit } /** @@ -28,7 +28,7 @@ private class InternalLambdaImpl : NativeBase, InternalLambda { - override external fun apply() : Unit + @JvmSynthetic override external fun apply() : Unit From 1530ebc6922b0f96c086f137b38f5a9cb6d4e704 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Thu, 19 Feb 2026 14:29:35 +0100 Subject: [PATCH 11/23] functional-tests: remove java interoperability cases that fail to compile Due to usage of jvm synthetic for functions of internal interfaces the interoperability cases stopped to compile -- functions in such interfaces are not visible for Java now. ``` > Task :functional:compileReleaseUnitTestJavaWithJavac FAILED /functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:34: error: method does not override or implement a method from a supertype @OverRide ^ /functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:39: error: method does not override or implement a method from a supertype @OverRide ``` Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../here/android/test/VisibilityAttributeTest.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java b/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java index c51ca997e5..02a28d03bd 100644 --- a/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java +++ b/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java @@ -31,18 +31,6 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; -class ImplementationOfSomeInternalInterface implements SomeInternalInterface { - @Override - public int foo() { - return 709; - } - - @Override - public long bar() { - return 121; - } -} - @RunWith(RobolectricTestRunner.class) @Config(sdk = Build.VERSION_CODES.M, application = RobolectricApplication.class) public class VisibilityAttributeTest { @@ -84,7 +72,6 @@ public void valueOfInternalEnumCanBeAccessed() { @Test public void internaInterfaceFromJavaCanBeUsed() { - ImplementationOfSomeInternalInterface someInterfaceImpl = new ImplementationOfSomeInternalInterface(); SomeStructWithInternalMembers someObject = new SomeStructWithInternalMembers(21); } From 369281681aa10e26a4468bf63eb363ab1f2cdbfb Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Thu, 19 Feb 2026 17:16:57 +0100 Subject: [PATCH 12/23] smoke-tests: show missing jvm synthetic for properties in interface The properties should also be synthetic to hide them from Java. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../smoke/annotations/input/Annotations.lime | 2 ++ .../example/smoke/OuterInternalInterface.kt | 29 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/gluecodium/src/test/resources/smoke/annotations/input/Annotations.lime b/gluecodium/src/test/resources/smoke/annotations/input/Annotations.lime index 0c5d7ca0aa..0ea6549845 100644 --- a/gluecodium/src/test/resources/smoke/annotations/input/Annotations.lime +++ b/gluecodium/src/test/resources/smoke/annotations/input/Annotations.lime @@ -32,6 +32,8 @@ class OuterPublicClazz { @Internal interface OuterInternalInterface { fun someFunction(): Int + property someProperty: Int + static property someStaticProperty: Int } @Internal diff --git a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalInterface.kt b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalInterface.kt index d3c5d9f414..afc7023d23 100644 --- a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalInterface.kt +++ b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalInterface.kt @@ -12,13 +12,31 @@ import com.example.MySmokeTestsInternalApi import com.example.NativeBase @MySmokeTestsInternalApi -internal fun interface OuterInternalInterface { +internal interface OuterInternalInterface { @JvmSynthetic fun someFunction() : Int + var someProperty: Int + get + set + + companion object { + + + @JvmStatic @JvmSynthetic fun getSomeStaticProperty() : Int { + return OuterInternalInterfaceImpl.getSomeStaticProperty() + } + + + + @JvmStatic @JvmSynthetic fun setSomeStaticProperty(value: Int) : Unit { + OuterInternalInterfaceImpl.setSomeStaticProperty(value) + } + + } } /** @@ -35,9 +53,18 @@ private class OuterInternalInterfaceImpl : NativeBase, OuterInternalInterface { @JvmSynthetic override external fun someFunction() : Int + override var someProperty: Int + external get + external set + companion object { @JvmStatic private external fun disposeNativeHandle(nativeHandle: Long) + + + @JvmStatic @JvmSynthetic @JvmName("getSomeStaticProperty") internal external fun getSomeStaticProperty() : Int + + @JvmStatic @JvmSynthetic @JvmName("setSomeStaticProperty") internal external fun setSomeStaticProperty(value: Int) : Unit } } From f459cef02cbd915f6b81b831da418aabccdcd4b1 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Wed, 28 Jan 2026 20:14:50 +0100 Subject: [PATCH 13/23] kotlin: annotate properties in internal interfaces as synthetic This hides them from Java. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../resources/templates/kotlin/KotlinProperty.mustache | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gluecodium/src/main/resources/templates/kotlin/KotlinProperty.mustache b/gluecodium/src/main/resources/templates/kotlin/KotlinProperty.mustache index 78489c72b5..3b47913cb1 100644 --- a/gluecodium/src/main/resources/templates/kotlin/KotlinProperty.mustache +++ b/gluecodium/src/main/resources/templates/kotlin/KotlinProperty.mustache @@ -26,20 +26,20 @@ }}{{#unless setter}}val{{/unless}}{{!! }} {{resolveName}}: {{resolveName typeRef}} {{#unless isCached}}{{#set thisProperty=this}}{{#thisProperty}} - {{#unless isInterface}}{{!! + {{resolveName thisProperty "synthetic"}}{{#unless isInterface}}{{!! }}{{#unless override}}{{!! -}}{{resolveName thisProperty "synthetic"}}{{#resolveName thisProperty "visibility" ""}}{{!! +}}{{#resolveName thisProperty "visibility" ""}}{{!! }}{{#unless this.isEmpty}}@JvmName("{{resolveName thisProperty "" "getter"}}") {{/unless}}{{!! }}{{#if this.isEmpty}}{{#ifPredicate thisProperty "propertyGetterRequiresJvmName"}}@JvmName("{{resolveName thisProperty "" "getter"}}") {{/ifPredicate}}{{/if}}{{!! }}{{/resolveName}}{{!! }}{{/unless}}{{!! }}external {{/unless}}get{{#setter}} - {{#unless isInterface}}{{!! + {{resolveName thisProperty "synthetic"}}{{#unless isInterface}}{{!! }}{{#unless override}}{{!! -}}{{resolveName thisProperty "synthetic"}}{{#resolveName thisProperty "visibility" ""}}{{!! +}}{{#resolveName thisProperty "visibility" ""}}{{!! }}{{#unless this.isEmpty}}@JvmName("{{resolveName thisProperty "" "setter"}}") {{/unless}}{{!! }}{{#if this.isEmpty}}{{#ifPredicate thisProperty "propertySetterRequiresJvmName"}}@JvmName("{{resolveName thisProperty "" "setter"}}") {{/ifPredicate}}{{/if}}{{!! }}{{/resolveName}}{{!! From 76d8843ae1a8351f9cc3a6a36c13cd57496d075c Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Thu, 19 Feb 2026 17:50:16 +0100 Subject: [PATCH 14/23] smoke-tests: showcase jvm synthetic for properties in internal interfaces Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../com/example/smoke/OuterInternalInterface.kt | 8 ++++---- .../com/example/smoke/InternalClassInherits.kt | 4 ++-- .../com/example/smoke/InternalInterfaceParent.kt | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalInterface.kt b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalInterface.kt index afc7023d23..f1a32b6fa1 100644 --- a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalInterface.kt +++ b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalInterface.kt @@ -19,8 +19,8 @@ internal interface OuterInternalInterface { @JvmSynthetic fun someFunction() : Int var someProperty: Int - get - set + @JvmSynthetic get + @JvmSynthetic set companion object { @@ -54,8 +54,8 @@ private class OuterInternalInterfaceImpl : NativeBase, OuterInternalInterface { @JvmSynthetic override external fun someFunction() : Int override var someProperty: Int - external get - external set + @JvmSynthetic external get + @JvmSynthetic external set diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassInherits.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassInherits.kt index 25a244eebf..57d574b415 100644 --- a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassInherits.kt +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassInherits.kt @@ -31,8 +31,8 @@ internal class InternalClassInherits : NativeBase, InternalInterfaceParent { @JvmSynthetic override external fun fooBar() : Unit override var prop: String - external get - external set + @JvmSynthetic external get + @JvmSynthetic external set diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterfaceParent.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterfaceParent.kt index a947124cb8..02fb61bf84 100644 --- a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterfaceParent.kt +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalInterfaceParent.kt @@ -17,8 +17,8 @@ internal interface InternalInterfaceParent { @JvmSynthetic fun fooBar() : Unit var prop: String - get - set + @JvmSynthetic get + @JvmSynthetic set } @@ -38,8 +38,8 @@ private class InternalInterfaceParentImpl : NativeBase, InternalInterfaceParent @JvmSynthetic override external fun fooBar() : Unit override var prop: String - external get - external set + @JvmSynthetic external get + @JvmSynthetic external set From cceb7acd150e0119a6cf1cfed13e2564a06d92b0 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Thu, 19 Feb 2026 18:15:46 +0100 Subject: [PATCH 15/23] functional-tests: use internal interface in Kotlin test In Kotlin the internal interface can be used and fields can be overridden. Similar example was implemented in Java, but it did not compile -- we could not override the required methods. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../here/android/test/VisibilityAttributeTest.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/VisibilityAttributeTest.kt b/functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/VisibilityAttributeTest.kt index de8e68a635..ad8ba19efc 100644 --- a/functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/VisibilityAttributeTest.kt +++ b/functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/VisibilityAttributeTest.kt @@ -27,11 +27,22 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -class ImplementationOfSomeInternalInterface: SomeInternalInterface { +class ImplementationOfSomeInternalInterface : SomeInternalInterface { override fun foo(): Int = 709 override fun bar(): Long = 121 } +class ImplementationOfInternalAttributeInterfaceParent : InternalAttributeInterfaceParent { + override fun fooBar() {} + override var prop: String + get() = propHolder + set(value) { + propHolder = value + } + + private var propHolder: String = "Hello world!" +} + @RunWith(RobolectricTestRunner::class) @Config(application = RobolectricApplication::class) class VisibilityAttributeTest { From f3440b6a00cedb0b56d7c70818801b7a68b58379 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Thu, 19 Feb 2026 18:18:13 +0100 Subject: [PATCH 16/23] Adjust CHANGELOG.md Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e39e64c727..258c837517 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 14.1.0 Release date 2026-02-21 * Kotlin: use `@JvmSynthetic` annotation for Kotlin internal elements whenever possible to hide them from Java code. + * Kotlin: ensure that visibility of each element nested in another internal element is explicitly set via 'internal' Kotlin keyword. * Kotlin: allow the usage of 'RequiresOptIn' and 'OptIn' annotations for internal API via CLI parameters. This way error is generated when the code, which is not intended to access the internal API uses it. Three new CLI parameters are available: 'androidrequiresoptinannotation', 'androidoptinannotation' and 'androidinternalapiannotationname'. * Kotlin: for external types usage in Kotlin, use 'internal' keyword for the types, which are generated by Gluecodium as their 'internal' representation. * 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'. From 341364cdd2298d5cd44cdde6d73005c77f247c69 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Thu, 19 Feb 2026 18:43:47 +0100 Subject: [PATCH 17/23] functional-tests: remove outdated tests that do not use internal fields The test case called 'internalFieldOfStructCanBeAccessed' initially used internal fields, but the usage was removed when it stopped to compile. The fields 'someInteger' and 'someLong' are public in public type: ``` struct SomeStructWithInternalMembers { someInteger: Int someLong: Long ``` Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../android/test/VisibilityAttributeTest.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java b/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java index 02a28d03bd..ffd2540e2a 100644 --- a/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java +++ b/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java @@ -40,16 +40,11 @@ public void internalConstructorOfClassCanBeCalled() { SomeClassWithInternalMembers someObject = new SomeClassWithInternalMembers(); } - @Test - public void internalFieldOfStructCanBeAccessed() { - SomeStructWithInternalMembers someObject = new SomeStructWithInternalMembers(21); - assertEquals(21, someObject.someInteger); - assertEquals(444, someObject.someLong); - } - @Test public void internalFreeArgsCtorCanBeCalled() { SomeStructWithInternalFreeArgsCtor someObject = new SomeStructWithInternalFreeArgsCtor(33); + + // Note: 'someObject.someString' is a public field of public structure. assertEquals("Special string", someObject.someString); } @@ -61,6 +56,8 @@ public void internalAllArgsCtorCanBeCalled() { @Test public void internalFieldCtorCanBeCalled() { SomeStructWithInternalFieldConstructor someObject = new SomeStructWithInternalFieldConstructor(77); + + // Note: 'someObject.someString' is a public field of public structure. assertEquals("QAZWSX", someObject.someString); } @@ -70,11 +67,6 @@ public void valueOfInternalEnumCanBeAccessed() { assertEquals(SomeInternalEnum.TWO, someObject); } - @Test - public void internaInterfaceFromJavaCanBeUsed() { - SomeStructWithInternalMembers someObject = new SomeStructWithInternalMembers(21); - } - @Test public void internalExceptionCanBeThrown() { SomethingBadHappenedException exception = assertThrows(SomethingBadHappenedException.class, () -> { From 52e143381bfe727dab273f6d9466170da01897c5 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Sun, 22 Feb 2026 15:15:17 +0100 Subject: [PATCH 18/23] smoke-tests: elements nested in internal class for Kotlin This commit brings new smoke tests related to elements nested in the internal class in Kotlin. The expected result is: "ALL NESTED ELEMENTS ARE TREATED AS INTERNAL". One finding, that will be fixed in upcoming commits: - the 'error' field of internal exception is not annotated as synthetic -> this must be adjusted Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../input/KotlinNestedInternalElements.lime | 193 +++++++ .../smoke/InternalClassWithNestedElements.kt | 513 ++++++++++++++++++ 2 files changed, 706 insertions(+) create mode 100644 gluecodium/src/test/resources/smoke/visibility_attribute/input/KotlinNestedInternalElements.lime create mode 100644 gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassWithNestedElements.kt diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/input/KotlinNestedInternalElements.lime b/gluecodium/src/test/resources/smoke/visibility_attribute/input/KotlinNestedInternalElements.lime new file mode 100644 index 0000000000..888966d6c4 --- /dev/null +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/input/KotlinNestedInternalElements.lime @@ -0,0 +1,193 @@ +# Copyright (C) 2016-2026 HERE Europe B.V. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# License-Filename: LICENSE + +package smoke + +############################################################################################# +# The test cases in this file are used to check if elements nested in any internal type are # +# explicitly annotated as internal in Kotlin. # +############################################################################################# + +// This is a class, which contains multiple nested elements. +// It has internal visibility because 'Internal' is used. +// Each element inside should be treated as internal. +@Skip(Dart, Java, Swift) +@Internal +class InternalClassWithNestedElements { + // This is a constant nested in internal class. + // It should be generated as internal and synthetic. + const SOME_CONSTANT: Int = 2026 + + // This is a constructor in internal class. It should be explicitly annotated as internal. + // Unfortunately, it cannot use synthetic, because constructor is not allowed target. + constructor some_constructor(x: Int, y: Int) + + // This is a function in internal class. It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + fun some_function(): Double + + // This is a static function in internal class. It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + static fun some_static_function(): String + + // This is a property in internal class. It should be explicitly annotated as internal. + // And use synthetic for get/set functions to hide them from Java. + property some_property: Long + + // This is a static property in internal class. + // It is represented as static get/set functions pair. + // They should be explicitly annotated as internal and use JvmStatic to hide them from Java. + static property some_static_property: Long + + // This is a class nested inside internal class. + // It should be generated with explicit 'internal' keyword. + // Each element nested inside this class should also have explicit internal visibility. + class NestedClassLevelOne { + // This is a constructor of class nested in internal class. + // It should be explicitly annotated as internal. + // Unfortunately, it cannot use synthetic, because constructor is not allowed target. + constructor some_nested_constructor(x: Int, y: Int) + + // This is a function in class nested inside internal class. + // It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + fun some_nested_function(): Double + + // This is a static function in class nested in internal class. + // It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + static fun some_nested_static_function(): String + + // This is a property in class nested in internal class. + // It should be explicitly annotated as internal. + // And use synthetic for get/set functions to hide them from Java. + property some_nested_property: Long + + // This is a static property in class nested in internal class. + // It is represented as static get/set functions pair. + // They should be explicitly annotated as internal and use JvmStatic to hide them from Java. + static property some_nested_static_property: Long + + // This is a class nested inside class, that is nested in internal class. + // It should be generated with explicit 'internal' keyword. + // Each element nested inside this class should also have explicit internal visibility. + class NestedClassLevelTwo { + // This is a constructor of class, that is nested in class, that is nested in internal class. + // It should be explicitly annotated as internal. + // Unfortunately, it cannot use synthetic, because constructor is not allowed target. + constructor double_nested_constructor(x: Int, y: Int) + + // This is a function in class, that is nested in class, that is nested in internal class. + // It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + fun double_nested_function(): Double + + // This is a static function in class, that is nested in class, that is nested in internal class. + // It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + static fun double_nested_static_function(): String + + // This is a property in class, that is nested in class, that is nested in internal class. + // It should be explicitly annotated as internal. + // And use synthetic for get/set functions to hide them from Java. + property double_nested_property: Long + + // This is a static property in class, that is nested in class, that is nested in internal class. + // It is represented as static get/set functions pair. + // They should be explicitly annotated as internal and use JvmStatic to hide them from Java. + static property double_nested_static_property: Long + } + } + + // This is an enumeration nested in internal class. + // It should be annotated as internal. + // Its value field should be synthetic to disallow accessing it from Java. + enum NestedEnum { + OPTION_1, + OPTION_2 + } + + // This is an exception nested in internal class. + // It should be annotated as internal. + // Its error field should be internal and synthetic to disallow accessing it from Java. + exception NestedException(NestedEnum) + + // This is a lambda nested in internal class. + // It should be annotated as internal. + // Its 'call()' function should be synthetic to disallow accessing it from Java. + lambda NestedLambda = () -> Void + + // This is a structure nested in internal class. + // It should be annotated as internal. + // Its fields should be internal and synthetic. + // Its functions should be internal and synthetic. + // Its constructors should be internal (sadly, they cannot be synthetic). + struct NestedStruct { + // This is a field of structure, which is nested in internal class. + // It should use 'internal' and synthetic. + firstField: Int + + // This is a field of structure, which is nested in internal class. + // It should use 'internal' and synthetic. + secondField: String + + // This is a field constructor of structure. The structure is nested in internal class. + // It should be explicitly internal. Sadly, it cannot be synthetic. + field constructor(firstField, secondField) + + // This is a constructor of structure, and the structure is nested in internal class. + // It should be explicitly annotated as internal. + // Unfortunately, it cannot use synthetic, because constructor is not allowed target. + constructor constructor_of_struct(x: Int, y: Int) + + // This is a function in structure nested inside internal class. + // It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + fun nested_struct_function(): Double + + // This is a static function in structure nested in internal class. + // It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + static fun nested_struct_static_function(): String + } + + // This is an interface nested in internal class. + // It should be annotated as internal. + // Its fields should be synthetic --> sadly, Kotlin does not allow 'internal' keyword in interfaces. + // But from Kotlin language POV elements are not accessible, because interface itself is internal. + // The elements should not be accessible in Java because of synthetic attribute. + interface NestedInterface { + // This is a function in interface nested inside internal class. + // It must use synthetic annotation. + // Sadly, it cannot use 'internal' keyword. + fun nested_interface_function(): Double + + // This is a static function in interface nested in internal class. + // Sadly, it cannot use 'internal' keyword. + static fun nested_interface_static_function(): String + + // This is a property in interface nested in internal class. + // It must use synthetic annotation for get/set functions. + // Sadly, it cannot use 'internal' keyword. + property nested_interface_property: Long + + // This is a static property in class nested in internal class. + // It must use synthetic annotation for get/set functions. + // Sadly, it cannot use 'internal' keyword. + static property nested_interface_static_property: Long + } +} diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassWithNestedElements.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassWithNestedElements.kt new file mode 100644 index 0000000000..e5280e5e2d --- /dev/null +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassWithNestedElements.kt @@ -0,0 +1,513 @@ +/* + + * + */ + +@file:JvmName("InternalClassWithNestedElementsExtensions") + + +package com.example.smoke + +import com.example.NativeBase + +/** + * This is a class, which contains multiple nested elements. + * It has internal visibility because 'Internal' is used. + * Each element inside should be treated as internal. + */ +internal class InternalClassWithNestedElements : NativeBase { + + /** + * This is an enumeration nested in internal class. + * It should be annotated as internal. + * Its value field should be synthetic to disallow accessing it from Java. + */ + internal enum class NestedEnum(@JvmField @JvmSynthetic internal val value: Int) { + OPTION_1(0), + OPTION_2(1); + } + /** + * This is an exception nested in internal class. + * It should be annotated as internal. + * Its error field should be internal and synthetic to disallow accessing it from Java. + */ + internal class NestedExceptionException(@JvmField val error: InternalClassWithNestedElements.NestedEnum) : Exception(error.toString()) + + + /** + * This is a structure nested in internal class. + * It should be annotated as internal. + * Its fields should be internal and synthetic. + * Its functions should be internal and synthetic. + * Its constructors should be internal (sadly, they cannot be synthetic). + */ + internal class NestedStruct { + /** + * This is a field of structure, which is nested in internal class. + * It should use 'internal' and synthetic. + */ + @JvmField @JvmSynthetic internal var firstField: Int + /** + * This is a field of structure, which is nested in internal class. + * It should use 'internal' and synthetic. + */ + @JvmField @JvmSynthetic internal var secondField: String + + + /** + * This is a constructor of structure, and the structure is nested in internal class. + * It should be explicitly annotated as internal. + * Unfortunately, it cannot use synthetic, because constructor is not allowed target. + * @param x + * @param y + */ + + + internal constructor(x: Int, y: Int) { + val _other = constructorOfStruct(x, y) + this.firstField = _other.firstField + this.secondField = _other.secondField + } + + /** + * This is a field constructor of structure. The structure is nested in internal class. + * It should be explicitly internal. Sadly, it cannot be synthetic. + * @param firstField This is a field of structure, which is nested in internal class. + * It should use 'internal' and synthetic. + * @param secondField This is a field of structure, which is nested in internal class. + * It should use 'internal' and synthetic. + */ + + internal constructor(firstField: Int, secondField: String) { + this.firstField = firstField + this.secondField = secondField + } + + + + /** + * This is a function in structure nested inside internal class. + * It should be explicitly annotated as internal. + * And use synthetic annotation to hide it from Java. + * @return + */ + + @JvmSynthetic @JvmName("nestedStructFunction") internal external fun nestedStructFunction() : Double + + + companion object { + + @JvmStatic @JvmSynthetic @JvmName("constructorOfStruct") private external fun constructorOfStruct(x: Int, y: Int) : NestedStruct + /** + * This is a static function in structure nested in internal class. + * It should be explicitly annotated as internal. + * And use synthetic annotation to hide it from Java. + * @return + */ + + @JvmStatic @JvmSynthetic @JvmName("nestedStructStaticFunction") internal external fun nestedStructStaticFunction() : String + } + } + + /** + * This is a class nested inside internal class. + * It should be generated with explicit 'internal' keyword. + * Each element nested inside this class should also have explicit internal visibility. + */ + internal class NestedClassLevelOne : NativeBase { + + /** + * This is a class nested inside class, that is nested in internal class. + * It should be generated with explicit 'internal' keyword. + * Each element nested inside this class should also have explicit internal visibility. + */ + internal class NestedClassLevelTwo : NativeBase { + + + /** + * This is a constructor of class, that is nested in class, that is nested in internal class. + * It should be explicitly annotated as internal. + * Unfortunately, it cannot use synthetic, because constructor is not allowed target. + * @param x + * @param y + */ + + internal constructor(x: Int, y: Int) : this(doubleNestedConstructor(x, y), null as Any?) { + cacheThisInstance(); + } + + /** + * For internal use only. + * @suppress + * @param nativeHandle The handle to resources on C++ side. + * @param tag Tag used by callers to avoid overload resolution problems. + */ + protected constructor(nativeHandle: Long, @Suppress("UNUSED_PARAMETER") tag: Any?) + : super(nativeHandle, { disposeNativeHandle(it) }) {} + + private external fun cacheThisInstance() + + + /** + * This is a function in class, that is nested in class, that is nested in internal class. + * It should be explicitly annotated as internal. + * And use synthetic annotation to hide it from Java. + * @return + */ + + @JvmSynthetic @JvmName("doubleNestedFunction") internal external fun doubleNestedFunction() : Double + + /** + * This is a property in class, that is nested in class, that is nested in internal class. + * It should be explicitly annotated as internal. + * And use synthetic for get/set functions to hide them from Java. + */ + internal var doubleNestedProperty: Long + @JvmSynthetic @JvmName("getDoubleNestedProperty") external get + @JvmSynthetic @JvmName("setDoubleNestedProperty") external set + + + + + companion object { + @JvmStatic private external fun disposeNativeHandle(nativeHandle: Long) + + @JvmStatic @JvmSynthetic @JvmName("doubleNestedConstructor") private external fun doubleNestedConstructor(x: Int, y: Int) : Long + /** + * This is a static function in class, that is nested in class, that is nested in internal class. + * It should be explicitly annotated as internal. + * And use synthetic annotation to hide it from Java. + * @return + */ + + @JvmStatic @JvmSynthetic @JvmName("doubleNestedStaticFunction") internal external fun doubleNestedStaticFunction() : String + /** + * + * @return This is a static property in class, that is nested in class, that is nested in internal class. + * It is represented as static get/set functions pair. + * They should be explicitly annotated as internal and use JvmStatic to hide them from Java. + */ + + @JvmStatic @JvmSynthetic @JvmName("getDoubleNestedStaticProperty") internal external fun getDoubleNestedStaticProperty() : Long/** + * + * @param value This is a static property in class, that is nested in class, that is nested in internal class. + * It is represented as static get/set functions pair. + * They should be explicitly annotated as internal and use JvmStatic to hide them from Java. + */ + + @JvmStatic @JvmSynthetic @JvmName("setDoubleNestedStaticProperty") internal external fun setDoubleNestedStaticProperty(value: Long) : Unit + } + } + + + /** + * This is a constructor of class nested in internal class. + * It should be explicitly annotated as internal. + * Unfortunately, it cannot use synthetic, because constructor is not allowed target. + * @param x + * @param y + */ + + internal constructor(x: Int, y: Int) : this(someNestedConstructor(x, y), null as Any?) { + cacheThisInstance(); + } + + /** + * For internal use only. + * @suppress + * @param nativeHandle The handle to resources on C++ side. + * @param tag Tag used by callers to avoid overload resolution problems. + */ + protected constructor(nativeHandle: Long, @Suppress("UNUSED_PARAMETER") tag: Any?) + : super(nativeHandle, { disposeNativeHandle(it) }) {} + + private external fun cacheThisInstance() + + + /** + * This is a function in class nested inside internal class. + * It should be explicitly annotated as internal. + * And use synthetic annotation to hide it from Java. + * @return + */ + + @JvmSynthetic @JvmName("someNestedFunction") internal external fun someNestedFunction() : Double + + /** + * This is a property in class nested in internal class. + * It should be explicitly annotated as internal. + * And use synthetic for get/set functions to hide them from Java. + */ + internal var someNestedProperty: Long + @JvmSynthetic @JvmName("getSomeNestedProperty") external get + @JvmSynthetic @JvmName("setSomeNestedProperty") external set + + + + + companion object { + @JvmStatic private external fun disposeNativeHandle(nativeHandle: Long) + + @JvmStatic @JvmSynthetic @JvmName("someNestedConstructor") private external fun someNestedConstructor(x: Int, y: Int) : Long + /** + * This is a static function in class nested in internal class. + * It should be explicitly annotated as internal. + * And use synthetic annotation to hide it from Java. + * @return + */ + + @JvmStatic @JvmSynthetic @JvmName("someNestedStaticFunction") internal external fun someNestedStaticFunction() : String + /** + * + * @return This is a static property in class nested in internal class. + * It is represented as static get/set functions pair. + * They should be explicitly annotated as internal and use JvmStatic to hide them from Java. + */ + + @JvmStatic @JvmSynthetic @JvmName("getSomeNestedStaticProperty") internal external fun getSomeNestedStaticProperty() : Long/** + * + * @param value This is a static property in class nested in internal class. + * It is represented as static get/set functions pair. + * They should be explicitly annotated as internal and use JvmStatic to hide them from Java. + */ + + @JvmStatic @JvmSynthetic @JvmName("setSomeNestedStaticProperty") internal external fun setSomeNestedStaticProperty(value: Long) : Unit + } + } + + /** + * This is an interface nested in internal class. + * It should be annotated as internal. + * Its fields should be synthetic --> sadly, Kotlin does not allow 'internal' keyword in interfaces. + * But from Kotlin language POV elements are not accessible, because interface itself is internal. + * The elements should not be accessible in Java because of synthetic attribute. + */ + internal interface NestedInterface { + + /** + * This is a function in interface nested inside internal class. + * It must use synthetic annotation. + * Sadly, it cannot use 'internal' keyword. + * @return + */ + + @JvmSynthetic fun nestedInterfaceFunction() : Double + + /** + * This is a property in interface nested in internal class. + * It must use synthetic annotation for get/set functions. + * Sadly, it cannot use 'internal' keyword. + */ + var nestedInterfaceProperty: Long + @JvmSynthetic get + @JvmSynthetic set + + + companion object { + /** + * This is a static function in interface nested in internal class. + * Sadly, it cannot use 'internal' keyword. + * @return + */ + + @JvmStatic @JvmSynthetic fun nestedInterfaceStaticFunction() : String { + return NestedInterfaceImpl.nestedInterfaceStaticFunction() + } + + /** + * + * @return This is a static property in class nested in internal class. + * It must use synthetic annotation for get/set functions. + * Sadly, it cannot use 'internal' keyword. + */ + + @JvmStatic @JvmSynthetic fun getNestedInterfaceStaticProperty() : Long { + return NestedInterfaceImpl.getNestedInterfaceStaticProperty() + } + + /** + * + * @param value This is a static property in class nested in internal class. + * It must use synthetic annotation for get/set functions. + * Sadly, it cannot use 'internal' keyword. + */ + + @JvmStatic @JvmSynthetic fun setNestedInterfaceStaticProperty(value: Long) : Unit { + NestedInterfaceImpl.setNestedInterfaceStaticProperty(value) + } + + } + } + + /** + * @suppress + * + * This class is used to represent C++ implementations of the interface or lambda in Kotlin. + * It is instantiated by JNI and should not be used by the end users. + */ + private class NestedInterfaceImpl : NativeBase, NestedInterface { + protected constructor(nativeHandle: Long, @Suppress("UNUSED_PARAMETER") tag: Any?) + : super(nativeHandle, { disposeNativeHandle(it) }) {} + + /** + * This is a function in interface nested inside internal class. + * It must use synthetic annotation. + * Sadly, it cannot use 'internal' keyword. + * @return + */ + + @JvmSynthetic override external fun nestedInterfaceFunction() : Double + + /** + * This is a property in interface nested in internal class. + * It must use synthetic annotation for get/set functions. + * Sadly, it cannot use 'internal' keyword. + */ + override var nestedInterfaceProperty: Long + @JvmSynthetic external get + @JvmSynthetic external set + + + + companion object { + @JvmStatic private external fun disposeNativeHandle(nativeHandle: Long) + /** + * This is a static function in interface nested in internal class. + * Sadly, it cannot use 'internal' keyword. + * @return + */ + + @JvmStatic @JvmSynthetic external fun nestedInterfaceStaticFunction() : String + /** + * + * @return This is a static property in class nested in internal class. + * It must use synthetic annotation for get/set functions. + * Sadly, it cannot use 'internal' keyword. + */ + + @JvmStatic @JvmSynthetic @JvmName("getNestedInterfaceStaticProperty") internal external fun getNestedInterfaceStaticProperty() : Long/** + * + * @param value This is a static property in class nested in internal class. + * It must use synthetic annotation for get/set functions. + * Sadly, it cannot use 'internal' keyword. + */ + + @JvmStatic @JvmSynthetic @JvmName("setNestedInterfaceStaticProperty") internal external fun setNestedInterfaceStaticProperty(value: Long) : Unit + } + } + /** + * This is a lambda nested in internal class. + * It should be annotated as internal. + * Its 'call()' function should be synthetic to disallow accessing it from Java. + */ + internal fun interface NestedLambda { + /** + * This is a lambda nested in internal class. + * It should be annotated as internal. + * Its 'call()' function should be synthetic to disallow accessing it from Java. + */ + + @JvmSynthetic fun apply() : Unit + } + + /** + * @suppress + * + * This class is used to represent C++ implementations of the interface or lambda in Kotlin. + * It is instantiated by JNI and should not be used by the end users. + */ + private class NestedLambdaImpl : NativeBase, NestedLambda { + protected constructor(nativeHandle: Long, @Suppress("UNUSED_PARAMETER") tag: Any?) + : super(nativeHandle, { disposeNativeHandle(it) }) {} + + /** + * This is a lambda nested in internal class. + * It should be annotated as internal. + * Its 'call()' function should be synthetic to disallow accessing it from Java. + */ + + @JvmSynthetic override external fun apply() : Unit + + + + companion object { + @JvmStatic private external fun disposeNativeHandle(nativeHandle: Long) + } + } + + /** + * This is a constructor in internal class. It should be explicitly annotated as internal. + * Unfortunately, it cannot use synthetic, because constructor is not allowed target. + * @param x + * @param y + */ + + internal constructor(x: Int, y: Int) : this(someConstructor(x, y), null as Any?) { + cacheThisInstance(); + } + + /** + * For internal use only. + * @suppress + * @param nativeHandle The handle to resources on C++ side. + * @param tag Tag used by callers to avoid overload resolution problems. + */ + protected constructor(nativeHandle: Long, @Suppress("UNUSED_PARAMETER") tag: Any?) + : super(nativeHandle, { disposeNativeHandle(it) }) {} + + private external fun cacheThisInstance() + + + /** + * This is a function in internal class. It should be explicitly annotated as internal. + * And use synthetic annotation to hide it from Java. + * @return + */ + + @JvmSynthetic @JvmName("someFunction") internal external fun someFunction() : Double + + /** + * This is a property in internal class. It should be explicitly annotated as internal. + * And use synthetic for get/set functions to hide them from Java. + */ + internal var someProperty: Long + @JvmSynthetic @JvmName("getSomeProperty") external get + @JvmSynthetic @JvmName("setSomeProperty") external set + + + + + companion object { + /** + * This is a constant nested in internal class. + * It should be generated as internal and synthetic. + */ + @JvmSynthetic internal @JvmField final val SOME_CONSTANT: Int = 2026 + @JvmStatic private external fun disposeNativeHandle(nativeHandle: Long) + + @JvmStatic @JvmSynthetic @JvmName("someConstructor") private external fun someConstructor(x: Int, y: Int) : Long + /** + * This is a static function in internal class. It should be explicitly annotated as internal. + * And use synthetic annotation to hide it from Java. + * @return + */ + + @JvmStatic @JvmSynthetic @JvmName("someStaticFunction") internal external fun someStaticFunction() : String + /** + * + * @return This is a static property in internal class. + * It is represented as static get/set functions pair. + * They should be explicitly annotated as internal and use JvmStatic to hide them from Java. + */ + + @JvmStatic @JvmSynthetic @JvmName("getSomeStaticProperty") internal external fun getSomeStaticProperty() : Long/** + * + * @param value This is a static property in internal class. + * It is represented as static get/set functions pair. + * They should be explicitly annotated as internal and use JvmStatic to hide them from Java. + */ + + @JvmStatic @JvmSynthetic @JvmName("setSomeStaticProperty") internal external fun setSomeStaticProperty(value: Long) : Unit + } +} + From 9c64eab8d73859593ba37b9c251ccd4dc27ef58c Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Sun, 22 Feb 2026 19:51:14 +0100 Subject: [PATCH 19/23] functional-tests: elements nested in internal class for Kotlin This commit brings functional tests used to validate if nested elements (including multi-level nesting) in internal class can be accessed and there are no runtime problems with symbols. This commit is implementation of tests similar to the smoke tests introduced in the previous commit. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- functional-tests/functional/CMakeLists.txt | 2 + .../test/NestedInternalElementsTest.kt | 198 ++++++++++++++ .../input/lime/NestedInternalElements.lime | 206 +++++++++++++++ .../input/src/cpp/NestedInternalElements.cpp | 249 ++++++++++++++++++ 4 files changed, 655 insertions(+) create mode 100644 functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/NestedInternalElementsTest.kt create mode 100644 functional-tests/functional/input/lime/NestedInternalElements.lime create mode 100644 functional-tests/functional/input/src/cpp/NestedInternalElements.cpp diff --git a/functional-tests/functional/CMakeLists.txt b/functional-tests/functional/CMakeLists.txt index 987eabac5c..89cd42b11d 100644 --- a/functional-tests/functional/CMakeLists.txt +++ b/functional-tests/functional/CMakeLists.txt @@ -456,9 +456,11 @@ feature(Visibility android android-kotlin swift dart SOURCES input/lime/VisibilityPlatform.lime input/lime/VisibilityPlatformReverse.lime input/lime/InternalFields.lime + input/lime/NestedInternalElements.lime input/src/cpp/VisibilityAttribute.cpp input/src/cpp/VisibilityInternal.cpp + input/src/cpp/NestedInternalElements.cpp ) feature(SkipAttribute cpp android android-kotlin swift dart SOURCES diff --git a/functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/NestedInternalElementsTest.kt b/functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/NestedInternalElementsTest.kt new file mode 100644 index 0000000000..3b999257fa --- /dev/null +++ b/functional-tests/functional/android-kotlin/src/test/kotlin/com/here/android/test/NestedInternalElementsTest.kt @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2016-2026 HERE Europe B.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * License-Filename: LICENSE + */ +@file:OptIn(com.here.android.lorem.ipsum.FunctionalTestsInternalAPI::class) + +package com.here.android.test + +import com.here.android.RobolectricApplication +import org.junit.Assert.assertEquals +import org.junit.Assert.assertThrows +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(application = RobolectricApplication::class) +class NestedInternalElementsTest { + ////////// 1. Tests related to direct members of 'InternalClassWithNestedElements'. + @Test + fun constantOfInternalClassCanBeAccessed() { + assertEquals(2026, InternalClassWithNestedElements.SOME_CONSTANT) + } + + @Test + fun functionOfInternalClassCanBeAccessed() { + val internalClassObj = InternalClassWithNestedElements(20, 26) + assertEquals(46, internalClassObj.someFunction()) + } + + @Test + fun staticFunctionOfInternalClassCanBeAccessed() { + assertEquals("SOME_STATIC_FUNCTION", InternalClassWithNestedElements.someStaticFunction()) + } + + @Test + fun propertyOfInternalClassCanBeAccessed() { + val internalClassObj = InternalClassWithNestedElements(20, 26) + assertEquals(2202, internalClassObj.someProperty) + + internalClassObj.someProperty = 77 + assertEquals(77, internalClassObj.someProperty) + } + + @Test + fun staticPropertyOfInternalClassCanBeAccessed() { + assertEquals(11, InternalClassWithNestedElements.getSomeStaticProperty()) + + InternalClassWithNestedElements.setSomeStaticProperty(37) + assertEquals(37, InternalClassWithNestedElements.getSomeStaticProperty()) + } + + ////////// 2. Tests related to direct members of 'InternalClassWithNestedElements.NestedClassLevelOne'. + @Test + fun functionOfClassNestedInInternalClassCanBeAccessed() { + val obj = InternalClassWithNestedElements.NestedClassLevelOne(2, 3) + assertEquals(6, obj.someNestedFunction()) + } + + @Test + fun staticFunctionOfClassNestedInInternalClassCanBeAccessed() { + assertEquals("LEVEL_1_NESTING", InternalClassWithNestedElements.NestedClassLevelOne.someNestedStaticFunction()) + } + + @Test + fun propertyOfClassNestedInInternalClassCanBeAccessed() { + val obj = InternalClassWithNestedElements.NestedClassLevelOne(2, 3) + assertEquals(333, obj.someNestedProperty) + + obj.someNestedProperty = 11 + assertEquals(11, obj.someNestedProperty) + } + + @Test + fun staticPropertyOfClassNestedInInternalClassCanBeAccessed() { + assertEquals(77, InternalClassWithNestedElements.NestedClassLevelOne.getSomeNestedStaticProperty()) + + InternalClassWithNestedElements.NestedClassLevelOne.setSomeNestedStaticProperty(37) + assertEquals(37, InternalClassWithNestedElements.NestedClassLevelOne.getSomeNestedStaticProperty()) + } + + ////////// 3. Tests related to direct members of 'InternalClassWithNestedElements.NestedClassLevelOne.NestedClassLevelTwo'. + @Test + fun functionOfClassNestedInClassNestedInInternalClassCanBeAccessed() { + val obj = InternalClassWithNestedElements.NestedClassLevelOne.NestedClassLevelTwo(2, 33) + assertEquals(66, obj.doubleNestedFunction()) + } + + @Test + fun staticFunctionOfClassNestedInClassNestedInInternalClassCanBeAccessed() { + assertEquals("LEVEL_2_NESTING", InternalClassWithNestedElements.NestedClassLevelOne.NestedClassLevelTwo.doubleNestedStaticFunction()) + } + + @Test + fun propertyOfClassNestedInClassNestedInInternalClassCanBeAccessed() { + val obj = InternalClassWithNestedElements.NestedClassLevelOne.NestedClassLevelTwo(2, 33) + assertEquals(17, obj.doubleNestedProperty) + + obj.doubleNestedProperty = 13 + assertEquals(13, obj.doubleNestedProperty) + } + + @Test + fun staticPropertyOfClassNestedInClassNestedInInternalClassCanBeAccessed() { + assertEquals(123, InternalClassWithNestedElements.NestedClassLevelOne.NestedClassLevelTwo.getDoubleNestedStaticProperty()) + + InternalClassWithNestedElements.NestedClassLevelOne.NestedClassLevelTwo.setDoubleNestedStaticProperty(9) + assertEquals(9, InternalClassWithNestedElements.NestedClassLevelOne.NestedClassLevelTwo.getDoubleNestedStaticProperty()) + } + + ////////// 4. Tests related to nested enum, nested exception and nested lambda. + @Test + fun enumeratorsOfEnumNestedInInternalClassCanBeAccessed() { + val someObject = InternalClassWithNestedElements.NestedEnum.OPTION_1 + assertEquals(1, someObject.value) + + assertEquals(InternalClassWithNestedElements.NestedEnum.OPTION_1, someObject) + } + + @Test + fun exceptionNestedInInternalClassCanBeAccessed() { + val exception = assertThrows(InternalClassWithNestedElements.NestedException::class.java) { + InternalClassWithNestedElements.throwNestedException() + } + assertEquals(exception.error, InternalClassWithNestedElements.NestedEnum.OPTION_1) + } + + @Test + fun lambdaNestedInInternalClassCanBeAccessedWhenObtainedFromCpp() { + val someLambda = InternalClassWithNestedElements.produceNestedLambda() + assertEquals("CONTENT_FROM_NESTED_LAMBDA_FROM_CPP: 'Kotlin 2026'", someLambda.apply("Kotlin ", 2026)) + } + + ////////// 5. Tests related to direct members of 'InternalClassWithNestedElements.NestedStruct'. + @Test + fun fieldsOfStructNestedInInternalClassCanBeAccessed() { + val obj = InternalClassWithNestedElements.NestedStruct(10, 2026) + assertEquals(10, obj.firstField) + assertEquals("2026", obj.secondField) + } + + @Test + fun fieldConstructorOfStructNestedInInternalClassCanBeAccessed() { + val obj = InternalClassWithNestedElements.NestedStruct(123, "NESTED_STRUCT") + assertEquals(123, obj.firstField) + assertEquals("NESTED_STRUCT", obj.secondField) + } + + @Test + fun functionOfStructNestedInInternalClassCanBeAccessed() { + val obj = InternalClassWithNestedElements.NestedStruct(10, 2026) + assertEquals(20, obj.nestedStructFunction()) + } + + @Test + fun staticFunctionOfStructNestedInInternalClassCanBeAccessed() { + assertEquals("NESTED_STRUCT_STATIC_FUN", InternalClassWithNestedElements.NestedStruct.nestedStructStaticFunction()) + } + + ////////// 6. Tests related to direct members of 'InternalClassWithNestedElements.NestedInterface'. + @Test + fun staticFunctionOfInterfaceNestedInInternalClassCanBeAccessed() { + assertEquals("NESTED_ITF_STATIC_FUN", InternalClassWithNestedElements.NestedInterface.nestedInterfaceStaticFunction()) + } + + @Test + fun staticPropertyOfInterfaceNestedInInternalClassCanBeAccessed() { + assertEquals(572, InternalClassWithNestedElements.NestedInterface.getNestedInterfaceStaticProperty()) + + InternalClassWithNestedElements.NestedInterface.setNestedInterfaceStaticProperty(37) + assertEquals(37, InternalClassWithNestedElements.NestedInterface.getNestedInterfaceStaticProperty()) + } + + @Test + fun interfaceNestedInInternalClassCanBeAccessedWhenObtainedFromCpp() { + val itfImpl = InternalClassWithNestedElements.produceNestedInterfaceImplementation() + assertEquals(909, itfImpl.nestedInterfaceFunction()) + assertEquals(77, itfImpl.nestedInterfaceProperty) + + itfImpl.nestedInterfaceProperty = 123 + assertEquals(123, itfImpl.nestedInterfaceProperty) + } +} \ No newline at end of file diff --git a/functional-tests/functional/input/lime/NestedInternalElements.lime b/functional-tests/functional/input/lime/NestedInternalElements.lime new file mode 100644 index 0000000000..f85fe035ca --- /dev/null +++ b/functional-tests/functional/input/lime/NestedInternalElements.lime @@ -0,0 +1,206 @@ +# Copyright (C) 2016-2026 HERE Europe B.V. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# License-Filename: LICENSE + +package test + +############################################################################################### +# The test cases in this file are used to check the access to elements nested in internal # +# type. They are explicitly treated as internal in Kotlin. Multi-level nesting is tested. # +############################################################################################### + +// This is a class, which contains multiple nested elements. +// It has internal visibility because 'Internal' is used. +// Each element inside should be treated as internal. +@Skip(Dart, Swift) +@Internal +class InternalClassWithNestedElements { + // This is a constant nested in internal class. + // It should be generated as internal and synthetic. + const SOME_CONSTANT: Int = 2026 + + // This is a constructor in internal class. It should be explicitly annotated as internal. + // Unfortunately, it cannot use synthetic, because constructor is not allowed target. + constructor some_constructor(x: Int, y: Int) + + // This is a function in internal class. It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + fun some_function(): Int + + // This is a static function in internal class. It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + static fun some_static_function(): String + + // This is a property in internal class. It should be explicitly annotated as internal. + // And use synthetic for get/set functions to hide them from Java. + property some_property: Long + + // This is a static property in internal class. + // It is represented as static get/set functions pair. + // They should be explicitly annotated as internal and use JvmStatic to hide them from Java. + static property some_static_property: Long + + // This is a static function used solely to throw the nested exception. + // It should be generated as internal and synthetic. + static fun throw_nested_exception() throws Nested + + // This is a static function used solely to created the nested lambda on C++ site. + // It should be generated as internal and synthetic. + static fun produce_nested_lambda(): NestedLambda + + // This is a static function used solely to created the nested interface impl on C++ site. + // It should be generated as internal and synthetic. + static fun produce_nested_interface_implementation(): NestedInterface + + // This is a class nested inside internal class. + // It should be generated with explicit 'internal' keyword. + // Each element nested inside this class should also have explicit internal visibility. + class NestedClassLevelOne { + // This is a constructor of class nested in internal class. + // It should be explicitly annotated as internal. + // Unfortunately, it cannot use synthetic, because constructor is not allowed target. + constructor some_nested_constructor(x: Int, y: Int) + + // This is a function in class nested inside internal class. + // It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + fun some_nested_function(): Int + + // This is a static function in class nested in internal class. + // It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + static fun some_nested_static_function(): String + + // This is a property in class nested in internal class. + // It should be explicitly annotated as internal. + // And use synthetic for get/set functions to hide them from Java. + property some_nested_property: Long + + // This is a static property in class nested in internal class. + // It is represented as static get/set functions pair. + // They should be explicitly annotated as internal and use JvmStatic to hide them from Java. + static property some_nested_static_property: Long + + // This is a class nested inside class, that is nested in internal class. + // It should be generated with explicit 'internal' keyword. + // Each element nested inside this class should also have explicit internal visibility. + class NestedClassLevelTwo { + // This is a constructor of class, that is nested in class, that is nested in internal class. + // It should be explicitly annotated as internal. + // Unfortunately, it cannot use synthetic, because constructor is not allowed target. + constructor double_nested_constructor(x: Int, y: Int) + + // This is a function in class, that is nested in class, that is nested in internal class. + // It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + fun double_nested_function(): Int + + // This is a static function in class, that is nested in class, that is nested in internal class. + // It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + static fun double_nested_static_function(): String + + // This is a property in class, that is nested in class, that is nested in internal class. + // It should be explicitly annotated as internal. + // And use synthetic for get/set functions to hide them from Java. + property double_nested_property: Long + + // This is a static property in class, that is nested in class, that is nested in internal class. + // It is represented as static get/set functions pair. + // They should be explicitly annotated as internal and use JvmStatic to hide them from Java. + static property double_nested_static_property: Long + } + } + + // This is an enumeration nested in internal class. + // It should be annotated as internal. + // Its value field should be synthetic to disallow accessing it from Java. + enum NestedEnum { + OPTION_1 = 1, + OPTION_2 = 2 + } + + // This is an exception nested in internal class. + // It should be annotated as internal. + // Its error field should be internal and synthetic to disallow accessing it from Java. + exception Nested(NestedEnum) + + // This is a lambda nested in internal class. + // It should be annotated as internal. + // Its 'call()' function should be synthetic to disallow accessing it from Java. + @Overloaded + lambda NestedLambda = (String, Long) -> String + + // This is a structure nested in internal class. + // It should be annotated as internal. + // Its fields should be internal and synthetic. + // Its functions should be internal and synthetic. + // Its constructors should be internal (sadly, they cannot be synthetic). + struct NestedStruct { + // This is a field of structure, which is nested in internal class. + // It should use 'internal' and synthetic. + firstField: Int + + // This is a field of structure, which is nested in internal class. + // It should use 'internal' and synthetic. + secondField: String + + // This is a field constructor of structure. The structure is nested in internal class. + // It should be explicitly internal. Sadly, it cannot be synthetic. + field constructor(firstField, secondField) + + // This is a constructor of structure, and the structure is nested in internal class. + // It should be explicitly annotated as internal. + // Unfortunately, it cannot use synthetic, because constructor is not allowed target. + constructor constructor_of_struct(x: Int, y: Int) + + // This is a function in structure nested inside internal class. + // It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + fun nested_struct_function(): Int + + // This is a static function in structure nested in internal class. + // It should be explicitly annotated as internal. + // And use synthetic annotation to hide it from Java. + static fun nested_struct_static_function(): String + } + + // This is an interface nested in internal class. + // It should be annotated as internal. + // Its fields should be synthetic --> sadly, Kotlin does not allow 'internal' keyword in interfaces. + // But from Kotlin language POV elements are not accessible, because interface itself is internal. + // The elements should not be accessible in Java because of synthetic attribute. + interface NestedInterface { + // This is a function in interface nested inside internal class. + // It must use synthetic annotation. + // Sadly, it cannot use 'internal' keyword. + fun nested_interface_function(): Int + + // This is a static function in interface nested in internal class. + // Sadly, it cannot use 'internal' keyword. + static fun nested_interface_static_function(): String + + // This is a property in interface nested in internal class. + // It must use synthetic annotation for get/set functions. + // Sadly, it cannot use 'internal' keyword. + property nested_interface_property: Long + + // This is a static property in class nested in internal class. + // It must use synthetic annotation for get/set functions. + // Sadly, it cannot use 'internal' keyword. + static property nested_interface_static_property: Long + } +} diff --git a/functional-tests/functional/input/src/cpp/NestedInternalElements.cpp b/functional-tests/functional/input/src/cpp/NestedInternalElements.cpp new file mode 100644 index 0000000000..4f7718510d --- /dev/null +++ b/functional-tests/functional/input/src/cpp/NestedInternalElements.cpp @@ -0,0 +1,249 @@ +// ------------------------------------------------------------------------------------------------- +// Copyright (C) 2016-2026 HERE Europe B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// License-Filename: LICENSE +// +// ------------------------------------------------------------------------------------------------- + +#include "test/InternalClassWithNestedElements.h" + +#include +#include + +namespace test +{ + +////////////// Implementation of type derived from nested interface type --> 'InternalClassWithNestedElements.NestedInterface' +class SomeImplOfNestedInterface : public InternalClassWithNestedElements::NestedInterface { +public: + SomeImplOfNestedInterface() = default; + ~SomeImplOfNestedInterface() override = default; + + int32_t nested_interface_function() override { + return 909; + } + + int64_t get_nested_interface_property() const override { + return m_prop; + } + + void set_nested_interface_property( const int64_t value ) override { + m_prop = value; + } + +private: + int64_t m_prop{77}; +}; + +////////////// Top level internal class: 'InternalClassWithNestedElements' +static int64_t g_some_static_property_of_internal_class = 11; + +class SomeImplOfInternalClassWithNestedElements : public InternalClassWithNestedElements { +public: + SomeImplOfInternalClassWithNestedElements(int32_t x, int32_t y) + : m_x{x}, m_y{y} + {} + + ~SomeImplOfInternalClassWithNestedElements() override = default; + + int32_t some_function() override { + return m_x + m_y; + } + + int64_t get_some_property() const override { + return m_some_property; + } + + void set_some_property(const int64_t value) override { + m_some_property = value; + } + +private: + int32_t m_x; + int32_t m_y; + int64_t m_some_property{2202}; +}; + +std::string +InternalClassWithNestedElements::some_static_function() { + return "SOME_STATIC_FUNCTION"; +} + +::std::error_code +InternalClassWithNestedElements::throw_nested_exception() { + return make_error_code(InternalClassWithNestedElements::NestedEnum::OPTION_1); +} + +InternalClassWithNestedElements::NestedLambda +InternalClassWithNestedElements::produce_nested_lambda() { + return [] (const std::string& p1, const int64_t p2) { + return "CONTENT_FROM_NESTED_LAMBDA_FROM_CPP: '" + p1 + std::to_string(p2) + "'"; + }; +} + +std::shared_ptr +InternalClassWithNestedElements::produce_nested_interface_implementation() { + return std::make_shared(); +} + +int64_t +InternalClassWithNestedElements::get_some_static_property() { + return g_some_static_property_of_internal_class; +} + +void +InternalClassWithNestedElements::set_some_static_property(const int64_t value) { + g_some_static_property_of_internal_class = value; +} + +std::shared_ptr +InternalClassWithNestedElements::some_constructor(int32_t x, int32_t y) { + return std::make_shared(x, y); +} + +////////////// A class nested in internal class: 'InternalClassWithNestedElements.NestedClassLevelOne' +static int64_t g_some_static_property_of_nested_level_1_class = 77; + +class SomeImplOfNestedClassLevelOne : public InternalClassWithNestedElements::NestedClassLevelOne { +public: + SomeImplOfNestedClassLevelOne(int32_t x, int32_t y) + : m_x{x}, m_y{y} + {} + + ~SomeImplOfNestedClassLevelOne() override = default; + + int32_t some_nested_function() override { + return m_x * m_y; + } + + int64_t get_some_nested_property() const override { + return m_some_nested_property; + } + + void set_some_nested_property(const int64_t value) override { + m_some_nested_property = value; + } + +private: + int32_t m_x; + int32_t m_y; + int64_t m_some_nested_property{333}; +}; + +std::string +InternalClassWithNestedElements::NestedClassLevelOne::some_nested_static_function() { + return "LEVEL_1_NESTING"; +} + +int64_t +InternalClassWithNestedElements::NestedClassLevelOne::get_some_nested_static_property() { + return g_some_static_property_of_nested_level_1_class; +} + +void +InternalClassWithNestedElements::NestedClassLevelOne::set_some_nested_static_property(const int64_t value) { + g_some_static_property_of_nested_level_1_class = value; +} + +std::shared_ptr +InternalClassWithNestedElements::NestedClassLevelOne::some_nested_constructor(int32_t x, int32_t y) { + return std::make_shared(x, y); +} + +////////////// A class nested in internal class: 'InternalClassWithNestedElements.NestedClassLevelOne.NestedClassLevelTwo' +static int64_t g_some_static_property_of_nested_level_2_class = 123; + +class SomeImplOfNestedClassLevelTwo : public InternalClassWithNestedElements::NestedClassLevelOne::NestedClassLevelTwo { +public: + SomeImplOfNestedClassLevelTwo(int32_t x, int32_t y) + : m_x{x}, m_y{y} + {} + + ~SomeImplOfNestedClassLevelTwo() override = default; + + int32_t double_nested_function() override { + return m_x * m_y; + } + + int64_t get_double_nested_property() const override { + return m_double_nested_property; + } + + void set_double_nested_property(const int64_t value) override { + m_double_nested_property = value; + } + +private: + int32_t m_x; + int32_t m_y; + int64_t m_double_nested_property{17}; +}; + +std::string +InternalClassWithNestedElements::NestedClassLevelOne::NestedClassLevelTwo::double_nested_static_function() { + return "LEVEL_2_NESTING"; +} + +int64_t +InternalClassWithNestedElements::NestedClassLevelOne::NestedClassLevelTwo::get_double_nested_static_property() { + return g_some_static_property_of_nested_level_2_class; +} + +void +InternalClassWithNestedElements::NestedClassLevelOne::NestedClassLevelTwo::set_double_nested_static_property(const int64_t value) { + g_some_static_property_of_nested_level_2_class = value; +} + +std::shared_ptr +InternalClassWithNestedElements::NestedClassLevelOne::NestedClassLevelTwo::double_nested_constructor(int32_t x, int32_t y) { + return std::make_shared(x, y); +} + +////////////// A struct nested in internal class: 'InternalClassWithNestedElements.NestedStruct' +InternalClassWithNestedElements::NestedStruct +InternalClassWithNestedElements::NestedStruct::constructor_of_struct(int32_t x, int32_t y) { + return InternalClassWithNestedElements::NestedStruct{x, std::to_string(y)}; +} + +int32_t +InternalClassWithNestedElements::NestedStruct::nested_struct_function() const { + return 2 * first_field; +} + +std::string +InternalClassWithNestedElements::NestedStruct::nested_struct_static_function() { + return "NESTED_STRUCT_STATIC_FUN"; +} + +////////////// A struct nested in internal class: 'InternalClassWithNestedElements.NestedInterface' +static int64_t g_nested_interface_static_property = 572; + +std::string +InternalClassWithNestedElements::NestedInterface::nested_interface_static_function() { + return "NESTED_ITF_STATIC_FUN"; +} + +int64_t +InternalClassWithNestedElements::NestedInterface::get_nested_interface_static_property() { + return g_nested_interface_static_property; +} + +void +InternalClassWithNestedElements::NestedInterface::set_nested_interface_static_property(const int64_t value) { + g_nested_interface_static_property = value; +} + +} // namespace test \ No newline at end of file From c90193914d8c6e2ddb3930a0b1570e0470de4c47 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Sun, 22 Feb 2026 20:03:07 +0100 Subject: [PATCH 20/23] Kotlin: add missing JvmSynthetic for exception's field This commit adjusts the mustache template to generate synthetic annotation for exceptions when they are internal. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../main/resources/templates/kotlin/KotlinException.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gluecodium/src/main/resources/templates/kotlin/KotlinException.mustache b/gluecodium/src/main/resources/templates/kotlin/KotlinException.mustache index 0d1f97387b..631e7e26a5 100644 --- a/gluecodium/src/main/resources/templates/kotlin/KotlinException.mustache +++ b/gluecodium/src/main/resources/templates/kotlin/KotlinException.mustache @@ -20,5 +20,5 @@ !}} {{>kotlin/KotlinDocComment}}{{>kotlin/KotlinAttributes}} {{>kotlin/KotlinTypeVisibility}}{{!! -}}class {{resolveName}}(@JvmField val error: {{resolveName errorType}}) : Exception(error.toString()) +}}class {{resolveName}}({{resolveName "synthetic"}}@JvmField val error: {{resolveName errorType}}) : Exception(error.toString()) From e95b5bc68e5aa70afcb7b84c597b076862771ceb Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Sun, 22 Feb 2026 20:04:13 +0100 Subject: [PATCH 21/23] smoke-tests: generate synthetic annotation for kotlin exceptions When exceptions are internal the synthetic annotation is used to hide the field. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../android-kotlin/com/example/smoke/OuterInternalException.kt | 2 +- .../com/example/smoke/SomethingBadHappenedException.kt | 3 ++- .../com/example/smoke/InternalClassWithNestedElements.kt | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalException.kt b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalException.kt index e9fc711d3d..0f9266f23c 100644 --- a/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalException.kt +++ b/gluecodium/src/test/resources/smoke/annotations/output/android-kotlin/com/example/smoke/OuterInternalException.kt @@ -11,6 +11,6 @@ package com.example.smoke import com.example.MySmokeTestsInternalApi @MySmokeTestsInternalApi -internal class OuterInternalException(@JvmField val error: OuterInternalEnum) : Exception(error.toString()) +internal class OuterInternalException(@JvmSynthetic @JvmField val error: OuterInternalEnum) : Exception(error.toString()) diff --git a/gluecodium/src/test/resources/smoke/errors/output/android-kotlin/com/example/smoke/SomethingBadHappenedException.kt b/gluecodium/src/test/resources/smoke/errors/output/android-kotlin/com/example/smoke/SomethingBadHappenedException.kt index 7b02006df3..f6d0beb181 100644 --- a/gluecodium/src/test/resources/smoke/errors/output/android-kotlin/com/example/smoke/SomethingBadHappenedException.kt +++ b/gluecodium/src/test/resources/smoke/errors/output/android-kotlin/com/example/smoke/SomethingBadHappenedException.kt @@ -5,9 +5,10 @@ @file:JvmName("SomethingBadHappenedExtensions") + package com.example.smoke -internal class SomethingBadHappenedException(@JvmField val error: SomeInternalEnum) : Exception(error.toString()) +internal class SomethingBadHappenedException(@JvmSynthetic @JvmField val error: SomeInternalEnum) : Exception(error.toString()) diff --git a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassWithNestedElements.kt b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassWithNestedElements.kt index e5280e5e2d..7b5dd64718 100644 --- a/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassWithNestedElements.kt +++ b/gluecodium/src/test/resources/smoke/visibility_attribute/output/android-kotlin/com/example/smoke/InternalClassWithNestedElements.kt @@ -31,7 +31,7 @@ internal class InternalClassWithNestedElements : NativeBase { * It should be annotated as internal. * Its error field should be internal and synthetic to disallow accessing it from Java. */ - internal class NestedExceptionException(@JvmField val error: InternalClassWithNestedElements.NestedEnum) : Exception(error.toString()) + internal class NestedExceptionException(@JvmSynthetic @JvmField val error: InternalClassWithNestedElements.NestedEnum) : Exception(error.toString()) /** From 03a08c6d03f13fc112571f23fbcb90cf273495b1 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Sun, 22 Feb 2026 20:04:57 +0100 Subject: [PATCH 22/23] functional-tests: do not trigger compilation error in Java/Kotlin interop test Because we generate 'JvmSynthetic' for exception fields, the compilation error was trigerred. ``` /functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java:76: error: cannot find symbol assertEquals(exception.error, SomeInternalEnum.ONE); ^ symbol: variable error location: variable exception of type SomethingBadHappenedException ``` Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- .../java/com/here/android/test/VisibilityAttributeTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java b/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java index ffd2540e2a..0429efe3c2 100644 --- a/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java +++ b/functional-tests/functional/android/src/test/java/com/here/android/test/VisibilityAttributeTest.java @@ -72,7 +72,5 @@ public void internalExceptionCanBeThrown() { SomethingBadHappenedException exception = assertThrows(SomethingBadHappenedException.class, () -> { throw new SomethingBadHappenedException(SomeInternalEnum.ONE); }); - - assertEquals(exception.error, SomeInternalEnum.ONE); } } From 1384afe1fb4599a5eac4f53c2560abb17eef9ed1 Mon Sep 17 00:00:00 2001 From: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> Date: Sun, 22 Feb 2026 20:14:20 +0100 Subject: [PATCH 23/23] Adjust CHANGELOG.md Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 258c837517..1292cebc29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Gluecodium project Release Notes +## Unreleased + * Kotlin: explicitly generate 'internal' visibility and synthetic attribute for elements nested in 'internal' type. + ## 14.1.0 Release date 2026-02-21 * Kotlin: use `@JvmSynthetic` annotation for Kotlin internal elements whenever possible to hide them from Java code.