Skip to content

Commit cb895e8

Browse files
committed
generate java code for primitive types and global functions
1 parent 8de85be commit cb895e8

File tree

4 files changed

+165
-1
lines changed

4 files changed

+165
-1
lines changed

Sources/JExtractSwiftLib/ImportedDecls.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible {
6060
self.swiftDecl.signatureString
6161
}
6262

63-
6463
var parentType: SwiftType? {
6564
guard let selfParameter = functionSignature.selfParameter else {
6665
return nil
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
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+
import JavaTypes
16+
17+
struct JNISwift2JavaGenerator: Swift2JavaGenerator {
18+
let analysis: AnalysisResult
19+
let swiftModuleName: String
20+
let javaPackage: String
21+
let logger: Logger
22+
let swiftOutputDirectory: String
23+
let javaOutputDirectory: String
24+
25+
var javaPackagePath: String {
26+
javaPackage.replacingOccurrences(of: ".", with: "/")
27+
}
28+
29+
init(
30+
translator: Swift2JavaTranslator,
31+
javaPackage: String,
32+
swiftOutputDirectory: String,
33+
javaOutputDirectory: String
34+
) {
35+
self.logger = Logger(label: "jni-generator", logLevel: translator.log.logLevel)
36+
self.analysis = translator.result
37+
self.swiftModuleName = translator.swiftModuleName
38+
self.javaPackage = javaPackage
39+
self.swiftOutputDirectory = swiftOutputDirectory
40+
self.javaOutputDirectory = javaOutputDirectory
41+
}
42+
43+
func generate() throws {
44+
try writeExportedJavaSources()
45+
}
46+
}
47+
48+
extension JNISwift2JavaGenerator {
49+
func writeExportedJavaSources() throws {
50+
var printer = CodePrinter()
51+
52+
let filename = "\(self.swiftModuleName).java"
53+
logger.trace("Printing module class: \(filename)")
54+
printModule(&printer)
55+
56+
if let outputFile = try printer.writeContents(
57+
outputDirectory: javaOutputDirectory,
58+
javaPackagePath: javaPackagePath,
59+
filename: filename
60+
) {
61+
logger.info("[swift-java] Generated: \(self.swiftModuleName).java (at \(outputFile))")
62+
}
63+
}
64+
}
65+
66+
extension JNISwift2JavaGenerator {
67+
private func printModule(_ printer: inout CodePrinter) {
68+
printHeader(&printer)
69+
printPackage(&printer)
70+
71+
printModuleClass(&printer) { printer in
72+
for decl in analysis.importedGlobalFuncs {
73+
self.logger.trace("Print global function: \(decl)")
74+
printFunctionBinding(&printer, decl)
75+
}
76+
}
77+
}
78+
79+
private func printHeader(_ printer: inout CodePrinter) {
80+
printer.print(
81+
"""
82+
// Generated by jextract-swift
83+
// Swift module: \(swiftModuleName)
84+
85+
"""
86+
)
87+
}
88+
89+
private func printPackage(_ printer: inout CodePrinter) {
90+
printer.print(
91+
"""
92+
package \(javaPackage);
93+
94+
"""
95+
)
96+
}
97+
98+
private func printModuleClass(_ printer: inout CodePrinter, body: (inout CodePrinter) -> Void) {
99+
printer.printBraceBlock("public final class \(swiftModuleName)") { printer in
100+
body(&printer)
101+
}
102+
}
103+
104+
private func printFunctionBinding(_ printer: inout CodePrinter, _ decl: ImportedFunc) {
105+
let returnType = decl.functionSignature.result.type.javaType
106+
let params = decl.functionSignature.parameters.map {
107+
"\($0.type.javaType) \($0.parameterName!)"
108+
}
109+
110+
printer.print("public static native \(returnType) \(decl.name)(\(params.joined(separator: ", ")));")
111+
}
112+
}
113+
114+
extension SwiftType {
115+
var javaType: JavaType {
116+
switch self {
117+
case .nominal(let nominalType):
118+
if let knownType = nominalType.nominalTypeDecl.knownStandardLibraryType {
119+
guard let javaType = knownType.javaType else {
120+
fatalError("unsupported known type: \(knownType)")
121+
}
122+
return javaType
123+
}
124+
125+
fatalError("unsupported nominal type: \(nominalType)")
126+
127+
case .tuple([]):
128+
return .void
129+
130+
case .metatype, .optional, .tuple, .function:
131+
fatalError("unsupported")
132+
}
133+
}
134+
}
135+
136+
extension KnownStandardLibraryType {
137+
var javaType: JavaType? {
138+
switch self {
139+
case .bool: .boolean
140+
case .int, .uint: .long // TODO: Handle 32-bit or 64-bit
141+
case .int8, .uint8: .byte
142+
case .uint16: .char
143+
case .int16: .short
144+
case .int32, .uint32: .int
145+
case .int64, .uint64: .long
146+
case .float: .float
147+
case .double: .double
148+
case .void: .void
149+
case .unsafeRawPointer, .unsafeMutableRawPointer, .unsafePointer, .unsafeMutablePointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string: nil
150+
}
151+
}
152+
}

Sources/JExtractSwiftLib/Swift2Java.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ public struct SwiftToJava {
9797
javaOutputDirectory: outputJavaDirectory
9898
)
9999

100+
try generator.generate()
101+
102+
case .jni:
103+
let generator = JNISwift2JavaGenerator(
104+
translator: translator,
105+
javaPackage: config.javaPackage ?? "",
106+
swiftOutputDirectory: outputSwiftDirectory,
107+
javaOutputDirectory: outputJavaDirectory
108+
)
109+
100110
try generator.generate()
101111
}
102112

Sources/JavaKitConfigurationShared/GenerationMode.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,7 @@
1515
public enum GenerationMode: String, Codable {
1616
/// Foreign Value and Memory API
1717
case ffm
18+
19+
/// Java Native Interface
20+
case jni
1821
}

0 commit comments

Comments
 (0)