From 5019f5d654c10bc4d7c226d030051bd2dc35a13f Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Fri, 26 Dec 2025 23:59:53 +0100 Subject: [PATCH 1/2] add support for date --- .../Sources/MySwiftLibrary/Date.swift | 28 +++++ .../test/java/com/example/swift/DateTest.java | 36 ++++++ .../FFM/CDeclLowering/CRepresentation.swift | 2 +- ...ift2JavaGenerator+SwiftThunkPrinting.swift | 58 +-------- .../JNI/JNIJavaTypeTranslator.swift | 2 +- ...ISwift2JavaGenerator+JavaTranslation.swift | 35 +++++- ...wift2JavaGenerator+NativeTranslation.swift | 23 +++- ...ift2JavaGenerator+SwiftThunkPrinting.swift | 2 + .../JavaTypes/JavaType+JDK.swift | 4 + .../SwiftTypes/SwiftKnownModules.swift | 2 + .../SwiftTypes/SwiftKnownTypeDecls.swift | 2 + .../SwiftTypes/SwiftSymbolTable.swift | 56 +++++++++ Tests/JExtractSwiftTests/DateTests.swift | 111 ++++++++++++++++++ .../FoundationImportTests.swift | 63 ++++++++++ 14 files changed, 364 insertions(+), 60 deletions(-) create mode 100644 Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Date.swift create mode 100644 Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DateTest.java create mode 100644 Tests/JExtractSwiftTests/DateTests.swift create mode 100644 Tests/JExtractSwiftTests/FoundationImportTests.swift diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Date.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Date.swift new file mode 100644 index 000000000..35f78cbce --- /dev/null +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Date.swift @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif +import SwiftJava + +public func compareDates(date1: Date, date2: Date) -> Bool { + return date1 == date2 +} + +public func dateFromSeconds(_ seconds: Double) -> Date { + return Date(timeIntervalSince1970: seconds) +} diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DateTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DateTest.java new file mode 100644 index 000000000..5a844046e --- /dev/null +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DateTest.java @@ -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 +// +//===----------------------------------------------------------------------===// + +package com.example.swift; + +import org.junit.jupiter.api.Test; + +import java.time.Instant; + +import static org.junit.jupiter.api.Assertions.*; + +public class DateTest { + @Test + void date() { + assertEquals(Instant.ofEpochSecond(1000), MySwiftLibrary.dateFromSeconds(1000.0)); + assertEquals(Instant.ofEpochSecond(1000, 500_000_000), MySwiftLibrary.dateFromSeconds(1000.50)); + assertTrue(MySwiftLibrary.compareDates(Instant.ofEpochSecond(5000), Instant.ofEpochSecond(5000))); + assertFalse(MySwiftLibrary.compareDates(Instant.ofEpochSecond(4999, 500_000_000), Instant.ofEpochSecond(5000))); + assertTrue(MySwiftLibrary.compareDates(MySwiftLibrary.dateFromSeconds(1000.5), Instant.ofEpochSecond(1000, 500_000_000))); + + var date = MySwiftLibrary.dateFromSeconds(50000.5); + assertEquals(50_000, date.getEpochSecond()); + assertEquals(500_000_000, date.getNano()); + } +} \ No newline at end of file diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift index 6b0e33991..3c0a00267 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift @@ -129,7 +129,7 @@ extension SwiftKnownTypeDeclKind { case .void: .void case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .foundationData, .foundationDataProtocol, - .essentialsData, .essentialsDataProtocol, .optional: + .essentialsData, .essentialsDataProtocol, .optional, .foundationDate, .essentialsDate: nil } } diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift index 09de17782..21b3fa679 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift @@ -111,7 +111,7 @@ extension FFMSwift2JavaGenerator { """) - printSwiftThunkImports(&printer) + self.lookupContext.symbolTable.printImportedModules(&printer) for thunk in stt.renderGlobalThunks() { printer.print(thunk) @@ -140,67 +140,13 @@ extension FFMSwift2JavaGenerator { """ ) - printSwiftThunkImports(&printer) + self.lookupContext.symbolTable.printImportedModules(&printer) for thunk in stt.renderThunks(forType: ty) { printer.print("\(thunk)") printer.print("") } } - - 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)") - } - } - - if (importGroups.keys.isEmpty) { - printer.print("import \(module)") - } else { - printer.print("#else") - printer.print("import \(module)") - printer.print("#endif") - } - } - printer.println() - } } struct SwiftThunkTranslator { diff --git a/Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift b/Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift index 491331bce..0f95f4b6f 100644 --- a/Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift @@ -44,7 +44,7 @@ enum JNIJavaTypeTranslator { .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, - .optional, .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol, .array: + .optional, .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol, .array, .foundationDate, .essentialsDate: return nil } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index c6f9713ba..6fa54a242 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -389,6 +389,12 @@ extension JNISwift2JavaGenerator { parameterName: parameterName ) + case .foundationDate, .essentialsDate: + return TranslatedParameter( + parameter: JavaParameter(name: parameterName, type: .javaTimeInstant, annotations: parameterAnnotations), + conversion: .instantToDouble + ) + default: guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config) else { throw JavaTranslationError.unsupportedSwiftType(swiftType) @@ -671,6 +677,14 @@ extension JNISwift2JavaGenerator { elementType: elementType ) + case .foundationDate, .essentialsDate: + return TranslatedResult( + javaType: .javaTimeInstant, + annotations: resultAnnotations, + outParameters: [], + conversion: .doubleToInstant + ) + default: guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config) else { throw JavaTranslationError.unsupportedSwiftType(swiftType) @@ -1109,6 +1123,12 @@ extension JNISwift2JavaGenerator { .method(.constant("Arrays"), function: "stream", arguments: [argument]) } + /// Convert a `java.time.Instant` to a seconds double + case instantToDouble + + /// Convert a double to `java.time.Instant` + case doubleToInstant + /// Returns the conversion string applied to the placeholder. func render(_ printer: inout CodePrinter, _ placeholder: String) -> String { // NOTE: 'printer' is used if the conversion wants to cause side-effects. @@ -1242,13 +1262,26 @@ extension JNISwift2JavaGenerator { case .requireNonNull(let inner, let message): let inner = inner.render(&printer, placeholder) return #"Objects.requireNonNull(\#(inner), "\#(message)")"# + + case .instantToDouble: + return "(\(placeholder).getEpochSecond() + (\(placeholder).getNano() / 1_000_000_000.0))" + + case .doubleToInstant: + printer.print( + """ + double $instant = \(placeholder); + long $seconds = (long) $instant; + long $nanos = (long) (($instant - $seconds) * 1_000_000_000); + """ + ) + return "java.time.Instant.ofEpochSecond($seconds, $nanos)" } } /// Whether the conversion uses SwiftArena. var requiresSwiftArena: Bool { switch self { - case .placeholder, .constant, .isOptionalPresent, .combinedName: + case .placeholder, .constant, .isOptionalPresent, .combinedName, .instantToDouble, .doubleToInstant: return false case .constructSwiftValue, .wrapMemoryAddressUnsafe: diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index d8b0a1d1b..48d00bddb 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -98,6 +98,20 @@ extension JNISwift2JavaGenerator { } return try translateArrayParameter(elementType: elementType, parameterName: parameterName) + case .foundationDate, .essentialsDate: + return NativeParameter( + parameters: [ + JavaParameter(name: parameterName, type: .double) + ], + conversion: .method( + .constant("Date"), + function: "init", + arguments: [ + ("timeIntervalSince1970", .initFromJNI(.placeholder, swiftType: self.knownTypes.double)) + ] + ) + ) + default: guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config), javaType.implementsJavaValue else { @@ -484,6 +498,13 @@ extension JNISwift2JavaGenerator { } return try translateArrayResult(elementType: elementType, resultName: resultName) + case .foundationDate, .essentialsDate: + return NativeResult( + javaType: .double, + conversion: .getJNIValue(.member(.placeholder, member: "timeIntervalSince1970")), + outParameters: [] + ) + default: guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config), javaType.implementsJavaValue else { throw JavaTranslationError.unsupportedSwiftType(swiftResult.type) @@ -1111,4 +1132,4 @@ extension JNISwift2JavaGenerator { } } } -} \ No newline at end of file +} diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 8005485d2..89d1ec07d 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -710,6 +710,8 @@ extension JNISwift2JavaGenerator { """ ) + + self.lookupContext.symbolTable.printImportedModules(&printer) } private func printTypeMetadataAddressThunk(_ printer: inout CodePrinter, _ type: ImportedNominalType) { diff --git a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift index e1aabd7fd..84657b6b5 100644 --- a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift +++ b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift @@ -55,4 +55,8 @@ extension JavaType { static func future(_ T: JavaType) -> JavaType { .class(package: "java.util.concurrent", name: "Future", typeParameters: [T.boxedType]) } + + static var javaTimeInstant: JavaType { + .class(package: "java.time", name: "Instant") + } } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift index 1be8071c4..9a111993f 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift @@ -113,6 +113,8 @@ private let foundationEssentialsSourceFile: SourceFileSyntax = """ public var count: Int { get } public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) -> Void) } + + public struct Date {} """ private var foundationSourceFile: SourceFileSyntax { diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift index 5401f0fab..16240449a 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift @@ -47,6 +47,8 @@ enum SwiftKnownTypeDeclKind: String, Hashable { case essentialsDataProtocol = "FoundationEssentials.DataProtocol" case foundationData = "Foundation.Data" case essentialsData = "FoundationEssentials.Data" + case foundationDate = "Foundation.Date" + case essentialsDate = "FoundationEssentials.Date" var moduleAndName: (module: String, name: String) { let qualified = self.rawValue diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift index ef299bab3..166aeb6f2 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift @@ -147,3 +147,59 @@ extension SwiftSymbolTable { return found } } + +extension SwiftSymbolTable { + func printImportedModules(_ printer: inout CodePrinter) { + let mainSymbolSourceModules = Set( + self.importedModules.values.filter { $0.alternativeModules?.isMainSourceOfSymbols ?? false }.map(\.moduleName) + ) + + for module in self.importedModules.keys.sorted() { + guard module != "Swift" else { + continue + } + + guard let alternativeModules = self.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.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() + } +} diff --git a/Tests/JExtractSwiftTests/DateTests.swift b/Tests/JExtractSwiftTests/DateTests.swift new file mode 100644 index 000000000..2e88a6b3b --- /dev/null +++ b/Tests/JExtractSwiftTests/DateTests.swift @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import JExtractSwiftLib +import SwiftJavaConfigurationShared +import Testing + +struct DateTests { + @Test( + "Import: accept Date", + arguments: [ + ( + JExtractGenerationMode.jni, + /* expected Java chunks */ + [ + """ + public static void acceptDate(java.time.Instant date) { + SwiftModule.$acceptDate((date.getEpochSecond() + (date.getNano() / 1_000_000_000.0))); + } + """, + ], + /* expected Swift chunks */ + [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024acceptDate__D") + func Java_com_example_swift_SwiftModule__00024acceptDate__D(environment: UnsafeMutablePointer!, thisClass: jclass, date: jdouble) { + SwiftModule.acceptDate(date: Date.init(timeIntervalSince1970: Double(fromJNI: date, in: environment))) + } + """ + ], + ) + ] + ) + func func_accept_date(mode: JExtractGenerationMode, expectedJavaChunks: [String], expectedSwiftChunks: [String]) throws { + let text = + """ + import Foundation + + public func acceptDate(date: Date) + """ + + try assertOutput( + input: text, + mode, .java, + detectChunkByInitialLines: 1, + expectedChunks: expectedJavaChunks) + + try assertOutput( + input: text, + mode, .swift, + detectChunkByInitialLines: 1, + expectedChunks: expectedSwiftChunks) + } + + @Test( + "Import: return Date", + arguments: [ + ( + JExtractGenerationMode.jni, + /* expected Java chunks */ + [ + """ + public static java.time.Instant returnDate() { + double $instant = SwiftModule.$returnDate(); + long $seconds = (long) $instant; + long $nanos = (long) (($instant - $seconds) * 1_000_000_000); + return java.time.Instant.ofEpochSecond($seconds, $nanos); + } + """ + ], + /* expected Swift chunks */ + [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024returnDate__") + func Java_com_example_swift_SwiftModule__00024returnDate__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jdouble { + return SwiftModule.returnDate().timeIntervalSince1970.getJNIValue(in: environment) + } + """ + ] + ) + ] + ) + func func_return_Date(mode: JExtractGenerationMode, expectedJavaChunks: [String], expectedSwiftChunks: [String]) throws { + let text = + """ + import Foundation + public func returnDate() -> Date + """ + + try assertOutput( + input: text, + mode, .java, + expectedChunks: expectedJavaChunks) + + try assertOutput( + input: text, + mode, .swift, + expectedChunks: expectedSwiftChunks) + } +} diff --git a/Tests/JExtractSwiftTests/FoundationImportTests.swift b/Tests/JExtractSwiftTests/FoundationImportTests.swift new file mode 100644 index 000000000..f961882f6 --- /dev/null +++ b/Tests/JExtractSwiftTests/FoundationImportTests.swift @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 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 +// +//===----------------------------------------------------------------------===// + +import JExtractSwiftLib +import Testing +import SwiftJavaConfigurationShared + +struct FoundationImportTests { + @Test("Import Foundation", arguments: [JExtractGenerationMode.jni, JExtractGenerationMode.ffm]) + func import_foundation(mode: JExtractGenerationMode) throws { + + try assertOutput( + input: "import Foundation", mode, .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + "import Foundation" + ] + ) + } + + @Test("Import FoundationEssentials", arguments: [JExtractGenerationMode.jni, JExtractGenerationMode.ffm]) + func import_foundationEssentials(mode: JExtractGenerationMode) throws { + + try assertOutput( + input: "import FoundationEssentials", mode, .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + "import FoundationEssentials" + ] + ) + } + + @Test("Import conditional foundation", arguments: [JExtractGenerationMode.jni, JExtractGenerationMode.ffm]) + func import_conditionalFoundation(mode: JExtractGenerationMode) throws { + let ifConfigImport = + """ + #if canImport(FoundationEssentials) + import FoundationEssentials + #else + import Foundation + #endif + """ + + try assertOutput( + input: ifConfigImport, mode, .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + ifConfigImport + ] + ) + } +} From 1e00493dab565bef837223e65264e8c2539e3942 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Sat, 27 Dec 2025 09:51:32 +0100 Subject: [PATCH 2/2] supported features --- .../Documentation.docc/SupportedFeatures.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md b/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md index 370f33925..e469e7c70 100644 --- a/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md +++ b/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md @@ -69,6 +69,7 @@ SwiftJava's `swift-java jextract` tool automates generating Java bindings from S | Existential parameters `f(x: any (A & B)) ` | ❌ | ✅ | | Existential return types `f() -> any Collection ` | ❌ | ❌ | | Foundation Data and DataProtocol: `f(x: any DataProtocol) -> Data` | ✅ | ❌ | +| Foundation Date: `f(date: Date) -> Date` | ❌ | ✅ | | Opaque parameters: `func take(worker: some Builder) -> some Builder` | ❌ | ✅ | | Opaque return types: `func get() -> some Builder` | ❌ | ❌ | | Optional parameters: `func f(i: Int?, class: MyClass?)` | ✅ | ✅ |