Skip to content

Commit dc30a0d

Browse files
committed
output swift thunk for JNI
1 parent 200dc8b commit dc30a0d

File tree

2 files changed

+97
-5
lines changed

2 files changed

+97
-5
lines changed

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import JavaTypes
1616

17-
struct JNISwift2JavaGenerator: Swift2JavaGenerator {
17+
class JNISwift2JavaGenerator: Swift2JavaGenerator {
1818
let analysis: AnalysisResult
1919
let swiftModuleName: String
2020
let javaPackage: String
@@ -26,6 +26,8 @@ struct JNISwift2JavaGenerator: Swift2JavaGenerator {
2626
javaPackage.replacingOccurrences(of: ".", with: "/")
2727
}
2828

29+
var thunkNameRegistry = ThunkNameRegistry()
30+
2931
init(
3032
translator: Swift2JavaTranslator,
3133
javaPackage: String,
@@ -41,6 +43,7 @@ struct JNISwift2JavaGenerator: Swift2JavaGenerator {
4143
}
4244

4345
func generate() throws {
46+
try writeSwiftThunkSources()
4447
try writeExportedJavaSources()
4548
}
4649
}
@@ -61,6 +64,84 @@ extension JNISwift2JavaGenerator {
6164
logger.info("[swift-java] Generated: \(self.swiftModuleName).java (at \(outputFile))")
6265
}
6366
}
67+
68+
func writeSwiftThunkSources() throws {
69+
var printer = CodePrinter()
70+
71+
let moduleFilenameBase = "\(self.swiftModuleName)Module+SwiftJava"
72+
let moduleFilename = "\(moduleFilenameBase).swift"
73+
74+
logger.trace("Printing swift module class: \(moduleFilename)")
75+
76+
try printGlobalSwiftThunkSources(&printer)
77+
78+
if let outputFile = try printer.writeContents(
79+
outputDirectory: self.swiftOutputDirectory,
80+
javaPackagePath: nil,
81+
filename: moduleFilename)
82+
{
83+
print("[swift-java] Generated: \(moduleFilenameBase.bold).swift (at \(outputFile)")
84+
}
85+
}
86+
}
87+
88+
extension JNISwift2JavaGenerator {
89+
private func printGlobalSwiftThunkSources(_ printer: inout CodePrinter) throws {
90+
printer.print(
91+
"""
92+
// Generated by swift-java
93+
94+
import JavaKit
95+
96+
""")
97+
98+
for decl in analysis.importedGlobalFuncs {
99+
printSwiftFunctionThunk(&printer, decl)
100+
printer.println()
101+
}
102+
}
103+
104+
private func printSwiftFunctionThunk(_ printer: inout CodePrinter, _ decl: ImportedFunc) {
105+
// TODO: Replace swiftModuleName with class name if non-global
106+
let cName = "Java_" + self.javaPackage.replacingOccurrences(of: ".", with: "_") + "_\(swiftModuleName)_" + decl.name
107+
let thunkName = thunkNameRegistry.functionThunkName(decl: decl)
108+
let translatedParameters = decl.functionSignature.parameters.enumerated().map { idx, param in
109+
(param.parameterName ?? "arg\(idx)", param.type.javaType)
110+
}
111+
112+
let thunkParameters = [
113+
"environment: UnsafeMutablePointer<JNIEnv?>!",
114+
"thisClass: jclass"
115+
] + translatedParameters.map { "\($0.0): \($0.1.jniTypeName)"}
116+
let swiftReturnType = decl.functionSignature.result.type
117+
118+
let thunkReturnType = !swiftReturnType.isVoid ? " -> \(swiftReturnType.javaType.jniTypeName)" : ""
119+
120+
printer.printBraceBlock(
121+
"""
122+
@_cdecl("\(cName)")
123+
func \(thunkName)(\(thunkParameters.joined(separator: ", ")))\(thunkReturnType)
124+
"""
125+
) { printer in
126+
let downcallParameters = zip(decl.functionSignature.parameters, translatedParameters).map { originalParam, translatedParam in
127+
let label = originalParam.argumentLabel ?? originalParam.parameterName ?? ""
128+
return "\(label)\(!label.isEmpty ? ": " : "")\(translatedParam.0)"
129+
}
130+
let functionDowncall = "\(swiftModuleName).\(decl.name)(\(downcallParameters.joined(separator: ", ")))"
131+
printer.print("\(functionDowncall)")
132+
133+
if swiftReturnType.isVoid {
134+
printer.print(functionDowncall)
135+
} else {
136+
printer.print(
137+
"""
138+
let result = \(functionDowncall)
139+
return result.getJNIValue(in: environment)")
140+
"""
141+
)
142+
}
143+
}
144+
}
64145
}
65146

66147
extension JNISwift2JavaGenerator {
@@ -72,6 +153,7 @@ extension JNISwift2JavaGenerator {
72153
for decl in analysis.importedGlobalFuncs {
73154
self.logger.trace("Print global function: \(decl)")
74155
printFunctionBinding(&printer, decl)
156+
printer.println()
75157
}
76158
}
77159
}
@@ -103,10 +185,20 @@ extension JNISwift2JavaGenerator {
103185

104186
private func printFunctionBinding(_ printer: inout CodePrinter, _ decl: ImportedFunc) {
105187
let returnType = decl.functionSignature.result.type.javaType
106-
let params = decl.functionSignature.parameters.map {
107-
"\($0.type.javaType) \($0.parameterName!)"
188+
let params = decl.functionSignature.parameters.enumerated().map { idx, param in
189+
"\(param.type.javaType) \(param.parameterName ?? "arg\(idx))")"
108190
}
109191

192+
printer.print(
193+
"""
194+
/**
195+
* Downcall to Swift:
196+
* {@snippet lang=swift :
197+
* \(decl.signatureString)
198+
* }
199+
*/
200+
"""
201+
)
110202
printer.print("public static native \(returnType) \(decl.name)(\(params.joined(separator: ", ")));")
111203
}
112204
}
@@ -128,7 +220,7 @@ extension SwiftType {
128220
return .void
129221

130222
case .metatype, .optional, .tuple, .function:
131-
fatalError("unsupported")
223+
fatalError("unsupported type: \(self)")
132224
}
133225
}
134226
}

Sources/JavaTypes/JavaType+JNI.swift

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

1515
extension JavaType {
1616
/// Map this Java type to the appropriate JNI type name.
17-
var jniTypeName: String {
17+
package var jniTypeName: String {
1818
switch self {
1919
case .boolean: "jboolean"
2020
case .float: "jfloat"

0 commit comments

Comments
 (0)