From 91f12b1fbd14c7fff741f374e4f57afd07dbaf3c Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Thu, 5 Jun 2025 12:01:53 +0200 Subject: [PATCH 1/8] refactor FFM generation into protocol backed implementation --- .editorconfig | 6 ++ Sources/JExtractSwiftLib/AnalysisResult.swift | 4 + Sources/JExtractSwiftLib/CodePrinter.swift | 2 + ...2JavaGenerator+JavaBindingsPrinting.swift} | 8 +- ...Swift2JavaGenerator+JavaTranslation.swift} | 0 .../FFMSwift2JavaGenerator.swift} | 101 ++++++++++++++---- Sources/JExtractSwiftLib/Swift2Java.swift | 13 +++ .../Swift2JavaGenerator.swift | 3 + .../Swift2JavaTranslator.swift | 31 ++---- 9 files changed, 117 insertions(+), 51 deletions(-) create mode 100644 .editorconfig create mode 100644 Sources/JExtractSwiftLib/AnalysisResult.swift rename Sources/JExtractSwiftLib/{Swift2JavaTranslator+JavaBindingsPrinting.swift => FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift} (98%) rename Sources/JExtractSwiftLib/{Swift2JavaTranslator+JavaTranslation.swift => FFM/FFMSwift2JavaGenerator+JavaTranslation.swift} (100%) rename Sources/JExtractSwiftLib/{Swift2JavaTranslator+Printing.swift => FFM/FFMSwift2JavaGenerator.swift} (76%) create mode 100644 Sources/JExtractSwiftLib/Swift2JavaGenerator.swift diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..9c71277f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +insert_final_newline = true diff --git a/Sources/JExtractSwiftLib/AnalysisResult.swift b/Sources/JExtractSwiftLib/AnalysisResult.swift new file mode 100644 index 00000000..578093d9 --- /dev/null +++ b/Sources/JExtractSwiftLib/AnalysisResult.swift @@ -0,0 +1,4 @@ +struct AnalysisResult { + let importedTypes: [String: ImportedNominalType] + let importedGlobalFuncs: [ImportedFunc] +} diff --git a/Sources/JExtractSwiftLib/CodePrinter.swift b/Sources/JExtractSwiftLib/CodePrinter.swift index db823aa2..449cab3d 100644 --- a/Sources/JExtractSwiftLib/CodePrinter.swift +++ b/Sources/JExtractSwiftLib/CodePrinter.swift @@ -14,6 +14,8 @@ import Foundation +let PATH_SEPARATOR = "/" // TODO: Windows + public struct CodePrinter { var contents: String = "" diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift similarity index 98% rename from Sources/JExtractSwiftLib/Swift2JavaTranslator+JavaBindingsPrinting.swift rename to Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift index 90f98577..db02a9cf 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift @@ -14,8 +14,8 @@ import JavaTypes -extension Swift2JavaTranslator { - public func printFunctionDowncallMethods( +extension FFMSwift2JavaGenerator { + func printFunctionDowncallMethods( _ printer: inout CodePrinter, _ decl: ImportedFunc ) { @@ -33,7 +33,7 @@ extension Swift2JavaTranslator { } /// Print FFM Java binding descriptors for the imported Swift API. - package func printJavaBindingDescriptorClass( + func printJavaBindingDescriptorClass( _ printer: inout CodePrinter, _ decl: ImportedFunc ) { @@ -211,7 +211,7 @@ extension Swift2JavaTranslator { let memoryLayout = renderMemoryLayoutValue(for: outParameter.type) let arena = if let className = outParameter.type.className, - self.importedTypes[className] != nil { + analysis.importedTypes[className] != nil { // Use passed-in 'SwiftArena' for 'SwiftValue'. "swiftArena$" } else { diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift similarity index 100% rename from Sources/JExtractSwiftLib/Swift2JavaTranslator+JavaTranslation.swift rename to Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift similarity index 76% rename from Sources/JExtractSwiftLib/Swift2JavaTranslator+Printing.swift rename to Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 13e337e0..1f9d8610 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator+Printing.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -16,27 +16,81 @@ import JavaTypes import SwiftSyntax import SwiftSyntaxBuilder +class FFMSwift2JavaGenerator: Swift2JavaGenerator { + private let log = Logger(label: "ffm-generator", logLevel: .info) + + let analysis: AnalysisResult + let swiftModuleName: String + let javaPackage: String + let swiftOutputDirectory: String + let javaOutputDirectory: String + let swiftStdlibTypes: SwiftStandardLibraryTypes + + var javaPackagePath: String { + javaPackage.replacingOccurrences(of: ".", with: "/") + } + + var thunkNameRegistry: ThunkNameRegistry = ThunkNameRegistry() + + init( + analysis: AnalysisResult, + swiftModuleName: String, + javaPackage: String, + swiftOutputDirectory: String, + javaOutputDirectory: String + ) { + self.analysis = analysis + self.swiftModuleName = swiftModuleName + self.javaPackage = javaPackage + self.swiftOutputDirectory = swiftOutputDirectory + self.javaOutputDirectory = javaOutputDirectory + + var parsedSwiftModule = SwiftParsedModuleSymbolTable(moduleName: "Swift") + self.swiftStdlibTypes = SwiftStandardLibraryTypes(into: &parsedSwiftModule) + } + + func generate() throws { + try writeExportedJavaSources() + } +} + +// ===== -------------------------------------------------------------------------------------------------------------- +// MARK: Defaults + +extension FFMSwift2JavaGenerator { + + /// Default set Java imports for every generated file + static let defaultJavaImports: Array = [ + "org.swift.swiftkit.*", + "org.swift.swiftkit.SwiftKit", + "org.swift.swiftkit.util.*", + + // Necessary for native calls and type mapping + "java.lang.foreign.*", + "java.lang.invoke.*", + "java.util.Arrays", + "java.util.stream.Collectors", + "java.util.concurrent.atomic.*", + "java.nio.charset.StandardCharsets", + ] +} + // ==== --------------------------------------------------------------------------------------------------------------- // MARK: File writing -let PATH_SEPARATOR = "/" // TODO: Windows -extension Swift2JavaTranslator { +extension FFMSwift2JavaGenerator { /// Every imported public type becomes a public class in its own file in Java. - public func writeExportedJavaSources(outputDirectory: String) throws { + func writeExportedJavaSources() throws { var printer = CodePrinter() - try writeExportedJavaSources(outputDirectory: outputDirectory, printer: &printer) - } - - public func writeExportedJavaSources(outputDirectory: String, printer: inout CodePrinter) throws { - for (_, ty) in importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) { + for (_, ty) in analysis.importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) { let filename = "\(ty.swiftNominal.name).java" log.info("Printing contents: \(filename)") printImportedNominal(&printer, ty) if let outputFile = try printer.writeContents( - outputDirectory: outputDirectory, + outputDirectory: javaOutputDirectory, javaPackagePath: javaPackagePath, filename: filename ) { @@ -50,7 +104,7 @@ extension Swift2JavaTranslator { printModule(&printer) if let outputFile = try printer.writeContents( - outputDirectory: outputDirectory, + outputDirectory: javaOutputDirectory, javaPackagePath: javaPackagePath, filename: filename) { @@ -63,26 +117,26 @@ extension Swift2JavaTranslator { // ==== --------------------------------------------------------------------------------------------------------------- // MARK: Java/text printing -extension Swift2JavaTranslator { +extension FFMSwift2JavaGenerator { /// Render the Java file contents for an imported Swift module. /// /// This includes any Swift global functions in that module, and some general type information and helpers. - public func printModule(_ printer: inout CodePrinter) { + func printModule(_ printer: inout CodePrinter) { printHeader(&printer) printPackage(&printer) printImports(&printer) printModuleClass(&printer) { printer in // TODO: print all "static" methods - for decl in importedGlobalFuncs { + for decl in analysis.importedGlobalFuncs { self.log.trace("Print imported decl: \(decl)") printFunctionDowncallMethods(&printer, decl) } } } - package func printImportedNominal(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { + func printImportedNominal(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { printHeader(&printer) printPackage(&printer) printImports(&printer) @@ -143,7 +197,7 @@ extension Swift2JavaTranslator { } } - public func printHeader(_ printer: inout CodePrinter) { + func printHeader(_ printer: inout CodePrinter) { printer.print( """ // Generated by jextract-swift @@ -153,7 +207,7 @@ extension Swift2JavaTranslator { ) } - public func printPackage(_ printer: inout CodePrinter) { + func printPackage(_ printer: inout CodePrinter) { printer.print( """ package \(javaPackage); @@ -162,14 +216,14 @@ extension Swift2JavaTranslator { ) } - public func printImports(_ printer: inout CodePrinter) { - for i in Swift2JavaTranslator.defaultJavaImports { + func printImports(_ printer: inout CodePrinter) { + for i in FFMSwift2JavaGenerator.defaultJavaImports { printer.print("import \(i);") } printer.print("") } - package func printNominal( + func printNominal( _ printer: inout CodePrinter, _ decl: ImportedNominalType, body: (inout CodePrinter) -> Void ) { let parentProtocol: String @@ -188,7 +242,7 @@ extension Swift2JavaTranslator { } } - public func printModuleClass(_ printer: inout CodePrinter, body: (inout CodePrinter) -> Void) { + func printModuleClass(_ printer: inout CodePrinter, body: (inout CodePrinter) -> Void) { printer.printBraceBlock("public final class \(swiftModuleName)") { printer in printPrivateConstructor(&printer, swiftModuleName) @@ -261,7 +315,7 @@ extension Swift2JavaTranslator { } } - private func printClassConstants(printer: inout CodePrinter) { + func printClassConstants(printer: inout CodePrinter) { printer.print( """ static final String LIB_NAME = "\(swiftModuleName)"; @@ -270,7 +324,7 @@ extension Swift2JavaTranslator { ) } - private func printPrivateConstructor(_ printer: inout CodePrinter, _ typeName: String) { + func printPrivateConstructor(_ printer: inout CodePrinter, _ typeName: String) { printer.print( """ private \(typeName)() { @@ -296,7 +350,7 @@ extension Swift2JavaTranslator { ) } - package func printToStringMethod( + func printToStringMethod( _ printer: inout CodePrinter, _ decl: ImportedNominalType ) { printer.print( @@ -312,3 +366,4 @@ extension Swift2JavaTranslator { """) } } + diff --git a/Sources/JExtractSwiftLib/Swift2Java.swift b/Sources/JExtractSwiftLib/Swift2Java.swift index 79cb0fec..a793ae25 100644 --- a/Sources/JExtractSwiftLib/Swift2Java.swift +++ b/Sources/JExtractSwiftLib/Swift2Java.swift @@ -89,6 +89,19 @@ public struct SwiftToJava { try translator.analyze() + let generator = FFMSwift2JavaGenerator( + analysis: translator.result, + swiftModuleName: self.swiftModule, + javaPackage: self.packageName, + swiftOutputDirectory: outputDirectorySwift, + javaOutputDirectory: outputDirectoryJava + ) + + try generator.generate() + + + print("[swift-java] Generated Java sources (\(packageName)) in: \(outputDirectoryJava)/") + try translator.writeSwiftThunkSources(outputDirectory: outputSwiftDirectory) print("[swift-java] Generated Swift sources (module: '\(config.swiftModule ?? "")') in: \(outputSwiftDirectory)/") diff --git a/Sources/JExtractSwiftLib/Swift2JavaGenerator.swift b/Sources/JExtractSwiftLib/Swift2JavaGenerator.swift new file mode 100644 index 00000000..e78598e5 --- /dev/null +++ b/Sources/JExtractSwiftLib/Swift2JavaGenerator.swift @@ -0,0 +1,3 @@ +protocol Swift2JavaGenerator { + func generate() throws +} diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift index 496c9b44..f53820bd 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift @@ -91,6 +91,13 @@ extension Swift2JavaTranslator { /// a checked truncation operation at the Java/Swift board. var javaPrimitiveForSwiftInt: JavaType { .long } + var result: AnalysisResult { + AnalysisResult( + importedTypes: self.importedTypes, + importedGlobalFuncs: self.importedGlobalFuncs + ) + } + package func add(filePath: String, text: String) { log.trace("Adding: \(filePath)") let sourceFileSyntax = Parser.parse(source: text) @@ -128,30 +135,6 @@ extension Swift2JavaTranslator { } } -// ===== -------------------------------------------------------------------------------------------------------------- -// MARK: Defaults - -extension Swift2JavaTranslator { - /// Default formatting options. - static let defaultFormat = BasicFormat(indentationWidth: .spaces(2)) - - /// Default set Java imports for every generated file - static let defaultJavaImports: Array = [ - "org.swift.swiftkit.*", - "org.swift.swiftkit.SwiftKit", - "org.swift.swiftkit.util.*", - - // Necessary for native calls and type mapping - "java.lang.foreign.*", - "java.lang.invoke.*", - "java.util.Arrays", - "java.util.stream.Collectors", - "java.util.concurrent.atomic.*", - "java.nio.charset.StandardCharsets", - ] - -} - // ==== ---------------------------------------------------------------------------------------------------------------- // MARK: Type translation extension Swift2JavaTranslator { From 9393a3a443610fe3437788d990b2e36ffead8617 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Sat, 7 Jun 2025 15:25:17 +0200 Subject: [PATCH 2/8] fix tests --- Sources/JExtractSwiftLib/AnalysisResult.swift | 1 + .../CDeclLowering/CRepresentation.swift | 0 ...wift2JavaGenerator+FunctionLowering.swift} | 2 +- .../{ => FFM}/ConversionStep.swift | 0 ...t2JavaGenerator+JavaBindingsPrinting.swift | 2 +- ...MSwift2JavaGenerator+JavaTranslation.swift | 2 +- ...ft2JavaGenerator+SwiftThunkPrinting.swift} | 25 +++--- .../FFM/FFMSwift2JavaGenerator.swift | 32 +++++--- Sources/JExtractSwiftLib/GenerationMode.swift | 6 ++ Sources/JExtractSwiftLib/Swift2Java.swift | 20 ++--- .../Swift2JavaTranslator.swift | 30 +------ .../JExtractSwiftLib/Swift2JavaVisitor.swift | 11 +-- .../Asserts/LoweringAssertions.swift | 22 +++-- .../Asserts/TextAssertions.swift | 11 ++- .../ClassPrintingTests.swift | 1 - .../FuncCallbackImportTests.swift | 10 ++- .../FunctionDescriptorImportTests.swift | 20 ++++- .../MethodImportTests.swift | 80 +++++++++++++++---- .../JExtractSwiftTests/MethodThunkTests.swift | 1 - .../StringPassingTests.swift | 1 - .../VariableImportTests.swift | 1 - 21 files changed, 169 insertions(+), 109 deletions(-) rename Sources/JExtractSwiftLib/{ => FFM}/CDeclLowering/CRepresentation.swift (100%) rename Sources/JExtractSwiftLib/{CDeclLowering/Swift2JavaTranslator+FunctionLowering.swift => FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift} (99%) rename Sources/JExtractSwiftLib/{ => FFM}/ConversionStep.swift (100%) rename Sources/JExtractSwiftLib/{Swift2JavaTranslator+SwiftThunkPrinting.swift => FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift} (87%) create mode 100644 Sources/JExtractSwiftLib/GenerationMode.swift diff --git a/Sources/JExtractSwiftLib/AnalysisResult.swift b/Sources/JExtractSwiftLib/AnalysisResult.swift index 578093d9..1e37d5c5 100644 --- a/Sources/JExtractSwiftLib/AnalysisResult.swift +++ b/Sources/JExtractSwiftLib/AnalysisResult.swift @@ -1,4 +1,5 @@ struct AnalysisResult { let importedTypes: [String: ImportedNominalType] + let importedGlobalVariables: [ImportedFunc] let importedGlobalFuncs: [ImportedFunc] } diff --git a/Sources/JExtractSwiftLib/CDeclLowering/CRepresentation.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift similarity index 100% rename from Sources/JExtractSwiftLib/CDeclLowering/CRepresentation.swift rename to Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift diff --git a/Sources/JExtractSwiftLib/CDeclLowering/Swift2JavaTranslator+FunctionLowering.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift similarity index 99% rename from Sources/JExtractSwiftLib/CDeclLowering/Swift2JavaTranslator+FunctionLowering.swift rename to Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift index 671f373e..cd5ea0a2 100644 --- a/Sources/JExtractSwiftLib/CDeclLowering/Swift2JavaTranslator+FunctionLowering.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift @@ -15,7 +15,7 @@ import JavaTypes import SwiftSyntax -extension Swift2JavaTranslator { +extension FFMSwift2JavaGenerator { /// Lower the given function declaration to a C-compatible entrypoint, /// providing all of the mappings between the parameter and result types /// of the original function and its `@_cdecl` counterpart. diff --git a/Sources/JExtractSwiftLib/ConversionStep.swift b/Sources/JExtractSwiftLib/FFM/ConversionStep.swift similarity index 100% rename from Sources/JExtractSwiftLib/ConversionStep.swift rename to Sources/JExtractSwiftLib/FFM/ConversionStep.swift diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift index db02a9cf..77ab9a81 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift @@ -33,7 +33,7 @@ extension FFMSwift2JavaGenerator { } /// Print FFM Java binding descriptors for the imported Swift API. - func printJavaBindingDescriptorClass( + package func printJavaBindingDescriptorClass( _ printer: inout CodePrinter, _ decl: ImportedFunc ) { diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index bf1cd9d2..56eb606d 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -14,7 +14,7 @@ import JavaTypes -extension Swift2JavaTranslator { +extension FFMSwift2JavaGenerator { func translatedSignature( for decl: ImportedFunc ) -> TranslatedFunctionSignature? { diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift similarity index 87% rename from Sources/JExtractSwiftLib/Swift2JavaTranslator+SwiftThunkPrinting.swift rename to Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift index 51547ba8..416bc851 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift @@ -15,14 +15,13 @@ import SwiftSyntax import SwiftSyntaxBuilder -extension Swift2JavaTranslator { - public func writeSwiftThunkSources(outputDirectory: String) throws { +extension FFMSwift2JavaGenerator { + package func writeSwiftThunkSources() throws { var printer = CodePrinter() - - try writeSwiftThunkSources(outputDirectory: outputDirectory, printer: &printer) + try writeSwiftThunkSources(printer: &printer) } - public func writeSwiftThunkSources(outputDirectory: String, printer: inout CodePrinter) throws { + package func writeSwiftThunkSources(printer: inout CodePrinter) throws { let moduleFilenameBase = "\(self.swiftModuleName)Module+SwiftJava" let moduleFilename = "\(moduleFilenameBase).swift" do { @@ -31,7 +30,7 @@ extension Swift2JavaTranslator { try printGlobalSwiftThunkSources(&printer) if let outputFile = try printer.writeContents( - outputDirectory: outputDirectory, + outputDirectory: self.swiftOutputDirectory, javaPackagePath: nil, filename: moduleFilename) { @@ -42,7 +41,7 @@ extension Swift2JavaTranslator { } // === All types - for (_, ty) in importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) { + for (_, ty) in self.analysis.importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) { let fileNameBase = "\(ty.swiftNominal.qualifiedName)+SwiftJava" let filename = "\(fileNameBase).swift" log.info("Printing contents: \(filename)") @@ -51,7 +50,7 @@ extension Swift2JavaTranslator { try printSwiftThunkSources(&printer, ty: ty) if let outputFile = try printer.writeContents( - outputDirectory: outputDirectory, + outputDirectory: self.swiftOutputDirectory, javaPackagePath: nil, filename: filename) { @@ -110,23 +109,23 @@ extension Swift2JavaTranslator { struct SwiftThunkTranslator { - let st: Swift2JavaTranslator + let st: FFMSwift2JavaGenerator - init(_ st: Swift2JavaTranslator) { + init(_ st: FFMSwift2JavaGenerator) { self.st = st } func renderGlobalThunks() -> [DeclSyntax] { var decls: [DeclSyntax] = [] decls.reserveCapacity( - st.importedGlobalVariables.count + st.importedGlobalFuncs.count + st.analysis.importedGlobalVariables.count + st.analysis.importedGlobalFuncs.count ) - for decl in st.importedGlobalVariables { + for decl in st.analysis.importedGlobalVariables { decls.append(contentsOf: render(forFunc: decl)) } - for decl in st.importedGlobalFuncs { + for decl in st.analysis.importedGlobalFuncs { decls.append(contentsOf: render(forFunc: decl)) } diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 1f9d8610..492168f6 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -16,8 +16,8 @@ import JavaTypes import SwiftSyntax import SwiftSyntaxBuilder -class FFMSwift2JavaGenerator: Swift2JavaGenerator { - private let log = Logger(label: "ffm-generator", logLevel: .info) +package class FFMSwift2JavaGenerator: Swift2JavaGenerator { + let log = Logger(label: "ffm-generator", logLevel: .info) let analysis: AnalysisResult let swiftModuleName: String @@ -25,6 +25,7 @@ class FFMSwift2JavaGenerator: Swift2JavaGenerator { let swiftOutputDirectory: String let javaOutputDirectory: String let swiftStdlibTypes: SwiftStandardLibraryTypes + let symbolTable: SwiftSymbolTable var javaPackagePath: String { javaPackage.replacingOccurrences(of: ".", with: "/") @@ -32,25 +33,27 @@ class FFMSwift2JavaGenerator: Swift2JavaGenerator { var thunkNameRegistry: ThunkNameRegistry = ThunkNameRegistry() - init( - analysis: AnalysisResult, - swiftModuleName: String, + /// Cached Java translation result. 'nil' indicates failed translation. + var translatedSignatures: [ImportedFunc: TranslatedFunctionSignature?] = [:] + + package init( + translator: Swift2JavaTranslator, javaPackage: String, swiftOutputDirectory: String, - javaOutputDirectory: String + javaOutputDirectory: String, ) { - self.analysis = analysis - self.swiftModuleName = swiftModuleName + self.analysis = translator.result + self.swiftModuleName = translator.swiftModuleName self.javaPackage = javaPackage self.swiftOutputDirectory = swiftOutputDirectory self.javaOutputDirectory = javaOutputDirectory - - var parsedSwiftModule = SwiftParsedModuleSymbolTable(moduleName: "Swift") - self.swiftStdlibTypes = SwiftStandardLibraryTypes(into: &parsedSwiftModule) + self.symbolTable = translator.symbolTable + self.swiftStdlibTypes = translator.swiftStdlibTypes } func generate() throws { try writeExportedJavaSources() + try writeSwiftThunkSources() } } @@ -80,10 +83,13 @@ extension FFMSwift2JavaGenerator { extension FFMSwift2JavaGenerator { + package func writeExportedJavaSources() throws { + var printer = CodePrinter() + try writeExportedJavaSources(printer: &printer) + } /// Every imported public type becomes a public class in its own file in Java. - func writeExportedJavaSources() throws { - var printer = CodePrinter() + package func writeExportedJavaSources(printer: inout CodePrinter) throws { for (_, ty) in analysis.importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) { let filename = "\(ty.swiftNominal.name).java" log.info("Printing contents: \(filename)") diff --git a/Sources/JExtractSwiftLib/GenerationMode.swift b/Sources/JExtractSwiftLib/GenerationMode.swift new file mode 100644 index 00000000..95266e1a --- /dev/null +++ b/Sources/JExtractSwiftLib/GenerationMode.swift @@ -0,0 +1,6 @@ +import ArgumentParser + +public enum GenerationMode: String, ExpressibleByArgument { + /// Foreign Value and Memory API + case ffm +} diff --git a/Sources/JExtractSwiftLib/Swift2Java.swift b/Sources/JExtractSwiftLib/Swift2Java.swift index a793ae25..895dc61f 100644 --- a/Sources/JExtractSwiftLib/Swift2Java.swift +++ b/Sources/JExtractSwiftLib/Swift2Java.swift @@ -89,15 +89,17 @@ public struct SwiftToJava { try translator.analyze() - let generator = FFMSwift2JavaGenerator( - analysis: translator.result, - swiftModuleName: self.swiftModule, - javaPackage: self.packageName, - swiftOutputDirectory: outputDirectorySwift, - javaOutputDirectory: outputDirectoryJava - ) - - try generator.generate() + switch mode { + case .ffm: + let generator = FFMSwift2JavaGenerator( + translator: translator, + javaPackage: self.packageName, + swiftOutputDirectory: outputDirectorySwift, + javaOutputDirectory: outputDirectoryJava + ) + + try generator.generate() + } print("[swift-java] Generated Java sources (\(packageName)) in: \(outputDirectoryJava)/") diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift index f53820bd..ace5f444 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift @@ -33,13 +33,6 @@ public final class Swift2JavaTranslator { var inputs: [Input] = [] - // ==== Output configuration - let javaPackage: String - - var javaPackagePath: String { - javaPackage.replacingOccurrences(of: ".", with: "/") - } - // ==== Output state package var importedGlobalVariables: [ImportedFunc] = [] @@ -54,21 +47,14 @@ public final class Swift2JavaTranslator { package let symbolTable: SwiftSymbolTable - package var thunkNameRegistry: ThunkNameRegistry = ThunkNameRegistry() - - /// Cached Java translation result. 'nil' indicates failed translation. - var translatedSignatures: [ImportedFunc: TranslatedFunctionSignature?] = [:] - /// The name of the Swift module being translated. var swiftModuleName: String { symbolTable.moduleName } public init( - javaPackage: String, swiftModuleName: String ) { - self.javaPackage = javaPackage self.symbolTable = SwiftSymbolTable(parsedModuleName: swiftModuleName) // Create a mock of the Swift standard library. @@ -82,18 +68,10 @@ public final class Swift2JavaTranslator { // MARK: Analysis extension Swift2JavaTranslator { - /// The primitive Java type to use for Swift's Int type, which follows the - /// size of a pointer. - /// - /// FIXME: Consider whether to extract this information from the Swift - /// interface file, so that it would be 'int' for 32-bit targets or 'long' for - /// 64-bit targets but make the Java code different for the two, vs. adding - /// a checked truncation operation at the Java/Swift board. - var javaPrimitiveForSwiftInt: JavaType { .long } - var result: AnalysisResult { AnalysisResult( importedTypes: self.importedTypes, + importedGlobalVariables: self.importedGlobalVariables, importedGlobalFuncs: self.importedGlobalFuncs ) } @@ -117,11 +95,7 @@ extension Swift2JavaTranslator { func analyze() throws { prepareForTranslation() - let visitor = Swift2JavaVisitor( - moduleName: self.swiftModuleName, - targetJavaPackage: self.javaPackage, - translator: self - ) + let visitor = Swift2JavaVisitor(translator: self) for input in self.inputs { log.trace("Analyzing \(input.filePath)") diff --git a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift index 4bb85a46..0b4d6bbf 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift @@ -19,13 +19,6 @@ import SwiftSyntax final class Swift2JavaVisitor: SyntaxVisitor { let translator: Swift2JavaTranslator - /// The Swift module we're visiting declarations in - let moduleName: String - - /// The target java package we are going to generate types into eventually, - /// store this along with type names as we import them. - let targetJavaPackage: String - /// Type context stack associated with the syntax. var typeContext: [(syntaxID: Syntax.ID, type: ImportedNominalType)] = [] @@ -42,9 +35,7 @@ final class Swift2JavaVisitor: SyntaxVisitor { var log: Logger { translator.log } - init(moduleName: String, targetJavaPackage: String, translator: Swift2JavaTranslator) { - self.moduleName = moduleName - self.targetJavaPackage = targetJavaPackage + init(translator: Swift2JavaTranslator) { self.translator = translator super.init(viewMode: .all) diff --git a/Tests/JExtractSwiftTests/Asserts/LoweringAssertions.swift b/Tests/JExtractSwiftTests/Asserts/LoweringAssertions.swift index b306689d..5dd28cc8 100644 --- a/Tests/JExtractSwiftTests/Asserts/LoweringAssertions.swift +++ b/Tests/JExtractSwiftTests/Asserts/LoweringAssertions.swift @@ -32,7 +32,6 @@ func assertLoweredFunction( column: Int = #column ) throws { let translator = Swift2JavaTranslator( - javaPackage: javaPackage, swiftModuleName: swiftModuleName ) @@ -42,18 +41,25 @@ func assertLoweredFunction( translator.prepareForTranslation() + let generator = FFMSwift2JavaGenerator( + translator: translator, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let swiftFunctionName: String let apiKind: SwiftAPIKind let loweredFunction: LoweredFunctionSignature if let inputFunction = inputDecl.as(FunctionDeclSyntax.self) { - loweredFunction = try translator.lowerFunctionSignature( + loweredFunction = try generator.lowerFunctionSignature( inputFunction, enclosingType: enclosingType ) swiftFunctionName = inputFunction.name.text apiKind = .function } else if let inputInitializer = inputDecl.as(InitializerDeclSyntax.self) { - loweredFunction = try translator.lowerFunctionSignature( + loweredFunction = try generator.lowerFunctionSignature( inputInitializer, enclosingType: enclosingType ) @@ -112,7 +118,6 @@ func assertLoweredVariableAccessor( column: Int = #column ) throws { let translator = Swift2JavaTranslator( - javaPackage: javaPackage, swiftModuleName: swiftModuleName ) @@ -122,8 +127,15 @@ func assertLoweredVariableAccessor( translator.prepareForTranslation() + let generator = FFMSwift2JavaGenerator( + translator: translator, + javaPackage: javaPackage, + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let swiftVariableName = inputDecl.bindings.first!.pattern.description - let loweredFunction = try translator.lowerFunctionSignature(inputDecl, isSet: isSet, enclosingType: enclosingType) + let loweredFunction = try generator.lowerFunctionSignature(inputDecl, isSet: isSet, enclosingType: enclosingType) let loweredCDecl = loweredFunction?.cdeclThunk( cName: "c_\(swiftVariableName)", diff --git a/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift b/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift index 3923cf3e..3ea03d98 100644 --- a/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift +++ b/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift @@ -35,13 +35,20 @@ func assertOutput( ) throws { try! translator.analyze(file: "/fake/Fake.swiftinterface", text: input) + let generator = FFMSwift2JavaGenerator( + translator: translator, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let output: String var printer: CodePrinter = CodePrinter(mode: .accumulateAll) switch renderKind { case .swift: - try translator.writeSwiftThunkSources(outputDirectory: "/fake", printer: &printer) + try generator.writeSwiftThunkSources(printer: &printer) case .java: - try translator.writeExportedJavaSources(outputDirectory: "/fake", printer: &printer) + try generator.writeExportedJavaSources(printer: &printer) } output = printer.finalize() diff --git a/Tests/JExtractSwiftTests/ClassPrintingTests.swift b/Tests/JExtractSwiftTests/ClassPrintingTests.swift index 1edcdaf0..f0c59456 100644 --- a/Tests/JExtractSwiftTests/ClassPrintingTests.swift +++ b/Tests/JExtractSwiftTests/ClassPrintingTests.swift @@ -42,7 +42,6 @@ struct ClassPrintingTests { @Test("Import: class layout") func class_layout() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) diff --git a/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift b/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift index 8b401ce3..f1afa7c0 100644 --- a/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift +++ b/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift @@ -35,7 +35,6 @@ final class FuncCallbackImportTests { @Test("Import: public func callMe(callback: () -> ())") func func_callMeFunc_Runnable() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) st.log.logLevel = .error @@ -44,8 +43,15 @@ final class FuncCallbackImportTests { let funcDecl = st.importedGlobalFuncs.first { $0.name == "callMe" }! + let generator = FFMSwift2JavaGenerator( + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let output = CodePrinter.toString { printer in - st.printJavaBindingWrapperMethod(&printer, funcDecl) + generator.printJavaBindingWrapperMethod(&printer, funcDecl) } assertOutput( diff --git a/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift b/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift index 2963ce9e..f3a101aa 100644 --- a/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift +++ b/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift @@ -234,7 +234,6 @@ extension FunctionDescriptorTests { body: (String) throws -> Void ) throws { let st = Swift2JavaTranslator( - javaPackage: javaPackage, swiftModuleName: swiftModuleName ) st.log.logLevel = logLevel @@ -245,8 +244,15 @@ extension FunctionDescriptorTests { $0.name == methodIdentifier }! + let generator = FFMSwift2JavaGenerator( + translator: st, + javaPackage: javaPackage, + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let output = CodePrinter.toString { printer in - st.printJavaBindingDescriptorClass(&printer, funcDecl) + generator.printJavaBindingDescriptorClass(&printer, funcDecl) } try body(output) @@ -261,13 +267,19 @@ extension FunctionDescriptorTests { body: (String) throws -> Void ) throws { let st = Swift2JavaTranslator( - javaPackage: javaPackage, swiftModuleName: swiftModuleName ) st.log.logLevel = logLevel try st.analyze(file: "/fake/Sample.swiftinterface", text: interfaceFile) + let generator = FFMSwift2JavaGenerator( + translator: st, + javaPackage: javaPackage, + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let accessorDecl: ImportedFunc? = st.importedTypes.values.compactMap { $0.variables.first { @@ -279,7 +291,7 @@ extension FunctionDescriptorTests { } let getOutput = CodePrinter.toString { printer in - st.printJavaBindingDescriptorClass(&printer, accessorDecl) + generator.printJavaBindingDescriptorClass(&printer, accessorDecl) } try body(getOutput) diff --git a/Tests/JExtractSwiftTests/MethodImportTests.swift b/Tests/JExtractSwiftTests/MethodImportTests.swift index 48515ff2..cd2bc150 100644 --- a/Tests/JExtractSwiftTests/MethodImportTests.swift +++ b/Tests/JExtractSwiftTests/MethodImportTests.swift @@ -63,17 +63,23 @@ final class MethodImportTests { @Test("Import: public func helloWorld()") func method_helloWorld() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) st.log.logLevel = .error try st.analyze(file: "Fake.swift", text: class_interfaceFile) + let generator = FFMSwift2JavaGenerator( + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let funcDecl = st.importedGlobalFuncs.first { $0.name == "helloWorld" }! let output = CodePrinter.toString { printer in - st.printJavaBindingWrapperMethod(&printer, funcDecl) + generator.printJavaBindingWrapperMethod(&printer, funcDecl) } assertOutput( @@ -96,7 +102,6 @@ final class MethodImportTests { @Test("Import: public func globalTakeInt(i: Int)") func func_globalTakeInt() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) st.log.logLevel = .error @@ -107,8 +112,15 @@ final class MethodImportTests { $0.name == "globalTakeInt" }! + let generator = FFMSwift2JavaGenerator( + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let output = CodePrinter.toString { printer in - st.printJavaBindingWrapperMethod(&printer, funcDecl) + generator.printJavaBindingWrapperMethod(&printer, funcDecl) } assertOutput( @@ -131,7 +143,6 @@ final class MethodImportTests { @Test("Import: public func globalTakeIntLongString(i32: Int32, l: Int64, s: String)") func func_globalTakeIntLongString() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) st.log.logLevel = .error @@ -142,8 +153,15 @@ final class MethodImportTests { $0.name == "globalTakeIntLongString" }! + let generator = FFMSwift2JavaGenerator( + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let output = CodePrinter.toString { printer in - st.printJavaBindingWrapperMethod(&printer, funcDecl) + generator.printJavaBindingWrapperMethod(&printer, funcDecl) } assertOutput( @@ -169,7 +187,6 @@ final class MethodImportTests { @Test("Import: public func globalReturnClass() -> MySwiftClass") func func_globalReturnClass() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) st.log.logLevel = .error @@ -180,8 +197,15 @@ final class MethodImportTests { $0.name == "globalReturnClass" }! + let generator = FFMSwift2JavaGenerator( + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let output = CodePrinter.toString { printer in - st.printJavaBindingWrapperMethod(&printer, funcDecl) + generator.printJavaBindingWrapperMethod(&printer, funcDecl) } assertOutput( @@ -207,7 +231,6 @@ final class MethodImportTests { @Test func method_class_helloMemberFunction() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) st.log.logLevel = .error @@ -218,8 +241,15 @@ final class MethodImportTests { $0.name == "helloMemberFunction" }! + let generator = FFMSwift2JavaGenerator( + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let output = CodePrinter.toString { printer in - st.printJavaBindingWrapperMethod(&printer, funcDecl) + generator.printJavaBindingWrapperMethod(&printer, funcDecl) } assertOutput( @@ -243,7 +273,6 @@ final class MethodImportTests { @Test func method_class_makeInt() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) st.log.logLevel = .info @@ -254,8 +283,15 @@ final class MethodImportTests { $0.name == "makeInt" }! + let generator = FFMSwift2JavaGenerator( + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let output = CodePrinter.toString { printer in - st.printJavaBindingWrapperMethod(&printer, funcDecl) + generator.printJavaBindingWrapperMethod(&printer, funcDecl) } assertOutput( @@ -279,7 +315,6 @@ final class MethodImportTests { @Test func class_constructor() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) st.log.logLevel = .info @@ -290,8 +325,15 @@ final class MethodImportTests { $0.name == "init" }! + let generator = FFMSwift2JavaGenerator( + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let output = CodePrinter.toString { printer in - st.printJavaBindingWrapperMethod(&printer, initDecl) + generator.printJavaBindingWrapperMethod(&printer, initDecl) } assertOutput( @@ -316,7 +358,6 @@ final class MethodImportTests { @Test func struct_constructor() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) st.log.logLevel = .info @@ -327,8 +368,15 @@ final class MethodImportTests { $0.name == "init" }! + let generator = FFMSwift2JavaGenerator( + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + let output = CodePrinter.toString { printer in - st.printJavaBindingWrapperMethod(&printer, initDecl) + generator.printJavaBindingWrapperMethod(&printer, initDecl) } assertOutput( diff --git a/Tests/JExtractSwiftTests/MethodThunkTests.swift b/Tests/JExtractSwiftTests/MethodThunkTests.swift index f6f5ce71..b91c0d8f 100644 --- a/Tests/JExtractSwiftTests/MethodThunkTests.swift +++ b/Tests/JExtractSwiftTests/MethodThunkTests.swift @@ -33,7 +33,6 @@ final class MethodThunkTests { @Test("Thunk overloads: globalFunc(a: Int32, b: Int64) & globalFunc(i32: Int32, l: Int64)") func thunk_overloads() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "FakeModule" ) st.log.logLevel = .error diff --git a/Tests/JExtractSwiftTests/StringPassingTests.swift b/Tests/JExtractSwiftTests/StringPassingTests.swift index ae131b94..d0ad7784 100644 --- a/Tests/JExtractSwiftTests/StringPassingTests.swift +++ b/Tests/JExtractSwiftTests/StringPassingTests.swift @@ -26,7 +26,6 @@ final class StringPassingTests { @Test("Import: public func writeString(string: String) -> Int") func method_helloWorld() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "__FakeModule" ) st.log.logLevel = .trace diff --git a/Tests/JExtractSwiftTests/VariableImportTests.swift b/Tests/JExtractSwiftTests/VariableImportTests.swift index 9d8650b4..9d873cc3 100644 --- a/Tests/JExtractSwiftTests/VariableImportTests.swift +++ b/Tests/JExtractSwiftTests/VariableImportTests.swift @@ -36,7 +36,6 @@ final class VariableImportTests { @Test("Import: var counter: Int") func variable_int() throws { let st = Swift2JavaTranslator( - javaPackage: "com.example.swift", swiftModuleName: "FakeModule" ) st.log.logLevel = .error From 1e5419580517946e35f54fe6e76a22fe3756fabc Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Sun, 8 Jun 2025 10:17:35 +0200 Subject: [PATCH 3/8] only add .editorconfig for .swift files --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 9c71277f..d8f9516f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,6 @@ root = true -[*] +[*.swift] indent_style = space indent_size = 2 insert_final_newline = true From cabbdc045f8b4be2f1c9362ace6fbf373ae9a713 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Sun, 8 Jun 2025 10:20:28 +0200 Subject: [PATCH 4/8] remove trailing comma --- Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 492168f6..fa235edf 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -40,7 +40,7 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { translator: Swift2JavaTranslator, javaPackage: String, swiftOutputDirectory: String, - javaOutputDirectory: String, + javaOutputDirectory: String ) { self.analysis = translator.result self.swiftModuleName = translator.swiftModuleName From a16cecfee447631cafda004de0043431682e0e78 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Mon, 9 Jun 2025 09:19:27 +0200 Subject: [PATCH 5/8] add license headers --- Sources/JExtractSwiftLib/AnalysisResult.swift | 14 ++++++++++++++ Sources/JExtractSwiftLib/GenerationMode.swift | 14 ++++++++++++++ Sources/JExtractSwiftLib/Swift2JavaGenerator.swift | 14 ++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/Sources/JExtractSwiftLib/AnalysisResult.swift b/Sources/JExtractSwiftLib/AnalysisResult.swift index 1e37d5c5..4d33bea1 100644 --- a/Sources/JExtractSwiftLib/AnalysisResult.swift +++ b/Sources/JExtractSwiftLib/AnalysisResult.swift @@ -1,3 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + struct AnalysisResult { let importedTypes: [String: ImportedNominalType] let importedGlobalVariables: [ImportedFunc] diff --git a/Sources/JExtractSwiftLib/GenerationMode.swift b/Sources/JExtractSwiftLib/GenerationMode.swift index 95266e1a..e2c4b486 100644 --- a/Sources/JExtractSwiftLib/GenerationMode.swift +++ b/Sources/JExtractSwiftLib/GenerationMode.swift @@ -1,3 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + import ArgumentParser public enum GenerationMode: String, ExpressibleByArgument { diff --git a/Sources/JExtractSwiftLib/Swift2JavaGenerator.swift b/Sources/JExtractSwiftLib/Swift2JavaGenerator.swift index e78598e5..f9c32ad5 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaGenerator.swift @@ -1,3 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + protocol Swift2JavaGenerator { func generate() throws } From f85aad238369670f477c1701a78e4a44da3d4a77 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Mon, 9 Jun 2025 09:20:32 +0200 Subject: [PATCH 6/8] bring back old generation order --- Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index fa235edf..c5faa49a 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -52,8 +52,8 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { } func generate() throws { - try writeExportedJavaSources() try writeSwiftThunkSources() + try writeExportedJavaSources() } } From 632eb0be7f61841e6e24fbe88c20b3c8dd8eb8a7 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Mon, 9 Jun 2025 14:39:34 +0200 Subject: [PATCH 7/8] use log level from translator --- Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index c5faa49a..5abd145e 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -17,8 +17,7 @@ import SwiftSyntax import SwiftSyntaxBuilder package class FFMSwift2JavaGenerator: Swift2JavaGenerator { - let log = Logger(label: "ffm-generator", logLevel: .info) - + let log: Logger let analysis: AnalysisResult let swiftModuleName: String let javaPackage: String @@ -42,6 +41,7 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { swiftOutputDirectory: String, javaOutputDirectory: String ) { + self.log = Logger(label: "ffm-generator", logLevel: translator.log.logLevel) self.analysis = translator.result self.swiftModuleName = translator.swiftModuleName self.javaPackage = javaPackage From 30b0ce0fdfe1e751d13ff5540cdb336ee94b1b37 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Mon, 9 Jun 2025 14:58:53 +0200 Subject: [PATCH 8/8] migrate to shared tool --- .../FFM/FFMSwift2JavaGenerator.swift | 3 +++ .../ForeignValueLayouts.swift | 0 Sources/JExtractSwiftLib/Swift2Java.swift | 20 +++++-------------- .../Configuration.swift | 4 +++- .../GenerationMode.swift | 4 +--- Sources/SwiftJavaTool/SwiftJava.swift | 6 ++++++ 6 files changed, 18 insertions(+), 19 deletions(-) rename Sources/JExtractSwiftLib/{JavaConstants => FFM}/ForeignValueLayouts.swift (100%) rename Sources/{JExtractSwiftLib => JavaKitConfigurationShared}/GenerationMode.swift (86%) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 5abd145e..bd3251b4 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -53,7 +53,10 @@ package class FFMSwift2JavaGenerator: Swift2JavaGenerator { func generate() throws { try writeSwiftThunkSources() + print("[swift-java] Generated Swift sources (module: '\(self.swiftModuleName)') in: \(swiftOutputDirectory)/") + try writeExportedJavaSources() + print("[swift-java] Generated Java sources (package: '\(javaPackage)') in: \(javaOutputDirectory)/") } } diff --git a/Sources/JExtractSwiftLib/JavaConstants/ForeignValueLayouts.swift b/Sources/JExtractSwiftLib/FFM/ForeignValueLayouts.swift similarity index 100% rename from Sources/JExtractSwiftLib/JavaConstants/ForeignValueLayouts.swift rename to Sources/JExtractSwiftLib/FFM/ForeignValueLayouts.swift diff --git a/Sources/JExtractSwiftLib/Swift2Java.swift b/Sources/JExtractSwiftLib/Swift2Java.swift index 895dc61f..8adb7670 100644 --- a/Sources/JExtractSwiftLib/Swift2Java.swift +++ b/Sources/JExtractSwiftLib/Swift2Java.swift @@ -31,7 +31,6 @@ public struct SwiftToJava { } let translator = Swift2JavaTranslator( - javaPackage: config.javaPackage ?? "", // no package is ok, we'd generate all into top level swiftModuleName: swiftModule ) translator.log.logLevel = config.logLevel ?? .info @@ -89,27 +88,18 @@ public struct SwiftToJava { try translator.analyze() - switch mode { - case .ffm: + switch config.mode { + case .some(.ffm), .none: let generator = FFMSwift2JavaGenerator( translator: translator, - javaPackage: self.packageName, - swiftOutputDirectory: outputDirectorySwift, - javaOutputDirectory: outputDirectoryJava + javaPackage: config.javaPackage ?? "", + swiftOutputDirectory: outputSwiftDirectory, + javaOutputDirectory: outputJavaDirectory ) try generator.generate() } - - print("[swift-java] Generated Java sources (\(packageName)) in: \(outputDirectoryJava)/") - - try translator.writeSwiftThunkSources(outputDirectory: outputSwiftDirectory) - print("[swift-java] Generated Swift sources (module: '\(config.swiftModule ?? "")') in: \(outputSwiftDirectory)/") - - try translator.writeExportedJavaSources(outputDirectory: outputJavaDirectory) - print("[swift-java] Generated Java sources (package: '\(config.javaPackage ?? "")') in: \(outputJavaDirectory)/") - print("[swift-java] Imported Swift module '\(swiftModule)': " + "done.".green) } diff --git a/Sources/JavaKitConfigurationShared/Configuration.swift b/Sources/JavaKitConfigurationShared/Configuration.swift index 1c4fad56..2314a1b8 100644 --- a/Sources/JavaKitConfigurationShared/Configuration.swift +++ b/Sources/JavaKitConfigurationShared/Configuration.swift @@ -38,6 +38,8 @@ public struct Configuration: Codable { public var outputJavaDirectory: String? + public var mode: GenerationMode? + // ==== java 2 swift --------------------------------------------------------- /// The Java class path that should be passed along to the Java2Swift tool. @@ -255,4 +257,4 @@ extension LogLevel { } try container.encode(text) } -} \ No newline at end of file +} diff --git a/Sources/JExtractSwiftLib/GenerationMode.swift b/Sources/JavaKitConfigurationShared/GenerationMode.swift similarity index 86% rename from Sources/JExtractSwiftLib/GenerationMode.swift rename to Sources/JavaKitConfigurationShared/GenerationMode.swift index e2c4b486..ff1d081d 100644 --- a/Sources/JExtractSwiftLib/GenerationMode.swift +++ b/Sources/JavaKitConfigurationShared/GenerationMode.swift @@ -12,9 +12,7 @@ // //===----------------------------------------------------------------------===// -import ArgumentParser - -public enum GenerationMode: String, ExpressibleByArgument { +public enum GenerationMode: String, Codable { /// Foreign Value and Memory API case ffm } diff --git a/Sources/SwiftJavaTool/SwiftJava.swift b/Sources/SwiftJavaTool/SwiftJava.swift index a0ba75dc..cae7a2a4 100644 --- a/Sources/SwiftJavaTool/SwiftJava.swift +++ b/Sources/SwiftJavaTool/SwiftJava.swift @@ -72,6 +72,9 @@ struct SwiftJava: AsyncParsableCommand { @Option(help: "The Java package the generated Java code should be emitted into.") var javaPackage: String? = nil + @Option(help: "The mode of generation to use for the output files. Used with jextract mode.") + var mode: GenerationMode = .ffm + // TODO: clarify this vs outputSwift (history: outputSwift is jextract, and this was java2swift) @Option(name: .shortAndLong, help: "The directory in which to output the generated Swift files or the SwiftJava configuration file.") var outputDirectory: String? = nil @@ -227,6 +230,7 @@ struct SwiftJava: AsyncParsableCommand { config.inputSwiftDirectory = self.inputSwift config.outputSwiftDirectory = self.outputSwift config.outputJavaDirectory = self.outputJava + config.mode = self.mode toolMode = .jextract } else if jar { @@ -512,3 +516,5 @@ extension SwiftJava.ToolMode { } } } + +extension GenerationMode: ExpressibleByArgument {}