Skip to content

Commit af2a727

Browse files
authored
Fix JNI generation for sending closures (#700)
1 parent cbbe9c0 commit af2a727

2 files changed

Lines changed: 36 additions & 15 deletions

File tree

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,23 +1633,34 @@ extension JNISwift2JavaGenerator {
16331633
// Global ref all indirect returns
16341634
for outParameter in nativeFunctionSignature.result.outParameters {
16351635
printer.print(
1636-
"let \(outParameter.name) = environment.interface.NewGlobalRef(environment, \(outParameter.name))"
1636+
"nonisolated(unsafe) let \(outParameter.name) = environment.interface.NewGlobalRef(environment, \(outParameter.name))"
16371637
)
16381638
globalRefs.append(outParameter.name)
16391639
}
16401640

16411641
// We also need to global ref any objects passed in
16421642
for parameter in nativeFunctionSignature.parameters.flatMap(\.parameters) where !parameter.type.isPrimitive {
1643-
printer.print("let \(parameter.name) = environment.interface.NewGlobalRef(environment, \(parameter.name))")
1643+
printer.print("nonisolated(unsafe) let \(parameter.name) = environment.interface.NewGlobalRef(environment, \(parameter.name))")
16441644
globalRefs.append(parameter.name)
16451645
}
16461646

16471647
printer.print(
16481648
"""
1649-
let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
1649+
nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
16501650
"""
16511651
)
16521652

1653+
if let selfParameter = nativeFunctionSignature.selfParameter {
1654+
for parameter in selfParameter.parameters {
1655+
printer.print("nonisolated(unsafe) let \(parameter.name)Sendable$ = \(parameter.name)$")
1656+
}
1657+
}
1658+
if let selfTypeParameter = nativeFunctionSignature.selfTypeParameter {
1659+
for parameter in selfTypeParameter.parameters {
1660+
printer.print("nonisolated(unsafe) let \(parameter.name)Sendable$ = \(parameter.name)$")
1661+
}
1662+
}
1663+
16531664
func printDo(printer: inout CodePrinter) {
16541665
// Make sure try/await are printed when necessary and avoid duplicate, or wrong-order, keywords (which would cause warnings)
16551666
let placeholderWithoutTry =
@@ -1692,6 +1703,16 @@ extension JNISwift2JavaGenerator {
16921703
}
16931704

16941705
func printTaskBody(printer: inout CodePrinter) {
1706+
if let selfParameter = nativeFunctionSignature.selfParameter {
1707+
for parameter in selfParameter.parameters {
1708+
printer.print("let \(parameter.name)$ = \(parameter.name)Sendable$")
1709+
}
1710+
}
1711+
if let selfTypeParameter = nativeFunctionSignature.selfTypeParameter {
1712+
for parameter in selfTypeParameter.parameters {
1713+
printer.print("let \(parameter.name)$ = \(parameter.name)Sendable$")
1714+
}
1715+
}
16951716
printer.printBraceBlock("defer") { printer in
16961717
// Defer might on any thread, so we need to attach environment.
16971718
printer.print("let deferEnvironment = try! JavaVirtualMachine.shared().environment()")
@@ -1722,8 +1743,8 @@ extension JNISwift2JavaGenerator {
17221743
printer.printHashIfBlock("swift(>=6.2)") { printer in
17231744
printer.printBraceBlock("if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *)") { printer in
17241745
printer.printBraceBlock("task = Task.immediate") { printer in
1725-
// Immediate runs on the caller thread, so we don't need to attach the environment again.
1726-
printer.print("var environment = environment!") // this is to ensure we always use the same environment name, even though we are rebinding it.
1746+
// Even immediate tasks are a sending closure in Swift 6.2+, so reattach instead of capturing the caller's environment directly.
1747+
printer.print("var environment = try! JavaVirtualMachine.shared().environment()")
17271748
printTaskBody(printer: &printer)
17281749
}
17291750
}

Tests/JExtractSwiftTests/JNI/JNIAsyncTests.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ struct JNIAsyncTests {
6161
"""
6262
@_cdecl("Java_com_example_swift_SwiftModule__00024asyncVoid__Ljava_util_concurrent_CompletableFuture_2")
6363
public func Java_com_example_swift_SwiftModule__00024asyncVoid__Ljava_util_concurrent_CompletableFuture_2(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, result_future: jobject?) {
64-
let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
64+
nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
6565
var task: Task<Void, Never>? = nil
6666
#if swift(>=6.2)
6767
if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) {
6868
task = Task.immediate {
69-
var environment = environment!
69+
var environment = try! JavaVirtualMachine.shared().environment()
7070
defer {
7171
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
7272
deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
@@ -137,12 +137,12 @@ struct JNIAsyncTests {
137137
"""
138138
@_cdecl("Java_com_example_swift_SwiftModule__00024async__Ljava_util_concurrent_CompletableFuture_2")
139139
public func Java_com_example_swift_SwiftModule__00024async__Ljava_util_concurrent_CompletableFuture_2(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, result_future: jobject?) {
140-
let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
140+
nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
141141
var task: Task<Void, Never>? = nil
142142
#if swift(>=6.2)
143143
if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) {
144144
task = Task.immediate {
145-
var environment = environment!
145+
var environment = try! JavaVirtualMachine.shared().environment()
146146
defer {
147147
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
148148
deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
@@ -227,12 +227,12 @@ struct JNIAsyncTests {
227227
"""
228228
@_cdecl("Java_com_example_swift_SwiftModule__00024async__JLjava_util_concurrent_CompletableFuture_2")
229229
public func Java_com_example_swift_SwiftModule__00024async__JLjava_util_concurrent_CompletableFuture_2(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, i: jlong, result_future: jobject?) {
230-
let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
230+
nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
231231
var task: Task<Void, Never>? = nil
232232
#if swift(>=6.2)
233233
if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) {
234234
task = Task.immediate {
235-
var environment = environment!
235+
var environment = try! JavaVirtualMachine.shared().environment()
236236
defer {
237237
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
238238
deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
@@ -320,12 +320,12 @@ struct JNIAsyncTests {
320320
guard let c$ else {
321321
fatalError("c memory address was null in call to \\(#function)!")
322322
}
323-
let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
323+
nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
324324
var task: Task<Void, Never>? = nil
325325
#if swift(>=6.2)
326326
if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) {
327327
task = Task.immediate {
328-
var environment = environment!
328+
var environment = try! JavaVirtualMachine.shared().environment()
329329
defer {
330330
let deferEnvironment = try! JavaVirtualMachine.shared().environment()
331331
deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture)
@@ -403,8 +403,8 @@ struct JNIAsyncTests {
403403
"""
404404
@_cdecl("Java_com_example_swift_SwiftModule__00024async__Ljava_lang_String_2Ljava_util_concurrent_CompletableFuture_2")
405405
public func Java_com_example_swift_SwiftModule__00024async__Ljava_lang_String_2Ljava_util_concurrent_CompletableFuture_2(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, s: jstring?, result_future: jobject?) {
406-
let s = environment.interface.NewGlobalRef(environment, s)
407-
let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
406+
nonisolated(unsafe) let s = environment.interface.NewGlobalRef(environment, s)
407+
nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future)
408408
...
409409
defer {
410410
let deferEnvironment = try! JavaVirtualMachine.shared().environment()

0 commit comments

Comments
 (0)