Skip to content

Commit 2ed85e2

Browse files
authored
[jextract] Add async legacy mode (#462)
1 parent c0c6fd5 commit 2ed85e2

File tree

11 files changed

+597
-67
lines changed

11 files changed

+597
-67
lines changed

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

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,32 @@
2626
import java.util.OptionalLong;
2727
import java.util.concurrent.CompletableFuture;
2828
import java.util.concurrent.ExecutionException;
29+
import java.util.concurrent.Future;
2930

3031
import static org.junit.jupiter.api.Assertions.*;
3132

3233
public class AsyncTest {
3334
@Test
34-
void asyncSum() {
35-
CompletableFuture<Long> future = MySwiftLibrary.asyncSum(10, 12);
35+
void asyncSum() throws Exception {
36+
Future<Long> future = MySwiftLibrary.asyncSum(10, 12);
3637

37-
Long result = future.join();
38+
Long result = future.get();
3839
assertEquals(22, result);
3940
}
4041

4142
@Test
42-
void asyncSleep() {
43-
CompletableFuture<Void> future = MySwiftLibrary.asyncSleep();
44-
future.join();
43+
void asyncSleep() throws Exception {
44+
Future<Void> future = MySwiftLibrary.asyncSleep();
45+
future.get();
4546
}
4647

4748
@Test
48-
void asyncCopy() {
49+
void asyncCopy() throws Exception {
4950
try (var arena = SwiftArena.ofConfined()) {
5051
MySwiftClass obj = MySwiftClass.init(10, 5, arena);
51-
CompletableFuture<MySwiftClass> future = MySwiftLibrary.asyncCopy(obj, arena);
52+
Future<MySwiftClass> future = MySwiftLibrary.asyncCopy(obj, arena);
5253

53-
MySwiftClass result = future.join();
54+
MySwiftClass result = future.get();
5455

5556
assertEquals(10, result.getX());
5657
assertEquals(5, result.getY());
@@ -59,7 +60,7 @@ void asyncCopy() {
5960

6061
@Test
6162
void asyncThrows() {
62-
CompletableFuture<Void> future = MySwiftLibrary.asyncThrows();
63+
Future<Void> future = MySwiftLibrary.asyncThrows();
6364

6465
ExecutionException ex = assertThrows(ExecutionException.class, future::get);
6566

@@ -70,14 +71,14 @@ void asyncThrows() {
7071
}
7172

7273
@Test
73-
void asyncOptional() {
74-
CompletableFuture<OptionalLong> future = MySwiftLibrary.asyncOptional(42);
75-
assertEquals(OptionalLong.of(42), future.join());
74+
void asyncOptional() throws Exception {
75+
Future<OptionalLong> future = MySwiftLibrary.asyncOptional(42);
76+
assertEquals(OptionalLong.of(42), future.get());
7677
}
7778

7879
@Test
79-
void asyncString() {
80-
CompletableFuture<String> future = MySwiftLibrary.asyncString("hey");
81-
assertEquals("hey", future.join());
80+
void asyncString() throws Exception {
81+
Future<String> future = MySwiftLibrary.asyncString("hey");
82+
assertEquals("hey", future.get());
8283
}
8384
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,10 @@ void addXWithJavaLong() {
165165
}
166166

167167
@Test
168-
void getAsyncVariable() {
168+
void getAsyncVariable() throws Exception {
169169
try (var arena = SwiftArena.ofConfined()) {
170170
MySwiftClass c1 = MySwiftClass.init(20, 10, arena);
171-
assertEquals(42, c1.getGetAsync().join());
171+
assertEquals(42, c1.getGetAsync().get());
172172
}
173173
}
174174
}

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -501,40 +501,55 @@ extension JNISwift2JavaGenerator {
501501
originalFunctionSignature: SwiftFunctionSignature,
502502
mode: JExtractAsyncFuncMode
503503
) {
504+
// Update translated function
505+
let nativeFutureType: JavaType
506+
let translatedFutureType: JavaType
507+
let completeMethodID: String
508+
let completeExceptionallyMethodID: String
509+
504510
switch mode {
505511
case .completableFuture:
506-
// Update translated function
507-
508-
let nativeFutureType = JavaType.completableFuture(nativeFunctionSignature.result.javaType)
512+
nativeFutureType = .completableFuture(nativeFunctionSignature.result.javaType)
513+
translatedFutureType = .completableFuture(translatedFunctionSignature.resultType.javaType)
514+
completeMethodID = "_JNIMethodIDCache.CompletableFuture.complete"
515+
completeExceptionallyMethodID = "_JNIMethodIDCache.CompletableFuture.completeExceptionally"
516+
517+
case .legacyFuture:
518+
nativeFutureType = .simpleCompletableFuture(nativeFunctionSignature.result.javaType)
519+
translatedFutureType = .future(translatedFunctionSignature.resultType.javaType)
520+
completeMethodID = "_JNIMethodIDCache.SimpleCompletableFuture.complete"
521+
completeExceptionallyMethodID = "_JNIMethodIDCache.SimpleCompletableFuture.completeExceptionally"
522+
}
509523

510-
let futureOutParameter = OutParameter(
511-
name: "$future",
512-
type: nativeFutureType,
513-
allocation: .new
514-
)
524+
let futureOutParameter = OutParameter(
525+
name: "future$",
526+
type: nativeFutureType,
527+
allocation: .new
528+
)
515529

516-
let result = translatedFunctionSignature.resultType
517-
translatedFunctionSignature.resultType = TranslatedResult(
518-
javaType: .completableFuture(translatedFunctionSignature.resultType.javaType),
519-
annotations: result.annotations,
520-
outParameters: result.outParameters + [futureOutParameter],
521-
conversion: .aggregate(variable: nil, [
522-
.print(.placeholder), // Make the downcall
523-
.method(.constant("$future"), function: "thenApply", arguments: [
524-
.lambda(args: ["futureResult$"], body: .replacingPlaceholder(result.conversion, placeholder: "futureResult$"))
525-
])
530+
let result = translatedFunctionSignature.resultType
531+
translatedFunctionSignature.resultType = TranslatedResult(
532+
javaType: translatedFutureType,
533+
annotations: result.annotations,
534+
outParameters: result.outParameters + [futureOutParameter],
535+
conversion: .aggregate(variable: nil, [
536+
.print(.placeholder), // Make the downcall
537+
.method(.constant("future$"), function: "thenApply", arguments: [
538+
.lambda(args: ["futureResult$"], body: .replacingPlaceholder(result.conversion, placeholder: "futureResult$"))
526539
])
527-
)
540+
])
541+
)
528542

529-
// Update native function
530-
nativeFunctionSignature.result.conversion = .asyncCompleteFuture(
531-
swiftFunctionResultType: originalFunctionSignature.result.type,
532-
nativeFunctionSignature: nativeFunctionSignature,
533-
isThrowing: originalFunctionSignature.isThrowing
534-
)
535-
nativeFunctionSignature.result.javaType = .void
536-
nativeFunctionSignature.result.outParameters.append(.init(name: "result_future", type: nativeFutureType))
537-
}
543+
// Update native function
544+
nativeFunctionSignature.result.conversion = .asyncCompleteFuture(
545+
swiftFunctionResultType: originalFunctionSignature.result.type,
546+
nativeFunctionSignature: nativeFunctionSignature,
547+
isThrowing: originalFunctionSignature.isThrowing,
548+
completeMethodID: completeMethodID,
549+
completeExceptionallyMethodID: completeExceptionallyMethodID
550+
)
551+
nativeFunctionSignature.result.javaType = .void
552+
nativeFunctionSignature.result.outParameters.append(.init(name: "result_future", type: nativeFutureType))
538553
}
539554

540555
func translateProtocolParameter(

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,9 @@ extension JNISwift2JavaGenerator {
689689
indirect case asyncCompleteFuture(
690690
swiftFunctionResultType: SwiftType,
691691
nativeFunctionSignature: NativeFunctionSignature,
692-
isThrowing: Bool
692+
isThrowing: Bool,
693+
completeMethodID: String,
694+
completeExceptionallyMethodID: String
693695
)
694696

695697
/// `{ (args) -> return body }`
@@ -927,7 +929,9 @@ extension JNISwift2JavaGenerator {
927929
case .asyncCompleteFuture(
928930
let swiftFunctionResultType,
929931
let nativeFunctionSignature,
930-
let isThrowing
932+
let isThrowing,
933+
let completeMethodID,
934+
let completeExceptionallyMethodID
931935
):
932936
var globalRefs: [String] = ["globalFuture"]
933937

@@ -954,7 +958,7 @@ extension JNISwift2JavaGenerator {
954958
printer.print("environment = try! JavaVirtualMachine.shared().environment()")
955959
let inner = nativeFunctionSignature.result.conversion.render(&printer, "swiftResult$")
956960
if swiftFunctionResultType.isVoid {
957-
printer.print("environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: nil)])")
961+
printer.print("environment.interface.CallBooleanMethodA(environment, globalFuture, \(completeMethodID), [jvalue(l: nil)])")
958962
} else {
959963
let result: String
960964
if nativeFunctionSignature.result.javaType.requiresBoxing {
@@ -964,7 +968,7 @@ extension JNISwift2JavaGenerator {
964968
result = inner
965969
}
966970

967-
printer.print("environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: \(result))])")
971+
printer.print("environment.interface.CallBooleanMethodA(environment, globalFuture, \(completeMethodID), [jvalue(l: \(result))])")
968972
}
969973
}
970974

@@ -986,7 +990,7 @@ extension JNISwift2JavaGenerator {
986990
"""
987991
let catchEnvironment = try! JavaVirtualMachine.shared().environment()
988992
let exception = catchEnvironment.interface.NewObjectA(catchEnvironment, _JNIMethodIDCache.Exception.class, _JNIMethodIDCache.Exception.constructWithMessage, [String(describing: error).getJValue(in: catchEnvironment)])
989-
catchEnvironment.interface.CallBooleanMethodA(catchEnvironment, globalFuture, _JNIMethodIDCache.CompletableFuture.completeExceptionally, [jvalue(l: exception)])
993+
catchEnvironment.interface.CallBooleanMethodA(catchEnvironment, globalFuture, \(completeExceptionallyMethodID), [jvalue(l: exception)])
990994
"""
991995
)
992996
}

Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,9 @@ extension JavaType {
4444
static func completableFuture(_ T: JavaType) -> JavaType {
4545
.class(package: "java.util.concurrent", name: "CompletableFuture", typeParameters: [T.boxedType])
4646
}
47+
48+
/// The description of the type java.util.concurrent.Future<T>
49+
static func future(_ T: JavaType) -> JavaType {
50+
.class(package: "java.util.concurrent", name: "Future", typeParameters: [T.boxedType])
51+
}
4752
}

Sources/JExtractSwiftLib/JavaTypes/JavaType+SwiftKit.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,9 @@ extension JavaType {
8585
}
8686
}
8787

88+
/// The description of the type org.swift.swiftkit.core.SimpleCompletableFuture<T>
89+
static func simpleCompletableFuture(_ T: JavaType) -> JavaType {
90+
.class(package: "org.swift.swiftkit.core", name: "SimpleCompletableFuture", typeParameters: [T.boxedType])
91+
}
92+
8893
}

Sources/SwiftJavaConfigurationShared/JExtract/JExtractAsyncFuncMode.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public enum JExtractAsyncFuncMode: String, Codable {
2323
/// Android 23 and below.
2424
///
2525
/// - Note: Prefer using the `completableFuture` mode instead, if possible.
26-
// case future
26+
case legacyFuture
2727
}
2828

2929
extension JExtractAsyncFuncMode {

Sources/SwiftJavaRuntimeSupport/DefaultCaches.swift renamed to Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,36 @@ extension _JNIMethodIDCache {
4747
}
4848
}
4949

50+
public enum SimpleCompletableFuture {
51+
private static let completeMethod = Method(
52+
name: "complete",
53+
signature: "(Ljava/lang/Object;)Z"
54+
)
55+
56+
private static let completeExceptionallyMethod = Method(
57+
name: "completeExceptionally",
58+
signature: "(Ljava/lang/Throwable;)Z"
59+
)
60+
61+
private static let cache = _JNIMethodIDCache(
62+
environment: try! JavaVirtualMachine.shared().environment(),
63+
className: "org/swift/swiftkit/core/SimpleCompletableFuture",
64+
methods: [completeMethod, completeExceptionallyMethod]
65+
)
66+
67+
public static var `class`: jclass {
68+
cache.javaClass
69+
}
70+
71+
public static var complete: jmethodID {
72+
cache.methods[completeMethod]!
73+
}
74+
75+
public static var completeExceptionally: jmethodID {
76+
cache.methods[completeExceptionallyMethod]!
77+
}
78+
}
79+
5080
public enum Exception {
5181
private static let messageConstructor = Method(name: "<init>", signature: "(Ljava/lang/String;)V")
5282

0 commit comments

Comments
 (0)