Skip to content

Commit e0b3930

Browse files
committed
get upcall working
1 parent a774558 commit e0b3930

File tree

15 files changed

+169
-150
lines changed

15 files changed

+169
-150
lines changed

Package.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,15 @@ let package = Package(
170170
// Support library written in Swift for SwiftKit "Java"
171171
.library(
172172
name: "SwiftJavaRuntimeSupport",
173-
type: .dynamic,
174173
targets: ["SwiftJavaRuntimeSupport"]
175174
),
176175

176+
.library(
177+
name: "SwiftRuntimeFunctions",
178+
type: .dynamic,
179+
targets: ["SwiftRuntimeFunctions"]
180+
),
181+
177182
.library(
178183
name: "JExtractSwiftLib",
179184
targets: ["JExtractSwiftLib"]
@@ -214,6 +219,7 @@ let package = Package(
214219
dependencies: [
215220
"SwiftJava",
216221
"SwiftJavaRuntimeSupport",
222+
"SwiftRuntimeFunctions",
217223
]
218224
),
219225

@@ -362,6 +368,18 @@ let package = Package(
362368
]
363369
),
364370

371+
.target(
372+
name: "SwiftRuntimeFunctions",
373+
dependencies: [
374+
"CSwiftJavaJNI",
375+
"SwiftJava"
376+
],
377+
swiftSettings: [
378+
.swiftLanguageMode(.v5),
379+
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
380+
]
381+
),
382+
365383
.target(
366384
name: "CSwiftJavaJNI",
367385
swiftSettings: [

Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Async.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ public func asyncOptional(i: Int64) async throws -> Int64? {
3333
return i
3434
}
3535

36+
public func asyncThrows() async throws {
37+
throw MySwiftError.swiftError
38+
}
39+
3640
public func asyncRunningSum() async -> Int64 {
3741
let totalSum = await withTaskGroup(of: Int64.self) { group in
3842
// 1. Add child tasks to the group

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,22 @@ void asyncCopy() {
5656
assertEquals(5, result.getY());
5757
}
5858
}
59+
60+
@Test
61+
void asyncThrows() {
62+
CompletableFuture<Void> future = MySwiftLibrary.asyncThrows();
63+
64+
ExecutionException ex = assertThrows(ExecutionException.class, future::get);
65+
66+
Throwable cause = ex.getCause();
67+
assertNotNull(cause);
68+
assertEquals(Exception.class, cause.getClass());
69+
assertEquals("swiftError", cause.getMessage());
70+
}
71+
72+
@Test
73+
void asyncOptional() {
74+
CompletableFuture<OptionalLong> future = MySwiftLibrary.asyncOptional(42);
75+
assertEquals(OptionalLong.of(42), future.join());
76+
}
5977
}

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ extension FFMSwift2JavaGenerator {
108108
"""
109109
// Generated by swift-java
110110
111-
import SwiftJavaRuntimeSupport
111+
import SwiftRuntimeFunctions
112112
113113
""")
114114

@@ -136,7 +136,7 @@ extension FFMSwift2JavaGenerator {
136136
"""
137137
// Generated by swift-java
138138
139-
import SwiftJavaRuntimeSupport
139+
import SwiftRuntimeFunctions
140140
141141
"""
142142
)

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,8 @@ extension JNISwift2JavaGenerator {
528528
nativeFunctionSignature.result.conversion,
529529
swiftFunctionResultType: originalFunctionSignature.result.type,
530530
nativeReturnType: nativeFunctionSignature.result.javaType,
531-
outParameters: nativeFunctionSignature.result.outParameters
531+
outParameters: nativeFunctionSignature.result.outParameters,
532+
isThrowing: originalFunctionSignature.isThrowing
532533
)
533534
nativeFunctionSignature.result.outParameters.append(.init(name: "result_future", type: nativeFutureType))
534535

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift

Lines changed: 51 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,8 @@ extension JNISwift2JavaGenerator {
573573
NativeSwiftConversionStep,
574574
swiftFunctionResultType: SwiftType,
575575
nativeReturnType: JavaType,
576-
outParameters: [JavaParameter]
576+
outParameters: [JavaParameter],
577+
isThrowing: Bool
577578
)
578579

579580
/// Returns the conversion string applied to the placeholder.
@@ -808,7 +809,8 @@ extension JNISwift2JavaGenerator {
808809
let inner,
809810
let swiftFunctionResultType,
810811
let nativeReturnType,
811-
let outParameters
812+
let outParameters,
813+
let isThrowing
812814
):
813815
// Global ref all indirect returns
814816
for outParameter in outParameters {
@@ -818,71 +820,66 @@ extension JNISwift2JavaGenerator {
818820
printer.print(
819821
"""
820822
let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
821-
let completableFutureClazz$ = environment.interface.FindClass(environment, "java/util/concurrent/CompletableFuture")!
822-
let completeMethodID = environment.interface.GetMethodID(environment, completableFutureClazz$, "complete", "(Ljava/lang/Object;)Z")!
823823
"""
824824
)
825825

826-
printer.printBraceBlock("Task") { printer in
826+
func printDo(printer: inout CodePrinter) {
827827
printer.print("let swiftResult$ = await \(placeholder)")
828-
printer.print(
829-
"""
830-
let environment = try JavaVirtualMachine.shared().environment()
831-
"""
832-
)
833-
printer.printBraceBlock("defer") { printer in
834-
printer.print("environment.interface.DeleteGlobalRef(environment, globalFuture)")
835-
for outParameter in outParameters {
836-
printer.print("environment.interface.DeleteGlobalRef(environment, \(outParameter.name))")
837-
}
838-
}
828+
printer.print("environment = try JavaVirtualMachine.shared().environment()")
839829
let inner = inner.render(&printer, "swiftResult$")
840830
if swiftFunctionResultType.isVoid {
841-
printer.print(inner)
831+
printer.print("environment.interface.CallBooleanMethodA(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, [jvalue(l: nil)])")
842832
} else {
843833
printer.printBraceBlock("withVaList([SwiftJavaRuntimeSupport._JNIBoxedConversions.box(\(inner), in: environment)])") { printer in
844-
printer.print("environment.interface.CallBooleanMethodV(environment, globalFuture, completeMethodID, $0)")
834+
printer.print("environment.interface.CallBooleanMethodV(environment, globalFuture, _JNIMethodIDCache.CompletableFuture.complete, $0)")
845835
}
846836
}
847837
}
848838

849-
return ""
839+
func printTask(printer: inout CodePrinter) {
840+
printer.printBraceBlock("defer") { printer in
841+
// Defer might on any thread, so we need to attach environment.
842+
printer.print("let deferEnvironment = try! JavaVirtualMachine.shared().environment()")
843+
printer.print("environment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)")
844+
for outParameter in outParameters {
845+
printer.print("environment.interface.DeleteGlobalRef(deferEnvironment, \(outParameter.name))")
846+
}
847+
}
848+
if isThrowing {
849+
printer.printBraceBlock("do") { printer in
850+
printDo(printer: &printer)
851+
}
852+
printer.printBraceBlock("catch") { printer in
853+
// We might not be on the same thread after the suspension, so we need to attach the thread again.
854+
printer.print(
855+
"""
856+
let catchEnvironment = try! JavaVirtualMachine.shared().environment()
857+
let exception = catchEnvironment.interface.NewObjectA(catchEnvironment, _JNIMethodIDCache.Exception.class, _JNIMethodIDCache.Exception.constructWithMessage, [String(describing: error).getJValue(in: catchEnvironment)])
858+
catchEnvironment.interface.CallBooleanMethodA(catchEnvironment, globalFuture, _JNIMethodIDCache.CompletableFuture.completeExceptionally, [jvalue(l: exception)])
859+
"""
860+
)
861+
}
862+
} else {
863+
printDo(printer: &printer)
864+
}
865+
}
850866

851-
// printer.print("let _semaphore$ = _Semaphore(value: 0)")
852-
// let resultType = isThrowing ? "Result<\(swiftFunctionResultType), any Error>" : swiftFunctionResultType.description
853-
// printer.print("var swiftResult$: \(resultType)!")
854-
//
855-
// func printInner(printer: inout CodePrinter) {
856-
// if isThrowing {
857-
// printer.printBraceBlock("do") { printer in
858-
// printer.print("swiftResult$ = await Result.success(\(placeholder))")
859-
// }
860-
// printer.printBraceBlock("catch") { printer in
861-
// printer.print("swiftResult$ = Result.failure(error)")
862-
// }
863-
// } else {
864-
// printer.print("swiftResult$ = await \(placeholder)")
865-
// }
866-
// printer.print("_semaphore$.signal()")
867-
// }
868-
//
869-
// printer.printBraceBlock("if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *)") { printer in
870-
// printer.printBraceBlock("Task.immediate") { printer in
871-
// printInner(printer: &printer)
872-
// }
873-
// }
874-
// printer.printBraceBlock("else") { printer in
875-
// printer.printBraceBlock("Task") { printer in
876-
// printInner(printer: &printer)
877-
// }
878-
// }
879-
// printer.print(
880-
// """
881-
// _semaphore$.wait()
882-
// """
883-
// )
884-
// let inner = inner.render(&printer, isThrowing ? "try swiftResult$.get()" : "swiftResult$")
885-
// return inner
867+
printer.printBraceBlock("if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *)") { printer in
868+
printer.printBraceBlock("Task.immediate") { printer in
869+
// Immediate runs on the caller thread, so we don't need to attach the environment again.
870+
printer.print("var environment = environment!") // this is to ensure we always use the same environment name, even though we are rebinding it.
871+
printTask(printer: &printer)
872+
}
873+
}
874+
printer.printBraceBlock("else") { printer in
875+
printer.printBraceBlock("Task") { printer in
876+
// We can be on any thread, so we need to attach the thread.
877+
printer.print("var environment = try! JavaVirtualMachine.shared().environment()")
878+
printTask(printer: &printer)
879+
}
880+
}
881+
882+
return ""
886883
}
887884
}
888885
}

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ extension JNISwift2JavaGenerator {
363363
}
364364
}
365365

366-
if decl.isThrowing {
366+
if decl.isThrowing, !decl.isAsync {
367367
let dummyReturn: String
368368

369369
if nativeSignature.result.javaType.isVoid {

Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ public struct SwiftFunctionSignature: Equatable {
3434
effectSpecifiers.contains(.async)
3535
}
3636

37+
var isThrowing: Bool {
38+
effectSpecifiers.contains(.throws)
39+
}
40+
3741
init(
3842
selfParameter: SwiftSelfParameter? = nil,
3943
parameters: [SwiftParameter],
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import SwiftJava
2+
3+
extension _JNIMethodIDCache {
4+
public enum CompletableFuture {
5+
private static let completeMethod = Method(
6+
name: "complete",
7+
signature: "(Ljava/lang/Object;)Z"
8+
)
9+
10+
private static let completeExceptionallyMethod = Method(
11+
name: "completeExceptionally",
12+
signature: "(Ljava/lang/Throwable;)Z"
13+
)
14+
15+
private static let cache = _JNIMethodIDCache(
16+
environment: try! JavaVirtualMachine.shared().environment(),
17+
className: "java/util/concurrent/CompletableFuture",
18+
methods: [completeMethod, completeExceptionallyMethod]
19+
)
20+
21+
public static var `class`: jclass {
22+
cache.javaClass
23+
}
24+
25+
/// CompletableFuture<T>.complete(T)
26+
public static var complete: jmethodID {
27+
cache.methods[completeMethod]!
28+
}
29+
30+
/// CompletableFuture<T>.completeExceptionally(Throwable)
31+
public static var completeExceptionally: jmethodID {
32+
cache.methods[completeExceptionallyMethod]!
33+
}
34+
}
35+
36+
public enum Exception {
37+
private static let messageConstructor = Method(name: "<init>", signature: "(Ljava/lang/String;)V")
38+
39+
private static let cache = _JNIMethodIDCache(
40+
environment: try! JavaVirtualMachine.shared().environment(),
41+
className: "java/lang/Exception",
42+
methods: [messageConstructor]
43+
)
44+
45+
public static var `class`: jclass {
46+
cache.javaClass
47+
}
48+
49+
public static var constructWithMessage: jmethodID {
50+
cache.methods[messageConstructor]!
51+
}
52+
}
53+
}

Sources/SwiftJavaRuntimeSupport/_Semaphore.swift

Lines changed: 0 additions & 76 deletions
This file was deleted.

0 commit comments

Comments
 (0)