From 593eef26cce43c81bca44dd83afafb1bdb004862 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 9 Nov 2026 17:30:17 +0900 Subject: [PATCH 1/3] Add reproduction code --- .../Sources/MySwiftLibrary/CallbackProtcol.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/CallbackProtcol.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/CallbackProtcol.swift index 3ea172696..30fd7e499 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/CallbackProtcol.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/CallbackProtcol.swift @@ -105,3 +105,11 @@ public func outputCallbacks( optionalString: callbacks.withOptionalString(optionalString) ) } + +public struct CallbackHolder { + public init(callback: any CallbackProtocol) { + self.callback = callback + } + + public var callback: any CallbackProtocol +} From 1b4a457e4f7f97257dfb36ad7c848633b26e1072 Mon Sep 17 00:00:00 2001 From: Iceman Date: Tue, 10 Nov 2026 09:40:16 +0900 Subject: [PATCH 2/3] Add unit test --- .../JNI/JNIProtocolTests.swift | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift b/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift index eef6119d9..707a650b1 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift @@ -366,6 +366,59 @@ struct JNIProtocolTests { ) } + @Test + func takeAsSetterNewValue() throws { + let source = """ + public protocol Animal { + public func bark() + } + + public struct Owner { + public var animal: any Animal + } + """ + + try assertOutput( + input: source, + config: config, + .jni, + .java, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + public <_T0 extends Animal> void setAnimal(_T0 newValue) { + Owner.$setAnimal(newValue, this.$memoryAddress()); + } + """, + """ + private static native void $setAnimal(java.lang.Object newValue, long selfPointer); + """, + ] + ) + + try assertOutput( + input: source, + config: config, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + final class _Owner_setAnimal_newValue_Wrapper: SwiftJavaAnimalWrapper { + ... + } + """, + """ + @_cdecl("Java_com_example_swift_Owner__00024setAnimal__Ljava_lang_Object_2J") + public func Java_com_example_swift_Owner__00024setAnimal__Ljava_lang_Object_2J(environment: UnsafeMutablePointer!, thisClass: jclass, newValue: jobject?, selfPointer: jlong) { + """, + """ + newValueswiftObject$ = _Owner_setAnimal_newValue_Wrapper(_javaAnimalInterface: JavaAnimal(javaThis: newValue!, environment: environment)) + """, + ] + ) + } + @Test func generatesProtocolWrappers() throws { try assertOutput( From 138c8093d27d63e123f9639a487410e8917fae7b Mon Sep 17 00:00:00 2001 From: Iceman Date: Tue, 10 Nov 2026 10:05:03 +0900 Subject: [PATCH 3/3] Use translated name for protocol parameter helper class --- .../MySwiftLibrary/CallbackProtcol.swift | 8 ++-- ...ift2JavaGenerator+SwiftThunkPrinting.swift | 33 +++++++-------- .../JNI/JNIProtocolTests.swift | 42 +++++++++++++++---- 3 files changed, 54 insertions(+), 29 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/CallbackProtcol.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/CallbackProtcol.swift index 30fd7e499..e1add48f5 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/CallbackProtcol.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/CallbackProtcol.swift @@ -107,9 +107,9 @@ public func outputCallbacks( } public struct CallbackHolder { - public init(callback: any CallbackProtocol) { - self.callback = callback - } + public init(callback: any CallbackProtocol) { + self.callback = callback + } - public var callback: any CallbackProtocol + public var callback: any CallbackProtocol } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 0affc060e..f5ec477be 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -480,7 +480,7 @@ extension JNISwift2JavaGenerator { return } - printAllSwiftFunctionHelperClasses(&printer, decl) + printAllSwiftFunctionHelperClasses(&printer, decl, translatedDecl) printCDecl( &printer, @@ -497,14 +497,13 @@ extension JNISwift2JavaGenerator { private func printAllSwiftFunctionHelperClasses( _ printer: inout SwiftPrinter, _ decl: ExtractedFunc, + _ translatedDecl: TranslatedFunctionDecl, ) { // Emit the `@JavaInterface` Swift wrappers for any `@escaping` closure parameters. - if let translatedDecl = translatedDecl(for: decl) { - printEscapingClosureSwiftFunctionHelperClasses(&printer, translatedDecl) - } + printEscapingClosureSwiftFunctionHelperClasses(&printer, translatedDecl) // Emit the `@JavaInterface` Swift wrappers any existential / protocol parameters. - printProtocolParameterSwiftFunctionHelperClasses(&printer, decl) + printProtocolParameterSwiftFunctionHelperClasses(&printer, decl, translatedDecl) } /// Emits Swift wrapper types for any parameter type that is a protocol @@ -512,37 +511,39 @@ extension JNISwift2JavaGenerator { private func printProtocolParameterSwiftFunctionHelperClasses( _ printer: inout SwiftPrinter, _ decl: ExtractedFunc, + _ translatedDecl: TranslatedFunctionDecl, ) { - let protocolParameters = decl.functionSignature.parameters.compactMap { parameter in + let parameters = zip(decl.functionSignature.parameters, translatedDecl.translatedFunctionSignature.parameters) + let protocolParameters = parameters.compactMap { parameter, translatedParameter in if let concreteType = parameter.type.typeIn( genericParameters: decl.functionSignature.genericParameters, genericRequirements: decl.functionSignature.genericRequirements, ) { - return (parameter, concreteType) + return (translatedParameter.parameter.name, concreteType) } switch parameter.type { case .opaque(let protocolType), .existential(let protocolType): - return (parameter, protocolType) + return (translatedParameter.parameter.name, protocolType) default: return nil } - }.map { parameter, protocolType in + }.map { parameterName, protocolType in // We flatten any composite types switch protocolType { case .composite(let protocols): - return (parameter, protocols) + return (parameterName, protocols) default: - return (parameter, [protocolType]) + return (parameterName, [protocolType]) } } // For each parameter that is a generic or a protocol, // we generate a Swift class that conforms to all of those. - for (parameter, protocolTypes) in protocolParameters { + for (parameterName, protocolTypes) in protocolParameters { let protocolWrappers: [JavaInterfaceSwiftWrapper] = protocolTypes.compactMap { protocolType in guard let importedType = self.asExtractedNominalTypeDecl(protocolType), let wrapper = self.interfaceProtocolWrappers[importedType] @@ -560,14 +561,10 @@ extension JNISwift2JavaGenerator { continue } - guard let parameterName = parameter.parameterName else { - // TODO: Throw - fatalError() - } let swiftClassName = JNISwift2JavaGenerator.protocolParameterWrapperClassName( - methodName: decl.name, + methodName: translatedDecl.name, parameterName: parameterName, - parentName: decl.parentType?.asNominalType?.nominalTypeDecl.qualifiedTypeName ?? SwiftQualifiedTypeName(swiftModuleName), + parentName: translatedDecl.parentName, ) let implementingProtocols = protocolWrappers.map(\.wrapperName).joined(separator: .comma) diff --git a/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift b/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift index 707a650b1..1549abf20 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIProtocolTests.swift @@ -369,14 +369,14 @@ struct JNIProtocolTests { @Test func takeAsSetterNewValue() throws { let source = """ - public protocol Animal { - public func bark() - } + public protocol Animal { + public func bark() + } - public struct Owner { - public var animal: any Animal - } - """ + public struct Owner { + public var animal: any Animal + } + """ try assertOutput( input: source, @@ -419,6 +419,34 @@ struct JNIProtocolTests { ) } + @Test + func takeAsOverloadedParameter() throws { + let source = """ + public protocol Animal { + func bark() + } + + public struct Owner { + public func take(first: any Animal) {} + public func take(second: any Animal) {} + } + """ + + try assertOutput( + input: source, + config: config, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + "final class _Owner_takeFirst_first_Wrapper: SwiftJavaAnimalWrapper {", + "firstswiftObject$ = _Owner_takeFirst_first_Wrapper(_javaAnimalInterface: JavaAnimal(javaThis: first!, environment: environment))", + "final class _Owner_takeSecond_second_Wrapper: SwiftJavaAnimalWrapper {", + "secondswiftObject$ = _Owner_takeSecond_second_Wrapper(_javaAnimalInterface: JavaAnimal(javaThis: second!, environment: environment))", + ] + ) + } + @Test func generatesProtocolWrappers() throws { try assertOutput(