From 36e74f60d8517e08269c1bf7c7e4c61a87a71f20 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki <rishizaki@apple.com> Date: Tue, 15 Apr 2025 23:41:01 +0900 Subject: [PATCH] [jextract] Prepare all inputs before analyze() Register all the files to `Swift2JavaTranslator` (and `NominalTypeResolution`) and `analye()` all the files at once. `Swift2JavaVisitor.visit(_: ExtentionDeclSyntax)` requires all the nominal types are prepared in the `nominalResolution`. Otherwise it's just ignored. Also previously, writing files happend for each file iteration but actually, it is not a per-source file operation. Instead, it only writes the current state of the translator. --- Sources/JExtractSwift/Swift2Java.swift | 23 ++++++--- .../JExtractSwift/Swift2JavaTranslator.swift | 50 +++++++++++-------- .../Asserts/LoweringAssertions.swift | 4 +- 3 files changed, 46 insertions(+), 31 deletions(-) diff --git a/Sources/JExtractSwift/Swift2Java.swift b/Sources/JExtractSwift/Swift2Java.swift index a46d1ab4..5eaab18a 100644 --- a/Sources/JExtractSwift/Swift2Java.swift +++ b/Sources/JExtractSwift/Swift2Java.swift @@ -74,16 +74,23 @@ public struct SwiftToJava: ParsableCommand { } } - for file in allFiles where canExtract(from: file) { - translator.log.debug("Importing module '\(swiftModule)', file: \(file)") - - try translator.analyze(file: file.path) - try translator.writeExportedJavaSources(outputDirectory: outputDirectoryJava) - try translator.writeSwiftThunkSources(outputDirectory: outputDirectorySwift) - - log.debug("[swift-java] Imported interface file: \(file.path)") + // Register files to the translator. + for file in allFiles { + guard canExtract(from: file) else { + continue + } + guard let data = fileManager.contents(atPath: file.path) else { + continue + } + guard let text = String(data:data, encoding: .utf8) else { + continue + } + translator.add(filePath: file.path, text: text) } + try translator.analyze() + try translator.writeSwiftThunkSources(outputDirectory: outputDirectorySwift) + try translator.writeExportedJavaSources(outputDirectory: outputDirectoryJava) try translator.writeExportedJavaModule(outputDirectory: outputDirectoryJava) print("[swift-java] Generated Java sources (\(packageName)) in: \(outputDirectoryJava)/") print("[swift-java] Imported Swift module '\(swiftModule)': " + "done.".green) diff --git a/Sources/JExtractSwift/Swift2JavaTranslator.swift b/Sources/JExtractSwift/Swift2JavaTranslator.swift index 6577a37c..446dd4c2 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator.swift @@ -24,6 +24,15 @@ public final class Swift2JavaTranslator { package var log = Logger(label: "translator", logLevel: .info) + // ==== Input + + struct Input { + let filePath: String + let syntax: Syntax + } + + var inputs: [Input] = [] + // ==== Output configuration let javaPackage: String @@ -39,7 +48,7 @@ public final class Swift2JavaTranslator { /// type representation. package var importedTypes: [String: ImportedNominalType] = [:] - public var swiftStdlibTypes: SwiftStandardLibraryTypes + package var swiftStdlibTypes: SwiftStandardLibraryTypes let symbolTable: SwiftSymbolTable let nominalResolution: NominalTypeResolution = NominalTypeResolution() @@ -78,26 +87,24 @@ extension Swift2JavaTranslator { /// a checked truncation operation at the Java/Swift board. var javaPrimitiveForSwiftInt: JavaType { .long } - public func analyze( + package func add(filePath: String, text: String) { + log.trace("Adding: \(filePath)") + let sourceFileSyntax = Parser.parse(source: text) + self.nominalResolution.addSourceFile(sourceFileSyntax) + self.inputs.append(Input(filePath: filePath, syntax: Syntax(sourceFileSyntax))) + } + + /// Convenient method for analyzing single file. + package func analyze( file: String, - text: String? = nil + text: String ) throws { - guard text != nil || FileManager.default.fileExists(atPath: file) else { - throw Swift2JavaTranslatorError(message: "Missing input file: \(file)") - } - - log.trace("Analyze: \(file)") - let text = try text ?? String(contentsOfFile: file) - - try analyzeSwiftInterface(interfaceFilePath: file, text: text) - - log.debug("Done processing: \(file)") + self.add(filePath: file, text: text) + try self.analyze() } - package func analyzeSwiftInterface(interfaceFilePath: String, text: String) throws { - let sourceFileSyntax = Parser.parse(source: text) - - addSourceFile(sourceFileSyntax) + /// Analyze registered inputs. + func analyze() throws { prepareForTranslation() let visitor = Swift2JavaVisitor( @@ -105,16 +112,17 @@ extension Swift2JavaTranslator { targetJavaPackage: self.javaPackage, translator: self ) - visitor.walk(sourceFileSyntax) - } - package func addSourceFile(_ sourceFile: SourceFileSyntax) { - nominalResolution.addSourceFile(sourceFile) + for input in self.inputs { + log.trace("Analyzing \(input.filePath)") + visitor.walk(input.syntax) + } } package func prepareForTranslation() { nominalResolution.bindExtensions() + // Prepare symbol table for nominal type names. for (_, node) in nominalResolution.topLevelNominalTypes { symbolTable.parsedModule.addNominalTypeDeclaration(node, parent: nil) } diff --git a/Tests/JExtractSwiftTests/Asserts/LoweringAssertions.swift b/Tests/JExtractSwiftTests/Asserts/LoweringAssertions.swift index 6f7a5e83..c37325cb 100644 --- a/Tests/JExtractSwiftTests/Asserts/LoweringAssertions.swift +++ b/Tests/JExtractSwiftTests/Asserts/LoweringAssertions.swift @@ -22,7 +22,7 @@ func assertLoweredFunction( _ inputDecl: DeclSyntax, javaPackage: String = "org.swift.mypackage", swiftModuleName: String = "MyModule", - sourceFile: SourceFileSyntax? = nil, + sourceFile: String? = nil, enclosingType: TypeSyntax? = nil, expectedCDecl: DeclSyntax, expectedCFunction: String, @@ -37,7 +37,7 @@ func assertLoweredFunction( ) if let sourceFile { - translator.addSourceFile(sourceFile) + translator.add(filePath: "Fake.swift", text: sourceFile) } translator.prepareForTranslation()