Skip to content

Commit 4ebce39

Browse files
authored
Kotlin: minimize the usage of 'JvmSuppressWildcards' (#1793)
---------- Motivation ---------- The annotation mentioned in the title of the commit was used for parameter types of collections (List, Map, Set) to avoid interoperability problems when the generated code is accessed from Java. The problematic case is checked in KotlinInteropTest.java. However, it turns out that the mentioned annotation needs to be only used for enums and open types, because if the type is final then wildcard is not generated. ---------- Solution ---------- KotlinNameResolver class is adjusted to generate the annotation for parameter type of collection only when it is enum or the type is open. Smoke tests are adjusted to showcase that this change minimizes the usage of 'JvmSuppressWildcards' annotation. The functional test called 'KotlinInteropTest.java' passes without problems after the change. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com>
1 parent bc6a7d4 commit 4ebce39

42 files changed

Lines changed: 224 additions & 116 deletions

File tree

Some content is hidden

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

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* Kotlin: use `@JvmSynthetic` annotation for Kotlin internal elements whenever possible to hide them from Java code.
55
* 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'.
66
* Kotlin: for external types usage in Kotlin, use 'internal' keyword for the types, which are generated by Gluecodium as their 'internal' representation.
7+
* 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'.
78

89
## 14.0.0
910
Release date 2025-12-01

functional-tests/functional/android/src/test/java/com/here/android/test/KotlinInteropTest.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,61 @@
3232
@RunWith(RobolectricTestRunner.class)
3333
@Config(sdk = Build.VERSION_CODES.M, application = RobolectricApplication.class)
3434
public class KotlinInteropTest {
35-
private int someJavaFunctionThatTakesList(List<SomeOpenNumberWrapperClass> someList) {
35+
private int someJavaFunctionThatTakesListOfOpenClasses(List<SomeOpenNumberWrapperClass> someList) {
36+
return someList.size();
37+
}
38+
39+
private int someJavaFunctionThatTakesListOfInterfaces(List<SomeDummyInterface> someList) {
40+
return someList.size();
41+
}
42+
43+
private int someJavaFunctionThatTakesListOfStructs(List<SomeDummyStruct> someList) {
44+
return someList.size();
45+
}
46+
47+
private int someJavaFunctionThatTakesListOfFinalClass(List<PlainDataStructures> someList) {
48+
return someList.size();
49+
}
50+
51+
private int someJavaFunctionThatTakesListOfEnums(List<SomeDummyEnum> someList) {
3652
return someList.size();
3753
}
3854

3955
@Test
4056
public void javaFunctionWithCollectionParamsCanBeCalledWithOpenClassFields() {
4157
SomeStructWithCollection someStruct = new SomeStructWithCollection();
42-
assertEquals(0, someJavaFunctionThatTakesList(someStruct.openClassList));
58+
assertEquals(0, someJavaFunctionThatTakesListOfOpenClasses(someStruct.openClassList));
59+
}
60+
61+
@Test
62+
public void javaFunctionWithCollectionParamsCanBeCalledWithInterfaceFields() {
63+
SomeStructWithCollection someStruct = new SomeStructWithCollection();
64+
assertEquals(0, someJavaFunctionThatTakesListOfInterfaces(someStruct.interfacesList));
65+
}
66+
67+
@Test
68+
public void javaFunctionWithCollectionParamsCanBeCalledWithStructFields() {
69+
SomeStructWithCollection someStruct = new SomeStructWithCollection();
70+
assertEquals(0, someJavaFunctionThatTakesListOfStructs(someStruct.structsList));
71+
}
72+
73+
@Test
74+
public void javaFunctionWithCollectionParamsCanBeCalledWithFinalClassFields() {
75+
SomeStructWithCollection someStruct = new SomeStructWithCollection();
76+
assertEquals(0, someJavaFunctionThatTakesListOfFinalClass(someStruct.finalClassList));
77+
}
78+
79+
@Test
80+
public void javaFunctionWithCollectionParamsCanBeCalledWithEnumFields() {
81+
SomeStructWithCollection someStruct = new SomeStructWithCollection();
82+
assertEquals(0, someJavaFunctionThatTakesListOfEnums(someStruct.enumList));
4383
}
4484

4585
@Test
4686
public void whenJavaAccessesStructFieldOfListTypeThenElementsCanBeAdded() {
4787
SomeStructWithCollection someStruct = new SomeStructWithCollection();
4888
someStruct.openClassList.add(new SomeOpenNumberWrapperClass(777));
4989

50-
assertEquals(1, someJavaFunctionThatTakesList(someStruct.openClassList));
90+
assertEquals(1, someJavaFunctionThatTakesListOfOpenClasses(someStruct.openClassList));
5191
}
5292
}

functional-tests/functional/input/lime/Structs.lime

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,25 @@ open class SomeOpenNumberWrapperClass {
119119
constructor make(n: Int)
120120
}
121121

122+
interface SomeDummyInterface {
123+
fun getInt(): Int
124+
}
125+
126+
struct SomeDummyStruct {
127+
someInt: Int
128+
}
129+
130+
enum SomeDummyEnum {
131+
FOO,
132+
BAR
133+
}
134+
122135
struct SomeStructWithCollection {
123136
emptyNumbers: List<Int> = []
124137
numbers: List<Int> = [1, 2, 3]
125138
openClassList: List<SomeOpenNumberWrapperClass> = []
139+
interfacesList: List<SomeDummyInterface> = []
140+
structsList: List<SomeDummyStruct> = []
141+
finalClassList: List<PlainDataStructures> = []
142+
enumList: List<SomeDummyEnum> = []
126143
}

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ import com.here.gluecodium.model.lime.LimeAttributeValueType.FUNCTION_NAME
2929
import com.here.gluecodium.model.lime.LimeBasicType
3030
import com.here.gluecodium.model.lime.LimeBasicType.TypeId
3131
import com.here.gluecodium.model.lime.LimeComment
32+
import com.here.gluecodium.model.lime.LimeContainerWithInheritance
3233
import com.here.gluecodium.model.lime.LimeDirectTypeRef
3334
import com.here.gluecodium.model.lime.LimeElement
35+
import com.here.gluecodium.model.lime.LimeEnumeration
3436
import com.here.gluecodium.model.lime.LimeExternalDescriptor.Companion.NAME_NAME
3537
import com.here.gluecodium.model.lime.LimeFunction
3638
import com.here.gluecodium.model.lime.LimeGenericType
@@ -164,14 +166,29 @@ internal class KotlinNameResolver(
164166

165167
private fun resolveGenericTypeRef(limeType: LimeGenericType) =
166168
when (limeType) {
167-
is LimeList -> "List<@JvmSuppressWildcards ${resolveTypeRef(limeType.elementType)}>"
168-
is LimeSet -> "Set<@JvmSuppressWildcards ${resolveTypeRef(limeType.elementType)}>"
169-
is LimeMap -> "Map<@JvmSuppressWildcards ${resolveTypeRef(
170-
limeType.keyType,
171-
)}, @JvmSuppressWildcards ${resolveTypeRef(limeType.valueType)}>"
169+
is LimeList -> "List<${resolveContainerParamType(limeType.elementType)}>"
170+
is LimeSet -> "Set<${resolveContainerParamType(limeType.elementType)}>"
171+
is LimeMap -> "Map<${resolveContainerParamType(limeType.keyType)}, ${resolveContainerParamType(limeType.valueType)}>"
172172
else -> throw GluecodiumExecutionException("Unsupported element type ${limeType.javaClass.name}")
173173
}
174174

175+
private fun resolveContainerParamType(typeRef: LimeTypeRef) =
176+
if (needsToSuppressWildcards(typeRef.type)) {
177+
"@JvmSuppressWildcards ${resolveTypeRef(typeRef)}"
178+
} else {
179+
resolveTypeRef(typeRef)
180+
}
181+
182+
// The parameters of generic types (List, Set, Map) must use '@JvmSuppressWildcards'
183+
// for 'open' types to avoid compilation problems when accessed from Java code.
184+
private fun needsToSuppressWildcards(type: LimeType) =
185+
when (type) {
186+
is LimeContainerWithInheritance -> type.isOpen
187+
is LimeLambda -> true
188+
is LimeEnumeration -> true
189+
else -> false
190+
}
191+
175192
private fun resolveNestedNames(limeElement: LimeNamedElement): List<String> {
176193
val elementName = kotlinNameRules.getName(limeElement)
177194
val parentElement = if (limeElement.path.hasParent) getParentElement(limeElement) else null

gluecodium/src/test/resources/smoke/constants/output/android-kotlin/com/example/smoke/CollectionConstants.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
@file:JvmName("CollectionConstantsExtensions")
77

8+
89
package com.example.smoke
910

1011
import com.example.NativeBase
@@ -29,10 +30,10 @@ class CollectionConstants : NativeBase {
2930

3031

3132
companion object {
32-
@JvmField final val LIST_CONSTANT: List<@JvmSuppressWildcards String> = mutableListOf("foo", "bar")
33-
@JvmField final val SET_CONSTANT: Set<@JvmSuppressWildcards String> = mutableSetOf("foo", "bar")
34-
@JvmField final val MAP_CONSTANT: Map<@JvmSuppressWildcards String, @JvmSuppressWildcards String> = mutableMapOf("foo" to "bar")
35-
@JvmField final val MIXED_CONSTANT: Map<@JvmSuppressWildcards List<@JvmSuppressWildcards String>, @JvmSuppressWildcards Set<@JvmSuppressWildcards String>> = mutableMapOf(mutableListOf("foo") to mutableSetOf("bar"))
33+
@JvmField final val LIST_CONSTANT: List<String> = mutableListOf("foo", "bar")
34+
@JvmField final val SET_CONSTANT: Set<String> = mutableSetOf("foo", "bar")
35+
@JvmField final val MAP_CONSTANT: Map<String, String> = mutableMapOf("foo" to "bar")
36+
@JvmField final val MIXED_CONSTANT: Map<List<String>, Set<String>> = mutableMapOf(mutableListOf("foo") to mutableSetOf("bar"))
3637
@JvmStatic private external fun disposeNativeHandle(nativeHandle: Long)
3738
}
3839
}

gluecodium/src/test/resources/smoke/constructors/output/android-kotlin/com/example/smoke/Constructors.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ open class Constructors : NativeBase {
4343
}
4444

4545

46-
constructor(input: List<@JvmSuppressWildcards Double>) : this(create(input), null as Any?) {
46+
constructor(input: List<Double>) : this(create(input), null as Any?) {
4747
cacheThisInstance();
4848
}
4949

@@ -79,7 +79,7 @@ open class Constructors : NativeBase {
7979
@Throws(Constructors.ConstructorExplodedException::class)
8080
@JvmStatic private external fun create(input: String) : Long
8181

82-
@JvmStatic private external fun create(input: List<@JvmSuppressWildcards Double>) : Long
82+
@JvmStatic private external fun create(input: List<Double>) : Long
8383

8484
@JvmStatic private external fun create(input: Long) : Long
8585
}

gluecodium/src/test/resources/smoke/dates/output/android-kotlin/com/example/smoke/Dates.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
@file:JvmName("DatesExtensions")
77

8+
89
package com.example.smoke
910

1011
import com.example.NativeBase
@@ -53,7 +54,7 @@ class Dates : NativeBase {
5354
external get
5455
external set
5556

56-
var dateSet: Set<@JvmSuppressWildcards Date>
57+
var dateSet: Set<Date>
5758
external get
5859
external set
5960

gluecodium/src/test/resources/smoke/defaults/output/android-kotlin/com/example/smoke/DefaultValues.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
@file:JvmName("DefaultValuesExtensions")
77

8+
89
package com.example.smoke
910

1011
import com.example.NativeBase
@@ -85,11 +86,11 @@ class DefaultValues : NativeBase {
8586
}
8687

8788
class StructWithEmptyDefaults {
88-
@JvmField var intsField: List<@JvmSuppressWildcards Int>
89-
@JvmField var floatsField: List<@JvmSuppressWildcards Float>
90-
@JvmField var mapField: Map<@JvmSuppressWildcards Long, @JvmSuppressWildcards String>
89+
@JvmField var intsField: List<Int>
90+
@JvmField var floatsField: List<Float>
91+
@JvmField var mapField: Map<Long, String>
9192
@JvmField var structField: DefaultValues.StructWithDefaults
92-
@JvmField var setTypeField: Set<@JvmSuppressWildcards String>
93+
@JvmField var setTypeField: Set<String>
9394

9495

9596

gluecodium/src/test/resources/smoke/defaults_const/output/android-kotlin/com/example/smoke/EnumCollectionDefaults.kt

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

66
@file:JvmName("EnumCollectionDefaultsExtensions")
77

8+
89
package com.example.smoke
910

1011
import com.example.fire.Enum1

gluecodium/src/test/resources/smoke/defaults_const/output/android-kotlin/com/example/smoke/EnumCollectionDefaultsExternal.kt

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

66
@file:JvmName("EnumCollectionDefaultsExternalExtensions")
77

8+
89
package com.example.smoke
910

1011
import java.util.EnumSet

0 commit comments

Comments
 (0)