From f23798867c2794700cd252507053d6e4d4a98d75 Mon Sep 17 00:00:00 2001 From: nerzh Date: Sat, 2 May 2026 05:56:33 +0200 Subject: [PATCH 1/2] Fix async JNI strict concurrency --- ...wift2JavaGenerator+NativeTranslation.swift | 101 +++++++++++++----- .../JNI/JNIAsyncTests.swift | 40 +++++-- 2 files changed, 107 insertions(+), 34 deletions(-) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index 5335b81b1..e261fcf25 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -232,7 +232,8 @@ extension JNISwift2JavaGenerator { ], conversion: .pointee(.extractSwiftValue(.placeholder, swiftType: type)), indirectConversion: nil, - conversionCheck: nil + conversionCheck: nil, + asyncTaskCaptureNames: [NativeSwiftConversionStep.extractedSwiftValueName(for: parameterName)] ) case .tuple([]): @@ -329,7 +330,8 @@ extension JNISwift2JavaGenerator { ], conversion: .extractMetatypeValue(.placeholder), indirectConversion: nil, - conversionCheck: nil + conversionCheck: nil, + asyncTaskCaptureNames: [NativeSwiftConversionStep.extractedSwiftValueName(for: parameterName)] ) case .tuple(let elements) where elements.count == 1: @@ -367,6 +369,7 @@ extension JNISwift2JavaGenerator { ) throws -> NativeParameter { var allJNIParameters: [JavaParameter] = [] var elementConversions: [(label: String?, conversion: NativeSwiftConversionStep)] = [] + var asyncTaskCaptureNames: [String] = [] for (idx, element) in elements.enumerated() { let elementParamName = "\(parameterName)_\(idx)" @@ -380,13 +383,15 @@ extension JNISwift2JavaGenerator { ) allJNIParameters.append(contentsOf: elementNative.parameters) elementConversions.append((label: element.label, conversion: elementNative.conversion)) + asyncTaskCaptureNames.append(contentsOf: elementNative.asyncTaskCaptureNames) } return NativeParameter( parameters: allJNIParameters, conversion: .tupleConstruct(elements: elementConversions), indirectConversion: nil, - conversionCheck: nil + conversionCheck: nil, + asyncTaskCaptureNames: asyncTaskCaptureNames ) } @@ -452,7 +457,8 @@ extension JNISwift2JavaGenerator { allowsJavaImplementations: allowsJavaImplementations ), indirectConversion: nil, - conversionCheck: nil + conversionCheck: nil, + asyncTaskCaptureNames: [NativeSwiftConversionStep.swiftObjectName(for: parameterName)] ) } @@ -529,7 +535,8 @@ extension JNISwift2JavaGenerator { ) ), indirectConversion: nil, - conversionCheck: nil + conversionCheck: nil, + asyncTaskCaptureNames: [NativeSwiftConversionStep.extractedSwiftValueName(for: parameterName)] ) default: @@ -1072,6 +1079,24 @@ extension JNISwift2JavaGenerator { /// Represents check operations executed in if/guard conditional block for check during conversion let conversionCheck: NativeSwiftConversionCheck? + + /// Temporary values created by this parameter conversion that must be moved + /// into async task bodies through an unchecked Sendable wrapper. + let asyncTaskCaptureNames: [String] + + init( + parameters: [JavaParameter], + conversion: NativeSwiftConversionStep, + indirectConversion: NativeSwiftConversionStep?, + conversionCheck: NativeSwiftConversionCheck?, + asyncTaskCaptureNames: [String] = [] + ) { + self.parameters = parameters + self.conversion = conversion + self.indirectConversion = indirectConversion + self.conversionCheck = conversionCheck + self.asyncTaskCaptureNames = asyncTaskCaptureNames + } } struct NativeResult { @@ -1219,6 +1244,14 @@ extension JNISwift2JavaGenerator { /// Destructures a Swift tuple result and writes each element to an out-parameter. indirect case tupleDestructure(elements: [(index: Int, label: String?, conversion: NativeSwiftConversionStep, outParamName: String, javaType: JavaType)]) + static func extractedSwiftValueName(for name: String) -> String { + "\(name)$" + } + + static func swiftObjectName(for name: String) -> String { + "\(name)swiftObject$" + } + /// Promotes the outermost `.getJNIValue` to `.getJNILocalRefValue`. /// Used for `@_cdecl` return positions to ensure the local ref survives /// ARC destruction of temporary `JavaObject`s. @@ -1268,7 +1301,7 @@ extension JNISwift2JavaGenerator { let allowsJavaImplementations ): let inner = inner.render(&printer, placeholder) - let variableName = "\(inner)swiftObject$" + let variableName = Self.swiftObjectName(for: inner) let existentialType = SwiftKitPrinting.renderExistentialType(protocolTypes) printer.print("let \(variableName): \(existentialType)") @@ -1344,7 +1377,7 @@ extension JNISwift2JavaGenerator { case .extractSwiftValue(let inner, let swiftType, let allowNil, let convertLongFromJNI): let inner = inner.render(&printer, placeholder) - let pointerName = "\(inner)$" + let pointerName = Self.extractedSwiftValueName(for: inner) if !allowNil { printer.print(#"assert(\#(inner) != 0, "\#(inner) memory address was null")"#) } @@ -1367,7 +1400,7 @@ extension JNISwift2JavaGenerator { case .extractMetatypeValue(let inner): let inner = inner.render(&printer, placeholder) - let pointerName = "\(inner)$" + let pointerName = Self.extractedSwiftValueName(for: inner) printer.print( """ let \(inner)Bits$ = Int(Int64(fromJNI: \(inner), in: environment)) @@ -1629,36 +1662,52 @@ extension JNISwift2JavaGenerator { let completeExceptionallyMethodID ): var globalRefs: [String] = ["globalFuture"] + var asyncTaskCaptureNames: [String] = [] + + func appendAsyncTaskCaptureNames(_ names: [String]) { + for name in names where !asyncTaskCaptureNames.contains(name) { + asyncTaskCaptureNames.append(name) + } + } + + appendAsyncTaskCaptureNames(nativeFunctionSignature.selfParameter?.asyncTaskCaptureNames ?? []) + appendAsyncTaskCaptureNames(nativeFunctionSignature.selfTypeParameter?.asyncTaskCaptureNames ?? []) + for parameter in nativeFunctionSignature.parameters { + appendAsyncTaskCaptureNames(parameter.asyncTaskCaptureNames) + } + + printer.print( + """ + struct _SwiftJavaUncheckedSendable: @unchecked Sendable { + let value: T + } + """ + ) // Global ref all indirect returns for outParameter in nativeFunctionSignature.result.outParameters { printer.print( - "nonisolated(unsafe) let \(outParameter.name) = environment.interface.NewGlobalRef(environment, \(outParameter.name))" + "let \(outParameter.name)Sendable$ = _SwiftJavaUncheckedSendable(value: 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("nonisolated(unsafe) let \(parameter.name) = environment.interface.NewGlobalRef(environment, \(parameter.name))") + printer.print( + "let \(parameter.name)Sendable$ = _SwiftJavaUncheckedSendable(value: environment.interface.NewGlobalRef(environment, \(parameter.name)))" + ) globalRefs.append(parameter.name) } printer.print( """ - nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future) + let globalFutureSendable$ = _SwiftJavaUncheckedSendable(value: 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)$") - } + for name in asyncTaskCaptureNames { + printer.print("let \(name)Sendable$ = _SwiftJavaUncheckedSendable(value: \(name))") } func printDo(printer: inout CodePrinter) { @@ -1703,15 +1752,11 @@ 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$") - } + for globalRef in globalRefs { + printer.print("let \(globalRef) = \(globalRef)Sendable$.value") } - if let selfTypeParameter = nativeFunctionSignature.selfTypeParameter { - for parameter in selfTypeParameter.parameters { - printer.print("let \(parameter.name)$ = \(parameter.name)Sendable$") - } + for name in asyncTaskCaptureNames { + printer.print("let \(name) = \(name)Sendable$.value") } printer.printBraceBlock("defer") { printer in // Defer might on any thread, so we need to attach environment. diff --git a/Tests/JExtractSwiftTests/JNI/JNIAsyncTests.swift b/Tests/JExtractSwiftTests/JNI/JNIAsyncTests.swift index 7c968e419..492f10fcf 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIAsyncTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIAsyncTests.swift @@ -61,12 +61,16 @@ 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?) { - nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future) + struct _SwiftJavaUncheckedSendable: @unchecked Sendable { + let value: T + } + let globalFutureSendable$ = _SwiftJavaUncheckedSendable(value: 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 = try! JavaVirtualMachine.shared().environment() + let globalFuture = globalFutureSendable$.value defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) @@ -80,6 +84,7 @@ struct JNIAsyncTests { if task == nil { task = Task { var environment = try! JavaVirtualMachine.shared().environment() + let globalFuture = globalFutureSendable$.value defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) @@ -137,12 +142,16 @@ 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?) { - nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future) + struct _SwiftJavaUncheckedSendable: @unchecked Sendable { + let value: T + } + let globalFutureSendable$ = _SwiftJavaUncheckedSendable(value: 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 = try! JavaVirtualMachine.shared().environment() + let globalFuture = globalFutureSendable$.value defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) @@ -163,6 +172,7 @@ struct JNIAsyncTests { if task == nil { task = Task { var environment = try! JavaVirtualMachine.shared().environment() + let globalFuture = globalFutureSendable$.value defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) @@ -227,12 +237,16 @@ 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?) { - nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future) + struct _SwiftJavaUncheckedSendable: @unchecked Sendable { + let value: T + } + let globalFutureSendable$ = _SwiftJavaUncheckedSendable(value: 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 = try! JavaVirtualMachine.shared().environment() + let globalFuture = globalFutureSendable$.value defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) @@ -247,6 +261,7 @@ struct JNIAsyncTests { if task == nil { task = Task { var environment = try! JavaVirtualMachine.shared().environment() + let globalFuture = globalFutureSendable$.value defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) @@ -320,12 +335,18 @@ struct JNIAsyncTests { guard let c$ else { fatalError("c memory address was null in call to \\(#function)!") } - nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future) + struct _SwiftJavaUncheckedSendable: @unchecked Sendable { + let value: T + } + let globalFutureSendable$ = _SwiftJavaUncheckedSendable(value: environment.interface.NewGlobalRef(environment, result_future)) + let c$Sendable$ = _SwiftJavaUncheckedSendable(value: c$) 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 = try! JavaVirtualMachine.shared().environment() + let globalFuture = globalFutureSendable$.value + let c$ = c$Sendable$.value defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) @@ -343,6 +364,8 @@ struct JNIAsyncTests { if task == nil { task = Task { var environment = try! JavaVirtualMachine.shared().environment() + let globalFuture = globalFutureSendable$.value + let c$ = c$Sendable$.value defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) @@ -403,9 +426,14 @@ 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?) { - nonisolated(unsafe) let s = environment.interface.NewGlobalRef(environment, s) - nonisolated(unsafe) let globalFuture = environment.interface.NewGlobalRef(environment, result_future) + struct _SwiftJavaUncheckedSendable: @unchecked Sendable { + let value: T + } + let sSendable$ = _SwiftJavaUncheckedSendable(value: environment.interface.NewGlobalRef(environment, s)) + let globalFutureSendable$ = _SwiftJavaUncheckedSendable(value: environment.interface.NewGlobalRef(environment, result_future)) ... + let globalFuture = globalFutureSendable$.value + let s = sSendable$.value defer { let deferEnvironment = try! JavaVirtualMachine.shared().environment() deferEnvironment.interface.DeleteGlobalRef(deferEnvironment, globalFuture) From e4fd99ac204ec1275c7663ecf2ff0f45b0dd27ab Mon Sep 17 00:00:00 2001 From: nerzh Date: Thu, 14 May 2026 14:42:13 +0200 Subject: [PATCH 2/2] refactor async JNI captures --- ...wift2JavaGenerator+NativeTranslation.swift | 106 ++++++++++-------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index e261fcf25..32f52138a 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -230,10 +230,14 @@ extension JNISwift2JavaGenerator { parameters: [ JavaParameter(name: parameterName, type: .long) ], - conversion: .pointee(.extractSwiftValue(.placeholder, swiftType: type)), + conversion: .pointee( + .asyncTaskCapture( + .extractSwiftValue(.placeholder, swiftType: type), + name: NativeSwiftConversionStep.extractedSwiftValueName(for: parameterName) + ) + ), indirectConversion: nil, - conversionCheck: nil, - asyncTaskCaptureNames: [NativeSwiftConversionStep.extractedSwiftValueName(for: parameterName)] + conversionCheck: nil ) case .tuple([]): @@ -328,10 +332,12 @@ extension JNISwift2JavaGenerator { parameters: [ JavaParameter(name: parameterName, type: .long) ], - conversion: .extractMetatypeValue(.placeholder), + conversion: .asyncTaskCapture( + .extractMetatypeValue(.placeholder), + name: NativeSwiftConversionStep.extractedSwiftValueName(for: parameterName) + ), indirectConversion: nil, - conversionCheck: nil, - asyncTaskCaptureNames: [NativeSwiftConversionStep.extractedSwiftValueName(for: parameterName)] + conversionCheck: nil ) case .tuple(let elements) where elements.count == 1: @@ -369,7 +375,6 @@ extension JNISwift2JavaGenerator { ) throws -> NativeParameter { var allJNIParameters: [JavaParameter] = [] var elementConversions: [(label: String?, conversion: NativeSwiftConversionStep)] = [] - var asyncTaskCaptureNames: [String] = [] for (idx, element) in elements.enumerated() { let elementParamName = "\(parameterName)_\(idx)" @@ -383,15 +388,13 @@ extension JNISwift2JavaGenerator { ) allJNIParameters.append(contentsOf: elementNative.parameters) elementConversions.append((label: element.label, conversion: elementNative.conversion)) - asyncTaskCaptureNames.append(contentsOf: elementNative.asyncTaskCaptureNames) } return NativeParameter( parameters: allJNIParameters, conversion: .tupleConstruct(elements: elementConversions), indirectConversion: nil, - conversionCheck: nil, - asyncTaskCaptureNames: asyncTaskCaptureNames + conversionCheck: nil ) } @@ -446,19 +449,21 @@ extension JNISwift2JavaGenerator { parameters: [ JavaParameter(name: parameterName, type: .javaLangObject) ], - conversion: .interfaceToSwiftObject( - .placeholder, - swiftWrapperClassName: JNISwift2JavaGenerator.protocolParameterWrapperClassName( - methodName: methodName, - parameterName: parameterName, - parentName: parentName + conversion: .asyncTaskCapture( + .interfaceToSwiftObject( + .placeholder, + swiftWrapperClassName: JNISwift2JavaGenerator.protocolParameterWrapperClassName( + methodName: methodName, + parameterName: parameterName, + parentName: parentName + ), + protocolTypes: protocolTypes, + allowsJavaImplementations: allowsJavaImplementations ), - protocolTypes: protocolTypes, - allowsJavaImplementations: allowsJavaImplementations + name: NativeSwiftConversionStep.swiftObjectName(for: parameterName) ), indirectConversion: nil, - conversionCheck: nil, - asyncTaskCaptureNames: [NativeSwiftConversionStep.swiftObjectName(for: parameterName)] + conversionCheck: nil ) } @@ -527,16 +532,18 @@ extension JNISwift2JavaGenerator { parameters: [JavaParameter(name: parameterName, type: .long)], conversion: .pointee( .optionalChain( - .extractSwiftValue( - .placeholder, - swiftType: swiftType, - allowNil: true + .asyncTaskCapture( + .extractSwiftValue( + .placeholder, + swiftType: swiftType, + allowNil: true + ), + name: NativeSwiftConversionStep.extractedSwiftValueName(for: parameterName) ) ) ), indirectConversion: nil, - conversionCheck: nil, - asyncTaskCaptureNames: [NativeSwiftConversionStep.extractedSwiftValueName(for: parameterName)] + conversionCheck: nil ) default: @@ -1079,24 +1086,6 @@ extension JNISwift2JavaGenerator { /// Represents check operations executed in if/guard conditional block for check during conversion let conversionCheck: NativeSwiftConversionCheck? - - /// Temporary values created by this parameter conversion that must be moved - /// into async task bodies through an unchecked Sendable wrapper. - let asyncTaskCaptureNames: [String] - - init( - parameters: [JavaParameter], - conversion: NativeSwiftConversionStep, - indirectConversion: NativeSwiftConversionStep?, - conversionCheck: NativeSwiftConversionCheck?, - asyncTaskCaptureNames: [String] = [] - ) { - self.parameters = parameters - self.conversion = conversion - self.indirectConversion = indirectConversion - self.conversionCheck = conversionCheck - self.asyncTaskCaptureNames = asyncTaskCaptureNames - } } struct NativeResult { @@ -1244,6 +1233,10 @@ extension JNISwift2JavaGenerator { /// Destructures a Swift tuple result and writes each element to an out-parameter. indirect case tupleDestructure(elements: [(index: Int, label: String?, conversion: NativeSwiftConversionStep, outParamName: String, javaType: JavaType)]) + /// Marks a temporary value produced by the inner conversion as captured by + /// the async task body. + indirect case asyncTaskCapture(NativeSwiftConversionStep, name: String) + static func extractedSwiftValueName(for name: String) -> String { "\(name)$" } @@ -1264,6 +1257,22 @@ extension JNISwift2JavaGenerator { } } + var asyncTaskCaptureNames: [String] { + switch self { + case .asyncTaskCapture(let inner, let name): + return [name] + inner.asyncTaskCaptureNames + + case .pointee(let inner), .optionalChain(let inner): + return inner.asyncTaskCaptureNames + + case .tupleConstruct(let elements): + return elements.flatMap { $0.conversion.asyncTaskCaptureNames } + + default: + return [] + } + } + /// Returns the conversion string applied to the placeholder. func render(_ printer: inout CodePrinter, _ placeholder: String) -> String { // NOTE: 'printer' is used if the conversion wants to cause side-effects. @@ -1670,10 +1679,10 @@ extension JNISwift2JavaGenerator { } } - appendAsyncTaskCaptureNames(nativeFunctionSignature.selfParameter?.asyncTaskCaptureNames ?? []) - appendAsyncTaskCaptureNames(nativeFunctionSignature.selfTypeParameter?.asyncTaskCaptureNames ?? []) + appendAsyncTaskCaptureNames(nativeFunctionSignature.selfParameter?.conversion.asyncTaskCaptureNames ?? []) + appendAsyncTaskCaptureNames(nativeFunctionSignature.selfTypeParameter?.conversion.asyncTaskCaptureNames ?? []) for parameter in nativeFunctionSignature.parameters { - appendAsyncTaskCaptureNames(parameter.asyncTaskCaptureNames) + appendAsyncTaskCaptureNames(parameter.conversion.asyncTaskCaptureNames) } printer.print( @@ -1884,6 +1893,9 @@ extension JNISwift2JavaGenerator { } } return "" + + case .asyncTaskCapture(let inner, _): + return inner.render(&printer, placeholder) } } }