Skip to content

Commit eff20b9

Browse files
authored
Merge pull request #249 from madsodgaard/protocol-generation
2 parents 99fb077 + 4445a68 commit eff20b9

27 files changed

+308
-141
lines changed

.editorconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
root = true
2+
3+
[*.swift]
4+
indent_style = space
5+
indent_size = 2
6+
insert_final_newline = true
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
struct AnalysisResult {
16+
let importedTypes: [String: ImportedNominalType]
17+
let importedGlobalVariables: [ImportedFunc]
18+
let importedGlobalFuncs: [ImportedFunc]
19+
}

Sources/JExtractSwiftLib/CodePrinter.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
import Foundation
1616

17+
let PATH_SEPARATOR = "/" // TODO: Windows
18+
1719
public struct CodePrinter {
1820
var contents: String = ""
1921

Sources/JExtractSwiftLib/CDeclLowering/Swift2JavaTranslator+FunctionLowering.swift renamed to Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import JavaTypes
1616
import SwiftSyntax
1717

18-
extension Swift2JavaTranslator {
18+
extension FFMSwift2JavaGenerator {
1919
/// Lower the given function declaration to a C-compatible entrypoint,
2020
/// providing all of the mappings between the parameter and result types
2121
/// of the original function and its `@_cdecl` counterpart.

Sources/JExtractSwiftLib/Swift2JavaTranslator+JavaBindingsPrinting.swift renamed to Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
import JavaTypes
1616

17-
extension Swift2JavaTranslator {
18-
public func printFunctionDowncallMethods(
17+
extension FFMSwift2JavaGenerator {
18+
func printFunctionDowncallMethods(
1919
_ printer: inout CodePrinter,
2020
_ decl: ImportedFunc
2121
) {
@@ -211,7 +211,7 @@ extension Swift2JavaTranslator {
211211
let memoryLayout = renderMemoryLayoutValue(for: outParameter.type)
212212

213213
let arena = if let className = outParameter.type.className,
214-
self.importedTypes[className] != nil {
214+
analysis.importedTypes[className] != nil {
215215
// Use passed-in 'SwiftArena' for 'SwiftValue'.
216216
"swiftArena$"
217217
} else {

Sources/JExtractSwiftLib/Swift2JavaTranslator+JavaTranslation.swift renamed to Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import JavaTypes
1616

17-
extension Swift2JavaTranslator {
17+
extension FFMSwift2JavaGenerator {
1818
func translatedSignature(
1919
for decl: ImportedFunc
2020
) -> TranslatedFunctionSignature? {

Sources/JExtractSwiftLib/Swift2JavaTranslator+SwiftThunkPrinting.swift renamed to Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@
1515
import SwiftSyntax
1616
import SwiftSyntaxBuilder
1717

18-
extension Swift2JavaTranslator {
19-
public func writeSwiftThunkSources(outputDirectory: String) throws {
18+
extension FFMSwift2JavaGenerator {
19+
package func writeSwiftThunkSources() throws {
2020
var printer = CodePrinter()
21-
22-
try writeSwiftThunkSources(outputDirectory: outputDirectory, printer: &printer)
21+
try writeSwiftThunkSources(printer: &printer)
2322
}
2423

25-
public func writeSwiftThunkSources(outputDirectory: String, printer: inout CodePrinter) throws {
24+
package func writeSwiftThunkSources(printer: inout CodePrinter) throws {
2625
let moduleFilenameBase = "\(self.swiftModuleName)Module+SwiftJava"
2726
let moduleFilename = "\(moduleFilenameBase).swift"
2827
do {
@@ -31,7 +30,7 @@ extension Swift2JavaTranslator {
3130
try printGlobalSwiftThunkSources(&printer)
3231

3332
if let outputFile = try printer.writeContents(
34-
outputDirectory: outputDirectory,
33+
outputDirectory: self.swiftOutputDirectory,
3534
javaPackagePath: nil,
3635
filename: moduleFilename)
3736
{
@@ -42,7 +41,7 @@ extension Swift2JavaTranslator {
4241
}
4342

4443
// === All types
45-
for (_, ty) in importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) {
44+
for (_, ty) in self.analysis.importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) {
4645
let fileNameBase = "\(ty.swiftNominal.qualifiedName)+SwiftJava"
4746
let filename = "\(fileNameBase).swift"
4847
log.info("Printing contents: \(filename)")
@@ -51,7 +50,7 @@ extension Swift2JavaTranslator {
5150
try printSwiftThunkSources(&printer, ty: ty)
5251

5352
if let outputFile = try printer.writeContents(
54-
outputDirectory: outputDirectory,
53+
outputDirectory: self.swiftOutputDirectory,
5554
javaPackagePath: nil,
5655
filename: filename)
5756
{
@@ -110,23 +109,23 @@ extension Swift2JavaTranslator {
110109

111110
struct SwiftThunkTranslator {
112111

113-
let st: Swift2JavaTranslator
112+
let st: FFMSwift2JavaGenerator
114113

115-
init(_ st: Swift2JavaTranslator) {
114+
init(_ st: FFMSwift2JavaGenerator) {
116115
self.st = st
117116
}
118117

119118
func renderGlobalThunks() -> [DeclSyntax] {
120119
var decls: [DeclSyntax] = []
121120
decls.reserveCapacity(
122-
st.importedGlobalVariables.count + st.importedGlobalFuncs.count
121+
st.analysis.importedGlobalVariables.count + st.analysis.importedGlobalFuncs.count
123122
)
124123

125-
for decl in st.importedGlobalVariables {
124+
for decl in st.analysis.importedGlobalVariables {
126125
decls.append(contentsOf: render(forFunc: decl))
127126
}
128127

129-
for decl in st.importedGlobalFuncs {
128+
for decl in st.analysis.importedGlobalFuncs {
130129
decls.append(contentsOf: render(forFunc: decl))
131130
}
132131

Sources/JExtractSwiftLib/Swift2JavaTranslator+Printing.swift renamed to Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift

Lines changed: 87 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,90 @@ import JavaTypes
1616
import SwiftSyntax
1717
import SwiftSyntaxBuilder
1818

19+
package class FFMSwift2JavaGenerator: Swift2JavaGenerator {
20+
let log: Logger
21+
let analysis: AnalysisResult
22+
let swiftModuleName: String
23+
let javaPackage: String
24+
let swiftOutputDirectory: String
25+
let javaOutputDirectory: String
26+
let swiftStdlibTypes: SwiftStandardLibraryTypes
27+
let symbolTable: SwiftSymbolTable
28+
29+
var javaPackagePath: String {
30+
javaPackage.replacingOccurrences(of: ".", with: "/")
31+
}
32+
33+
var thunkNameRegistry: ThunkNameRegistry = ThunkNameRegistry()
34+
35+
/// Cached Java translation result. 'nil' indicates failed translation.
36+
var translatedSignatures: [ImportedFunc: TranslatedFunctionSignature?] = [:]
37+
38+
package init(
39+
translator: Swift2JavaTranslator,
40+
javaPackage: String,
41+
swiftOutputDirectory: String,
42+
javaOutputDirectory: String
43+
) {
44+
self.log = Logger(label: "ffm-generator", logLevel: translator.log.logLevel)
45+
self.analysis = translator.result
46+
self.swiftModuleName = translator.swiftModuleName
47+
self.javaPackage = javaPackage
48+
self.swiftOutputDirectory = swiftOutputDirectory
49+
self.javaOutputDirectory = javaOutputDirectory
50+
self.symbolTable = translator.symbolTable
51+
self.swiftStdlibTypes = translator.swiftStdlibTypes
52+
}
53+
54+
func generate() throws {
55+
try writeSwiftThunkSources()
56+
print("[swift-java] Generated Swift sources (module: '\(self.swiftModuleName)') in: \(swiftOutputDirectory)/")
57+
58+
try writeExportedJavaSources()
59+
print("[swift-java] Generated Java sources (package: '\(javaPackage)') in: \(javaOutputDirectory)/")
60+
}
61+
}
62+
63+
// ===== --------------------------------------------------------------------------------------------------------------
64+
// MARK: Defaults
65+
66+
extension FFMSwift2JavaGenerator {
67+
68+
/// Default set Java imports for every generated file
69+
static let defaultJavaImports: Array<String> = [
70+
"org.swift.swiftkit.*",
71+
"org.swift.swiftkit.SwiftKit",
72+
"org.swift.swiftkit.util.*",
73+
74+
// Necessary for native calls and type mapping
75+
"java.lang.foreign.*",
76+
"java.lang.invoke.*",
77+
"java.util.Arrays",
78+
"java.util.stream.Collectors",
79+
"java.util.concurrent.atomic.*",
80+
"java.nio.charset.StandardCharsets",
81+
]
82+
}
83+
1984
// ==== ---------------------------------------------------------------------------------------------------------------
2085
// MARK: File writing
2186

22-
let PATH_SEPARATOR = "/" // TODO: Windows
2387

24-
extension Swift2JavaTranslator {
25-
26-
/// Every imported public type becomes a public class in its own file in Java.
27-
public func writeExportedJavaSources(outputDirectory: String) throws {
88+
extension FFMSwift2JavaGenerator {
89+
package func writeExportedJavaSources() throws {
2890
var printer = CodePrinter()
29-
try writeExportedJavaSources(outputDirectory: outputDirectory, printer: &printer)
91+
try writeExportedJavaSources(printer: &printer)
3092
}
3193

32-
public func writeExportedJavaSources(outputDirectory: String, printer: inout CodePrinter) throws {
33-
for (_, ty) in importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) {
94+
/// Every imported public type becomes a public class in its own file in Java.
95+
package func writeExportedJavaSources(printer: inout CodePrinter) throws {
96+
for (_, ty) in analysis.importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) {
3497
let filename = "\(ty.swiftNominal.name).java"
3598
log.info("Printing contents: \(filename)")
3699
printImportedNominal(&printer, ty)
37100

38101
if let outputFile = try printer.writeContents(
39-
outputDirectory: outputDirectory,
102+
outputDirectory: javaOutputDirectory,
40103
javaPackagePath: javaPackagePath,
41104
filename: filename
42105
) {
@@ -50,7 +113,7 @@ extension Swift2JavaTranslator {
50113
printModule(&printer)
51114

52115
if let outputFile = try printer.writeContents(
53-
outputDirectory: outputDirectory,
116+
outputDirectory: javaOutputDirectory,
54117
javaPackagePath: javaPackagePath,
55118
filename: filename)
56119
{
@@ -63,26 +126,26 @@ extension Swift2JavaTranslator {
63126
// ==== ---------------------------------------------------------------------------------------------------------------
64127
// MARK: Java/text printing
65128

66-
extension Swift2JavaTranslator {
129+
extension FFMSwift2JavaGenerator {
67130

68131
/// Render the Java file contents for an imported Swift module.
69132
///
70133
/// This includes any Swift global functions in that module, and some general type information and helpers.
71-
public func printModule(_ printer: inout CodePrinter) {
134+
func printModule(_ printer: inout CodePrinter) {
72135
printHeader(&printer)
73136
printPackage(&printer)
74137
printImports(&printer)
75138

76139
printModuleClass(&printer) { printer in
77140
// TODO: print all "static" methods
78-
for decl in importedGlobalFuncs {
141+
for decl in analysis.importedGlobalFuncs {
79142
self.log.trace("Print imported decl: \(decl)")
80143
printFunctionDowncallMethods(&printer, decl)
81144
}
82145
}
83146
}
84147

85-
package func printImportedNominal(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
148+
func printImportedNominal(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
86149
printHeader(&printer)
87150
printPackage(&printer)
88151
printImports(&printer)
@@ -143,7 +206,7 @@ extension Swift2JavaTranslator {
143206
}
144207
}
145208

146-
public func printHeader(_ printer: inout CodePrinter) {
209+
func printHeader(_ printer: inout CodePrinter) {
147210
printer.print(
148211
"""
149212
// Generated by jextract-swift
@@ -153,7 +216,7 @@ extension Swift2JavaTranslator {
153216
)
154217
}
155218

156-
public func printPackage(_ printer: inout CodePrinter) {
219+
func printPackage(_ printer: inout CodePrinter) {
157220
printer.print(
158221
"""
159222
package \(javaPackage);
@@ -162,14 +225,14 @@ extension Swift2JavaTranslator {
162225
)
163226
}
164227

165-
public func printImports(_ printer: inout CodePrinter) {
166-
for i in Swift2JavaTranslator.defaultJavaImports {
228+
func printImports(_ printer: inout CodePrinter) {
229+
for i in FFMSwift2JavaGenerator.defaultJavaImports {
167230
printer.print("import \(i);")
168231
}
169232
printer.print("")
170233
}
171234

172-
package func printNominal(
235+
func printNominal(
173236
_ printer: inout CodePrinter, _ decl: ImportedNominalType, body: (inout CodePrinter) -> Void
174237
) {
175238
let parentProtocol: String
@@ -188,7 +251,7 @@ extension Swift2JavaTranslator {
188251
}
189252
}
190253

191-
public func printModuleClass(_ printer: inout CodePrinter, body: (inout CodePrinter) -> Void) {
254+
func printModuleClass(_ printer: inout CodePrinter, body: (inout CodePrinter) -> Void) {
192255
printer.printBraceBlock("public final class \(swiftModuleName)") { printer in
193256
printPrivateConstructor(&printer, swiftModuleName)
194257

@@ -261,7 +324,7 @@ extension Swift2JavaTranslator {
261324
}
262325
}
263326

264-
private func printClassConstants(printer: inout CodePrinter) {
327+
func printClassConstants(printer: inout CodePrinter) {
265328
printer.print(
266329
"""
267330
static final String LIB_NAME = "\(swiftModuleName)";
@@ -270,7 +333,7 @@ extension Swift2JavaTranslator {
270333
)
271334
}
272335

273-
private func printPrivateConstructor(_ printer: inout CodePrinter, _ typeName: String) {
336+
func printPrivateConstructor(_ printer: inout CodePrinter, _ typeName: String) {
274337
printer.print(
275338
"""
276339
private \(typeName)() {
@@ -296,7 +359,7 @@ extension Swift2JavaTranslator {
296359
)
297360
}
298361

299-
package func printToStringMethod(
362+
func printToStringMethod(
300363
_ printer: inout CodePrinter, _ decl: ImportedNominalType
301364
) {
302365
printer.print(
@@ -312,3 +375,4 @@ extension Swift2JavaTranslator {
312375
""")
313376
}
314377
}
378+

Sources/JExtractSwiftLib/Swift2Java.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ public struct SwiftToJava {
3131
}
3232

3333
let translator = Swift2JavaTranslator(
34-
javaPackage: config.javaPackage ?? "", // no package is ok, we'd generate all into top level
3534
swiftModuleName: swiftModule
3635
)
3736
translator.log.logLevel = config.logLevel ?? .info
@@ -89,11 +88,17 @@ public struct SwiftToJava {
8988

9089
try translator.analyze()
9190

92-
try translator.writeSwiftThunkSources(outputDirectory: outputSwiftDirectory)
93-
print("[swift-java] Generated Swift sources (module: '\(config.swiftModule ?? "")') in: \(outputSwiftDirectory)/")
91+
switch config.mode {
92+
case .some(.ffm), .none:
93+
let generator = FFMSwift2JavaGenerator(
94+
translator: translator,
95+
javaPackage: config.javaPackage ?? "",
96+
swiftOutputDirectory: outputSwiftDirectory,
97+
javaOutputDirectory: outputJavaDirectory
98+
)
9499

95-
try translator.writeExportedJavaSources(outputDirectory: outputJavaDirectory)
96-
print("[swift-java] Generated Java sources (package: '\(config.javaPackage ?? "")') in: \(outputJavaDirectory)/")
100+
try generator.generate()
101+
}
97102

98103
print("[swift-java] Imported Swift module '\(swiftModule)': " + "done.".green)
99104
}

0 commit comments

Comments
 (0)