Skip to content

Commit 11e285d

Browse files
committed
Maintain a Swift type name -> imported type mapping in jextract-swift
Switch the representation of the imported types from a flat list over to a dictionary mapping from the Swift name to the imported type. We need this so that we can find information about the Swift type when it is referenced elsewhere in the syntax tree, such as extensions or uses of that type in other function signatures. While here, stop recording intermediate results within the visitor itself. Instead, keep a reference to the full translator and record results there incrementally. This will become more important when we start handling extensions.
1 parent b3bd8e1 commit 11e285d

File tree

6 files changed

+62
-62
lines changed

6 files changed

+62
-62
lines changed

Sources/JExtractSwift/ImportedDecls.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,28 @@ public struct ImportedProtocol: ImportedDecl {
2525
public var identifier: String
2626
}
2727

28-
public struct ImportedClass: ImportedDecl {
28+
/// Describes a Swift nominal type (e.g., a class, struct, enum) that has been
29+
/// imported and is being translated into Java.
30+
public struct ImportedNominalType: ImportedDecl {
2931
public var name: ImportedTypeName
32+
public var kind: NominalTypeKind
3033

3134
public var initializers: [ImportedFunc] = []
3235
public var methods: [ImportedFunc] = []
3336

34-
public init(name: ImportedTypeName) {
37+
public init(name: ImportedTypeName, kind: NominalTypeKind) {
3538
self.name = name
39+
self.kind = kind
3640
}
3741
}
3842

43+
public enum NominalTypeKind {
44+
case `actor`
45+
case `class`
46+
case `enum`
47+
case `struct`
48+
}
49+
3950
public struct ImportedParam: Hashable {
4051
let param: FunctionParameterSyntax
4152

@@ -97,9 +108,10 @@ public struct ImportedTypeName: Hashable {
97108
javaType.className
98109
}
99110

100-
public init(swiftTypeName: String, javaType: JavaType) {
111+
public init(swiftTypeName: String, javaType: JavaType, swiftMangledName: String? = nil) {
101112
self.swiftTypeName = swiftTypeName
102113
self.javaType = javaType
114+
self.swiftMangledName = swiftMangledName ?? ""
103115
}
104116
}
105117

Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ extension Swift2JavaTranslator {
2727
public func writeImportedTypesTo(outputDirectory: String) throws {
2828
var printer = CodePrinter()
2929

30-
for ty in importedTypes {
30+
for (_, ty) in importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) {
3131
let filename = "\(ty.name.javaClassName!).java"
3232
log.info("Printing contents: \(filename)")
3333
printImportedClass(&printer, ty)
@@ -106,7 +106,7 @@ extension Swift2JavaTranslator {
106106
}
107107
}
108108

109-
public func printImportedClass(_ printer: inout CodePrinter, _ decl: ImportedClass) {
109+
public func printImportedClass(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
110110
printHeader(&printer)
111111
printPackage(&printer)
112112
printImports(&printer) // TODO print any imports the file may need, it we talk to other Swift modules
@@ -164,7 +164,7 @@ extension Swift2JavaTranslator {
164164
printer.print("")
165165
}
166166

167-
public func printClass(_ printer: inout CodePrinter, _ decl: ImportedClass, body: (inout CodePrinter) -> Void) {
167+
public func printClass(_ printer: inout CodePrinter, _ decl: ImportedNominalType, body: (inout CodePrinter) -> Void) {
168168
printer.printTypeDecl("public final class \(decl.name.javaClassName!)") { printer in
169169
// ==== Storage of the class
170170
// FIXME: implement the self storage for the memory address and accessors
@@ -270,7 +270,7 @@ extension Swift2JavaTranslator {
270270
)
271271
}
272272

273-
private func printClassMemorySegmentConstructor(_ printer: inout CodePrinter, _ decl: ImportedClass) {
273+
private func printClassMemorySegmentConstructor(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
274274
printer.print(
275275
"""
276276
/** Instances are created using static {@code init} methods rather than through the constructor directly. */
@@ -282,7 +282,7 @@ extension Swift2JavaTranslator {
282282
}
283283

284284
/// Print a property where we can store the "self" pointer of a class.
285-
private func printClassSelfProperty(_ printer: inout CodePrinter, _ decl: ImportedClass) {
285+
private func printClassSelfProperty(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
286286
printer.print(
287287
"""
288288
// Pointer to the referred to class instance's "self".
@@ -295,7 +295,7 @@ extension Swift2JavaTranslator {
295295
)
296296
}
297297

298-
private func printClassMemoryLayout(_ printer: inout CodePrinter, _ decl: ImportedClass) {
298+
private func printClassMemoryLayout(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
299299
printer.print(
300300
"""
301301
private static final GroupLayout $LAYOUT = MemoryLayout.structLayout(

Sources/JExtractSwift/Swift2JavaTranslator.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ public final class Swift2JavaTranslator {
3737
// TODO: consider how/if we need to store those etc
3838
public var importedGlobalFuncs: [ImportedFunc] = []
3939

40-
public var importedTypes: [ImportedClass] = []
40+
/// A mapping from Swift type names (e.g., A.B) over to the imported nominal
41+
/// type representation.
42+
public var importedTypes: [String: ImportedNominalType] = [:]
4143

4244
public init(
4345
javaPackage: String,
@@ -83,13 +85,10 @@ extension Swift2JavaTranslator {
8385
let visitor = Swift2JavaVisitor(
8486
moduleName: self.swiftModuleName,
8587
targetJavaPackage: self.javaPackage,
86-
log: log
88+
translator: self
8789
)
8890
visitor.walk(sourceFileSyntax)
8991

90-
self.importedGlobalFuncs.append(contentsOf: visitor.javaMethodDecls)
91-
self.importedTypes.append(contentsOf: visitor.javaTypeDecls)
92-
9392
try await self.postProcessImportedDecls()
9493
}
9594

@@ -119,7 +118,7 @@ extension Swift2JavaTranslator {
119118
return funcDecl
120119
}
121120

122-
importedTypes = try await importedTypes._mapAsync { tyDecl in
121+
importedTypes = Dictionary(uniqueKeysWithValues: try await importedTypes._mapAsync { (tyName, tyDecl) in
123122
var tyDecl = tyDecl
124123
log.info("Mapping type: \(tyDecl.name)")
125124

@@ -140,8 +139,8 @@ extension Swift2JavaTranslator {
140139
return funcDecl
141140
}
142141

143-
return tyDecl
144-
}
142+
return (tyName, tyDecl)
143+
})
145144
}
146145
}
147146

Sources/JExtractSwift/Swift2JavaVisitor.swift

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,23 @@ import SwiftParser
1616
import SwiftSyntax
1717

1818
final class Swift2JavaVisitor: SyntaxVisitor {
19-
let log: Logger
19+
let translator: Swift2JavaTranslator
2020

2121
/// The Swift module we're visiting declarations in
2222
let moduleName: String
23+
2324
/// The target java package we are going to generate types into eventually,
2425
/// store this along with type names as we import them.
2526
let targetJavaPackage: String
2627

27-
var javaMethodDecls: [ImportedFunc] = []
28-
var javaTypeDecls: [ImportedClass] = []
28+
var currentTypeName: String? = nil
2929

30-
var currentTypeDecl: ImportedClass? = nil
30+
var log: Logger { translator.log }
3131

32-
init(moduleName: String, targetJavaPackage: String, log: Logger) {
32+
init(moduleName: String, targetJavaPackage: String, translator: Swift2JavaTranslator) {
3333
self.moduleName = moduleName
3434
self.targetJavaPackage = targetJavaPackage
35-
self.log = log
35+
self.translator = translator
3636

3737
super.init(viewMode: .all)
3838
}
@@ -43,30 +43,28 @@ final class Swift2JavaVisitor: SyntaxVisitor {
4343
}
4444

4545
log.info("Import: \(node.kind) \(node.name)")
46-
currentTypeDecl = ImportedClass(
46+
let typeName = node.name.text
47+
currentTypeName = typeName
48+
translator.importedTypes[typeName] = ImportedNominalType(
4749
// TODO: support nested classes (parent name here)
4850
name: ImportedTypeName(
49-
swiftTypeName: node.name.text,
51+
swiftTypeName: typeName,
5052
javaType: .class(
5153
package: targetJavaPackage,
52-
name: node.name.text
53-
)
54-
)
54+
name: typeName
55+
),
56+
swiftMangledName: node.mangledNameFromComment
57+
),
58+
kind: .class
5559
)
5660

57-
// Retrieve the mangled name, if available.
58-
if let mangledName = node.mangledNameFromComment {
59-
currentTypeDecl!.name.swiftMangledName = mangledName
60-
}
61-
6261
return .visitChildren
6362
}
6463

6564
override func visitPost(_ node: ClassDeclSyntax) {
66-
if let currentTypeDecl {
65+
if currentTypeName != nil {
6766
log.info("Completed import: \(node.kind) \(node.name)")
68-
self.javaTypeDecls.append(currentTypeDecl)
69-
self.currentTypeDecl = nil
67+
currentTypeName = nil
7068
}
7169
}
7270

@@ -113,7 +111,7 @@ final class Swift2JavaVisitor: SyntaxVisitor {
113111
let fullName = "\(node.name.text)(\(argumentLabelsStr))"
114112

115113
var funcDecl = ImportedFunc(
116-
parentName: currentTypeDecl?.name,
114+
parentName: currentTypeName.map { translator.importedTypes[$0] }??.name,
117115
identifier: fullName,
118116
returnType: javaResultType,
119117
parameters: params
@@ -125,26 +123,26 @@ final class Swift2JavaVisitor: SyntaxVisitor {
125123
funcDecl.swiftMangledName = mangledName
126124
}
127125

128-
if var currentTypeDecl = self.currentTypeDecl {
129-
log.info("Record method in \(currentTypeDecl.name.javaType.description)")
130-
currentTypeDecl.methods.append(funcDecl)
131-
self.currentTypeDecl = currentTypeDecl
126+
if let currentTypeName {
127+
log.info("Record method in \(currentTypeName)")
128+
translator.importedTypes[currentTypeName]?.methods.append(funcDecl)
132129
} else {
133-
javaMethodDecls.append(funcDecl)
130+
translator.importedGlobalFuncs.append(funcDecl)
134131
}
135132

136133
return .skipChildren
137134
}
138135

139136
override func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind {
140-
guard var currentTypeDecl = self.currentTypeDecl else {
137+
guard let currentTypeName,
138+
let currentType = translator.importedTypes[currentTypeName] else {
141139
fatalError("Initializer must be within a current type, was: \(node)")
142140
}
143141
guard node.shouldImport(log: log) else {
144142
return .skipChildren
145143
}
146144

147-
self.log.info("Import initializer: \(node.kind) \(currentTypeDecl.name.javaType.description)")
145+
self.log.info("Import initializer: \(node.kind) \(currentType.name.javaType.description)")
148146
let params: [ImportedParam]
149147
do {
150148
params = try node.signature.parameterClause.parameters.map { param in
@@ -164,9 +162,9 @@ final class Swift2JavaVisitor: SyntaxVisitor {
164162
"init(\(params.compactMap { $0.effectiveName ?? "_" }.joined(separator: ":")))"
165163

166164
var funcDecl = ImportedFunc(
167-
parentName: currentTypeDecl.name,
165+
parentName: currentType.name,
168166
identifier: initIdentifier,
169-
returnType: currentTypeDecl.name,
167+
returnType: currentType.name,
170168
parameters: params
171169
)
172170
funcDecl.isInit = true
@@ -177,9 +175,8 @@ final class Swift2JavaVisitor: SyntaxVisitor {
177175
funcDecl.swiftMangledName = mangledName
178176
}
179177

180-
log.info("Record initializer method in \(currentTypeDecl.name.javaType.description): \(funcDecl.identifier)")
181-
currentTypeDecl.initializers.append(funcDecl)
182-
self.currentTypeDecl = currentTypeDecl
178+
log.info("Record initializer method in \(currentType.name.javaType.description): \(funcDecl.identifier)")
179+
translator.importedTypes[currentTypeName]!.initializers.append(funcDecl)
183180

184181
return .skipChildren
185182
}

Sources/JExtractSwift/SwiftDylib.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ package struct SwiftDylib { // FIXME: remove this entire utility; replace with
3131
self.log = Logger(label: "SwiftDylib(\(path))", logLevel: .trace) // TODO: take from env
3232
}
3333

34-
package func fillInTypeMangledName(_ decl: ImportedClass) async throws -> ImportedClass {
34+
package func fillInTypeMangledName(_ decl: ImportedNominalType) async throws -> ImportedNominalType {
3535
// TODO: this is hacky, not precise at all and will be removed entirely
3636
guard decl.name.swiftMangledName.isEmpty else {
3737
// it was already processed

Tests/JExtractSwiftTests/FuncImportTests.swift

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,7 @@ final class MethodImportTests: XCTestCase {
181181

182182
try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile)
183183

184-
let funcDecl: ImportedFunc = st.importedTypes.first {
185-
$0.name.javaClassName == "MySwiftClass"
186-
}!.methods.first {
184+
let funcDecl: ImportedFunc = st.importedTypes["MySwiftClass"]!.methods.first {
187185
$0.baseIdentifier == "helloMemberFunction"
188186
}!
189187

@@ -224,9 +222,7 @@ final class MethodImportTests: XCTestCase {
224222

225223
try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile)
226224

227-
let funcDecl: ImportedFunc = st.importedTypes.first {
228-
$0.name.javaClassName == "MySwiftClass"
229-
}!.methods.first {
225+
let funcDecl: ImportedFunc = st.importedTypes["MySwiftClass"]!.methods.first {
230226
$0.baseIdentifier == "helloMemberFunction"
231227
}!
232228

@@ -267,9 +263,7 @@ final class MethodImportTests: XCTestCase {
267263

268264
try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile)
269265

270-
let funcDecl: ImportedFunc = st.importedTypes.first {
271-
$0.name.javaClassName == "MySwiftClass"
272-
}!.methods.first {
266+
let funcDecl: ImportedFunc = st.importedTypes["MySwiftClass"]!.methods.first {
273267
$0.baseIdentifier == "helloMemberFunction"
274268
}!
275269

@@ -302,9 +296,7 @@ final class MethodImportTests: XCTestCase {
302296

303297
try await st.analyze(swiftInterfacePath: "/fake/__FakeModule/SwiftFile.swiftinterface", text: class_interfaceFile)
304298

305-
let funcDecl: ImportedFunc = st.importedTypes.first {
306-
$0.name.javaClassName == "MySwiftClass"
307-
}!.methods.first {
299+
let funcDecl: ImportedFunc = st.importedTypes["MySwiftClass"]!.methods.first {
308300
$0.baseIdentifier == "makeInt"
309301
}!
310302

0 commit comments

Comments
 (0)