From fc1a9b84f72c548ec63fc7ce94d786eb62a66c45 Mon Sep 17 00:00:00 2001 From: pelekon <13712101+pelekon@users.noreply.github.com> Date: Sun, 19 Oct 2025 18:36:53 +0200 Subject: [PATCH 1/6] Propetly extract usage of Data type from FoundationEssentials. --- .../FFM/CDeclLowering/CRepresentation.swift | 4 +++- .../JNI/JNIJavaTypeTranslator.swift | 2 +- .../Swift2JavaTranslator.swift | 8 ++++---- .../SwiftTypes/SwiftKnownModules.swift | 19 +++++++++++++++++-- .../SwiftTypes/SwiftKnownTypeDecls.swift | 4 ++++ 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift index 193b545f..9e10f717 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift @@ -125,7 +125,9 @@ extension SwiftKnownTypeDeclKind { .qualified(const: true, volatile: false, type: .void) ) case .void: .void - case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data, .dataProtocol, .optional: + case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, + .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data, .dataProtocol, .essentialsData, + .essentialsDataProtocol, .optional: nil } } diff --git a/Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift b/Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift index 983396ac..2e082c18 100644 --- a/Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift @@ -50,7 +50,7 @@ enum JNIJavaTypeTranslator { .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, - .optional, .data, .dataProtocol: + .optional, .data, .dataProtocol, .essentialsData, .essentialsDataProtocol: return nil } } diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift index ccbbd274..dc2784c2 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift @@ -108,10 +108,10 @@ extension Swift2JavaTranslator { visitor.visit(sourceFile: input.syntax) } - // If any API uses 'Foundation.Data', import 'Data' as if it's declared in - // this module. - if let dataDecl = self.symbolTable[.data] { - let dataProtocolDecl = self.symbolTable[.dataProtocol]! + // If any API uses 'Foundation.Data' or 'FoundationEssentials.Data', + // import 'Data' as if it's declared in this module. + if let dataDecl = self.symbolTable[.data] ?? self.symbolTable[.essentialsData] { + let dataProtocolDecl = (self.symbolTable[.dataProtocol] ?? self.symbolTable[.essentialsDataProtocol])! if self.isUsing(where: { $0 == dataDecl || $0 == dataProtocolDecl }) { visitor.visit(nominalDecl: dataDecl.syntax!.asNominal!, in: nil) } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift index 938d0c4a..fd52ef58 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift @@ -18,6 +18,7 @@ import SwiftSyntaxBuilder enum SwiftKnownModule: String { case swift = "Swift" case foundation = "Foundation" + case foundationEssentials = "FoundationEssentials" var name: String { return self.rawValue @@ -27,13 +28,15 @@ enum SwiftKnownModule: String { return switch self { case .swift: swiftSymbolTable case .foundation: foundationSymbolTable + case .foundationEssentials: foundationEssentialsSymbolTable } } var sourceFile: SourceFileSyntax { return switch self { case .swift: swiftSourceFile - case .foundation: foundationSourceFile + case .foundation: foundationEssentialsSourceFile + case .foundationEssentials: foundationEssentialsSourceFile } } } @@ -44,6 +47,12 @@ private var swiftSymbolTable: SwiftModuleSymbolTable { return builder.finalize() } +private var foundationEssentialsSymbolTable: SwiftModuleSymbolTable { + var builder = SwiftParsedModuleSymbolTableBuilder(moduleName: "FoundationEssentials", importedModules: ["Swift": swiftSymbolTable]) + builder.handle(sourceFile: foundationEssentialsSourceFile) + return builder.finalize() +} + private var foundationSymbolTable: SwiftModuleSymbolTable { var builder = SwiftParsedModuleSymbolTableBuilder(moduleName: "Foundation", importedModules: ["Swift": swiftSymbolTable]) builder.handle(sourceFile: foundationSourceFile) @@ -87,7 +96,7 @@ private let swiftSourceFile: SourceFileSyntax = """ } """ -private let foundationSourceFile: SourceFileSyntax = """ +private let foundationEssentialsSourceFile: SourceFileSyntax = """ public protocol DataProtocol {} public struct Data: DataProtocol { @@ -96,3 +105,9 @@ private let foundationSourceFile: SourceFileSyntax = """ public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) -> Void) } """ + +private var foundationSourceFile: SourceFileSyntax { + // On platforms other than Darwin, imports such as FoundationEssentials, FoundationNetworking, etc. are used, + // so this file should be created by combining the files of the aforementioned modules. + foundationEssentialsSourceFile +} \ No newline at end of file diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift index 11ff25c4..285e4e35 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift @@ -15,6 +15,7 @@ import SwiftSyntax enum SwiftKnownTypeDeclKind: String, Hashable { + // MARK: -Std case bool = "Swift.Bool" case int = "Swift.Int" case uint = "Swift.UInt" @@ -40,8 +41,11 @@ enum SwiftKnownTypeDeclKind: String, Hashable { case void = "Swift.Void" case string = "Swift.String" + // MARK: -Foundation case dataProtocol = "Foundation.DataProtocol" + case essentialsDataProtocol = "FoundationEssentials.DataProtocol" case data = "Foundation.Data" + case essentialsData = "FoundationEssentials.Data" var moduleAndName: (module: String, name: String) { let qualified = self.rawValue From ef6649329e312d467886004d6173d37abfac9a92 Mon Sep 17 00:00:00 2001 From: pelekon <13712101+pelekon@users.noreply.github.com> Date: Sun, 19 Oct 2025 19:12:23 +0200 Subject: [PATCH 2/6] Tests and fixes. --- ...Swift2JavaGenerator+FunctionLowering.swift | 10 +-- ...MSwift2JavaGenerator+JavaTranslation.swift | 6 +- .../SwiftTypes/SwiftKnownTypes.swift | 3 + .../JExtractSwiftTests/DataImportTests.swift | 86 +++++++++++++++++++ 4 files changed, 97 insertions(+), 8 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift index 5dfa5b71..6ccd582d 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift @@ -282,7 +282,7 @@ struct CdeclLowering { ) } - case .data: + case .data, .essentialsData: break default: @@ -375,7 +375,7 @@ struct CdeclLowering { case .nominal(let nominal): if let knownType = nominal.nominalTypeDecl.knownTypeKind { switch knownType { - case .data: + case .data, .essentialsData: break case .unsafeRawPointer, .unsafeMutableRawPointer: throw LoweringError.unhandledType(.optional(wrappedType)) @@ -387,7 +387,7 @@ struct CdeclLowering { throw LoweringError.unhandledType(.optional(wrappedType)) case .void, .string: throw LoweringError.unhandledType(.optional(wrappedType)) - case .dataProtocol: + case .dataProtocol, .essentialsDataProtocol: throw LoweringError.unhandledType(.optional(wrappedType)) default: // Unreachable? Should be handled by `CType(cdeclType:)` lowering above. @@ -505,7 +505,7 @@ struct CdeclLowering { ]) ) - case .data: + case .data, .essentialsData: break default: @@ -606,7 +606,7 @@ struct CdeclLowering { case .void: return LoweredResult(cdeclResultType: .void, cdeclOutParameters: [], conversion: .placeholder) - case .data: + case .data, .essentialsData: break case .string, .optional: diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index 438393cb..683a300e 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -411,7 +411,7 @@ extension FFMSwift2JavaGenerator { conversion: .call(.placeholder, function: "SwiftRuntime.toCString", withArena: true) ) - case .data: + case .data, .essentialsData: break default: @@ -515,7 +515,7 @@ extension FFMSwift2JavaGenerator { case .nominal(let nominal): if let knownType = nominal.nominalTypeDecl.knownTypeKind { switch knownType { - case .data, .dataProtocol: + case .data, .dataProtocol, .essentialsData, .essentialsDataProtocol: break default: throw JavaTranslationError.unhandledType(.optional(swiftType)) @@ -658,7 +658,7 @@ extension FFMSwift2JavaGenerator { ) ) - case .data: + case .data, .essentialsData: break case .unsafePointer, .unsafeMutablePointer: diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift index 8c70f7e2..2aafc0eb 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift @@ -37,6 +37,8 @@ struct SwiftKnownTypes { var dataProtocol: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.dataProtocol])) } var data: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.data])) } + var essentialsDataProtocol: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.essentialsDataProtocol])) } + var essentialsData: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.essentialsData])) } func unsafePointer(_ pointeeType: SwiftType) -> SwiftType { .nominal( @@ -79,6 +81,7 @@ struct SwiftKnownTypes { func representativeType(of knownProtocol: SwiftKnownTypeDeclKind) -> SwiftType? { switch knownProtocol { case .dataProtocol: return self.data + case .essentialsDataProtocol: return self.essentialsData default: return nil } } diff --git a/Tests/JExtractSwiftTests/DataImportTests.swift b/Tests/JExtractSwiftTests/DataImportTests.swift index cf4ed387..91369122 100644 --- a/Tests/JExtractSwiftTests/DataImportTests.swift +++ b/Tests/JExtractSwiftTests/DataImportTests.swift @@ -31,6 +31,21 @@ final class DataImportTests { public func receiveDataProtocol(dat: some DataProtocol, dat2: T?) """ + let essentials_data_interfaceFile = + """ + import FoundationEssentials + + public func receiveData(dat: Data) + public func returnData() -> Data + """ + + let essentials_dataProtocol_interfaceFile = + """ + import FoundationEssentials + + public func receiveDataProtocol(dat: some DataProtocol, dat2: T?) + """ + @Test("Import Data: Swift thunks") func data_swiftThunk() throws { @@ -85,6 +100,57 @@ final class DataImportTests { """, ] ) + + try assertOutput( + input: essentials_data_interfaceFile, .ffm, .swift, + expectedChunks: [ + """ + import FoundationEssentials + """, + """ + @_cdecl("swiftjava_SwiftModule_receiveData_dat") + public func swiftjava_SwiftModule_receiveData_dat(_ dat: UnsafeRawPointer) { + receiveData(dat: dat.assumingMemoryBound(to: Data.self).pointee) + } + """, + """ + @_cdecl("swiftjava_SwiftModule_returnData") + public func swiftjava_SwiftModule_returnData(_ _result: UnsafeMutableRawPointer) { + _result.assumingMemoryBound(to: Data.self).initialize(to: returnData()) + } + """, + + """ + @_cdecl("swiftjava_getType_SwiftModule_Data") + public func swiftjava_getType_SwiftModule_Data() -> UnsafeMutableRawPointer /* Any.Type */ { + return unsafeBitCast(Data.self, to: UnsafeMutableRawPointer.self) + } + """, + + """ + @_cdecl("swiftjava_SwiftModule_Data_init_bytes_count") + public func swiftjava_SwiftModule_Data_init_bytes_count(_ bytes: UnsafeRawPointer, _ count: Int, _ _result: UnsafeMutableRawPointer) { + _result.assumingMemoryBound(to: Data.self).initialize(to: Data(bytes: bytes, count: count)) + } + """, + + """ + @_cdecl("swiftjava_SwiftModule_Data_count$get") + public func swiftjava_SwiftModule_Data_count$get(_ self: UnsafeRawPointer) -> Int { + return self.assumingMemoryBound(to: Data.self).pointee.count + } + """, + + """ + @_cdecl("swiftjava_SwiftModule_Data_withUnsafeBytes__") + public func swiftjava_SwiftModule_Data_withUnsafeBytes__(_ body: @convention(c) (UnsafeRawPointer?, Int) -> Void, _ self: UnsafeRawPointer) { + self.assumingMemoryBound(to: Data.self).pointee.withUnsafeBytes({ (_0) in + return body(_0.baseAddress, _0.count) + }) + } + """, + ] + ) } @Test("Import Data: JavaBindings") @@ -354,6 +420,26 @@ final class DataImportTests { """ ] ) + + try assertOutput( + input: essentials_dataProtocol_interfaceFile, .ffm, .swift, + expectedChunks: [ + """ + import FoundationEssentials + """, + """ + @_cdecl("swiftjava_SwiftModule_receiveDataProtocol_dat_dat2") + public func swiftjava_SwiftModule_receiveDataProtocol_dat_dat2(_ dat: UnsafeRawPointer, _ dat2: UnsafeRawPointer?) { + receiveDataProtocol(dat: dat.assumingMemoryBound(to: Data.self).pointee, dat2: dat2?.assumingMemoryBound(to: Data.self).pointee) + } + """, + + // Just to make sure 'Data' is imported. + """ + @_cdecl("swiftjava_getType_SwiftModule_Data") + """ + ] + ) } @Test("Import DataProtocol: JavaBindings") From d4222aea8752b242d2e76fee0266c09f51b61689 Mon Sep 17 00:00:00 2001 From: pelekon <13712101+pelekon@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:52:44 +0200 Subject: [PATCH 3/6] Review fixes. --- .../FFM/CDeclLowering/CRepresentation.swift | 4 ++-- .../FFMSwift2JavaGenerator+FunctionLowering.swift | 10 +++++----- .../FFM/FFMSwift2JavaGenerator+JavaTranslation.swift | 6 +++--- .../JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift | 2 +- Sources/JExtractSwiftLib/Swift2JavaTranslator.swift | 4 ++-- .../SwiftTypes/SwiftKnownTypeDecls.swift | 8 ++++---- .../JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift | 6 +++--- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift index 9e10f717..2621dd8b 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift @@ -126,8 +126,8 @@ extension SwiftKnownTypeDeclKind { ) case .void: .void case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, - .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data, .dataProtocol, .essentialsData, - .essentialsDataProtocol, .optional: + .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .foundationData, .foundationDataProtocol, + .essentialsData, .essentialsDataProtocol, .optional: nil } } diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift index 6ccd582d..7c8d7ab2 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift @@ -282,7 +282,7 @@ struct CdeclLowering { ) } - case .data, .essentialsData: + case .foundationData, .essentialsData: break default: @@ -375,7 +375,7 @@ struct CdeclLowering { case .nominal(let nominal): if let knownType = nominal.nominalTypeDecl.knownTypeKind { switch knownType { - case .data, .essentialsData: + case .foundationData, .essentialsData: break case .unsafeRawPointer, .unsafeMutableRawPointer: throw LoweringError.unhandledType(.optional(wrappedType)) @@ -387,7 +387,7 @@ struct CdeclLowering { throw LoweringError.unhandledType(.optional(wrappedType)) case .void, .string: throw LoweringError.unhandledType(.optional(wrappedType)) - case .dataProtocol, .essentialsDataProtocol: + case .foundationDataProtocol, .essentialsDataProtocol: throw LoweringError.unhandledType(.optional(wrappedType)) default: // Unreachable? Should be handled by `CType(cdeclType:)` lowering above. @@ -505,7 +505,7 @@ struct CdeclLowering { ]) ) - case .data, .essentialsData: + case .foundationData, .essentialsData: break default: @@ -606,7 +606,7 @@ struct CdeclLowering { case .void: return LoweredResult(cdeclResultType: .void, cdeclOutParameters: [], conversion: .placeholder) - case .data, .essentialsData: + case .foundationData, .essentialsData: break case .string, .optional: diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index 683a300e..5d38a0f9 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -411,7 +411,7 @@ extension FFMSwift2JavaGenerator { conversion: .call(.placeholder, function: "SwiftRuntime.toCString", withArena: true) ) - case .data, .essentialsData: + case .foundationData, .essentialsData: break default: @@ -515,7 +515,7 @@ extension FFMSwift2JavaGenerator { case .nominal(let nominal): if let knownType = nominal.nominalTypeDecl.knownTypeKind { switch knownType { - case .data, .dataProtocol, .essentialsData, .essentialsDataProtocol: + case .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol: break default: throw JavaTranslationError.unhandledType(.optional(swiftType)) @@ -658,7 +658,7 @@ extension FFMSwift2JavaGenerator { ) ) - case .data, .essentialsData: + case .foundationData, .essentialsData: break case .unsafePointer, .unsafeMutablePointer: diff --git a/Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift b/Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift index 2e082c18..0d1fc675 100644 --- a/Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift @@ -50,7 +50,7 @@ enum JNIJavaTypeTranslator { .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, - .optional, .data, .dataProtocol, .essentialsData, .essentialsDataProtocol: + .optional, .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol: return nil } } diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift index dc2784c2..9dcba340 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift @@ -110,8 +110,8 @@ extension Swift2JavaTranslator { // If any API uses 'Foundation.Data' or 'FoundationEssentials.Data', // import 'Data' as if it's declared in this module. - if let dataDecl = self.symbolTable[.data] ?? self.symbolTable[.essentialsData] { - let dataProtocolDecl = (self.symbolTable[.dataProtocol] ?? self.symbolTable[.essentialsDataProtocol])! + if let dataDecl = self.symbolTable[.foundationData] ?? self.symbolTable[.essentialsData] { + let dataProtocolDecl = (self.symbolTable[.foundationDataProtocol] ?? self.symbolTable[.essentialsDataProtocol])! if self.isUsing(where: { $0 == dataDecl || $0 == dataProtocolDecl }) { visitor.visit(nominalDecl: dataDecl.syntax!.asNominal!, in: nil) } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift index 285e4e35..809f2aa1 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift @@ -15,7 +15,7 @@ import SwiftSyntax enum SwiftKnownTypeDeclKind: String, Hashable { - // MARK: -Std + // Swift case bool = "Swift.Bool" case int = "Swift.Int" case uint = "Swift.UInt" @@ -41,10 +41,10 @@ enum SwiftKnownTypeDeclKind: String, Hashable { case void = "Swift.Void" case string = "Swift.String" - // MARK: -Foundation - case dataProtocol = "Foundation.DataProtocol" + // Foundation + case foundationDataProtocol = "Foundation.DataProtocol" case essentialsDataProtocol = "FoundationEssentials.DataProtocol" - case data = "Foundation.Data" + case foundationData = "Foundation.Data" case essentialsData = "FoundationEssentials.Data" var moduleAndName: (module: String, name: String) { diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift index 2aafc0eb..25b1135a 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift @@ -35,8 +35,8 @@ struct SwiftKnownTypes { var unsafeRawPointer: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.unsafeRawPointer])) } var unsafeMutableRawPointer: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.unsafeMutableRawPointer])) } - var dataProtocol: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.dataProtocol])) } - var data: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.data])) } + var foundationDataProtocol: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.foundationDataProtocol])) } + var foundationData: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.foundationData])) } var essentialsDataProtocol: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.essentialsDataProtocol])) } var essentialsData: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.essentialsData])) } @@ -80,7 +80,7 @@ struct SwiftKnownTypes { /// given protocol kind. E.g. `String` for `StringProtocol` func representativeType(of knownProtocol: SwiftKnownTypeDeclKind) -> SwiftType? { switch knownProtocol { - case .dataProtocol: return self.data + case .foundationDataProtocol: return self.foundationData case .essentialsDataProtocol: return self.essentialsData default: return nil } From b1cc8167fbe8157352a48cee7d9aabc279f90d96 Mon Sep 17 00:00:00 2001 From: pelekon <13712101+pelekon@users.noreply.github.com> Date: Tue, 21 Oct 2025 20:53:02 +0200 Subject: [PATCH 4/6] Small rework of analyzing imported modules and printing import statements to output swift file. --- ...ift2JavaGenerator+SwiftThunkPrinting.swift | 40 +++++ .../SwiftTypes/DependencyScanner.swift | 65 +++++++- .../SwiftTypes/ImportedSwiftModule.swift | 32 ++++ .../SwiftTypes/SwiftKnownModules.swift | 11 +- .../SwiftTypes/SwiftModuleSymbolTable.swift | 20 +++ .../SwiftParsedModuleSymbolTableBuilder.swift | 13 +- .../SwiftTypes/SwiftSymbolTable.swift | 21 ++- .../JExtractSwiftTests/DataImportTests.swift | 143 ++++++------------ 8 files changed, 237 insertions(+), 108 deletions(-) create mode 100644 Sources/JExtractSwiftLib/SwiftTypes/ImportedSwiftModule.swift diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift index f35973eb..776e5a93 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift @@ -125,11 +125,51 @@ extension FFMSwift2JavaGenerator { } func printSwiftThunkImports(_ printer: inout CodePrinter) { + let mainSymbolSourceModules = Set( + self.lookupContext.symbolTable.importedModules.values.filter { $0.alternativeModules?.isMainSourceOfSymbols ?? false }.map(\.moduleName) + ) + for module in self.lookupContext.symbolTable.importedModules.keys.sorted() { guard module != "Swift" else { continue } + + guard let alternativeModules = self.lookupContext.symbolTable.importedModules[module]?.alternativeModules else { + printer.print("import \(module)") + continue + } + + // Try to print only on main module from relation chain as it has every other module. + guard !mainSymbolSourceModules.isDisjoint(with: alternativeModules.moduleNames) || alternativeModules.isMainSourceOfSymbols else { + if !alternativeModules.isMainSourceOfSymbols { + printer.print("import \(module)") + } + continue + } + + var importGroups: [String: [String]] = [:] + for name in alternativeModules.moduleNames { + guard let otherModule = self.lookupContext.symbolTable.importedModules[name] else { continue } + + let groupKey = otherModule.requiredAvailablityOfModuleWithName ?? otherModule.moduleName + importGroups[groupKey, default: []].append(otherModule.moduleName) + } + + for (index, group) in importGroups.keys.sorted().enumerated() { + if index > 0 && importGroups.keys.count > 1 { + printer.print("#elseif canImport(\(group))") + } else { + printer.print("#if canImport(\(group))") + } + + for groupModule in importGroups[group] ?? [] { + printer.print("import \(groupModule)") + } + } + + printer.print("#else") printer.print("import \(module)") + printer.print("#endif") } printer.println() } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/DependencyScanner.swift b/Sources/JExtractSwiftLib/SwiftTypes/DependencyScanner.swift index 409c81a7..79abe981 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/DependencyScanner.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/DependencyScanner.swift @@ -15,15 +15,74 @@ import SwiftSyntax /// Scan importing modules. -func importingModuleNames(sourceFile: SourceFileSyntax) -> [String] { - var importingModuleNames: [String] = [] +func importingModules(sourceFile: SourceFileSyntax) -> [ImportedSwiftModule] { + var importingModuleNames: [ImportedSwiftModule] = [] for item in sourceFile.statements { if let importDecl = item.item.as(ImportDeclSyntax.self) { guard let moduleName = importDecl.path.first?.name.text else { continue } - importingModuleNames.append(moduleName) + importingModuleNames.append(ImportedSwiftModule(name: moduleName, availableWithModuleName: nil, alternativeModuleNames: [])) + } else if let ifConfigDecl = item.item.as(IfConfigDeclSyntax.self) { + importingModuleNames.append(contentsOf: modules(from: ifConfigDecl)) } } return importingModuleNames } + +private func modules(from ifConfigDecl: IfConfigDeclSyntax) -> [ImportedSwiftModule] { + guard + let firstClause = ifConfigDecl.clauses.first, + let calledExpression = firstClause.condition?.as(FunctionCallExprSyntax.self)?.calledExpression.as(DeclReferenceExprSyntax.self), + calledExpression.baseName.text == "canImport" else { + return [] + } + + var modules: [ImportedSwiftModule] = [] + modules.reserveCapacity(ifConfigDecl.clauses.count) + + for (index, clause) in ifConfigDecl.clauses.enumerated() { + let importedModuleNames = clause.elements?.as(CodeBlockItemListSyntax.self)? + .compactMap { CodeBlockItemSyntax($0) } + .compactMap { $0.item.as(ImportDeclSyntax.self) } + .compactMap { $0.path.first?.name.text } ?? [] + + let importModuleName: String? = if + let funcCallExpr = clause.condition?.as(FunctionCallExprSyntax.self), + let calledDeclReference = funcCallExpr.calledExpression.as(DeclReferenceExprSyntax.self), + calledDeclReference.baseName.text == "canImport", + let moduleNameSyntax = funcCallExpr.arguments.first?.expression.as(DeclReferenceExprSyntax.self) { + moduleNameSyntax.baseName.text + } else { + nil + } + + let clauseModules = importedModuleNames.map { + ImportedSwiftModule(name: $0, + availableWithModuleName: importModuleName, + alternativeModuleNames: []) + } + + // Assume single import from #else statement is fallback and use it as main source of symbols + if + clauseModules.count == 1 && + index == (ifConfigDecl.clauses.count - 1) && + clause.poundKeyword.tokenKind == .poundElse { + var fallbackModule: ImportedSwiftModule = clauseModules[0] + var moduleNames: [String] = [] + moduleNames.reserveCapacity(modules.count) + + for i in 0.. + var isMainSourceOfSymbols: Bool + + init(name: String, availableWithModuleName: String? = nil, alternativeModuleNames: Set = [], isMainSourceOfSymbols: Bool = false) { + self.name = name + self.availableWithModuleName = availableWithModuleName + self.alternativeModuleNames = alternativeModuleNames + self.isMainSourceOfSymbols = isMainSourceOfSymbols + } + + func hash(into hasher: inout Hasher) { + hasher.combine(name) + } +} \ No newline at end of file diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift index fd52ef58..45d5df5a 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift @@ -48,13 +48,20 @@ private var swiftSymbolTable: SwiftModuleSymbolTable { } private var foundationEssentialsSymbolTable: SwiftModuleSymbolTable { - var builder = SwiftParsedModuleSymbolTableBuilder(moduleName: "FoundationEssentials", importedModules: ["Swift": swiftSymbolTable]) + var builder = SwiftParsedModuleSymbolTableBuilder( + moduleName: "FoundationEssentials", + requiredAvailablityOfModuleWithName: "FoundationEssentials", + alternativeModules: .init(isMainSourceOfSymbols: false, moduleNames: ["Foundation"]), + importedModules: ["Swift": swiftSymbolTable]) builder.handle(sourceFile: foundationEssentialsSourceFile) return builder.finalize() } private var foundationSymbolTable: SwiftModuleSymbolTable { - var builder = SwiftParsedModuleSymbolTableBuilder(moduleName: "Foundation", importedModules: ["Swift": swiftSymbolTable]) + var builder = SwiftParsedModuleSymbolTableBuilder( + moduleName: "Foundation", + alternativeModules: .init(isMainSourceOfSymbols: true, moduleNames: ["FoundationEssentials"]), + importedModules: ["Swift": swiftSymbolTable]) builder.handle(sourceFile: foundationSourceFile) return builder.finalize() } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftModuleSymbolTable.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftModuleSymbolTable.swift index f1bbaa12..6328045f 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftModuleSymbolTable.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftModuleSymbolTable.swift @@ -19,6 +19,12 @@ struct SwiftModuleSymbolTable: SwiftSymbolTableProtocol { /// The name of this module. let moduleName: String + /// The name of module required to be imported and checked via canImport statement. + let requiredAvailablityOfModuleWithName: String? + + /// Data about alternative modules which provides desired symbos e.g. FoundationEssentials is non-Darwin platform alternative for Foundation + let alternativeModules: AlternativeModuleNamesData? + /// The top-level nominal types, found by name. var topLevelTypes: [String: SwiftNominalTypeDeclaration] = [:] @@ -36,4 +42,18 @@ struct SwiftModuleSymbolTable: SwiftSymbolTableProtocol { func lookupNestedType(_ name: String, parent: SwiftNominalTypeDeclaration) -> SwiftNominalTypeDeclaration? { nestedTypes[parent]?[name] } + + func isAlternative(for moduleName: String) -> Bool { + alternativeModules.flatMap { $0.moduleNames.contains(moduleName) } ?? false + } } + +extension SwiftModuleSymbolTable { + struct AlternativeModuleNamesData { + /// Flag indicating module should be used as source of symbols to avoid duplication of symbols. + let isMainSourceOfSymbols: Bool + + /// Names of modules which are alternative for currently checked module. + let moduleNames: Set + } +} \ No newline at end of file diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift index 9b8ef236..8abb21f5 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift @@ -26,9 +26,18 @@ struct SwiftParsedModuleSymbolTableBuilder { /// Extension decls their extended type hasn't been resolved. var unresolvedExtensions: [ExtensionDeclSyntax] - init(moduleName: String, importedModules: [String: SwiftModuleSymbolTable], log: Logger? = nil) { + init( + moduleName: String, + requiredAvailablityOfModuleWithName: String? = nil, + alternativeModules: SwiftModuleSymbolTable.AlternativeModuleNamesData? = nil, + importedModules: [String: SwiftModuleSymbolTable], + log: Logger? = nil + ) { self.log = log - self.symbolTable = .init(moduleName: moduleName) + self.symbolTable = .init( + moduleName: moduleName, + requiredAvailablityOfModuleWithName: requiredAvailablityOfModuleWithName, + alternativeModules: alternativeModules) self.importedModules = importedModules self.unresolvedExtensions = [] } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift index 239d38c4..4271e297 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift @@ -42,6 +42,9 @@ package class SwiftSymbolTable { let parsedModule:SwiftModuleSymbolTable private var knownTypeToNominal: [SwiftKnownTypeDeclKind: SwiftNominalTypeDeclaration] = [:] + private var prioritySortedImportedModules: [SwiftModuleSymbolTable] { + importedModules.values.sorted(by: { ($0.alternativeModules?.isMainSourceOfSymbols ?? false) && $0.moduleName < $1.moduleName }) + } init(parsedModule: SwiftModuleSymbolTable, importedModules: [String: SwiftModuleSymbolTable]) { self.parsedModule = parsedModule @@ -58,18 +61,22 @@ extension SwiftSymbolTable { // Prepare imported modules. // FIXME: Support arbitrary dependencies. - var moduleNames: Set = [] + var modules: Set = [] for sourceFile in sourceFiles { - moduleNames.formUnion(importingModuleNames(sourceFile: sourceFile)) + modules.formUnion(importingModules(sourceFile: sourceFile)) } var importedModules: [String: SwiftModuleSymbolTable] = [:] importedModules[SwiftKnownModule.swift.name] = SwiftKnownModule.swift.symbolTable - for moduleName in moduleNames.sorted() { + for module in modules { + // We don't need duplicates of symbols, first known definition is enough to parse module + // e.g Data from FoundationEssentials and Foundation collide and lead to different results due to random order of keys in Swift's Dictionary + // guard module.isMainSourceOfSymbols || !importedModules.contains(where: { $0.value.isAlternative(for: String)}) else { continue } + if - importedModules[moduleName] == nil, - let knownModule = SwiftKnownModule(rawValue: moduleName) + importedModules[module.name] == nil, + let knownModule = SwiftKnownModule(rawValue: module.name) { - importedModules[moduleName] = knownModule.symbolTable + importedModules[module.name] = knownModule.symbolTable } } @@ -95,7 +102,7 @@ extension SwiftSymbolTable: SwiftSymbolTableProtocol { return parsedResult } - for importedModule in importedModules.values { + for importedModule in prioritySortedImportedModules { if let result = importedModule.lookupTopLevelNominalType(name) { return result } diff --git a/Tests/JExtractSwiftTests/DataImportTests.swift b/Tests/JExtractSwiftTests/DataImportTests.swift index 91369122..bad4174d 100644 --- a/Tests/JExtractSwiftTests/DataImportTests.swift +++ b/Tests/JExtractSwiftTests/DataImportTests.swift @@ -16,7 +16,14 @@ import JExtractSwiftLib import Testing final class DataImportTests { - let data_interfaceFile = + private static let ifConfigImport = """ + #if canImport(FoundationEssentials) + import FoundationEssentials + #else + import Foundation + #endif + """ + private static let foundationData_interfaceFile = """ import Foundation @@ -24,14 +31,14 @@ final class DataImportTests { public func returnData() -> Data """ - let dataProtocol_interfaceFile = + private static let foundationDataProtocol_interfaceFile = """ import Foundation public func receiveDataProtocol(dat: some DataProtocol, dat2: T?) """ - let essentials_data_interfaceFile = + private static let essentialsData_interfaceFile = """ import FoundationEssentials @@ -39,74 +46,38 @@ final class DataImportTests { public func returnData() -> Data """ - let essentials_dataProtocol_interfaceFile = + private static let essentialsDataProtocol_interfaceFile = """ import FoundationEssentials public func receiveDataProtocol(dat: some DataProtocol, dat2: T?) """ + private static let ifConfigData_interfaceFile = + """ + \(ifConfigImport) + + public func receiveData(dat: Data) + public func returnData() -> Data + """ + private static let ifConfigDataProtocol_interfaceFile = + """ + \(ifConfigImport) + + public func receiveDataProtocol(dat: some DataProtocol, dat2: T?) + """ - @Test("Import Data: Swift thunks") - func data_swiftThunk() throws { - - try assertOutput( - input: data_interfaceFile, .ffm, .swift, - expectedChunks: [ - """ - import Foundation - """, - """ - @_cdecl("swiftjava_SwiftModule_receiveData_dat") - public func swiftjava_SwiftModule_receiveData_dat(_ dat: UnsafeRawPointer) { - receiveData(dat: dat.assumingMemoryBound(to: Data.self).pointee) - } - """, - """ - @_cdecl("swiftjava_SwiftModule_returnData") - public func swiftjava_SwiftModule_returnData(_ _result: UnsafeMutableRawPointer) { - _result.assumingMemoryBound(to: Data.self).initialize(to: returnData()) - } - """, - - """ - @_cdecl("swiftjava_getType_SwiftModule_Data") - public func swiftjava_getType_SwiftModule_Data() -> UnsafeMutableRawPointer /* Any.Type */ { - return unsafeBitCast(Data.self, to: UnsafeMutableRawPointer.self) - } - """, - - """ - @_cdecl("swiftjava_SwiftModule_Data_init_bytes_count") - public func swiftjava_SwiftModule_Data_init_bytes_count(_ bytes: UnsafeRawPointer, _ count: Int, _ _result: UnsafeMutableRawPointer) { - _result.assumingMemoryBound(to: Data.self).initialize(to: Data(bytes: bytes, count: count)) - } - """, - - """ - @_cdecl("swiftjava_SwiftModule_Data_count$get") - public func swiftjava_SwiftModule_Data_count$get(_ self: UnsafeRawPointer) -> Int { - return self.assumingMemoryBound(to: Data.self).pointee.count - } - """, - - """ - @_cdecl("swiftjava_SwiftModule_Data_withUnsafeBytes__") - public func swiftjava_SwiftModule_Data_withUnsafeBytes__(_ body: @convention(c) (UnsafeRawPointer?, Int) -> Void, _ self: UnsafeRawPointer) { - self.assumingMemoryBound(to: Data.self).pointee.withUnsafeBytes({ (_0) in - return body(_0.baseAddress, _0.count) - }) - } - """, - ] - ) + @Test("Import Data: Swift thunks", arguments: zip( + [Self.foundationData_interfaceFile, Self.essentialsData_interfaceFile, Self.ifConfigData_interfaceFile], + ["import Foundation", "import FoundationEssentials", Self.ifConfigImport] + )) + func data_swiftThunk(fileContent: String, expectedImportChunk: String) throws { try assertOutput( - input: essentials_data_interfaceFile, .ffm, .swift, + input: fileContent, .ffm, .swift, + detectChunkByInitialLines: 10, expectedChunks: [ - """ - import FoundationEssentials - """, + expectedImportChunk, """ @_cdecl("swiftjava_SwiftModule_receiveData_dat") public func swiftjava_SwiftModule_receiveData_dat(_ dat: UnsafeRawPointer) { @@ -153,11 +124,12 @@ final class DataImportTests { ) } - @Test("Import Data: JavaBindings") - func data_javaBindings() throws { - + @Test("Import Data: JavaBindings", arguments: [ + Self.foundationData_interfaceFile, Self.essentialsData_interfaceFile, Self.ifConfigData_interfaceFile + ]) + func data_javaBindings(fileContent: String) throws { try assertOutput( - input: data_interfaceFile, .ffm, .java, + input: fileContent, .ffm, .java, expectedChunks: [ """ /** @@ -399,34 +371,15 @@ final class DataImportTests { ) } - @Test("Import DataProtocol: Swift thunks") - func dataProtocol_swiftThunk() throws { + @Test("Import DataProtocol: Swift thunks", arguments: zip( + [Self.foundationDataProtocol_interfaceFile, Self.essentialsDataProtocol_interfaceFile, Self.ifConfigDataProtocol_interfaceFile], + ["import Foundation", "import FoundationEssentials", Self.ifConfigImport] + )) + func dataProtocol_swiftThunk(fileContent: String, expectedImportChunk: String) throws { try assertOutput( - input: dataProtocol_interfaceFile, .ffm, .swift, + input: fileContent, .ffm, .swift, expectedChunks: [ - """ - import Foundation - """, - """ - @_cdecl("swiftjava_SwiftModule_receiveDataProtocol_dat_dat2") - public func swiftjava_SwiftModule_receiveDataProtocol_dat_dat2(_ dat: UnsafeRawPointer, _ dat2: UnsafeRawPointer?) { - receiveDataProtocol(dat: dat.assumingMemoryBound(to: Data.self).pointee, dat2: dat2?.assumingMemoryBound(to: Data.self).pointee) - } - """, - - // Just to make sure 'Data' is imported. - """ - @_cdecl("swiftjava_getType_SwiftModule_Data") - """ - ] - ) - - try assertOutput( - input: essentials_dataProtocol_interfaceFile, .ffm, .swift, - expectedChunks: [ - """ - import FoundationEssentials - """, + expectedImportChunk, """ @_cdecl("swiftjava_SwiftModule_receiveDataProtocol_dat_dat2") public func swiftjava_SwiftModule_receiveDataProtocol_dat_dat2(_ dat: UnsafeRawPointer, _ dat2: UnsafeRawPointer?) { @@ -442,11 +395,13 @@ final class DataImportTests { ) } - @Test("Import DataProtocol: JavaBindings") - func dataProtocol_javaBindings() throws { + @Test("Import DataProtocol: JavaBindings", arguments: [ + Self.foundationDataProtocol_interfaceFile, Self.essentialsDataProtocol_interfaceFile, Self.ifConfigDataProtocol_interfaceFile + ]) + func dataProtocol_javaBindings(fileContent: String) throws { try assertOutput( - input: dataProtocol_interfaceFile, .ffm, .java, + input: fileContent, .ffm, .java, expectedChunks: [ """ /** From 91ef19d7bd8f73b59a989fca5d6c9675cac2d28a Mon Sep 17 00:00:00 2001 From: pelekon <13712101+pelekon@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:30:52 +0200 Subject: [PATCH 5/6] Forgotten equatable for ImportedSwiftModule. --- Sources/JExtractSwiftLib/SwiftTypes/ImportedSwiftModule.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/JExtractSwiftLib/SwiftTypes/ImportedSwiftModule.swift b/Sources/JExtractSwiftLib/SwiftTypes/ImportedSwiftModule.swift index 59cd0c5c..d71f6dac 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/ImportedSwiftModule.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/ImportedSwiftModule.swift @@ -26,6 +26,10 @@ struct ImportedSwiftModule: Hashable { self.isMainSourceOfSymbols = isMainSourceOfSymbols } + static func ==(lhs: Self, rhs: Self) -> Bool { + lhs.name == rhs.name + } + func hash(into hasher: inout Hasher) { hasher.combine(name) } From 2330950ba6b44a21451ab5f1edb6ff0f158a6a3b Mon Sep 17 00:00:00 2001 From: pelekon <13712101+pelekon@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:41:17 +0200 Subject: [PATCH 6/6] Tests fix --- .../FFMSwift2JavaGenerator+SwiftThunkPrinting.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift index 776e5a93..e43ea4c5 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift @@ -128,7 +128,7 @@ extension FFMSwift2JavaGenerator { let mainSymbolSourceModules = Set( self.lookupContext.symbolTable.importedModules.values.filter { $0.alternativeModules?.isMainSourceOfSymbols ?? false }.map(\.moduleName) ) - + for module in self.lookupContext.symbolTable.importedModules.keys.sorted() { guard module != "Swift" else { continue @@ -167,9 +167,13 @@ extension FFMSwift2JavaGenerator { } } - printer.print("#else") - printer.print("import \(module)") - printer.print("#endif") + if (importGroups.keys.isEmpty) { + printer.print("import \(module)") + } else { + printer.print("#else") + printer.print("import \(module)") + printer.print("#endif") + } } printer.println() }