diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index 954c865a..dc565af5 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -1633,23 +1633,34 @@ extension JNISwift2JavaGenerator { // Global ref all indirect returns for outParameter in nativeFunctionSignature.result.outParameters { printer.print( - "let \(outParameter.name) = environment.interface.NewGlobalRef(environment, \(outParameter.name))" + "nonisolated(unsafe) let \(outParameter.name) = environment.interface.NewGlobalRef(environment, \(outParameter.name))" ) globalRefs.append(outParameter.name) } // We also need to global ref any objects passed in for parameter in nativeFunctionSignature.parameters.flatMap(\.parameters) where !parameter.type.isPrimitive { - printer.print("let \(parameter.name) = environment.interface.NewGlobalRef(environment, \(parameter.name))") + printer.print("nonisolated(unsafe) let \(parameter.name) = environment.interface.NewGlobalRef(environment, \(parameter.name))") globalRefs.append(parameter.name) } printer.print( """ - let globalFuture = environment.interface.NewGlobalRef(environment, result_future) + nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future) """ ) + if let selfParameter = nativeFunctionSignature.selfParameter { + for parameter in selfParameter.parameters { + printer.print("nonisolated(unsafe) let \(parameter.name)Sendable$ = \(parameter.name)$") + } + } + if let selfTypeParameter = nativeFunctionSignature.selfTypeParameter { + for parameter in selfTypeParameter.parameters { + printer.print("nonisolated(unsafe) let \(parameter.name)Sendable$ = \(parameter.name)$") + } + } + func printDo(printer: inout CodePrinter) { // Make sure try/await are printed when necessary and avoid duplicate, or wrong-order, keywords (which would cause warnings) let placeholderWithoutTry = @@ -1692,6 +1703,16 @@ extension JNISwift2JavaGenerator { } func printTaskBody(printer: inout CodePrinter) { + if let selfParameter = nativeFunctionSignature.selfParameter { + for parameter in selfParameter.parameters { + printer.print("let \(parameter.name)$ = \(parameter.name)Sendable$") + } + } + if let selfTypeParameter = nativeFunctionSignature.selfTypeParameter { + for parameter in selfTypeParameter.parameters { + printer.print("let \(parameter.name)$ = \(parameter.name)Sendable$") + } + } printer.printBraceBlock("defer") { printer in // Defer might on any thread, so we need to attach environment. printer.print("let deferEnvironment = try! JavaVirtualMachine.shared().environment()") @@ -1722,8 +1743,8 @@ extension JNISwift2JavaGenerator { printer.printHashIfBlock("swift(>=6.2)") { printer in printer.printBraceBlock("if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *)") { printer in printer.printBraceBlock("task = Task.immediate") { printer in - // Immediate runs on the caller thread, so we don't need to attach the environment again. - printer.print("var environment = environment!") // this is to ensure we always use the same environment name, even though we are rebinding it. + // Even immediate tasks are a sending closure in Swift 6.2+, so reattach instead of capturing the caller's environment directly. + printer.print("var environment = try! JavaVirtualMachine.shared().environment()") printTaskBody(printer: &printer) } } diff --git a/Tests/JExtractSwiftTests/JNI/JNIAsyncTests.swift b/Tests/JExtractSwiftTests/JNI/JNIAsyncTests.swift index b7b4a41a..7c968e41 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIAsyncTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIAsyncTests.swift @@ -61,12 +61,12 @@ struct JNIAsyncTests { """ @_cdecl("Java_com_example_swift_SwiftModule__00024asyncVoid__Ljava_util_concurrent_CompletableFuture_2") public func Java_com_example_swift_SwiftModule__00024asyncVoid__Ljava_util_concurrent_CompletableFuture_2(environment: UnsafeMutablePointer!, thisClass: jclass, result_future: jobject?) { - let globalFuture = environment.interface.NewGlobalRef(environment, result_future) + nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future) var task: Task? = nil #if swift(>=6.2) if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) { task = Task.immediate { - var environment = environment! + var environment = try! JavaVirtualMachine.shared().environment() defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) @@ -137,12 +137,12 @@ struct JNIAsyncTests { """ @_cdecl("Java_com_example_swift_SwiftModule__00024async__Ljava_util_concurrent_CompletableFuture_2") public func Java_com_example_swift_SwiftModule__00024async__Ljava_util_concurrent_CompletableFuture_2(environment: UnsafeMutablePointer!, thisClass: jclass, result_future: jobject?) { - let globalFuture = environment.interface.NewGlobalRef(environment, result_future) + nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future) var task: Task? = nil #if swift(>=6.2) if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) { task = Task.immediate { - var environment = environment! + var environment = try! JavaVirtualMachine.shared().environment() defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) @@ -227,12 +227,12 @@ struct JNIAsyncTests { """ @_cdecl("Java_com_example_swift_SwiftModule__00024async__JLjava_util_concurrent_CompletableFuture_2") public func Java_com_example_swift_SwiftModule__00024async__JLjava_util_concurrent_CompletableFuture_2(environment: UnsafeMutablePointer!, thisClass: jclass, i: jlong, result_future: jobject?) { - let globalFuture = environment.interface.NewGlobalRef(environment, result_future) + nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future) var task: Task? = nil #if swift(>=6.2) if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) { task = Task.immediate { - var environment = environment! + var environment = try! JavaVirtualMachine.shared().environment() defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) @@ -320,12 +320,12 @@ struct JNIAsyncTests { guard let c$ else { fatalError("c memory address was null in call to \\(#function)!") } - let globalFuture = environment.interface.NewGlobalRef(environment, result_future) + nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future) var task: Task? = nil #if swift(>=6.2) if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *) { task = Task.immediate { - var environment = environment! + var environment = try! JavaVirtualMachine.shared().environment() defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) @@ -403,8 +403,8 @@ struct JNIAsyncTests { """ @_cdecl("Java_com_example_swift_SwiftModule__00024async__Ljava_lang_String_2Ljava_util_concurrent_CompletableFuture_2") public func Java_com_example_swift_SwiftModule__00024async__Ljava_lang_String_2Ljava_util_concurrent_CompletableFuture_2(environment: UnsafeMutablePointer!, thisClass: jclass, s: jstring?, result_future: jobject?) { - let s = environment.interface.NewGlobalRef(environment, s) - let globalFuture = environment.interface.NewGlobalRef(environment, result_future) + nonisolated(unsafe) let s = environment.interface.NewGlobalRef(environment, s) + nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future) ... defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment()