Skip to content

Commit c8009d7

Browse files
committed
[JExtract] Lazy Cdecl lowering and Java translation
Make `Swift2JavaTranslator.analyze()` analyze only Swift signatures. Postpone Cdecl lowering and Java translation for cleaner separation of responsibilities.
1 parent 72bf28f commit c8009d7

7 files changed

+74
-66
lines changed

Sources/JExtractSwift/ImportedDecls.swift

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -52,30 +52,18 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible {
5252

5353
public var swiftDecl: any DeclSyntaxProtocol
5454

55-
var translatedSignature: TranslatedFunctionSignature
56-
5755
package var apiKind: SwiftAPIKind
5856

57+
var functionSignature: SwiftFunctionSignature
58+
5959
public var signatureString: String {
6060
// FIXME: Remove comments and normalize trivia.
6161
self.swiftDecl.signatureString
6262
}
6363

64-
var loweredSignature: LoweredFunctionSignature {
65-
translatedSignature.loweredSignature
66-
}
67-
68-
var swiftSignature: SwiftFunctionSignature {
69-
loweredSignature.original
70-
}
71-
72-
package func cFunctionDecl(cName: String) -> CFunction {
73-
// 'try!' because we know 'loweredSignature' can be described with C.
74-
try! loweredSignature.cFunctionDecl(cName: cName)
75-
}
7664

7765
var parentType: SwiftType? {
78-
guard let selfParameter = swiftSignature.selfParameter else {
66+
guard let selfParameter = functionSignature.selfParameter else {
7967
return nil
8068
}
8169
switch selfParameter {
@@ -92,7 +80,7 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible {
9280
/// this will contain that declaration's imported name.
9381
///
9482
/// This is necessary when rendering accessor Java code we need the type that "self" is expecting to have.
95-
public var hasParent: Bool { translatedSignature.selfParameter != nil }
83+
public var hasParent: Bool { functionSignature.selfParameter != nil }
9684

9785
/// A display name to use to refer to the Swift declaration with its
9886
/// enclosing type, if there is one.
@@ -117,13 +105,13 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible {
117105
swiftDecl: any DeclSyntaxProtocol,
118106
name: String,
119107
apiKind: SwiftAPIKind,
120-
translatedSignature: TranslatedFunctionSignature
108+
functionSignature: SwiftFunctionSignature
121109
) {
122110
self.module = module
123111
self.name = name
124112
self.swiftDecl = swiftDecl
125113
self.apiKind = apiKind
126-
self.translatedSignature = translatedSignature
114+
self.functionSignature = functionSignature
127115
}
128116

129117
public var description: String {

Sources/JExtractSwift/Swift2JavaTranslator+JavaBindingsPrinting.swift

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ extension Swift2JavaTranslator {
1919
_ printer: inout CodePrinter,
2020
_ decl: ImportedFunc
2121
) {
22+
guard let _ = translatedSignature(for: decl) else {
23+
// Failed to translate. Skip.
24+
return
25+
}
26+
2227
printer.printSeparator(decl.displayName)
2328

2429
printJavaBindingDescriptorClass(&printer, decl)
@@ -33,7 +38,9 @@ extension Swift2JavaTranslator {
3338
_ decl: ImportedFunc
3439
) {
3540
let thunkName = thunkNameRegistry.functionThunkName(decl: decl)
36-
let cFunc = decl.cFunctionDecl(cName: thunkName)
41+
let translatedSignature = self.translatedSignature(for: decl)!
42+
// 'try!' because we know 'loweredSignature' can be described with C.
43+
let cFunc = try! translatedSignature.loweredSignature.cFunctionDecl(cName: thunkName)
3744

3845
printer.printBraceBlock(
3946
"""
@@ -129,19 +136,20 @@ extension Swift2JavaTranslator {
129136
}
130137

131138
var modifiers = "public"
132-
switch decl.swiftSignature.selfParameter {
139+
switch decl.functionSignature.selfParameter {
133140
case .staticMethod, .initializer, nil:
134141
modifiers.append(" static")
135142
default:
136143
break
137144
}
138145

139-
let returnTy = decl.translatedSignature.result.javaResultType
146+
let translatedSignature = self.translatedSignature(for: decl)!
147+
let returnTy = translatedSignature.result.javaResultType
140148

141-
var paramDecls = decl.translatedSignature.parameters
149+
var paramDecls = translatedSignature.parameters
142150
.flatMap(\.javaParameters)
143151
.map { "\($0.type) \($0.name)" }
144-
if decl.translatedSignature.requiresSwiftArena {
152+
if translatedSignature.requiresSwiftArena {
145153
paramDecls.append("SwiftArena swiftArena$")
146154
}
147155

@@ -157,7 +165,7 @@ extension Swift2JavaTranslator {
157165
\(modifiers) \(returnTy) \(methodName)(\(paramDecls.joined(separator: ", ")))
158166
"""
159167
) { printer in
160-
if case .instance(_) = decl.swiftSignature.selfParameter {
168+
if case .instance(_) = decl.functionSignature.selfParameter {
161169
// Make sure the object has not been destroyed.
162170
printer.print("$ensureAlive();")
163171
}
@@ -174,7 +182,9 @@ extension Swift2JavaTranslator {
174182
_ decl: ImportedFunc
175183
) {
176184
//=== Part 1: prepare temporary arena if needed.
177-
if decl.translatedSignature.requiresTemporaryArena {
185+
let translatedSignature = self.translatedSignature(for: decl)!
186+
187+
if translatedSignature.requiresTemporaryArena {
178188
printer.print("try(var arena$ = Arena.ofConfined()) {")
179189
printer.indent();
180190
}
@@ -183,21 +193,21 @@ extension Swift2JavaTranslator {
183193
var downCallArguments: [String] = []
184194

185195
// Regular parameters.
186-
for (i, parameter) in decl.translatedSignature.parameters.enumerated() {
187-
let original = decl.swiftSignature.parameters[i]
196+
for (i, parameter) in translatedSignature.parameters.enumerated() {
197+
let original = decl.functionSignature.parameters[i]
188198
let parameterName = original.parameterName ?? "_\(i)"
189199
let lowered = parameter.conversion.render(&printer, parameterName)
190200
downCallArguments.append(lowered)
191201
}
192202

193203
// 'self' parameter.
194-
if let selfParameter = decl.translatedSignature.selfParameter {
204+
if let selfParameter = translatedSignature.selfParameter {
195205
let lowered = selfParameter.conversion.render(&printer, "this")
196206
downCallArguments.append(lowered)
197207
}
198208

199209
// Indirect return receivers.
200-
for outParameter in decl.translatedSignature.result.outParameters {
210+
for outParameter in translatedSignature.result.outParameters {
201211
let memoryLayout = renderMemoryLayoutValue(for: outParameter.type)
202212

203213
let arena = if let className = outParameter.type.className,
@@ -222,27 +232,27 @@ extension Swift2JavaTranslator {
222232
let downCall = "\(thunkName).call(\(downCallArguments.joined(separator: ", ")))"
223233

224234
//=== Part 4: Convert the return value.
225-
if decl.translatedSignature.result.javaResultType == .void {
235+
if translatedSignature.result.javaResultType == .void {
226236
printer.print("\(downCall);")
227237
} else {
228238
let placeholder: String
229-
if decl.translatedSignature.result.outParameters.isEmpty {
239+
if translatedSignature.result.outParameters.isEmpty {
230240
placeholder = downCall
231241
} else {
232242
// FIXME: Support cdecl thunk returning a value while populating the out parameters.
233243
printer.print("\(downCall);")
234244
placeholder = "_result"
235245
}
236-
let result = decl.translatedSignature.result.conversion.render(&printer, placeholder)
246+
let result = translatedSignature.result.conversion.render(&printer, placeholder)
237247

238-
if decl.translatedSignature.result.javaResultType != .void {
248+
if translatedSignature.result.javaResultType != .void {
239249
printer.print("return \(result);")
240250
} else {
241251
printer.print("\(result);")
242252
}
243253
}
244254

245-
if decl.translatedSignature.requiresTemporaryArena {
255+
if translatedSignature.requiresTemporaryArena {
246256
printer.outdent()
247257
printer.print("}")
248258
}

Sources/JExtractSwift/Swift2JavaTranslator+JavaTranslation.swift

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,26 @@
1515
import JavaTypes
1616

1717
extension Swift2JavaTranslator {
18-
func translate(
19-
swiftSignature: SwiftFunctionSignature
20-
) throws -> TranslatedFunctionSignature {
21-
let lowering = CdeclLowering(swiftStdlibTypes: self.swiftStdlibTypes)
22-
let loweredSignature = try lowering.lowerFunctionSignature(swiftSignature)
18+
func translatedSignature(
19+
for decl: ImportedFunc
20+
) -> TranslatedFunctionSignature? {
21+
if let cached = translatedSignatures[decl] {
22+
return cached
23+
}
2324

24-
let translation = JavaTranslation(swiftStdlibTypes: self.swiftStdlibTypes)
25-
let translated = try translation.translate(loweredFunctionSignature: loweredSignature)
25+
let translated: TranslatedFunctionSignature?
26+
do {
27+
let lowering = CdeclLowering(swiftStdlibTypes: self.swiftStdlibTypes)
28+
let loweredSignature = try lowering.lowerFunctionSignature(decl.functionSignature)
29+
30+
let translation = JavaTranslation(swiftStdlibTypes: self.swiftStdlibTypes)
31+
translated = try translation.translate(loweredFunctionSignature: loweredSignature)
32+
} catch {
33+
self.log.info("Failed to translate: '\(decl.swiftDecl.qualifiedNameForDebug)'; \(error)")
34+
translated = nil
35+
}
2636

37+
translatedSignatures[decl] = translated
2738
return translated
2839
}
2940
}

Sources/JExtractSwift/SwiftThunkTranslator.swift renamed to Sources/JExtractSwift/Swift2JavaTranslator+SwiftThunkPrinting.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,13 @@ struct SwiftThunkTranslator {
174174

175175
func render(forFunc decl: ImportedFunc) -> [DeclSyntax] {
176176
st.log.trace("Rendering thunks for: \(decl.displayName)")
177+
177178
let thunkName = st.thunkNameRegistry.functionThunkName(decl: decl)
178-
let thunkFunc = decl.loweredSignature.cdeclThunk(
179+
guard let translatedSignatures = st.translatedSignature(for: decl) else {
180+
return []
181+
}
182+
183+
let thunkFunc = translatedSignatures.loweredSignature.cdeclThunk(
179184
cName: thunkName,
180185
swiftAPIName: decl.name,
181186
as: decl.apiKind,

Sources/JExtractSwift/Swift2JavaTranslator.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public final class Swift2JavaTranslator {
5656

5757
package var thunkNameRegistry: ThunkNameRegistry = ThunkNameRegistry()
5858

59+
/// Cached Java translation result. 'nil' indicates failed translation.
60+
var translatedSignatures: [ImportedFunc: TranslatedFunctionSignature?] = [:]
61+
5962
/// The name of the Swift module being translated.
6063
var swiftModuleName: String {
6164
symbolTable.moduleName

Sources/JExtractSwift/Swift2JavaVisitor.swift

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,15 @@ final class Swift2JavaVisitor: SyntaxVisitor {
121121

122122
self.log.debug("Import function: '\(node.qualifiedNameForDebug)'")
123123

124-
let translatedSignature: TranslatedFunctionSignature
124+
let signature: SwiftFunctionSignature
125125
do {
126-
let swiftSignature = try SwiftFunctionSignature(
126+
signature = try SwiftFunctionSignature(
127127
node,
128128
enclosingType: self.currentSwiftType,
129129
symbolTable: self.translator.symbolTable
130130
)
131-
translatedSignature = try translator.translate(swiftSignature: swiftSignature)
132131
} catch {
133-
self.log.debug("Failed to translate: '\(node.qualifiedNameForDebug)'; \(error)")
132+
self.log.debug("Failed to import: '\(node.qualifiedNameForDebug)'; \(error)")
134133
return .skipChildren
135134
}
136135

@@ -139,7 +138,7 @@ final class Swift2JavaVisitor: SyntaxVisitor {
139138
swiftDecl: node,
140139
name: node.name.text,
141140
apiKind: .function,
142-
translatedSignature: translatedSignature
141+
functionSignature: signature
143142
)
144143

145144
log.debug("Record imported method \(node.qualifiedNameForDebug)")
@@ -166,26 +165,19 @@ final class Swift2JavaVisitor: SyntaxVisitor {
166165
self.log.debug("Import variable: \(node.kind) '\(node.qualifiedNameForDebug)'")
167166

168167
func importAccessor(kind: SwiftAPIKind) throws {
169-
let translatedSignature: TranslatedFunctionSignature
170-
do {
171-
let swiftSignature = try SwiftFunctionSignature(
172-
node,
173-
isSet: kind == .setter,
174-
enclosingType: self.currentSwiftType,
175-
symbolTable: self.translator.symbolTable
176-
)
177-
translatedSignature = try translator.translate(swiftSignature: swiftSignature)
178-
} catch {
179-
self.log.debug("Failed to translate: \(node.qualifiedNameForDebug); \(error)")
180-
throw error
181-
}
168+
let signature = try SwiftFunctionSignature(
169+
node,
170+
isSet: kind == .setter,
171+
enclosingType: self.currentSwiftType,
172+
symbolTable: self.translator.symbolTable
173+
)
182174

183175
let imported = ImportedFunc(
184176
module: translator.swiftModuleName,
185177
swiftDecl: node,
186178
name: varName,
187179
apiKind: kind,
188-
translatedSignature: translatedSignature
180+
functionSignature: signature
189181
)
190182

191183
log.debug("Record imported variable accessor \(kind == .getter ? "getter" : "setter"):\(node.qualifiedNameForDebug)")
@@ -222,14 +214,13 @@ final class Swift2JavaVisitor: SyntaxVisitor {
222214

223215
self.log.debug("Import initializer: \(node.kind) '\(node.qualifiedNameForDebug)'")
224216

225-
let translatedSignature: TranslatedFunctionSignature
217+
let signature: SwiftFunctionSignature
226218
do {
227-
let swiftSignature = try SwiftFunctionSignature(
219+
signature = try SwiftFunctionSignature(
228220
node,
229221
enclosingType: self.currentSwiftType,
230222
symbolTable: self.translator.symbolTable
231223
)
232-
translatedSignature = try translator.translate(swiftSignature: swiftSignature)
233224
} catch {
234225
self.log.debug("Failed to translate: \(node.qualifiedNameForDebug); \(error)")
235226
return .skipChildren
@@ -239,7 +230,7 @@ final class Swift2JavaVisitor: SyntaxVisitor {
239230
swiftDecl: node,
240231
name: "init",
241232
apiKind: .initializer,
242-
translatedSignature: translatedSignature
233+
functionSignature: signature
243234
)
244235

245236
currentType.initializers.append(imported)

Sources/JExtractSwift/ThunkNameRegistry.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ package struct ThunkNameRegistry {
3737
case .setter:
3838
suffix = "$set"
3939
default:
40-
suffix = decl.swiftSignature.parameters
40+
suffix = decl.functionSignature.parameters
4141
.map { "_" + ($0.argumentLabel ?? "_") }
4242
.joined()
4343
}

0 commit comments

Comments
 (0)