Skip to content

Commit cfbcf6c

Browse files
authored
Properly extract usage of Data type from FoundationEssentials. (#405)
Co-authored-by: pelekon <[email protected]>
1 parent f7485cc commit cfbcf6c

15 files changed

+303
-56
lines changed

Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ extension SwiftKnownTypeDeclKind {
125125
.qualified(const: true, volatile: false, type: .void)
126126
)
127127
case .void: .void
128-
case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data, .dataProtocol, .optional:
128+
case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer,
129+
.unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .foundationData, .foundationDataProtocol,
130+
.essentialsData, .essentialsDataProtocol, .optional:
129131
nil
130132
}
131133
}

Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ struct CdeclLowering {
282282
)
283283
}
284284

285-
case .data:
285+
case .foundationData, .essentialsData:
286286
break
287287

288288
default:
@@ -375,7 +375,7 @@ struct CdeclLowering {
375375
case .nominal(let nominal):
376376
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
377377
switch knownType {
378-
case .data:
378+
case .foundationData, .essentialsData:
379379
break
380380
case .unsafeRawPointer, .unsafeMutableRawPointer:
381381
throw LoweringError.unhandledType(.optional(wrappedType))
@@ -387,7 +387,7 @@ struct CdeclLowering {
387387
throw LoweringError.unhandledType(.optional(wrappedType))
388388
case .void, .string:
389389
throw LoweringError.unhandledType(.optional(wrappedType))
390-
case .dataProtocol:
390+
case .foundationDataProtocol, .essentialsDataProtocol:
391391
throw LoweringError.unhandledType(.optional(wrappedType))
392392
default:
393393
// Unreachable? Should be handled by `CType(cdeclType:)` lowering above.
@@ -505,7 +505,7 @@ struct CdeclLowering {
505505
])
506506
)
507507

508-
case .data:
508+
case .foundationData, .essentialsData:
509509
break
510510

511511
default:
@@ -606,7 +606,7 @@ struct CdeclLowering {
606606
case .void:
607607
return LoweredResult(cdeclResultType: .void, cdeclOutParameters: [], conversion: .placeholder)
608608

609-
case .data:
609+
case .foundationData, .essentialsData:
610610
break
611611

612612
case .string, .optional:

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ extension FFMSwift2JavaGenerator {
411411
conversion: .call(.placeholder, function: "SwiftRuntime.toCString", withArena: true)
412412
)
413413

414-
case .data:
414+
case .foundationData, .essentialsData:
415415
break
416416

417417
default:
@@ -515,7 +515,7 @@ extension FFMSwift2JavaGenerator {
515515
case .nominal(let nominal):
516516
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
517517
switch knownType {
518-
case .data, .dataProtocol:
518+
case .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol:
519519
break
520520
default:
521521
throw JavaTranslationError.unhandledType(.optional(swiftType))
@@ -658,7 +658,7 @@ extension FFMSwift2JavaGenerator {
658658
)
659659
)
660660

661-
case .data:
661+
case .foundationData, .essentialsData:
662662
break
663663

664664
case .unsafePointer, .unsafeMutablePointer:

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,55 @@ extension FFMSwift2JavaGenerator {
125125
}
126126

127127
func printSwiftThunkImports(_ printer: inout CodePrinter) {
128+
let mainSymbolSourceModules = Set(
129+
self.lookupContext.symbolTable.importedModules.values.filter { $0.alternativeModules?.isMainSourceOfSymbols ?? false }.map(\.moduleName)
130+
)
131+
128132
for module in self.lookupContext.symbolTable.importedModules.keys.sorted() {
129133
guard module != "Swift" else {
130134
continue
131135
}
132-
printer.print("import \(module)")
136+
137+
guard let alternativeModules = self.lookupContext.symbolTable.importedModules[module]?.alternativeModules else {
138+
printer.print("import \(module)")
139+
continue
140+
}
141+
142+
// Try to print only on main module from relation chain as it has every other module.
143+
guard !mainSymbolSourceModules.isDisjoint(with: alternativeModules.moduleNames) || alternativeModules.isMainSourceOfSymbols else {
144+
if !alternativeModules.isMainSourceOfSymbols {
145+
printer.print("import \(module)")
146+
}
147+
continue
148+
}
149+
150+
var importGroups: [String: [String]] = [:]
151+
for name in alternativeModules.moduleNames {
152+
guard let otherModule = self.lookupContext.symbolTable.importedModules[name] else { continue }
153+
154+
let groupKey = otherModule.requiredAvailablityOfModuleWithName ?? otherModule.moduleName
155+
importGroups[groupKey, default: []].append(otherModule.moduleName)
156+
}
157+
158+
for (index, group) in importGroups.keys.sorted().enumerated() {
159+
if index > 0 && importGroups.keys.count > 1 {
160+
printer.print("#elseif canImport(\(group))")
161+
} else {
162+
printer.print("#if canImport(\(group))")
163+
}
164+
165+
for groupModule in importGroups[group] ?? [] {
166+
printer.print("import \(groupModule)")
167+
}
168+
}
169+
170+
if (importGroups.keys.isEmpty) {
171+
printer.print("import \(module)")
172+
} else {
173+
printer.print("#else")
174+
printer.print("import \(module)")
175+
printer.print("#endif")
176+
}
133177
}
134178
printer.println()
135179
}

Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ enum JNIJavaTypeTranslator {
5050
.unsafePointer, .unsafeMutablePointer,
5151
.unsafeRawBufferPointer, .unsafeMutableRawBufferPointer,
5252
.unsafeBufferPointer, .unsafeMutableBufferPointer,
53-
.optional, .data, .dataProtocol:
53+
.optional, .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol:
5454
return nil
5555
}
5656
}

Sources/JExtractSwiftLib/Swift2JavaTranslator.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,10 @@ extension Swift2JavaTranslator {
108108
visitor.visit(sourceFile: input.syntax)
109109
}
110110

111-
// If any API uses 'Foundation.Data', import 'Data' as if it's declared in
112-
// this module.
113-
if let dataDecl = self.symbolTable[.data] {
114-
let dataProtocolDecl = self.symbolTable[.dataProtocol]!
111+
// If any API uses 'Foundation.Data' or 'FoundationEssentials.Data',
112+
// import 'Data' as if it's declared in this module.
113+
if let dataDecl = self.symbolTable[.foundationData] ?? self.symbolTable[.essentialsData] {
114+
let dataProtocolDecl = (self.symbolTable[.foundationDataProtocol] ?? self.symbolTable[.essentialsDataProtocol])!
115115
if self.isUsing(where: { $0 == dataDecl || $0 == dataProtocolDecl }) {
116116
visitor.visit(nominalDecl: dataDecl.syntax!.asNominal!, in: nil)
117117
}

Sources/JExtractSwiftLib/SwiftTypes/DependencyScanner.swift

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,74 @@
1515
import SwiftSyntax
1616

1717
/// Scan importing modules.
18-
func importingModuleNames(sourceFile: SourceFileSyntax) -> [String] {
19-
var importingModuleNames: [String] = []
18+
func importingModules(sourceFile: SourceFileSyntax) -> [ImportedSwiftModule] {
19+
var importingModuleNames: [ImportedSwiftModule] = []
2020
for item in sourceFile.statements {
2121
if let importDecl = item.item.as(ImportDeclSyntax.self) {
2222
guard let moduleName = importDecl.path.first?.name.text else {
2323
continue
2424
}
25-
importingModuleNames.append(moduleName)
25+
importingModuleNames.append(ImportedSwiftModule(name: moduleName, availableWithModuleName: nil, alternativeModuleNames: []))
26+
} else if let ifConfigDecl = item.item.as(IfConfigDeclSyntax.self) {
27+
importingModuleNames.append(contentsOf: modules(from: ifConfigDecl))
2628
}
2729
}
2830
return importingModuleNames
2931
}
32+
33+
private func modules(from ifConfigDecl: IfConfigDeclSyntax) -> [ImportedSwiftModule] {
34+
guard
35+
let firstClause = ifConfigDecl.clauses.first,
36+
let calledExpression = firstClause.condition?.as(FunctionCallExprSyntax.self)?.calledExpression.as(DeclReferenceExprSyntax.self),
37+
calledExpression.baseName.text == "canImport" else {
38+
return []
39+
}
40+
41+
var modules: [ImportedSwiftModule] = []
42+
modules.reserveCapacity(ifConfigDecl.clauses.count)
43+
44+
for (index, clause) in ifConfigDecl.clauses.enumerated() {
45+
let importedModuleNames = clause.elements?.as(CodeBlockItemListSyntax.self)?
46+
.compactMap { CodeBlockItemSyntax($0) }
47+
.compactMap { $0.item.as(ImportDeclSyntax.self) }
48+
.compactMap { $0.path.first?.name.text } ?? []
49+
50+
let importModuleName: String? = if
51+
let funcCallExpr = clause.condition?.as(FunctionCallExprSyntax.self),
52+
let calledDeclReference = funcCallExpr.calledExpression.as(DeclReferenceExprSyntax.self),
53+
calledDeclReference.baseName.text == "canImport",
54+
let moduleNameSyntax = funcCallExpr.arguments.first?.expression.as(DeclReferenceExprSyntax.self) {
55+
moduleNameSyntax.baseName.text
56+
} else {
57+
nil
58+
}
59+
60+
let clauseModules = importedModuleNames.map {
61+
ImportedSwiftModule(name: $0,
62+
availableWithModuleName: importModuleName,
63+
alternativeModuleNames: [])
64+
}
65+
66+
// Assume single import from #else statement is fallback and use it as main source of symbols
67+
if
68+
clauseModules.count == 1 &&
69+
index == (ifConfigDecl.clauses.count - 1) &&
70+
clause.poundKeyword.tokenKind == .poundElse {
71+
var fallbackModule: ImportedSwiftModule = clauseModules[0]
72+
var moduleNames: [String] = []
73+
moduleNames.reserveCapacity(modules.count)
74+
75+
for i in 0..<modules.count {
76+
modules[i].alternativeModuleNames.insert(fallbackModule.name)
77+
moduleNames.append(modules[i].name)
78+
}
79+
80+
fallbackModule.alternativeModuleNames = Set(modules.map(\.name))
81+
fallbackModule.isMainSourceOfSymbols = true
82+
}
83+
84+
modules.append(contentsOf: clauseModules)
85+
}
86+
87+
return modules
88+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
16+
struct ImportedSwiftModule: Hashable {
17+
let name: String
18+
let availableWithModuleName: String?
19+
var alternativeModuleNames: Set<String>
20+
var isMainSourceOfSymbols: Bool
21+
22+
init(name: String, availableWithModuleName: String? = nil, alternativeModuleNames: Set<String> = [], isMainSourceOfSymbols: Bool = false) {
23+
self.name = name
24+
self.availableWithModuleName = availableWithModuleName
25+
self.alternativeModuleNames = alternativeModuleNames
26+
self.isMainSourceOfSymbols = isMainSourceOfSymbols
27+
}
28+
29+
static func ==(lhs: Self, rhs: Self) -> Bool {
30+
lhs.name == rhs.name
31+
}
32+
33+
func hash(into hasher: inout Hasher) {
34+
hasher.combine(name)
35+
}
36+
}

Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import SwiftSyntaxBuilder
1818
enum SwiftKnownModule: String {
1919
case swift = "Swift"
2020
case foundation = "Foundation"
21+
case foundationEssentials = "FoundationEssentials"
2122

2223
var name: String {
2324
return self.rawValue
@@ -27,13 +28,15 @@ enum SwiftKnownModule: String {
2728
return switch self {
2829
case .swift: swiftSymbolTable
2930
case .foundation: foundationSymbolTable
31+
case .foundationEssentials: foundationEssentialsSymbolTable
3032
}
3133
}
3234

3335
var sourceFile: SourceFileSyntax {
3436
return switch self {
3537
case .swift: swiftSourceFile
36-
case .foundation: foundationSourceFile
38+
case .foundation: foundationEssentialsSourceFile
39+
case .foundationEssentials: foundationEssentialsSourceFile
3740
}
3841
}
3942
}
@@ -44,8 +47,21 @@ private var swiftSymbolTable: SwiftModuleSymbolTable {
4447
return builder.finalize()
4548
}
4649

50+
private var foundationEssentialsSymbolTable: SwiftModuleSymbolTable {
51+
var builder = SwiftParsedModuleSymbolTableBuilder(
52+
moduleName: "FoundationEssentials",
53+
requiredAvailablityOfModuleWithName: "FoundationEssentials",
54+
alternativeModules: .init(isMainSourceOfSymbols: false, moduleNames: ["Foundation"]),
55+
importedModules: ["Swift": swiftSymbolTable])
56+
builder.handle(sourceFile: foundationEssentialsSourceFile)
57+
return builder.finalize()
58+
}
59+
4760
private var foundationSymbolTable: SwiftModuleSymbolTable {
48-
var builder = SwiftParsedModuleSymbolTableBuilder(moduleName: "Foundation", importedModules: ["Swift": swiftSymbolTable])
61+
var builder = SwiftParsedModuleSymbolTableBuilder(
62+
moduleName: "Foundation",
63+
alternativeModules: .init(isMainSourceOfSymbols: true, moduleNames: ["FoundationEssentials"]),
64+
importedModules: ["Swift": swiftSymbolTable])
4965
builder.handle(sourceFile: foundationSourceFile)
5066
return builder.finalize()
5167
}
@@ -87,7 +103,7 @@ private let swiftSourceFile: SourceFileSyntax = """
87103
}
88104
"""
89105

90-
private let foundationSourceFile: SourceFileSyntax = """
106+
private let foundationEssentialsSourceFile: SourceFileSyntax = """
91107
public protocol DataProtocol {}
92108
93109
public struct Data: DataProtocol {
@@ -96,3 +112,9 @@ private let foundationSourceFile: SourceFileSyntax = """
96112
public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) -> Void)
97113
}
98114
"""
115+
116+
private var foundationSourceFile: SourceFileSyntax {
117+
// On platforms other than Darwin, imports such as FoundationEssentials, FoundationNetworking, etc. are used,
118+
// so this file should be created by combining the files of the aforementioned modules.
119+
foundationEssentialsSourceFile
120+
}

Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import SwiftSyntax
1616

1717
enum SwiftKnownTypeDeclKind: String, Hashable {
18+
// Swift
1819
case bool = "Swift.Bool"
1920
case int = "Swift.Int"
2021
case uint = "Swift.UInt"
@@ -40,8 +41,11 @@ enum SwiftKnownTypeDeclKind: String, Hashable {
4041
case void = "Swift.Void"
4142
case string = "Swift.String"
4243

43-
case dataProtocol = "Foundation.DataProtocol"
44-
case data = "Foundation.Data"
44+
// Foundation
45+
case foundationDataProtocol = "Foundation.DataProtocol"
46+
case essentialsDataProtocol = "FoundationEssentials.DataProtocol"
47+
case foundationData = "Foundation.Data"
48+
case essentialsData = "FoundationEssentials.Data"
4549

4650
var moduleAndName: (module: String, name: String) {
4751
let qualified = self.rawValue

0 commit comments

Comments
 (0)