From 1752824bc722301d0bb3f5172782b350b41e50af Mon Sep 17 00:00:00 2001 From: amanmaurya92 Date: Mon, 3 Aug 2026 16:17:24 +0530 Subject: [PATCH 01/13] Fix #830: Gracefully skip unsupported variadic parameters --- ...MSwift2JavaGenerator+JavaTranslation.swift | 6 +++++ ...ISwift2JavaGenerator+JavaTranslation.swift | 7 ++++++ .../JNI/JNIModuleTests.swift | 22 +++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index 7b6cb7e56..839ac0a59 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -294,6 +294,9 @@ extension FFMSwift2JavaGenerator { methodName: String ) throws -> TranslatedFunctionSignature { let swiftSignature = loweredFunctionSignature.original + if swiftSignature.hasVariadicParams { + throw JavaTranslationError.unsupportedVariadicParameter + } // 'self' let selfParameter: TranslatedParameter? @@ -1109,4 +1112,7 @@ enum JavaTranslationError: Error { case inoutNotSupported(SwiftType, file: String = #file, line: Int = #line) case unhandledType(SwiftType, file: String = #file, line: Int = #line) case unhandledType(known: SwiftKnownType, file: String = #file, line: Int = #line) + + /// Variadic parameters (e.g. `Int...`) are not supported due to Swift limitations with array splatting. + case unsupportedVariadicParameter } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 4e94de345..50efb9bc7 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -276,6 +276,10 @@ extension JNISwift2JavaGenerator { methodName: String, parentName: SwiftQualifiedTypeName, ) throws -> TranslatedFunctionSignature { + if functionSignature.hasVariadicParams { + throw JavaTranslationError.unsupportedVariadicParameter + } + let parameters = try translateParameters( functionSignature.parameters.map { ($0.parameterName, $0.type) }, methodName: methodName, @@ -2191,6 +2195,9 @@ extension JNISwift2JavaGenerator { // FIXME: Remove once we support protocol variables case protocolVariablesNotSupported + + /// Variadic parameters (e.g. `Int...`) are not supported due to Swift limitations with array splatting. + case unsupportedVariadicParameter case protocolStaticRequirementsNotSupported diff --git a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift index 8b63d982a..47b9b61de 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift @@ -326,4 +326,26 @@ struct JNIModuleTests { ] ) } + + @Test + func skipsVariadicParameter() throws { + let input = """ + public func helloWorld() + public func sum(_ xs: Int64...) -> Int64 { xs.reduce(0, +) } + """ + + try assertOutput( + input: input, + .jni, + .java, + expectedChunks: [ + """ + public static void helloWorld() + """ + ], + notExpectedChunks: [ + "sum" + ] + ) + } } From 6184d86c580930bcd3b876172da9dd9e49b89d0e Mon Sep 17 00:00:00 2001 From: amanmaurya92 Date: Tue, 4 Aug 2026 15:10:55 +0530 Subject: [PATCH 02/13] Expand variadic functions into overloads instead of skipping them --- ...MSwift2JavaGenerator+JavaTranslation.swift | 6 -- .../FFM/FFMSwift2JavaGenerator.swift | 15 ++++- ...ISwift2JavaGenerator+JavaTranslation.swift | 7 --- .../JNI/JNISwift2JavaGenerator.swift | 15 ++++- Sources/SwiftExtract/ExtractedDecls.swift | 55 +++++++++++++++++++ .../Configuration.swift | 8 +++ .../JNI/JNIModuleTests.swift | 20 ++++++- 7 files changed, 109 insertions(+), 17 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index 839ac0a59..7b6cb7e56 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -294,9 +294,6 @@ extension FFMSwift2JavaGenerator { methodName: String ) throws -> TranslatedFunctionSignature { let swiftSignature = loweredFunctionSignature.original - if swiftSignature.hasVariadicParams { - throw JavaTranslationError.unsupportedVariadicParameter - } // 'self' let selfParameter: TranslatedParameter? @@ -1112,7 +1109,4 @@ enum JavaTranslationError: Error { case inoutNotSupported(SwiftType, file: String = #file, line: Int = #line) case unhandledType(SwiftType, file: String = #file, line: Int = #line) case unhandledType(known: SwiftKnownType, file: String = #file, line: Int = #line) - - /// Variadic parameters (e.g. `Int...`) are not supported due to Swift limitations with array splatting. - case unsupportedVariadicParameter } diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index feda997c4..bf059b67b 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -24,7 +24,7 @@ import struct Foundation.URL package class FFMSwift2JavaGenerator: Swift2JavaGenerator { let log: Logger let config: Configuration - let analysis: AnalysisResult + var analysis: AnalysisResult let swiftModuleName: String let javaPackage: String let swiftOutputDirectory: String @@ -116,6 +116,19 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { } else { self.expectedOutputSwiftFileNames = [] } + + // Expand variadic functions into N overloads + let maxOverloads = config.effectiveMaxVariadicOverloads + self.analysis.extractedGlobalFuncs = self.analysis.extractedGlobalFuncs.flatMap { + $0.expandingVariadicOverloads(maxOverloads: maxOverloads) + } + + var expandedTypes = self.analysis.extractedTypes + for (name, type) in expandedTypes { + type.methods = type.methods.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } + type.initializers = type.initializers.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } + } + self.analysis.extractedTypes = expandedTypes } func generate() throws { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 50efb9bc7..4e94de345 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -276,10 +276,6 @@ extension JNISwift2JavaGenerator { methodName: String, parentName: SwiftQualifiedTypeName, ) throws -> TranslatedFunctionSignature { - if functionSignature.hasVariadicParams { - throw JavaTranslationError.unsupportedVariadicParameter - } - let parameters = try translateParameters( functionSignature.parameters.map { ($0.parameterName, $0.type) }, methodName: methodName, @@ -2195,9 +2191,6 @@ extension JNISwift2JavaGenerator { // FIXME: Remove once we support protocol variables case protocolVariablesNotSupported - - /// Variadic parameters (e.g. `Int...`) are not supported due to Swift limitations with array splatting. - case unsupportedVariadicParameter case protocolStaticRequirementsNotSupported diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift index c3006b00c..08bcd8660 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift @@ -28,7 +28,7 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { let logger: Logger let config: Configuration - let analysis: AnalysisResult + var analysis: AnalysisResult let swiftModuleName: String let javaPackage: String let swiftOutputDirectory: String @@ -132,6 +132,19 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { self.existentialProtocolBoxes = self.analysis.extractedTypes.values .filter { $0.swiftNominal.kind == .protocol } .sorted { $0.swiftNominal.qualifiedName < $1.swiftNominal.qualifiedName } + + // Expand variadic functions into N overloads + let maxOverloads = config.effectiveMaxVariadicOverloads + self.analysis.extractedGlobalFuncs = self.analysis.extractedGlobalFuncs.flatMap { + $0.expandingVariadicOverloads(maxOverloads: maxOverloads) + } + + var expandedTypes = self.analysis.extractedTypes + for (name, type) in expandedTypes { + type.methods = type.methods.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } + type.initializers = type.initializers.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } + } + self.analysis.extractedTypes = expandedTypes } func generate() throws { diff --git a/Sources/SwiftExtract/ExtractedDecls.swift b/Sources/SwiftExtract/ExtractedDecls.swift index 580bddb47..16106f9fe 100644 --- a/Sources/SwiftExtract/ExtractedDecls.swift +++ b/Sources/SwiftExtract/ExtractedDecls.swift @@ -400,6 +400,61 @@ public final class ExtractedFunc: ExtractedSwiftDecl, CustomStringConvertible { functionSignature: functionSignature ) } + + /// Expands this function into `maxOverloads + 1` functions if it contains a variadic parameter. + /// Replaces the variadic parameter `T...` with `N` discrete parameters (`arg0: T`, `arg1: T`, etc.) + /// for `N` in `0...maxOverloads`. + /// Returns `[self]` if the function has no variadic parameters. + public func expandingVariadicOverloads(maxOverloads: Int) -> [ExtractedFunc] { + guard functionSignature.hasVariadicParams else { + return [self] + } + + var overloads: [ExtractedFunc] = [] + + // Find the index of the variadic parameter. Swift only allows one. + guard let variadicIndex = functionSignature.parameters.firstIndex(where: \.isVariadic) else { + return [self] + } + + let variadicParam = functionSignature.parameters[variadicIndex] + + for count in 0...maxOverloads { + var newParameters = functionSignature.parameters + newParameters.remove(at: variadicIndex) + + var expandedParams: [SwiftParameter] = [] + for i in 0.. Int64 { xs.reduce(0, +) } """ + var config = Configuration() + config.maxVariadicOverloads = 2 + try assertOutput( input: input, + config: config, .jni, .java, expectedChunks: [ """ public static void helloWorld() + """, + """ + public static long sum() + """, + """ + public static long sum(long arg0) + """, + """ + public static long sum(long arg0, long arg1) + """, + """ + public static long sum(long arg0, long arg1, long arg2) """ ], notExpectedChunks: [ - "sum" + "sum(long arg0, long arg1, long arg2, long arg3)" ] ) } From 9c5dfdbb2aa36b1cf69718880550f741d7c750d4 Mon Sep 17 00:00:00 2001 From: amanmaurya92 Date: Tue, 4 Aug 2026 15:17:14 +0530 Subject: [PATCH 03/13] Revert analysis to let and use local copy during init --- .../FFM/FFMSwift2JavaGenerator.swift | 10 ++--- .../JNI/JNISwift2JavaGenerator.swift | 37 ++++++++++--------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index bf059b67b..5ea7bb5f5 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -24,7 +24,7 @@ import struct Foundation.URL package class FFMSwift2JavaGenerator: Swift2JavaGenerator { let log: Logger let config: Configuration - var analysis: AnalysisResult + let analysis: AnalysisResult let swiftModuleName: String let javaPackage: String let swiftOutputDirectory: String @@ -118,17 +118,17 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { } // Expand variadic functions into N overloads + var expandedAnalysis = analysis let maxOverloads = config.effectiveMaxVariadicOverloads - self.analysis.extractedGlobalFuncs = self.analysis.extractedGlobalFuncs.flatMap { + expandedAnalysis.extractedGlobalFuncs = expandedAnalysis.extractedGlobalFuncs.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } - var expandedTypes = self.analysis.extractedTypes - for (name, type) in expandedTypes { + for type in expandedAnalysis.extractedTypes.values { type.methods = type.methods.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } type.initializers = type.initializers.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } } - self.analysis.extractedTypes = expandedTypes + self.analysis = expandedAnalysis } func generate() throws { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift index 08bcd8660..6e12758f0 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift @@ -28,7 +28,7 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { let logger: Logger let config: Configuration - var analysis: AnalysisResult + let analysis: AnalysisResult let swiftModuleName: String let javaPackage: String let swiftOutputDirectory: String @@ -120,31 +120,32 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { self.expectedOutputSwiftFileNames = [] } - if config.enableJavaCallbacks ?? false { - // We translate all the protocol wrappers - // as we need them to know what protocols we can allow the user to implement themselves - // in Java. - self.interfaceProtocolWrappers = self.generateInterfaceWrappers(Array(self.analysis.extractedTypes.values)) - } - - // Every extracted protocol that also gets a plain Java `interface` - // generated for it is eligible to be boxed as an existential. - self.existentialProtocolBoxes = self.analysis.extractedTypes.values - .filter { $0.swiftNominal.kind == .protocol } - .sorted { $0.swiftNominal.qualifiedName < $1.swiftNominal.qualifiedName } - // Expand variadic functions into N overloads + var expandedAnalysis = analysis let maxOverloads = config.effectiveMaxVariadicOverloads - self.analysis.extractedGlobalFuncs = self.analysis.extractedGlobalFuncs.flatMap { + expandedAnalysis.extractedGlobalFuncs = expandedAnalysis.extractedGlobalFuncs.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } - var expandedTypes = self.analysis.extractedTypes - for (name, type) in expandedTypes { + for type in expandedAnalysis.extractedTypes.values { type.methods = type.methods.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } type.initializers = type.initializers.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } } - self.analysis.extractedTypes = expandedTypes + + // Every extracted protocol that also gets a plain Java `interface` + // generated for it is eligible to be boxed as an existential. + self.existentialProtocolBoxes = expandedAnalysis.extractedTypes.values + .filter { $0.swiftNominal.kind == .protocol } + .sorted { $0.swiftNominal.qualifiedName < $1.swiftNominal.qualifiedName } + + if config.enableJavaCallbacks ?? false { + // We translate all the protocol wrappers + // as we need them to know what protocols we can allow the user to implement themselves + // in Java. + self.interfaceProtocolWrappers = self.generateInterfaceWrappers(Array(expandedAnalysis.extractedTypes.values)) + } + + self.analysis = expandedAnalysis } func generate() throws { From 28181f4525da8ebf44f7c1e23e4879f14a9ba965 Mon Sep 17 00:00:00 2001 From: amanmaurya92 Date: Tue, 4 Aug 2026 23:44:24 +0530 Subject: [PATCH 04/13] Refactor generator init to fix variadic overload expansions --- Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift | 2 +- Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 5ea7bb5f5..8c8e69357 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -77,7 +77,7 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { ) { self.log = Logger(label: "ffm-generator", logLevel: translator.log.logLevel) self.config = config - self.analysis = translator.result + let analysis = translator.result self.swiftModuleName = translator.swiftModuleName self.javaPackage = javaPackage self.swiftOutputDirectory = swiftOutputDirectory diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift index 6e12758f0..60af9fb59 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift @@ -78,7 +78,7 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { ) { self.config = config self.logger = Logger(label: "jni-generator", logLevel: translator.log.logLevel) - self.analysis = translator.result + let analysis = translator.result self.swiftModuleName = translator.swiftModuleName self.javaPackage = javaPackage self.swiftOutputDirectory = swiftOutputDirectory @@ -131,6 +131,7 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { type.methods = type.methods.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } type.initializers = type.initializers.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } } + self.analysis = expandedAnalysis // Every extracted protocol that also gets a plain Java `interface` // generated for it is eligible to be boxed as an existential. @@ -144,8 +145,6 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { // in Java. self.interfaceProtocolWrappers = self.generateInterfaceWrappers(Array(expandedAnalysis.extractedTypes.values)) } - - self.analysis = expandedAnalysis } func generate() throws { From 6fe9c2e4de9673e2a1f7a10d3e9fa6aaa2872a70 Mon Sep 17 00:00:00 2001 From: amanmaurya92 Date: Wed, 5 Aug 2026 07:51:58 +0530 Subject: [PATCH 05/13] Fix maxVariadicOverloads in expandsVariadicParameter test --- Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift index 9c532ad66..286a82161 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift @@ -335,7 +335,7 @@ struct JNIModuleTests { """ var config = Configuration() - config.maxVariadicOverloads = 2 + config.maxVariadicOverloads = 3 try assertOutput( input: input, From 96b832dbda7b4c008a54239dfd67419eb5aa3322 Mon Sep 17 00:00:00 2001 From: amanmaurya92 Date: Wed, 5 Aug 2026 08:41:44 +0530 Subject: [PATCH 06/13] Address PR comments: Variadic expansion refactoring and tests --- .../MySwiftLibrary/MySwiftLibrary.swift | 4 ++++ .../com/example/swift/MySwiftLibraryTest.java | 8 ++++++++ .../FFM/FFMSwift2JavaGenerator.swift | 10 +--------- .../JNI/JNISwift2JavaGenerator.swift | 12 ++---------- Sources/SwiftExtract/AnalysisResult.swift | 12 ++++++++++++ .../Configuration.swift | 18 +++++++++++++++++- 6 files changed, 44 insertions(+), 20 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift index 0335ff7e4..ecf6ddf6d 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -78,6 +78,10 @@ public func globalOverloaded(_ c: Int) -> Int { c + 3 } +public func globalConcatStrings(_ strings: String...) -> String { + strings.joined() +} + // ==== Internal helpers func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) { diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java index 64aedc61d..09f1057de 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java @@ -97,4 +97,12 @@ void call_consumeValueFromOtherModule_crossModule() { assertEquals(42, result); } } + + @Test + void variadicOverloads() { + assertEquals("", MySwiftLibrary.globalConcatStrings()); + assertEquals("a", MySwiftLibrary.globalConcatStrings("a")); + assertEquals("ab", MySwiftLibrary.globalConcatStrings("a", "b")); + assertEquals("abc", MySwiftLibrary.globalConcatStrings("a", "b", "c")); + } } diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 8c8e69357..3ed251305 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -119,15 +119,7 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { // Expand variadic functions into N overloads var expandedAnalysis = analysis - let maxOverloads = config.effectiveMaxVariadicOverloads - expandedAnalysis.extractedGlobalFuncs = expandedAnalysis.extractedGlobalFuncs.flatMap { - $0.expandingVariadicOverloads(maxOverloads: maxOverloads) - } - - for type in expandedAnalysis.extractedTypes.values { - type.methods = type.methods.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } - type.initializers = type.initializers.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } - } + expandedAnalysis.expandVariadicOverloads(maxOverloads: config.effectiveMaxVariadicOverloads) self.analysis = expandedAnalysis } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift index 60af9fb59..12dd13567 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift @@ -122,15 +122,7 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { // Expand variadic functions into N overloads var expandedAnalysis = analysis - let maxOverloads = config.effectiveMaxVariadicOverloads - expandedAnalysis.extractedGlobalFuncs = expandedAnalysis.extractedGlobalFuncs.flatMap { - $0.expandingVariadicOverloads(maxOverloads: maxOverloads) - } - - for type in expandedAnalysis.extractedTypes.values { - type.methods = type.methods.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } - type.initializers = type.initializers.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } - } + expandedAnalysis.expandVariadicOverloads(maxOverloads: config.effectiveMaxVariadicOverloads) self.analysis = expandedAnalysis // Every extracted protocol that also gets a plain Java `interface` @@ -139,7 +131,7 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { .filter { $0.swiftNominal.kind == .protocol } .sorted { $0.swiftNominal.qualifiedName < $1.swiftNominal.qualifiedName } - if config.enableJavaCallbacks ?? false { + if config.effectiveEnableJavaCallbacks { // We translate all the protocol wrappers // as we need them to know what protocols we can allow the user to implement themselves // in Java. diff --git a/Sources/SwiftExtract/AnalysisResult.swift b/Sources/SwiftExtract/AnalysisResult.swift index 4b39506de..f690b838a 100644 --- a/Sources/SwiftExtract/AnalysisResult.swift +++ b/Sources/SwiftExtract/AnalysisResult.swift @@ -28,4 +28,16 @@ public struct AnalysisResult { self.extractedGlobalVariables = extractedGlobalVariables self.extractedGlobalFuncs = extractedGlobalFuncs } + + /// Expands variadic functions into distinct overloads. + public mutating func expandVariadicOverloads(maxOverloads: Int) { + self.extractedGlobalFuncs = self.extractedGlobalFuncs.flatMap { + $0.expandingVariadicOverloads(maxOverloads: maxOverloads) + } + + for type in self.extractedTypes.values { + type.methods = type.methods.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } + type.initializers = type.initializers.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } + } + } } diff --git a/Sources/SwiftJavaConfigurationShared/Configuration.swift b/Sources/SwiftJavaConfigurationShared/Configuration.swift index a0a3913e3..ac6233113 100644 --- a/Sources/SwiftJavaConfigurationShared/Configuration.swift +++ b/Sources/SwiftJavaConfigurationShared/Configuration.swift @@ -75,7 +75,23 @@ public struct Configuration: Codable { /// Maximum number of overloads to generate for a function with a variadic parameter. /// When a variadic parameter `T...` is encountered, the generator will produce - /// `0` to `maxVariadicOverloads` distinct overloads instead of failing. + /// up to `maxVariadicOverloads` distinct overloads instead of failing. + /// + /// Example: + /// ```swift + /// func concat(s: String...) -> String + /// ``` + /// results in: + /// ```java + /// // Java + /// String concat() -> String + /// String concat(s0: String, s1: String) -> String + /// String concat(s0: String, s1: String, s2: String) -> String + /// ``` + /// + /// The reason for this is that Swift cannot "splat" an array into a `...` + /// parameter, therefore we cannot transfer an arbitrary amount of varargs + /// parameters over the native boundary. public var maxVariadicOverloads: Int? public var effectiveMaxVariadicOverloads: Int { maxVariadicOverloads ?? 3 From 75893b8de32711de626d36ed083093fa37fdc782 Mon Sep 17 00:00:00 2001 From: amanmaurya92 Date: Wed, 5 Aug 2026 10:12:12 +0530 Subject: [PATCH 07/13] Fix generator variadic label bug and Windows path bug --- ...FMSwift2JavaGenerator+FunctionLowering.swift | 12 +++++++++--- ...Swift2JavaGenerator+SwiftThunkPrinting.swift | 2 +- .../FFM/FFMSwift2JavaGenerator.swift | 4 ++-- ...Swift2JavaGenerator+SwiftThunkPrinting.swift | 10 +++++----- .../JNI/JNISwift2JavaGenerator.swift | 4 ++-- Sources/SwiftExtract/ExtractedDecls.swift | 2 +- test.exe | Bin 0 -> 24576 bytes test.exp | Bin 0 -> 635 bytes test.lib | Bin 0 -> 1646 bytes test.swift | 3 +++ 10 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 test.exe create mode 100644 test.exp create mode 100644 test.lib create mode 100644 test.swift diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift index ad8f0a6f4..f609588bb 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift @@ -1034,7 +1034,9 @@ extension LoweredFunctionSignature { let arguments = paramExprs.enumerated() .map { (i, argument) -> String in let argExpr = original.parameters[i].convention == .inout ? "&\(argument)" : argument - return LabeledExprSyntax(label: original.parameters[i].argumentLabel, expression: argExpr).description + let labelStr = original.parameters[i].argumentLabel + let label = labelStr == "_" ? nil : labelStr + return LabeledExprSyntax(label: label, expression: argExpr).description } .joined(separator: .comma) resultExpr = "\(callee)(\(raw: arguments))" @@ -1054,7 +1056,9 @@ extension LoweredFunctionSignature { case .subscriptGetter: let parameters = paramExprs.enumerated() .map { (i, argument) -> String in - LabeledExprSyntax(label: original.parameters[i].argumentLabel, expression: argument).description + let labelStr = original.parameters[i].argumentLabel + let label = labelStr == "_" ? nil : labelStr + return LabeledExprSyntax(label: label, expression: argument).description } .joined(separator: .comma) resultExpr = "\(callee)[\(raw: parameters)]" @@ -1066,7 +1070,9 @@ extension LoweredFunctionSignature { let parameters = argumentsWithoutNewValue.enumerated() .map { (i, argument) -> String in - LabeledExprSyntax(label: original.parameters[i].argumentLabel, expression: argument).description + let labelStr = original.parameters[i].argumentLabel + let label = labelStr == "_" ? nil : labelStr + return LabeledExprSyntax(label: label, expression: argument).description } .joined(separator: .comma) resultExpr = "\(callee)[\(raw: parameters)] = \(newValueArgument)" diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift index c78cb8688..c69950631 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift @@ -91,7 +91,7 @@ extension FFMSwift2JavaGenerator { .map(\.value) .sorted(by: { $0.qualifiedName < $1.qualifiedName }) - let inputFileName = "\(group.key)".split(separator: "/").last ?? "__Unknown.swift" + let inputFileName = "\(group.key)".split { $0 == "/" || $0 == "\\" }.last ?? "__Unknown.swift" let filename = "\(inputFileName)".replacing(/\.swift(interface)?/, with: "+SwiftJava.swift") // Print file header before all type thunks diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 3ed251305..7e08affa3 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -89,7 +89,7 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { if config.effectiveWriteEmptyFiles { self.expectedOutputSwiftFileNames = Set( translator.inputs.compactMap { (input) -> String? in - guard let fileName = input.path.split(separator: PATH_SEPARATOR).last else { + guard let fileName = input.path.split(whereSeparator: { $0 == "/" || $0 == "\\" }).last else { return nil } if fileName.hasSuffix(".swift") { @@ -102,7 +102,7 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { ) // Also include filtered-out files so SwiftPM gets the empty outputs it expects for path in translator.filteredOutPaths { - guard let fileName = path.split(separator: PATH_SEPARATOR).last else { + guard let fileName = path.split(whereSeparator: { $0 == "/" || $0 == "\\" }).last else { continue } if fileName.hasSuffix(".swift") { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index db8c66320..295d36ec3 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -96,7 +96,7 @@ extension JNISwift2JavaGenerator { .map(\.value) .sorted(by: { $0.qualifiedName < $1.qualifiedName }) - let inputFileName = "\(group.key)".split(separator: "/").last ?? "__Unknown.swift" + let inputFileName = "\(group.key)".split { $0 == "/" || $0 == "\\" }.last ?? "__Unknown.swift" let filename = "\(inputFileName)".replacing(/\.swift(interface)?/, with: "+SwiftJava.swift") for ty in extractedTypesForThisFile { @@ -698,7 +698,7 @@ extension JNISwift2JavaGenerator { decl.functionSignature.parameters, arguments, ).map { originalParam, argument in - let label = originalParam.argumentLabel.map { "\($0): " } ?? "" + let label = originalParam.argumentLabel.flatMap { $0 == "_" ? nil : "\($0): " } ?? "" return "\(label)\(argument)" } .joined(separator: .comma) @@ -709,7 +709,7 @@ extension JNISwift2JavaGenerator { decl.functionSignature.parameters, arguments, ).map { originalParam, argument in - let label = originalParam.argumentLabel.map { "\($0): " } ?? "" + let label = originalParam.argumentLabel.flatMap { $0 == "_" ? nil : "\($0): " } ?? "" return "\(label)\(argument)" } @@ -730,7 +730,7 @@ extension JNISwift2JavaGenerator { decl.functionSignature.parameters, arguments, ).map { originalParam, argument in - let label = originalParam.argumentLabel.map { "\($0): " } ?? "" + let label = originalParam.argumentLabel.flatMap { $0 == "_" ? nil : "\($0): " } ?? "" return "\(label)\(argument)" } .joined(separator: .comma) @@ -746,7 +746,7 @@ extension JNISwift2JavaGenerator { let indexArgs = arguments.dropLast() let parameters = zip(indexParams, indexArgs).map { originalParam, argument in - let label = originalParam.argumentLabel.map { "\($0): " } ?? "" + let label = originalParam.argumentLabel.flatMap { $0 == "_" ? nil : "\($0): " } ?? "" return "\(label)\(argument)" } .joined(separator: .comma) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift index 12dd13567..00869a960 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift @@ -92,7 +92,7 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { if config.effectiveWriteEmptyFiles { self.expectedOutputSwiftFileNames = Set( translator.inputs.compactMap { (input) -> String? in - guard let fileName = input.path.split(separator: PATH_SEPARATOR).last else { + guard let fileName = input.path.split(whereSeparator: { $0 == "/" || $0 == "\\" }).last else { return nil } if fileName.hasSuffix(".swift") { @@ -105,7 +105,7 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { ) // Also include filtered-out files so SwiftPM gets the empty outputs it expects for path in translator.filteredOutPaths { - guard let fileName = path.split(separator: PATH_SEPARATOR).last else { + guard let fileName = path.split(whereSeparator: { $0 == "/" || $0 == "\\" }).last else { continue } if fileName.hasSuffix(".swift") { diff --git a/Sources/SwiftExtract/ExtractedDecls.swift b/Sources/SwiftExtract/ExtractedDecls.swift index 16106f9fe..fe587c6db 100644 --- a/Sources/SwiftExtract/ExtractedDecls.swift +++ b/Sources/SwiftExtract/ExtractedDecls.swift @@ -429,7 +429,7 @@ public final class ExtractedFunc: ExtractedSwiftDecl, CustomStringConvertible { expandedParams.append( SwiftParameter( convention: variadicParam.convention, - argumentLabel: name, + argumentLabel: (i == 0) ? variadicParam.argumentLabel : nil, parameterName: name, // We use the same name so the call site matches `callee.sum(arg0, arg1)` type: variadicParam.type, isVariadic: false diff --git a/test.exe b/test.exe new file mode 100644 index 0000000000000000000000000000000000000000..06b82fcb848b015cb3fff74eb514d1c83ac66896 GIT binary patch literal 24576 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 P#=U=4O_{>~2P5!5CmnFf literal 0 HcmV?d00001 diff --git a/test.exp b/test.exp new file mode 100644 index 0000000000000000000000000000000000000000..b58657d82e679a3544409e783852f20980aec556 GIT binary patch literal 635 zcmY*W%TB^T6g_~7uPE$Ybio3mHWZK$A4#K$(I{xdu$s-l6f#;UY0D#ifD04%et>`C z54dw{;urV@dS_}CPkQd0dpeJsd&PYcUxb$gu#5<%BY4Pv5?XAcAU+cn7Kk=|rf}M$ zUaqM;g%9Gp5;L%oIaH~7Bev-&#EiZFYv=@_=}aB;xg21Xe$Ek7*q}JCl|fO}kwT7Q zTB|(8C9Sq7E^4LDeG=b@>qum2!O!hOY+^r~rj4)4)mTFrPme%dc6=}Jx}lN3Dj4Ud zjWaypAdAjK;Aocx6WI;7Z}^x)RY}gJ@AZ7%H;$$21V(;Zs}|VbB`q0@xN9`Sk&qs< zYV2?%U2*MsZopbcEE4r5NvmS+SZ29eDqE~A2kgWP!fCa_6kR#E)m^~~YFX7m-5$H+ z^@pYuRChu(IR(4x@X#z4Gt`5~nH{M*n8NJvlG+LG8ociSCCZ{bes4IY1gf#I6YWwB bj-APcFM#5&lWPT^+*|1Y$!%)(w#eWp=;gh zrvIQT7p{#9qb^Jr{TJ=Z#GW&A4-ZRQNVw$QdtSr&&THo7yX;PGb2|7Y+_=V~jl6K< ziik!HMXdz^bOG8H;5z{P=RhFD+qUCNspWLWo`1hGmyV}0%W1ou-z~$oldFlupIYp( z+EZp0R+EeN{7hzs%nQYByO=F}f_t=$)u>_h6WA>cIPd}I#DoGacxce!r=O3!wTt>G zzUEPIXp#JvyrBPIhRvxOYNz7E$5Z__K*)P20$vd(|Kan-$co1S(?lE!qizeK*+{|3 zma`*b(7%Q6$5>U z;pAvwsgT<#?UvTd;nDQ#a3Y>uRQiVN8F7q)H}#K8Q3G*PkxX2W_gtyCO)yW6H0|-R z-kuD>xO7Og7T_Y?6p0&`=SA6=Y(o#BYBN!juzZ*gwfNtGYI2Cyk)X5vZs|;S80KVk z{+x=A?yo~v-&|C5&}!Blj;E@gb=?sYA$P}_FzV*+=zP^%sU}z8uP=+BbVjQaWlYK2 zYc0qV^)q$%>pOYyYTG<#|Cp!=NfS>bPgMP~V97+8B4@iBVbslboy~14d3{0NJJW|T aQ@#xR9pI^SD*Lm?_kC`Be(!}Xg~D&LkRSa3 literal 0 HcmV?d00001 diff --git a/test.swift b/test.swift new file mode 100644 index 000000000..77e10e4e9 --- /dev/null +++ b/test.swift @@ -0,0 +1,3 @@ +let p = "C:/swift-java/Samples/Optionals.swift" +let name = p.split { $0 == "/" || $0 == "\\" }.last ?? "Unknown" +print("\(name)") From 6f8f4d0304878a5f7b849dc39c3edc0af0db5b55 Mon Sep 17 00:00:00 2001 From: amanmaurya92 Date: Wed, 5 Aug 2026 10:15:59 +0530 Subject: [PATCH 08/13] Handle wildcard labels defensively and remove dummy test files --- test.exe | Bin 24576 -> 0 bytes test.exp | Bin 635 -> 0 bytes test.lib | Bin 1646 -> 0 bytes test.swift | 3 --- 4 files changed, 3 deletions(-) delete mode 100644 test.exe delete mode 100644 test.exp delete mode 100644 test.lib delete mode 100644 test.swift diff --git a/test.exe b/test.exe deleted file mode 100644 index 06b82fcb848b015cb3fff74eb514d1c83ac66896..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 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 P#=U=4O_{>~2P5!5CmnFf diff --git a/test.exp b/test.exp deleted file mode 100644 index b58657d82e679a3544409e783852f20980aec556..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 635 zcmY*W%TB^T6g_~7uPE$Ybio3mHWZK$A4#K$(I{xdu$s-l6f#;UY0D#ifD04%et>`C z54dw{;urV@dS_}CPkQd0dpeJsd&PYcUxb$gu#5<%BY4Pv5?XAcAU+cn7Kk=|rf}M$ zUaqM;g%9Gp5;L%oIaH~7Bev-&#EiZFYv=@_=}aB;xg21Xe$Ek7*q}JCl|fO}kwT7Q zTB|(8C9Sq7E^4LDeG=b@>qum2!O!hOY+^r~rj4)4)mTFrPme%dc6=}Jx}lN3Dj4Ud zjWaypAdAjK;Aocx6WI;7Z}^x)RY}gJ@AZ7%H;$$21V(;Zs}|VbB`q0@xN9`Sk&qs< zYV2?%U2*MsZopbcEE4r5NvmS+SZ29eDqE~A2kgWP!fCa_6kR#E)m^~~YFX7m-5$H+ z^@pYuRChu(IR(4x@X#z4Gt`5~nH{M*n8NJvlG+LG8ociSCCZ{bes4IY1gf#I6YWwB bj-APcFM#5&lWPT^+*|1Y$!%)(w#eWp=;gh zrvIQT7p{#9qb^Jr{TJ=Z#GW&A4-ZRQNVw$QdtSr&&THo7yX;PGb2|7Y+_=V~jl6K< ziik!HMXdz^bOG8H;5z{P=RhFD+qUCNspWLWo`1hGmyV}0%W1ou-z~$oldFlupIYp( z+EZp0R+EeN{7hzs%nQYByO=F}f_t=$)u>_h6WA>cIPd}I#DoGacxce!r=O3!wTt>G zzUEPIXp#JvyrBPIhRvxOYNz7E$5Z__K*)P20$vd(|Kan-$co1S(?lE!qizeK*+{|3 zma`*b(7%Q6$5>U z;pAvwsgT<#?UvTd;nDQ#a3Y>uRQiVN8F7q)H}#K8Q3G*PkxX2W_gtyCO)yW6H0|-R z-kuD>xO7Og7T_Y?6p0&`=SA6=Y(o#BYBN!juzZ*gwfNtGYI2Cyk)X5vZs|;S80KVk z{+x=A?yo~v-&|C5&}!Blj;E@gb=?sYA$P}_FzV*+=zP^%sU}z8uP=+BbVjQaWlYK2 zYc0qV^)q$%>pOYyYTG<#|Cp!=NfS>bPgMP~V97+8B4@iBVbslboy~14d3{0NJJW|T aQ@#xR9pI^SD*Lm?_kC`Be(!}Xg~D&LkRSa3 diff --git a/test.swift b/test.swift deleted file mode 100644 index 77e10e4e9..000000000 --- a/test.swift +++ /dev/null @@ -1,3 +0,0 @@ -let p = "C:/swift-java/Samples/Optionals.swift" -let name = p.split { $0 == "/" || $0 == "\\" }.last ?? "Unknown" -print("\(name)") From 61d8d1da8943ab09e6a9c1d63f68920688ddaf94 Mon Sep 17 00:00:00 2001 From: amanmaurya92 Date: Thu, 6 Aug 2026 08:48:14 +0530 Subject: [PATCH 09/13] Fix trailing comma and regenerate config docs to fix CI --- .../Documentation.docc/SwiftJavaConfigFile.md | 31 +++++++++++++++++++ .../JNI/JNIModuleTests.swift | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftJavaDocumentation/Documentation.docc/SwiftJavaConfigFile.md b/Sources/SwiftJavaDocumentation/Documentation.docc/SwiftJavaConfigFile.md index fb166fff1..dccc2bfc1 100644 --- a/Sources/SwiftJavaDocumentation/Documentation.docc/SwiftJavaConfigFile.md +++ b/Sources/SwiftJavaDocumentation/Documentation.docc/SwiftJavaConfigFile.md @@ -118,6 +118,33 @@ The directory where generated Java files should be written. Generally used with --- +#### maxVariadicOverloads + +- **Type:** `Int?` +- **Default:** `3` + +Maximum number of overloads to generate for a function with a variadic parameter. +When a variadic parameter `T...` is encountered, the generator will produce +up to `maxVariadicOverloads` distinct overloads instead of failing. + +Example: +```swift +func concat(s: String...) -> String +``` +results in: +```java +// Java +String concat() -> String +String concat(s0: String, s1: String) -> String +String concat(s0: String, s1: String, s2: String) -> String +``` + +The reason for this is that Swift cannot "splat" an array into a `...` +parameter, therefore we cannot transfer an arbitrary amount of varargs +parameters over the native boundary. + +--- + #### mode - **Type:** `JExtractGenerationMode?` @@ -351,6 +378,8 @@ So this configuration option is geared towards times when you do not control the **`SpecializationConfigEntry`:** +==== ----------------------------------------------------------------------- +MARK: SpecializationConfigEntry Configuration entry for specializing a generic type into a concrete Java class. The dictionary key is the Java-facing name; this entry provides the base type and type argument mapping. @@ -534,6 +563,8 @@ If not set, defaults to mavenCentral(). **`MavenRepositoryDescriptor`:** +==== ----------------------------------------------------------------------- +MARK: MavenRepositoryDescriptor Describes a Maven-style repository for dependency resolution. Supported types based on https://docs.gradle.org/current/userguide/supported_repository_types.html: diff --git a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift index 286a82161..a6c379863 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift @@ -357,7 +357,7 @@ struct JNIModuleTests { """, """ public static long sum(long arg0, long arg1, long arg2) - """ + """, ], notExpectedChunks: [ "sum(long arg0, long arg1, long arg2, long arg3)" From d973b85ff5af42f8c0a8281c154c79267c1d1afd Mon Sep 17 00:00:00 2001 From: amanmaurya92 Date: Thu, 6 Aug 2026 09:19:16 +0530 Subject: [PATCH 10/13] Remove MARK comments mistakenly generated by swift-docc plugin on Windows --- .../Documentation.docc/SwiftJavaConfigFile.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Sources/SwiftJavaDocumentation/Documentation.docc/SwiftJavaConfigFile.md b/Sources/SwiftJavaDocumentation/Documentation.docc/SwiftJavaConfigFile.md index dccc2bfc1..880f82a9c 100644 --- a/Sources/SwiftJavaDocumentation/Documentation.docc/SwiftJavaConfigFile.md +++ b/Sources/SwiftJavaDocumentation/Documentation.docc/SwiftJavaConfigFile.md @@ -378,8 +378,7 @@ So this configuration option is geared towards times when you do not control the **`SpecializationConfigEntry`:** -==== ----------------------------------------------------------------------- -MARK: SpecializationConfigEntry + Configuration entry for specializing a generic type into a concrete Java class. The dictionary key is the Java-facing name; this entry provides the base type and type argument mapping. @@ -563,8 +562,7 @@ If not set, defaults to mavenCentral(). **`MavenRepositoryDescriptor`:** -==== ----------------------------------------------------------------------- -MARK: MavenRepositoryDescriptor + Describes a Maven-style repository for dependency resolution. Supported types based on https://docs.gradle.org/current/userguide/supported_repository_types.html: From 2c58a04d3f73787010be8fc51f647e05239bf0ce Mon Sep 17 00:00:00 2001 From: amanmaurya92 Date: Thu, 6 Aug 2026 14:22:39 +0530 Subject: [PATCH 11/13] Revert argument label scope creep and restore FFMSwift2JavaGenerator PATH_SEPARATOR --- Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift | 4 ++-- .../JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 7e08affa3..3ed251305 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -89,7 +89,7 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { if config.effectiveWriteEmptyFiles { self.expectedOutputSwiftFileNames = Set( translator.inputs.compactMap { (input) -> String? in - guard let fileName = input.path.split(whereSeparator: { $0 == "/" || $0 == "\\" }).last else { + guard let fileName = input.path.split(separator: PATH_SEPARATOR).last else { return nil } if fileName.hasSuffix(".swift") { @@ -102,7 +102,7 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { ) // Also include filtered-out files so SwiftPM gets the empty outputs it expects for path in translator.filteredOutPaths { - guard let fileName = path.split(whereSeparator: { $0 == "/" || $0 == "\\" }).last else { + guard let fileName = path.split(separator: PATH_SEPARATOR).last else { continue } if fileName.hasSuffix(".swift") { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 295d36ec3..78e4f7c76 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -698,7 +698,7 @@ extension JNISwift2JavaGenerator { decl.functionSignature.parameters, arguments, ).map { originalParam, argument in - let label = originalParam.argumentLabel.flatMap { $0 == "_" ? nil : "\($0): " } ?? "" + let label = originalParam.argumentLabel.map { "\($0): " } ?? "" return "\(label)\(argument)" } .joined(separator: .comma) @@ -709,7 +709,7 @@ extension JNISwift2JavaGenerator { decl.functionSignature.parameters, arguments, ).map { originalParam, argument in - let label = originalParam.argumentLabel.flatMap { $0 == "_" ? nil : "\($0): " } ?? "" + let label = originalParam.argumentLabel.map { "\($0): " } ?? "" return "\(label)\(argument)" } @@ -730,7 +730,7 @@ extension JNISwift2JavaGenerator { decl.functionSignature.parameters, arguments, ).map { originalParam, argument in - let label = originalParam.argumentLabel.flatMap { $0 == "_" ? nil : "\($0): " } ?? "" + let label = originalParam.argumentLabel.map { "\($0): " } ?? "" return "\(label)\(argument)" } .joined(separator: .comma) @@ -746,7 +746,7 @@ extension JNISwift2JavaGenerator { let indexArgs = arguments.dropLast() let parameters = zip(indexParams, indexArgs).map { originalParam, argument in - let label = originalParam.argumentLabel.flatMap { $0 == "_" ? nil : "\($0): " } ?? "" + let label = originalParam.argumentLabel.map { "\($0): " } ?? "" return "\(label)\(argument)" } .joined(separator: .comma) From f9e2f90b7d394a4e9679c6e74b4e6b8b9c328459 Mon Sep 17 00:00:00 2001 From: amanmaurya92 Date: Thu, 6 Aug 2026 15:22:50 +0530 Subject: [PATCH 12/13] Fix trailing whitespace in JNIModuleTests.swift for swift-format CI --- Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift index a6c379863..07fbda3a0 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIModuleTests.swift @@ -333,10 +333,10 @@ struct JNIModuleTests { public func helloWorld() public func sum(_ xs: Int64...) -> Int64 { xs.reduce(0, +) } """ - + var config = Configuration() config.maxVariadicOverloads = 3 - + try assertOutput( input: input, config: config, From 277545eec9dc962fa619bd826b6d76b3ecc25920 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Thu, 6 Aug 2026 21:43:38 +0900 Subject: [PATCH 13/13] Fix swift-format and regenerate generate-config-docs. --- .../FFM/FFMSwift2JavaGenerator.swift | 2 +- .../JNI/JNISwift2JavaGenerator.swift | 4 ++-- Sources/SwiftExtract/AnalysisResult.swift | 6 +++--- Sources/SwiftExtract/ExtractedDecls.swift | 16 ++++++++-------- .../Configuration.swift | 4 ++-- .../Documentation.docc/SwiftJavaConfigFile.md | 6 ++---- 6 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 3ed251305..ffde1222a 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -116,7 +116,7 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { } else { self.expectedOutputSwiftFileNames = [] } - + // Expand variadic functions into N overloads var expandedAnalysis = analysis expandedAnalysis.expandVariadicOverloads(maxOverloads: config.effectiveMaxVariadicOverloads) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift index 00869a960..4accdc0a3 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift @@ -124,13 +124,13 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { var expandedAnalysis = analysis expandedAnalysis.expandVariadicOverloads(maxOverloads: config.effectiveMaxVariadicOverloads) self.analysis = expandedAnalysis - + // Every extracted protocol that also gets a plain Java `interface` // generated for it is eligible to be boxed as an existential. self.existentialProtocolBoxes = expandedAnalysis.extractedTypes.values .filter { $0.swiftNominal.kind == .protocol } .sorted { $0.swiftNominal.qualifiedName < $1.swiftNominal.qualifiedName } - + if config.effectiveEnableJavaCallbacks { // We translate all the protocol wrappers // as we need them to know what protocols we can allow the user to implement themselves diff --git a/Sources/SwiftExtract/AnalysisResult.swift b/Sources/SwiftExtract/AnalysisResult.swift index f690b838a..b995ec1bd 100644 --- a/Sources/SwiftExtract/AnalysisResult.swift +++ b/Sources/SwiftExtract/AnalysisResult.swift @@ -31,10 +31,10 @@ public struct AnalysisResult { /// Expands variadic functions into distinct overloads. public mutating func expandVariadicOverloads(maxOverloads: Int) { - self.extractedGlobalFuncs = self.extractedGlobalFuncs.flatMap { - $0.expandingVariadicOverloads(maxOverloads: maxOverloads) + self.extractedGlobalFuncs = self.extractedGlobalFuncs.flatMap { + $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } - + for type in self.extractedTypes.values { type.methods = type.methods.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } type.initializers = type.initializers.flatMap { $0.expandingVariadicOverloads(maxOverloads: maxOverloads) } diff --git a/Sources/SwiftExtract/ExtractedDecls.swift b/Sources/SwiftExtract/ExtractedDecls.swift index fe587c6db..f276da292 100644 --- a/Sources/SwiftExtract/ExtractedDecls.swift +++ b/Sources/SwiftExtract/ExtractedDecls.swift @@ -411,18 +411,18 @@ public final class ExtractedFunc: ExtractedSwiftDecl, CustomStringConvertible { } var overloads: [ExtractedFunc] = [] - + // Find the index of the variadic parameter. Swift only allows one. guard let variadicIndex = functionSignature.parameters.firstIndex(where: \.isVariadic) else { return [self] } - + let variadicParam = functionSignature.parameters[variadicIndex] - + for count in 0...maxOverloads { var newParameters = functionSignature.parameters newParameters.remove(at: variadicIndex) - + var expandedParams: [SwiftParameter] = [] for i in 0.. String /// ``` /// - /// The reason for this is that Swift cannot "splat" an array into a `...` - /// parameter, therefore we cannot transfer an arbitrary amount of varargs + /// The reason for this is that Swift cannot "splat" an array into a `...` + /// parameter, therefore we cannot transfer an arbitrary amount of varargs /// parameters over the native boundary. public var maxVariadicOverloads: Int? public var effectiveMaxVariadicOverloads: Int { diff --git a/Sources/SwiftJavaDocumentation/Documentation.docc/SwiftJavaConfigFile.md b/Sources/SwiftJavaDocumentation/Documentation.docc/SwiftJavaConfigFile.md index 880f82a9c..0687233d1 100644 --- a/Sources/SwiftJavaDocumentation/Documentation.docc/SwiftJavaConfigFile.md +++ b/Sources/SwiftJavaDocumentation/Documentation.docc/SwiftJavaConfigFile.md @@ -139,8 +139,8 @@ String concat(s0: String, s1: String) -> String String concat(s0: String, s1: String, s2: String) -> String ``` -The reason for this is that Swift cannot "splat" an array into a `...` -parameter, therefore we cannot transfer an arbitrary amount of varargs +The reason for this is that Swift cannot "splat" an array into a `...` +parameter, therefore we cannot transfer an arbitrary amount of varargs parameters over the native boundary. --- @@ -378,7 +378,6 @@ So this configuration option is geared towards times when you do not control the **`SpecializationConfigEntry`:** - Configuration entry for specializing a generic type into a concrete Java class. The dictionary key is the Java-facing name; this entry provides the base type and type argument mapping. @@ -562,7 +561,6 @@ If not set, defaults to mavenCentral(). **`MavenRepositoryDescriptor`:** - Describes a Maven-style repository for dependency resolution. Supported types based on https://docs.gradle.org/current/userguide/supported_repository_types.html: