Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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, .foundationData, .foundationDataProtocol,
.essentialsData, .essentialsDataProtocol, .optional:
nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ struct CdeclLowering {
)
}

case .data:
case .foundationData, .essentialsData:
break

default:
Expand Down Expand Up @@ -375,7 +375,7 @@ struct CdeclLowering {
case .nominal(let nominal):
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
switch knownType {
case .data:
case .foundationData, .essentialsData:
break
case .unsafeRawPointer, .unsafeMutableRawPointer:
throw LoweringError.unhandledType(.optional(wrappedType))
Expand All @@ -387,7 +387,7 @@ struct CdeclLowering {
throw LoweringError.unhandledType(.optional(wrappedType))
case .void, .string:
throw LoweringError.unhandledType(.optional(wrappedType))
case .dataProtocol:
case .foundationDataProtocol, .essentialsDataProtocol:
throw LoweringError.unhandledType(.optional(wrappedType))
default:
// Unreachable? Should be handled by `CType(cdeclType:)` lowering above.
Expand Down Expand Up @@ -505,7 +505,7 @@ struct CdeclLowering {
])
)

case .data:
case .foundationData, .essentialsData:
break

default:
Expand Down Expand Up @@ -606,7 +606,7 @@ struct CdeclLowering {
case .void:
return LoweredResult(cdeclResultType: .void, cdeclOutParameters: [], conversion: .placeholder)

case .data:
case .foundationData, .essentialsData:
break

case .string, .optional:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ extension FFMSwift2JavaGenerator {
conversion: .call(.placeholder, function: "SwiftRuntime.toCString", withArena: true)
)

case .data:
case .foundationData, .essentialsData:
break

default:
Expand Down Expand Up @@ -515,7 +515,7 @@ extension FFMSwift2JavaGenerator {
case .nominal(let nominal):
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
switch knownType {
case .data, .dataProtocol:
case .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol:
break
default:
throw JavaTranslationError.unhandledType(.optional(swiftType))
Expand Down Expand Up @@ -658,7 +658,7 @@ extension FFMSwift2JavaGenerator {
)
)

case .data:
case .foundationData, .essentialsData:
break

case .unsafePointer, .unsafeMutablePointer:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,55 @@ 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
}
printer.print("import \(module)")

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)")
}
}

if (importGroups.keys.isEmpty) {
printer.print("import \(module)")
} else {
printer.print("#else")
printer.print("import \(module)")
printer.print("#endif")
}
}
printer.println()
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ enum JNIJavaTypeTranslator {
.unsafePointer, .unsafeMutablePointer,
.unsafeRawBufferPointer, .unsafeMutableRawBufferPointer,
.unsafeBufferPointer, .unsafeMutableBufferPointer,
.optional, .data, .dataProtocol:
.optional, .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol:
return nil
}
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/JExtractSwiftLib/Swift2JavaTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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[.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)
}
Expand Down
65 changes: 62 additions & 3 deletions Sources/JExtractSwiftLib/SwiftTypes/DependencyScanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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..<modules.count {
modules[i].alternativeModuleNames.insert(fallbackModule.name)
moduleNames.append(modules[i].name)
}

fallbackModule.alternativeModuleNames = Set(modules.map(\.name))
fallbackModule.isMainSourceOfSymbols = true
}

modules.append(contentsOf: clauseModules)
}

return modules
}
36 changes: 36 additions & 0 deletions Sources/JExtractSwiftLib/SwiftTypes/ImportedSwiftModule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//


struct ImportedSwiftModule: Hashable {
let name: String
let availableWithModuleName: String?
var alternativeModuleNames: Set<String>
var isMainSourceOfSymbols: Bool

init(name: String, availableWithModuleName: String? = nil, alternativeModuleNames: Set<String> = [], isMainSourceOfSymbols: Bool = false) {
self.name = name
self.availableWithModuleName = availableWithModuleName
self.alternativeModuleNames = alternativeModuleNames
self.isMainSourceOfSymbols = isMainSourceOfSymbols
}

static func ==(lhs: Self, rhs: Self) -> Bool {
lhs.name == rhs.name
}

func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}
28 changes: 25 additions & 3 deletions Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import SwiftSyntaxBuilder
enum SwiftKnownModule: String {
case swift = "Swift"
case foundation = "Foundation"
case foundationEssentials = "FoundationEssentials"

var name: String {
return self.rawValue
Expand All @@ -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
}
}
}
Expand All @@ -44,8 +47,21 @@ private var swiftSymbolTable: SwiftModuleSymbolTable {
return builder.finalize()
}

private var foundationEssentialsSymbolTable: SwiftModuleSymbolTable {
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()
}
Expand Down Expand Up @@ -87,7 +103,7 @@ private let swiftSourceFile: SourceFileSyntax = """
}
"""

private let foundationSourceFile: SourceFileSyntax = """
private let foundationEssentialsSourceFile: SourceFileSyntax = """
public protocol DataProtocol {}

public struct Data: DataProtocol {
Expand All @@ -96,3 +112,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
}
8 changes: 6 additions & 2 deletions Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import SwiftSyntax

enum SwiftKnownTypeDeclKind: String, Hashable {
// Swift
case bool = "Swift.Bool"
case int = "Swift.Int"
case uint = "Swift.UInt"
Expand All @@ -40,8 +41,11 @@ enum SwiftKnownTypeDeclKind: String, Hashable {
case void = "Swift.Void"
case string = "Swift.String"

case dataProtocol = "Foundation.DataProtocol"
case data = "Foundation.Data"
// Foundation
case foundationDataProtocol = "Foundation.DataProtocol"
case essentialsDataProtocol = "FoundationEssentials.DataProtocol"
case foundationData = "Foundation.Data"
case essentialsData = "FoundationEssentials.Data"

var moduleAndName: (module: String, name: String) {
let qualified = self.rawValue
Expand Down
9 changes: 6 additions & 3 deletions Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ 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])) }

func unsafePointer(_ pointeeType: SwiftType) -> SwiftType {
.nominal(
Expand Down Expand Up @@ -78,7 +80,8 @@ 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
}
}
Expand Down
Loading