Skip to content

Commit 1256c28

Browse files
committed
further improvements in handling type erased return types
1 parent a986fd0 commit 1256c28

File tree

5 files changed

+80
-27
lines changed

5 files changed

+80
-27
lines changed

Sources/SwiftJavaToolLib/JavaClassTranslator.swift

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -610,18 +610,19 @@ extension JavaClassTranslator {
610610
// TODO: make it more precise with the "origin" of the generic parameter (outer class etc)
611611
func collectMethodGenericParameters(
612612
genericParameters: [String],
613-
method javaMethod: Method
613+
method: Method
614614
) -> OrderedSet<String> {
615615
var allGenericParameters = OrderedSet(genericParameters)
616-
let typeParameters = javaMethod.getTypeParameters()
617-
if typeParameters.contains(where: {$0 != nil }) {
618-
allGenericParameters.appending(contentsOf: typeParameters.compactMap { typeParam in
619-
guard let typeParam else { return nil }
620-
guard genericParameterIsUsedInSignature(typeParam, in: javaMethod) else {
621-
return nil
622-
}
623-
return "\(typeParam.getTypeName()): AnyJavaObject"
624-
})
616+
617+
let typeParameters = method.getTypeParameters()
618+
for typeParameter in typeParameters {
619+
guard let typeParameter else { continue }
620+
621+
guard genericParameterIsUsedInSignature(typeParameter, in: method) else {
622+
continue
623+
}
624+
625+
allGenericParameters.append("\(typeParameter.getTypeName()): AnyJavaObject")
625626
}
626627

627628
return allGenericParameters
@@ -654,8 +655,8 @@ extension JavaClassTranslator {
654655
preferValueTypes: true,
655656
outerOptional: .implicitlyUnwrappedOptional
656657
)
657-
let typeEraseGenericResultType: Bool =
658-
isGenericJavaType(javaMethod.getGenericReturnType())
658+
let hasTypeEraseGenericResultType: Bool =
659+
isTypeErased(javaMethod.getGenericReturnType())
659660

660661
// FIXME: cleanup the checking here
661662
if resultType != "Void" && resultType != "Swift.Void" {
@@ -681,7 +682,7 @@ extension JavaClassTranslator {
681682
}
682683
// Do we need to record any generic information, in order to enable type-erasure for the upcalls?
683684
var parameters: [String] = []
684-
if typeEraseGenericResultType {
685+
if hasTypeEraseGenericResultType {
685686
parameters.append("genericResult: \"\(resultType)\"")
686687
}
687688
// TODO: generic parameters?
@@ -951,7 +952,7 @@ extension JavaClassTranslator {
951952
return true
952953
}
953954
} catch {
954-
log.warning("Failed to determine if method '\(method)' is an override, error: \(error)")
955+
log.debug("Failed to determine if method '\(method)' is an override, error: \(error)")
955956
}
956957
}
957958

Sources/SwiftJavaToolLib/JavaGenericsSupport.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,25 @@ func isGenericJavaType(_ type: Type?) -> Bool {
8787

8888
return false
8989
}
90+
91+
/// Check if a type is type-erased att runtime.
92+
///
93+
/// E.g. in a method returning a generic `T` the T is type erased and must
94+
/// be represented as a `java.lang.Object` instead.
95+
func isTypeErased(_ type: Type?) -> Bool {
96+
guard let type else {
97+
return false
98+
}
99+
100+
// Check if it's a type variable (e.g., T, E, etc.)
101+
if type.as(TypeVariable<JavaObject>.self) != nil {
102+
return true
103+
}
104+
105+
// Check if it's a wildcard type (e.g., ? extends Number, ? super String)
106+
if type.as(WildcardType.self) != nil {
107+
return true
108+
}
109+
110+
return false
111+
}

Sources/SwiftJavaToolLib/JavaTranslator.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ extension JavaTranslator {
133133
preferValueTypes: Bool,
134134
outerOptional: OptionalKind
135135
) throws -> String {
136-
// let returnType = method.getReturnType()
137136
let genericReturnType = method.getGenericReturnType()
138137

139138
// Special handle the case when the return type is the generic type of the method: `<T> T foo()`

Tests/SwiftJavaToolLibTests/Java2SwiftTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ class Java2SwiftTests: XCTestCase {
262262
public struct MyJavaObjects {
263263
""",
264264
"""
265-
@JavaStaticMethod
265+
@JavaStaticMethod(genericResult: "T!")
266266
public func requireNonNull<T: AnyJavaObject>(_ arg0: T?, _ arg1: MySupplier<JavaString>?) -> T
267267
""",
268268
]
@@ -475,7 +475,7 @@ class Java2SwiftTests: XCTestCase {
475475
public struct MyJavaIntFunction<R: AnyJavaObject> {
476476
""",
477477
"""
478-
@JavaMethod
478+
@JavaMethod(genericResult: "R!")
479479
public func apply(_ arg0: Int32) -> R!
480480
""",
481481
]

Tests/SwiftJavaToolLibTests/WrapJavaTests/GenericsWrapJavaTests.swift

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ final class GenericsWrapJavaTests: XCTestCase {
6262
open class ExampleSimpleClass: JavaObject {
6363
""",
6464
"""
65-
@JavaMethod
66-
open func getGeneric<KeyType: AnyJavaObject>(_ arg0: Item<KeyType>?) -> KeyType
65+
@JavaMethod(genericResult: "KeyType!")
66+
open func getGeneric<KeyType: AnyJavaObject>(_ arg0: Item<KeyType>?) -> KeyType!
6767
""",
6868
]
6969
)
@@ -100,8 +100,8 @@ final class GenericsWrapJavaTests: XCTestCase {
100100
classpath: [classpathURL],
101101
expectedChunks: [
102102
"""
103-
@JavaMethod
104-
open func getGeneric<KeyType: AnyJavaObject>() -> KeyType
103+
@JavaMethod(genericResult: "KeyType!")
104+
open func getGeneric<KeyType: AnyJavaObject>() -> KeyType!
105105
""",
106106
]
107107
)
@@ -162,8 +162,10 @@ final class GenericsWrapJavaTests: XCTestCase {
162162
"""
163163
package com.example;
164164
165+
// Mini decls in order to avoid warnings about some funcs we're not yet importing cleanly
165166
final class List<T> {}
166167
final class Map<T, U> {}
168+
final class Number {}
167169
168170
class GenericClass<T> {
169171
public T getClassGeneric() { return null; }
@@ -184,18 +186,41 @@ final class GenericsWrapJavaTests: XCTestCase {
184186

185187
try assertWrapJavaOutput(
186188
javaClassNames: [
189+
"com.example.Map",
190+
"com.example.List",
191+
"com.example.Number",
187192
"com.example.GenericClass",
188193
],
189194
classpath: [classpathURL],
190195
expectedChunks: [
196+
"""
197+
@JavaMethod(genericResult: "T!")
198+
open func getClassGeneric() -> T!
199+
""",
200+
"""
201+
@JavaMethod(genericResult: "M!")
202+
open func getMethodGeneric<M: AnyJavaObject>() -> M!
203+
""",
191204
"""
192205
@JavaMethod
193-
open func getClassGeneric() -> T
206+
open func getMixedGeneric<M: AnyJavaObject>() -> Map<T, M>!
194207
""",
195208
"""
196209
@JavaMethod
197210
open func getNonGeneric() -> String
198211
""",
212+
"""
213+
@JavaMethod
214+
open func getParameterizedClassGeneric() -> List<T>!
215+
""",
216+
"""
217+
@JavaMethod
218+
open func getWildcard() -> List<Number>!
219+
""",
220+
"""
221+
@JavaMethod
222+
open func getGenericArray() -> [T?]
223+
""",
199224
]
200225
)
201226
}
@@ -275,13 +300,15 @@ final class GenericsWrapJavaTests: XCTestCase {
275300
)
276301
}
277302

278-
func test_wrapJava_genericMethodTypeErasure_ofNullableOptional() async throws {
303+
func test_wrapJava_genericMethodTypeErasure_ofNullableOptional_staticMethods() async throws {
279304
let classpathURL = try await compileJava(
280305
"""
281306
package com.example;
282307
283308
final class Optional<T> {
284309
public static <T> Optional<T> ofNullable(T value) { return null; }
310+
311+
public static <T> T nonNull(T value) { return null; }
285312
}
286313
""")
287314

@@ -294,14 +321,18 @@ final class GenericsWrapJavaTests: XCTestCase {
294321
"""
295322
@JavaClass("com.example.Optional")
296323
open class Optional<T: AnyJavaObject>: JavaObject {
297-
298-
}
299324
""",
300325
"""
301326
extension JavaClass {
302-
@JavaStaticMethod(genericResult: "Optional<T>!")
303-
public func ofNullable<T: AnyJavaObject>(_ arg0: T?) -> Optional<T>! where ObjectType == Optional<T>
327+
""",
328+
"""
329+
@JavaStaticMethod
330+
public func ofNullable<T: AnyJavaObject>(_ arg0: T?) -> Optional<T>! where ObjectType == Optional<T>
304331
}
332+
""",
333+
"""
334+
@JavaStaticMethod(genericResult: "T!")
335+
public func nonNull<T: AnyJavaObject>(_ arg0: T?) -> T! where ObjectType == Optional<T>
305336
"""
306337
]
307338
)

0 commit comments

Comments
 (0)