Skip to content

Commit 9c44625

Browse files
authored
jextract/jni: Support generic types within Optional and Tuple (#657)
* Add more generic function pattern testing # Conflicts: # Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java * Reconsider the logic for wrapping return values * Fix generic type name is not printed for boxed type * Fix test failures for simple pattern * Add JNIGenericCombinationTests * print downcall first when the result type is void * Cleanup code * Update fixme comments * swift format * Delete comment outed functions * grammar fix * Avoid using optional int for example code
1 parent 34eda4f commit 9c44625

12 files changed

Lines changed: 551 additions & 195 deletions

File tree

Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,50 @@ public struct MyID<T> {
2222
}
2323
}
2424

25-
public func makeIntID(_ value: Int) -> MyID<Int> {
26-
MyID(value)
27-
}
25+
public enum MyIDs {
26+
public static func makeIntID(_ value: Int) -> MyID<Int> {
27+
MyID(value)
28+
}
2829

29-
public func makeStringID(_ value: String) -> MyID<String> {
30-
MyID(value)
31-
}
30+
public static func takeIntValue(from value: MyID<Int>) -> Int {
31+
value.rawValue
32+
}
3233

33-
public func takeIntValue(from value: MyID<Int>) -> Int {
34-
value.rawValue
35-
}
34+
public static func makeStringID(_ value: String) -> MyID<String> {
35+
MyID(value)
36+
}
3637

37-
public func takeStringValue(from value: MyID<String>) -> String {
38-
value.rawValue
38+
public static func takeStringValue(from value: MyID<String>) -> String {
39+
value.rawValue
40+
}
41+
42+
public static func makeIDs(_ stringValue: String, _ intValue: Int) -> (MyID<String>, MyID<Int>) {
43+
(MyID(stringValue), MyID(intValue))
44+
}
45+
46+
public static func takeValuesFromTuple(_ tuple: (MyID<String>, MyID<Int>)) -> (String, Int) {
47+
(tuple.0.rawValue, tuple.1.rawValue)
48+
}
49+
50+
public static func makeDoubleIDOptional(_ value: Double) -> MyID<Double>? {
51+
MyID(value)
52+
}
53+
54+
public static func takeDoubleValueOptional(from id: MyID<Double>?) -> Double? {
55+
id?.rawValue
56+
}
57+
58+
public static func takeDoubleValue(from value: MyID<Double>) -> Double {
59+
value.rawValue
60+
}
61+
62+
public static func makeOptionalStringID(_ value: String?) -> MyID<String?> {
63+
MyID(value)
64+
}
65+
66+
public static func takeOptionalStringValue(from id: MyID<String?>) -> String? {
67+
id.rawValue
68+
}
3969
}
4070

4171
public struct MyEntity {

Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,47 @@
1414

1515
package com.example.swift;
1616

17+
import java.util.Optional;
18+
import java.util.OptionalLong;
1719
import org.junit.jupiter.api.Test;
1820
import org.swift.swiftkit.core.SwiftArena;
21+
import org.swift.swiftkit.core.tuple.Tuple2;
1922

2023
import static org.junit.jupiter.api.Assertions.*;
2124

2225
public class GenericTypeTest {
2326
@Test
2427
void genericTypeValueRoundtrip() {
2528
try (var arena = SwiftArena.ofConfined()) {
26-
MyID<String> stringId = MySwiftLibrary.makeStringID("Java", arena);
29+
MyID<String> stringId = MyIDs.makeStringID("Java", arena);
2730
assertEquals("Java", stringId.getDescription());
28-
assertEquals("Java", MySwiftLibrary.takeStringValue(stringId));
31+
assertEquals("Java", MyIDs.takeStringValue(stringId));
2932

30-
MyID<Long> intId = MySwiftLibrary.makeIntID(42, arena);
33+
MyID<Long> intId = MyIDs.makeIntID(42, arena);
3134
assertEquals("42", intId.getDescription());
32-
assertEquals(42, MySwiftLibrary.takeIntValue(intId));
35+
assertEquals(42, MyIDs.takeIntValue(intId));
36+
37+
Tuple2<MyID<String>, MyID<Long>> ids = MyIDs.makeIDs("Java", 42, arena);
38+
assertEquals("Java", ids.$0.getDescription());
39+
assertEquals("42", ids.$1.getDescription());
40+
assertEquals("Java", MyIDs.takeValuesFromTuple(ids).$0);
41+
assertEquals(42, MyIDs.takeValuesFromTuple(ids).$1);
42+
43+
Optional<MyID<Double>> doubleIdOptional = MyIDs.makeDoubleIDOptional(42.195, arena);
44+
assertTrue(doubleIdOptional.isPresent());
45+
assertEquals(42.195, MyIDs.takeDoubleValueOptional(doubleIdOptional).getAsDouble());
46+
assertEquals(42.195, MyIDs.takeDoubleValue(doubleIdOptional.get())); // ensure wrapped value is alive
47+
48+
MyID<Optional<String>> optionalStringId = MyIDs.makeOptionalStringID(Optional.of("Java"), arena);
49+
assertEquals("Optional(\"Java\")", optionalStringId.getDescription());
50+
assertEquals("Java", MyIDs.takeOptionalStringValue(optionalStringId).get());
3351
}
3452
}
3553

3654
@Test
3755
void genericTypeProperty() {
3856
try (var arena = SwiftArena.ofConfined()) {
39-
MyID<Long> intId = MySwiftLibrary.makeIntID(42, arena);
57+
MyID<Long> intId = MyIDs.makeIntID(42, arena);
4058
MyEntity entity = MyEntity.init(intId, "name", arena);
4159
assertEquals("42", entity.getId(arena).getDescription());
4260
}

Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,18 +164,31 @@ extension JavaType {
164164
/// The boxed class name for this type, suitable for use as a generic type argument.
165165
var boxedName: String {
166166
switch self {
167-
case .boolean: "Boolean"
168-
case .byte: "Byte"
169-
case .char: "Character"
170-
case .short: "Short"
171-
case .int: "Integer"
172-
case .long: "Long"
173-
case .float: "Float"
174-
case .double: "Double"
175-
case .void: "Void"
176-
case .javaLangString: "String"
177-
case .class(_, let name, _): name
178-
case .array: description
167+
case .boolean: return "Boolean"
168+
case .byte: return "Byte"
169+
case .char: return "Character"
170+
case .short: return "Short"
171+
case .int: return "Integer"
172+
case .long: return "Long"
173+
case .float: return "Float"
174+
case .double: return "Double"
175+
case .void: return "Void"
176+
case .javaLangString: return "String"
177+
case .class(let package, let name, let typeParameters):
178+
let packagePart: String =
179+
if let package {
180+
"\(package)."
181+
} else {
182+
""
183+
}
184+
let genericClause: String =
185+
if !typeParameters.isEmpty {
186+
"<\(typeParameters.map(\.boxedName).joined(separator: ", "))>"
187+
} else {
188+
""
189+
}
190+
return "\(packagePart)\(name)\(genericClause)"
191+
case .array: return description
179192
}
180193
}
181194
}

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,13 @@ extension JNISwift2JavaGenerator {
732732
if translatedFunctionSignature.resultType.javaType.isVoid {
733733
printer.print("\(downcall);")
734734
} else {
735-
let result = translatedFunctionSignature.resultType.conversion.render(&printer, downcall)
735+
let result: String
736+
if translatedDecl.nativeFunctionSignature.result.javaType.isVoid {
737+
printer.print("\(downcall);")
738+
result = translatedFunctionSignature.resultType.conversion.render(&printer, "")
739+
} else {
740+
result = translatedFunctionSignature.resultType.conversion.render(&printer, downcall)
741+
}
736742
printer.print("return \(result);")
737743
}
738744
}

0 commit comments

Comments
 (0)