Skip to content
Open
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
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ let package = Package(
name: "SwiftJavaTests",
dependencies: [
"SwiftJava",
"SwiftJavaRuntimeSupport",
"JavaNet",
],
swiftSettings: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,16 @@ extension JavaType {
case .float: "Optional<Float>"
case .double: "OptionalDouble"
case .javaLangString: "Optional<String>"
case .javaNetURI, .javaUtilDate, .javaUtilLocale, .javaUtilTimeZone, .array(.byte): "Optional<\(self)>"
default: nil
}
}

var optionalWrapperType: String? {
switch self {
case .boolean, .byte, .char, .short, .float, .javaLangString: "Optional"
case .boolean, .byte, .char, .short, .float, .javaLangString,
.javaNetURI, .javaUtilDate, .javaUtilLocale, .javaUtilTimeZone, .array(.byte):
"Optional"
case .int: "OptionalInt"
case .long: "OptionalLong"
case .double: "OptionalDouble"
Expand Down Expand Up @@ -130,7 +133,8 @@ extension JavaType {
/// Returns whether this type returns `JavaValue` from SwiftJava
var implementsJavaValue: Bool {
switch self {
case .boolean, .byte, .char, .short, .int, .long, .float, .double, .void, .javaLangString:
case .boolean, .byte, .char, .short, .int, .long, .float, .double, .void, .javaLangString,
.javaNetURI, .javaUtilDate, .javaUtilLocale, .javaUtilTimeZone:
true
case .array(let element):
element.implementsJavaValue
Expand Down
30 changes: 23 additions & 7 deletions Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,30 @@ enum JNIJavaTypeTranslator {

case .string: return .javaLangString

case .foundationCGFloat: return .double

case .foundationURL, .essentialsURL:
return config.effectiveFoundationTypesAsJavaTypes ? .javaNetURI : nil
case .foundationDate, .essentialsDate:
return config.effectiveFoundationTypesAsJavaTypes ? .javaUtilDate : nil
case .foundationData, .essentialsData:
return config.effectiveFoundationTypesAsJavaTypes ? .array(.byte) : nil
case .foundationLocale, .essentialsLocale:
return config.effectiveFoundationTypesAsJavaTypes ? .javaUtilLocale : nil
case .foundationTimeZone, .essentialsTimeZone:
return config.effectiveFoundationTypesAsJavaTypes ? .javaUtilTimeZone : nil

case .unsafeRawPointer, .unsafeMutableRawPointer,
.unsafePointer, .unsafeMutablePointer,
.unsafeRawBufferPointer, .unsafeMutableRawBufferPointer,
.unsafeBufferPointer, .unsafeMutableBufferPointer,
.opaquePointer,
.optional,
.foundationData, .foundationDataProtocol,
.essentialsData, .essentialsDataProtocol,
.foundationDataProtocol, .essentialsDataProtocol,
.array,
.dictionary,
.set,
.foundationDate, .essentialsDate,
.foundationUUID, .essentialsUUID,
.foundationURL, .essentialsURL:
.foundationUUID, .essentialsUUID:
return nil
}
}
Expand All @@ -67,6 +77,7 @@ enum JNIJavaTypeTranslator {
switch knownKind {
case .int: knownTypes.int64
case .uint: knownTypes.uint64
case .foundationCGFloat: knownTypes.double

case .bool, .int8, .uint8, .int16, .uint16, .int32, .uint32, .int64, .uint64,
.float, .double, .void, .string,
Expand All @@ -83,7 +94,9 @@ enum JNIJavaTypeTranslator {
.set,
.foundationDate, .essentialsDate,
.foundationUUID, .essentialsUUID,
.foundationURL, .essentialsURL:
.foundationURL, .essentialsURL,
.foundationLocale, .essentialsLocale,
.foundationTimeZone, .essentialsTimeZone:
nil
}
}
Expand Down Expand Up @@ -111,7 +124,10 @@ enum JNIJavaTypeTranslator {
.set,
.foundationDate, .essentialsDate,
.foundationUUID, .essentialsUUID,
.foundationURL, .essentialsURL:
.foundationURL, .essentialsURL,
.foundationLocale, .essentialsLocale,
.foundationTimeZone, .essentialsTimeZone,
.foundationCGFloat:
nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,12 @@ extension JNISwift2JavaGenerator {
genericRequirements: genericRequirements,
)

case .foundationDate, .essentialsDate, .foundationData, .essentialsData, .foundationURL, .essentialsURL:
case .foundationDate where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsDate where !config.effectiveFoundationTypesAsJavaTypes,
.foundationData where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsData where !config.effectiveFoundationTypesAsJavaTypes,
.foundationURL where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsURL where !config.effectiveFoundationTypesAsJavaTypes:
break // Handled as wrapped struct

case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
Expand Down Expand Up @@ -851,11 +856,13 @@ extension JNISwift2JavaGenerator {

if let knownType = nominalType.nominalTypeDecl.knownTypeKind {
switch knownType {
case .foundationDate, .essentialsDate:
case .foundationDate where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsDate where !config.effectiveFoundationTypesAsJavaTypes:
// Handled as wrapped struct
break

case .foundationData, .essentialsData:
case .foundationData where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsData where !config.effectiveFoundationTypesAsJavaTypes:
// Handled as wrapped struct
break

Expand Down Expand Up @@ -971,7 +978,12 @@ extension JNISwift2JavaGenerator {
genericRequirements: genericRequirements,
)

case .foundationDate, .essentialsDate, .foundationData, .essentialsData, .foundationURL, .essentialsURL:
case .foundationDate where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsDate where !config.effectiveFoundationTypesAsJavaTypes,
.foundationData where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsData where !config.effectiveFoundationTypesAsJavaTypes,
.foundationURL where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsURL where !config.effectiveFoundationTypesAsJavaTypes:
// Handled as wrapped struct
break

Expand Down Expand Up @@ -1161,16 +1173,19 @@ extension JNISwift2JavaGenerator {
)
return .swiftSet(elementJavaType)

case .foundationDate, .essentialsDate:
case .foundationDate where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsDate where !config.effectiveFoundationTypesAsJavaTypes:
return .class(package: nil, name: "Date")

case .foundationData, .essentialsData:
case .foundationData where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsData where !config.effectiveFoundationTypesAsJavaTypes:
return .class(package: nil, name: "Data")

case .foundationDataProtocol, .essentialsDataProtocol:
return .class(package: nil, name: "DataProtocol")

case .foundationURL, .essentialsURL:
case .foundationURL where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsURL where !config.effectiveFoundationTypesAsJavaTypes:
return .class(package: nil, name: "URL")

case .foundationUUID, .essentialsUUID:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,12 @@ extension JNISwift2JavaGenerator {
conversionCheck: nil
)

case .foundationDate, .essentialsDate, .foundationData, .essentialsData, .foundationURL, .essentialsURL:
case .foundationDate where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsDate where !config.effectiveFoundationTypesAsJavaTypes,
.foundationData where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsData where !config.effectiveFoundationTypesAsJavaTypes,
.foundationURL where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsURL where !config.effectiveFoundationTypesAsJavaTypes:
// Handled as wrapped struct
break

Expand Down Expand Up @@ -536,11 +541,13 @@ extension JNISwift2JavaGenerator {

if let knownType = nominalType.nominalTypeDecl.knownTypeKind {
switch knownType {
case .foundationDate, .essentialsDate:
case .foundationDate where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsDate where !config.effectiveFoundationTypesAsJavaTypes:
// Handled as wrapped struct
break

case .foundationData, .essentialsData:
case .foundationData where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsData where !config.effectiveFoundationTypesAsJavaTypes:
// Handled as wrapped struct
break

Expand Down Expand Up @@ -818,7 +825,12 @@ extension JNISwift2JavaGenerator {
resultName: resultName
)

case .foundationDate, .essentialsDate, .foundationData, .essentialsData, .foundationURL, .essentialsURL:
case .foundationDate where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsDate where !config.effectiveFoundationTypesAsJavaTypes,
.foundationData where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsData where !config.effectiveFoundationTypesAsJavaTypes,
.foundationURL where !config.effectiveFoundationTypesAsJavaTypes,
.essentialsURL where !config.effectiveFoundationTypesAsJavaTypes:
// Handled as wrapped struct
break

Expand Down
19 changes: 18 additions & 1 deletion Sources/JExtractSwiftLib/JavaExtractDecider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public func makeSwiftJavaAnalyzer(config: Configuration) -> SwiftAnalyzer {
config: config,
extractDecider: JavaExtractDecider(
accessLevel: config.effectiveMinimumInputAccessLevelMode,
foundationTypesAsJavaTypes: config.effectiveMode == .jni && config.effectiveFoundationTypesAsJavaTypes,
logLevel: config.logLevel ?? .info
),
)
Expand All @@ -40,10 +41,17 @@ public func makeSwiftJavaAnalyzer(config: Configuration) -> SwiftAnalyzer {
/// operator-overload syntax, so the generator can't render them
public struct JavaExtractDecider: ExtractDecider {
public let accessLevel: AccessLevelMode
/// Whether `Data`, `Date` and `URL` cross as Java types, so no wrapper type is extracted for them
public let foundationTypesAsJavaTypes: Bool
let log: Logger

public init(accessLevel: AccessLevelMode = .default, logLevel: LogLevel = .info) {
public init(
accessLevel: AccessLevelMode = .default,
foundationTypesAsJavaTypes: Bool = false,
logLevel: LogLevel = .info
) {
self.accessLevel = accessLevel
self.foundationTypesAsJavaTypes = foundationTypesAsJavaTypes
self.log = Logger(label: "JavaExtractDecider", logLevel: logLevel)
}

Expand Down Expand Up @@ -91,4 +99,13 @@ public struct JavaExtractDecider: ExtractDecider {
}
return ok
}

public func shouldExtractFoundationType(_ kind: SwiftKnownTypeDeclKind) -> Bool {
switch kind {
case .foundationData, .essentialsData, .foundationDate, .essentialsDate, .foundationURL, .essentialsURL:
!foundationTypesAsJavaTypes
default:
true
}
}
}
16 changes: 16 additions & 0 deletions Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,20 @@ extension JavaType {
.class(package: "java.util", name: "UUID")
}

static var javaNetURI: JavaType {
.class(package: "java.net", name: "URI")
}

static var javaUtilDate: JavaType {
.class(package: "java.util", name: "Date")
}

static var javaUtilLocale: JavaType {
.class(package: "java.util", name: "Locale")
}

static var javaUtilTimeZone: JavaType {
.class(package: "java.util", name: "TimeZone")
}

}
10 changes: 10 additions & 0 deletions Sources/SwiftExtract/ExtractDecider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ public protocol ExtractDecider {
decl: DeclSyntax,
in parent: ExtractedNominalType?
) -> Bool

/// Whether a Foundation type the module uses, such as `Data`, `Date` or `URL`, is extracted as a type of
/// its own. Return `false` when the target language represents values of `kind` with one of its own types.
func shouldExtractFoundationType(_ kind: SwiftKnownTypeDeclKind) -> Bool
}

extension ExtractDecider {
public func shouldExtractFoundationType(_ kind: SwiftKnownTypeDeclKind) -> Bool {
true
}
}

/// Minimal `ExtractDecider` that enforces only the configured access-level
Expand Down
1 change: 1 addition & 0 deletions Sources/SwiftExtract/SwiftAnalyzer+Foundation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ extension SwiftAnalyzer {
]

for group in groups {
let group = group.filter { extractDecider.shouldExtractFoundationType($0.candidates[0]) }
let resolved: [(primary: SwiftNominalTypeDeclaration, source: String, candidates: [SwiftNominalTypeDeclaration])] =
group.compactMap { type in
let candidates = type.candidates.compactMap { self.symbolTable[$0] }
Expand Down
6 changes: 6 additions & 0 deletions Sources/SwiftExtract/SwiftTypes/SwiftKnownModules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ private let foundationEssentialsSourceFile: SourceFileSyntax = """
public init?(string: String)
public var absoluteString: String { get }
}

public struct Locale {}

public struct TimeZone {}

public struct CGFloat {}
"""

private var foundationSourceFile: SourceFileSyntax {
Expand Down
20 changes: 20 additions & 0 deletions Sources/SwiftExtract/SwiftTypes/SwiftKnownTypeDecls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public enum SwiftKnownType: Equatable {
case essentialsUUID
case foundationURL
case essentialsURL
case foundationLocale
case essentialsLocale
case foundationTimeZone
case essentialsTimeZone
case foundationCGFloat

public init?(kind: SwiftKnownTypeDeclKind, genericArguments: [SwiftType]?) {
switch kind {
Expand Down Expand Up @@ -112,6 +117,11 @@ public enum SwiftKnownType: Equatable {
case .essentialsUUID: self = .essentialsUUID
case .foundationURL: self = .foundationURL
case .essentialsURL: self = .essentialsURL
case .foundationLocale: self = .foundationLocale
case .essentialsLocale: self = .essentialsLocale
case .foundationTimeZone: self = .foundationTimeZone
case .essentialsTimeZone: self = .essentialsTimeZone
case .foundationCGFloat: self = .foundationCGFloat
}
}

Expand Down Expand Up @@ -155,6 +165,11 @@ public enum SwiftKnownType: Equatable {
case .essentialsUUID: .essentialsUUID
case .foundationURL: .foundationURL
case .essentialsURL: .essentialsURL
case .foundationLocale: .foundationLocale
case .essentialsLocale: .essentialsLocale
case .foundationTimeZone: .foundationTimeZone
case .essentialsTimeZone: .essentialsTimeZone
case .foundationCGFloat: .foundationCGFloat
}
}
}
Expand Down Expand Up @@ -201,6 +216,11 @@ public enum SwiftKnownTypeDeclKind: String, Hashable {
case essentialsUUID = "FoundationEssentials.UUID"
case foundationURL = "Foundation.URL"
case essentialsURL = "FoundationEssentials.URL"
case foundationLocale = "Foundation.Locale"
case essentialsLocale = "FoundationEssentials.Locale"
case foundationTimeZone = "Foundation.TimeZone"
case essentialsTimeZone = "FoundationEssentials.TimeZone"
case foundationCGFloat = "Foundation.CGFloat"

public var moduleAndName: (module: String, name: String) {
let qualified = self.rawValue
Expand Down
9 changes: 9 additions & 0 deletions Sources/SwiftJavaConfigurationShared/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ public struct Configuration: Codable {
enableJavaCallbacks ?? false
}

/// If true, Foundation values cross as standard Java types instead of Swift-backed wrapper classes generated
/// into each module: `URL` as `java.net.URI`, `Date` as `java.util.Date`, `Data` as `byte[]`, `Locale` as
/// `java.util.Locale` and `TimeZone` as `java.util.TimeZone`. Values are copied when they cross, and a `Date` keeps
/// millisecond precision. `UUID` is always a `java.util.UUID`. Only supported in `jni` mode.
public var foundationTypesAsJavaTypes: Bool?
public var effectiveFoundationTypesAsJavaTypes: Bool {
foundationTypesAsJavaTypes ?? false
}

/// If specified, JExtract will output to this file a list of paths to all generated Java source files.
public var generatedJavaSourcesListFileOutput: String?

Expand Down
Loading