From 99d52279d7d02b5854863418eeb3c079bfb73f95 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 3 Apr 2026 17:36:10 +0900 Subject: [PATCH 01/17] Generate foundation types in SwiftKitCore --- .../test/java/com/example/swift/DataTest.java | 1 + Sources/FakeFoundation/Data.swift | 26 ++++ Sources/FakeFoundation/DataProtocol.swift | 21 +++ Sources/FakeFoundation/Date.swift | 27 ++++ ...t2JavaGenerator+JavaBindingsPrinting.swift | 2 +- ...ISwift2JavaGenerator+JavaTranslation.swift | 53 +++++-- .../JavaTypes/JavaType+JDK.swift | 11 ++ .../Swift2JavaTranslator.swift | 2 +- .../generated/Data+SwiftJava.swift | 55 ++++++++ .../generated/DataProtocol+SwiftJava.swift | 19 +++ .../generated/Date+SwiftJava.swift | 53 +++++++ .../generated/SwiftJavaModule+SwiftJava.swift | 12 ++ .../swift/swiftkit/core/generated/Data.java | 133 ++++++++++++++++++ .../swiftkit/core/generated/DataProtocol.java | 14 ++ .../swift/swiftkit/core/generated/Date.java | 131 +++++++++++++++++ .../swiftkit/core/generated/SwiftJava.java | 20 +++ scripts/swiftkit-jni-generate-bindings.sh | 50 +++++++ 17 files changed, 616 insertions(+), 14 deletions(-) create mode 100644 Sources/FakeFoundation/Data.swift create mode 100644 Sources/FakeFoundation/DataProtocol.swift create mode 100644 Sources/FakeFoundation/Date.swift create mode 100644 Sources/SwiftJavaRuntimeSupport/generated/Data+SwiftJava.swift create mode 100644 Sources/SwiftJavaRuntimeSupport/generated/DataProtocol+SwiftJava.swift create mode 100644 Sources/SwiftJavaRuntimeSupport/generated/Date+SwiftJava.swift create mode 100644 Sources/SwiftJavaRuntimeSupport/generated/SwiftJavaModule+SwiftJava.swift create mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Data.java create mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/DataProtocol.java create mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Date.java create mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/SwiftJava.java create mode 100755 scripts/swiftkit-jni-generate-bindings.sh diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DataTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DataTest.java index a6024ca12..d947e7133 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DataTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DataTest.java @@ -16,6 +16,7 @@ import org.junit.jupiter.api.Test; import org.swift.swiftkit.core.SwiftArena; +import org.swift.swiftkit.core.generated.Data; import static org.junit.jupiter.api.Assertions.*; diff --git a/Sources/FakeFoundation/Data.swift b/Sources/FakeFoundation/Data.swift new file mode 100644 index 000000000..5fd160dd0 --- /dev/null +++ b/Sources/FakeFoundation/Data.swift @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 + +public struct Data: DataProtocol { + public init(bytes: UnsafeRawPointer, count: Int) + public init(_ bytes: [UInt8]) + public var count: Int { get } + public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) -> Void) +} diff --git a/Sources/FakeFoundation/DataProtocol.swift b/Sources/FakeFoundation/DataProtocol.swift new file mode 100644 index 000000000..d5a02d423 --- /dev/null +++ b/Sources/FakeFoundation/DataProtocol.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 + +public protocol DataProtocol {} diff --git a/Sources/FakeFoundation/Date.swift b/Sources/FakeFoundation/Date.swift new file mode 100644 index 000000000..ee18b8afc --- /dev/null +++ b/Sources/FakeFoundation/Date.swift @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 + +public struct Date { + /// The interval between the date object and 00:00:00 UTC on 1 January 1970. + public var timeIntervalSince1970: Double { get } + + /// Returns a `Date` initialized relative to 00:00:00 UTC on 1 January 1970 by a given number of seconds. + public init(timeIntervalSince1970: Double) +} diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 8f6d97a67..4581091b8 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -616,7 +616,7 @@ extension JNISwift2JavaGenerator { } generics.append((name, extends)) } - .map { "\($0) extends \($1.compactMap(\.className).joined(separator: " & "))" } + .map { "\($0) extends \($1.compactMap(\.description).joined(separator: " & "))" } .joined(separator: ", ") if !generics.isEmpty { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 69455ba69..8df7930f6 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -505,10 +505,24 @@ extension JNISwift2JavaGenerator { ) case .foundationDate, .essentialsDate: - break // Handled as wrapped struct + return TranslatedParameter( + parameter: JavaParameter( + name: parameterName, + type: .concrete(.swiftkitCoreGeneratedDate), + annotations: parameterAnnotations, + ), + conversion: .valueMemoryAddress(.placeholder), + ) case .foundationData, .essentialsData: - break // Handled as wrapped struct + return TranslatedParameter( + parameter: JavaParameter( + name: parameterName, + type: .concrete(.swiftkitCoreGeneratedData), + annotations: parameterAnnotations, + ), + conversion: .valueMemoryAddress(.placeholder), + ) case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer: return TranslatedParameter( @@ -945,12 +959,22 @@ extension JNISwift2JavaGenerator { ) case .foundationDate, .essentialsDate: - // Handled as wrapped struct - break + let javaType = JavaType.swiftkitCoreGeneratedDate + return TranslatedResult( + javaType: javaType, + annotations: resultAnnotations, + outParameters: [], + conversion: .wrapMemoryAddressUnsafe(.placeholder, javaType), + ) case .foundationData, .essentialsData: - // Handled as wrapped struct - break + let javaType = JavaType.swiftkitCoreGeneratedData + return TranslatedResult( + javaType: javaType, + annotations: resultAnnotations, + outParameters: [], + conversion: .wrapMemoryAddressUnsafe(.placeholder, javaType), + ) case .foundationUUID, .essentialsUUID: return TranslatedResult( @@ -1094,13 +1118,13 @@ extension JNISwift2JavaGenerator { return .swiftSet(elementJavaType) case .foundationDate, .essentialsDate: - return .class(package: nil, name: "Date") + return .swiftkitCoreGeneratedDate case .foundationData, .essentialsData: - return .class(package: nil, name: "Data") + return .swiftkitCoreGeneratedData case .foundationDataProtocol, .essentialsDataProtocol: - return .class(package: nil, name: "DataProtocol") + return .swiftkitCoreGeneratedDataProtocol case .foundationUUID, .essentialsUUID: return .javaUtilUUID @@ -1162,7 +1186,6 @@ extension JNISwift2JavaGenerator { genericParameters: [SwiftGenericParameterDeclaration], genericRequirements: [SwiftGenericRequirement], ) throws -> TranslatedResult { - let arity = elements.count var outParameters: [OutParameter] = [] var elementOutParamNames: [String] = [] var elementConversions: [JavaNativeConversionStep] = [] @@ -1854,16 +1877,22 @@ extension JNISwift2JavaGenerator { case .wrapMemoryAddressUnsafe(let inner, let javaType): let inner = inner.render(&printer, placeholder) - guard case .class(_, let className, let typeParameters) = javaType else { + guard case .class(let package, let className, let typeParameters) = javaType else { fatalError("\(javaType) is not class.") } + let packagePart: String = + if let package { + "\(package)." + } else { + "" + } let genericClause = if !typeParameters.isEmpty { "<\(typeParameters.map(\.description).joined(separator: ", "))>" } else { "" } - return "\(className).\(genericClause)wrapMemoryAddressUnsafe(\(inner), swiftArena)" + return "\(packagePart)\(className).\(genericClause)wrapMemoryAddressUnsafe(\(inner), swiftArena)" case .constructJavaClass(let inner, let javaType): let inner = inner.render(&printer, placeholder) diff --git a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift index 34cb7f458..56a6d65bf 100644 --- a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift +++ b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift @@ -59,4 +59,15 @@ extension JavaType { .class(package: "java.util", name: "UUID") } + static var swiftkitCoreGeneratedDate: JavaType { + .class(package: "org.swift.swiftkit.core.generated", name: "Date") + } + + static var swiftkitCoreGeneratedData: JavaType { + .class(package: "org.swift.swiftkit.core.generated", name: "Data") + } + + static var swiftkitCoreGeneratedDataProtocol: JavaType { + .class(package: "org.swift.swiftkit.core.generated", name: "DataProtocol") + } } diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift index a5d96a1af..d9952724c 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift @@ -111,7 +111,7 @@ extension Swift2JavaTranslator { // Apply any specializations registered after their target types were visited visitor.applyPendingSpecializations() - self.visitFoundationDeclsIfNeeded(with: visitor) +// self.visitFoundationDeclsIfNeeded(with: visitor) } private func visitFoundationDeclsIfNeeded(with visitor: Swift2JavaVisitor) { diff --git a/Sources/SwiftJavaRuntimeSupport/generated/Data+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/generated/Data+SwiftJava.swift new file mode 100644 index 000000000..dbe1a8f05 --- /dev/null +++ b/Sources/SwiftJavaRuntimeSupport/generated/Data+SwiftJava.swift @@ -0,0 +1,55 @@ + +// ==== -------------------------------------------------- +// Thunks for Data + +// Generated by swift-java + +import SwiftJava +import SwiftJavaJNICore +import SwiftJavaRuntimeSupport + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +enum _JNI_Data { +} // printJNICache(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:166 + + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_generated_Data__00024init___3B") +public func Java_org_swift_swiftkit_core_generated_Data__00024init___3B(environment: UnsafeMutablePointer!, thisClass: jclass, bytes: jbyteArray?) -> jlong { + let result$ = UnsafeMutablePointer.allocate(capacity: 1) + result$.initialize(to: Data.init([UInt8](fromJNI: bytes, in: environment))) + let resultBits$ = Int64(Int(bitPattern: result$)) + return resultBits$.getJNILocalRefValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:808 + + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_generated_Data__00024getCount__J") +public func Java_org_swift_swiftkit_core_generated_Data__00024getCount__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jlong { + assert(selfPointer != 0, "selfPointer memory address was null") + let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: environment)) + let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) + guard let selfPointer$ else { + fatalError("selfPointer memory address was null in call to \(#function)!") + } + return Int64(selfPointer$.pointee.count).getJNILocalRefValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:808 + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_generated_Data__00024typeMetadataAddressDowncall__") +public func Java_org_swift_swiftkit_core_generated_Data__00024typeMetadataAddressDowncall__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { + let metadataPointer = unsafeBitCast(Data.self, to: UnsafeRawPointer.self) + return Int64(Int(bitPattern: metadataPointer)).getJNIValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:808 + diff --git a/Sources/SwiftJavaRuntimeSupport/generated/DataProtocol+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/generated/DataProtocol+SwiftJava.swift new file mode 100644 index 000000000..3f7754f5e --- /dev/null +++ b/Sources/SwiftJavaRuntimeSupport/generated/DataProtocol+SwiftJava.swift @@ -0,0 +1,19 @@ + +// ==== -------------------------------------------------- +// Thunks for DataProtocol + +// Generated by swift-java + +import SwiftJava +import SwiftJavaJNICore +import SwiftJavaRuntimeSupport + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +enum _JNI_DataProtocol { +} // printJNICache(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:166 + diff --git a/Sources/SwiftJavaRuntimeSupport/generated/Date+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/generated/Date+SwiftJava.swift new file mode 100644 index 000000000..838d8729e --- /dev/null +++ b/Sources/SwiftJavaRuntimeSupport/generated/Date+SwiftJava.swift @@ -0,0 +1,53 @@ + +// ==== -------------------------------------------------- +// Thunks for Date + +// Generated by swift-java + +import SwiftJava +import SwiftJavaJNICore +import SwiftJavaRuntimeSupport + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +enum _JNI_Date { +} // printJNICache(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:166 + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_generated_Date__00024init__D") +public func Java_org_swift_swiftkit_core_generated_Date__00024init__D(environment: UnsafeMutablePointer!, thisClass: jclass, timeIntervalSince1970: jdouble) -> jlong { + let result$ = UnsafeMutablePointer.allocate(capacity: 1) + result$.initialize(to: Date.init(timeIntervalSince1970: Double(fromJNI: timeIntervalSince1970, in: environment))) + let resultBits$ = Int64(Int(bitPattern: result$)) + return resultBits$.getJNILocalRefValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:808 + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_generated_Date__00024getTimeIntervalSince1970__J") +public func Java_org_swift_swiftkit_core_generated_Date__00024getTimeIntervalSince1970__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jdouble { + assert(selfPointer != 0, "selfPointer memory address was null") + let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: environment)) + let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) + guard let selfPointer$ else { + fatalError("selfPointer memory address was null in call to \(#function)!") + } + return selfPointer$.pointee.timeIntervalSince1970.getJNILocalRefValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:808 + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_generated_Date__00024typeMetadataAddressDowncall__") +public func Java_org_swift_swiftkit_core_generated_Date__00024typeMetadataAddressDowncall__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { + let metadataPointer = unsafeBitCast(Date.self, to: UnsafeRawPointer.self) + return Int64(Int(bitPattern: metadataPointer)).getJNIValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:808 + diff --git a/Sources/SwiftJavaRuntimeSupport/generated/SwiftJavaModule+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/generated/SwiftJavaModule+SwiftJava.swift new file mode 100644 index 000000000..e24e89ffe --- /dev/null +++ b/Sources/SwiftJavaRuntimeSupport/generated/SwiftJavaModule+SwiftJava.swift @@ -0,0 +1,12 @@ +// Generated by swift-java + +import SwiftJava +import SwiftJavaJNICore +import SwiftJavaRuntimeSupport + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Data.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Data.java new file mode 100644 index 000000000..c6b0f3636 --- /dev/null +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Data.java @@ -0,0 +1,133 @@ +// Generated by jextract-swift +// Swift module: SwiftJava + +package org.swift.swiftkit.core.generated; + +import org.swift.swiftkit.core.*; +import org.swift.swiftkit.core.util.*; +import org.swift.swiftkit.core.collections.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import org.swift.swiftkit.core.annotations.*; + +public final class Data implements JNISwiftInstance, DataProtocol { + static final String LIB_NAME = "SwiftJava"; + + @SuppressWarnings("unused") + private static final boolean INITIALIZED_LIBS = initializeLibs(); + static boolean initializeLibs() { + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); + System.loadLibrary(LIB_NAME); + return true; + } + /** + * The designated constructor of any imported Swift types. + * + * @param selfPointer a pointer to the memory containing the value + * @param swiftArena the arena this object belongs to. When the arena goes out of scope, this value is destroyed. + */ + private Data(long selfPointer, SwiftArena swiftArena) { + SwiftObjects.requireNonZero(selfPointer, "selfPointer"); + this.selfPointer = selfPointer; + + // Only register once we have fully initialized the object since this will need the object pointer. + swiftArena.register(this); + } // printConcreteType(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:230 + + /** + * Assume that the passed {@code long} represents a memory address of a {@link Data}. + *

+ * Warnings: + *

    + *
  • No checks are performed about the compatibility of the pointed at memory and the actual Data types.
  • + *
  • This operation does not copy, or retain, the pointed at pointer, so its lifetime must be ensured manually to be valid when wrapping.
  • + *
+ */ + public static Data wrapMemoryAddressUnsafe(long selfPointer, SwiftArena swiftArena) { + return new Data(selfPointer, swiftArena); + } + + public static Data wrapMemoryAddressUnsafe(long selfPointer) { + return new Data(selfPointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); + } + /** Pointer to the "self". */ + private final long selfPointer; + + /** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */ + private final AtomicBoolean $state$destroyed = new AtomicBoolean(false); + + public long $memoryAddress() { + return this.selfPointer; + } + + @Override + public AtomicBoolean $statusDestroyedFlag() { + return $state$destroyed; + } + + + + // ==== -------------------------------------------------- + // Data.init + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public init(_ bytes: [UInt8]) + * } + */ + public static Data init(@Unsigned byte[] bytes, SwiftArena swiftArena) { + return Data.wrapMemoryAddressUnsafe(Data.$init(Objects.requireNonNull(bytes, "bytes must not be null")), swiftArena); + } // printJavaBindingWrapperMethod(_:_:importedFunc:skipMethodBody:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:684 + private static native long $init(byte[] bytes); + + + + // ==== -------------------------------------------------- + // getter:Data.count + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public var count: Int + * } + */ + public long getCount() { + return Data.$getCount(this.$memoryAddress()); + } // printJavaBindingWrapperMethod(_:_:importedFunc:skipMethodBody:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:684 + private static native long $getCount(long selfPointer); + + private static native long $typeMetadataAddressDowncall(); + @Override + public long $typeMetadataAddress() { + return Data.$typeMetadataAddressDowncall(); + } // printTypeMetadataAddressFunction(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:776 + + public String toString() { + return SwiftObjects.toString(this.$memoryAddress(), this.$typeMetadataAddress()); + } + + public String toDebugString() { + return SwiftObjects.toDebugString(this.$memoryAddress(), this.$typeMetadataAddress()); + } + + @Override + public Runnable $createDestroyFunction() { + long self$ = this.$memoryAddress(); + long selfType$ = this.$typeMetadataAddress(); + if (CallTraces.TRACE_DOWNCALLS) { + CallTraces.traceDowncall("Data.$createDestroyFunction", + "this", this, + "self", self$); + } + return new Runnable() { + @Override + public void run() { + if (CallTraces.TRACE_DOWNCALLS) { + CallTraces.traceDowncall("Data.$destroy", "self", self$); + } + SwiftObjects.destroy(self$, selfType$); + } + }; + } // printDestroyFunction(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:790 +} // printNominal(_:_:body:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:402 diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/DataProtocol.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/DataProtocol.java new file mode 100644 index 000000000..8e9c0b90f --- /dev/null +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/DataProtocol.java @@ -0,0 +1,14 @@ +// Generated by jextract-swift +// Swift module: SwiftJava + +package org.swift.swiftkit.core.generated; + +import org.swift.swiftkit.core.*; +import org.swift.swiftkit.core.util.*; +import org.swift.swiftkit.core.collections.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import org.swift.swiftkit.core.annotations.*; + +public interface DataProtocol extends JNISwiftInstance { +} // printProtocol(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:154 diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Date.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Date.java new file mode 100644 index 000000000..1e32774b5 --- /dev/null +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Date.java @@ -0,0 +1,131 @@ +// Generated by jextract-swift +// Swift module: SwiftJava + +package org.swift.swiftkit.core.generated; + +import org.swift.swiftkit.core.*; +import org.swift.swiftkit.core.util.*; +import org.swift.swiftkit.core.collections.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import org.swift.swiftkit.core.annotations.*; + +public final class Date implements JNISwiftInstance { + static final String LIB_NAME = "SwiftJava"; + + @SuppressWarnings("unused") + private static final boolean INITIALIZED_LIBS = initializeLibs(); + static boolean initializeLibs() { + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); + System.loadLibrary(LIB_NAME); + return true; + } + /** + * The designated constructor of any imported Swift types. + * + * @param selfPointer a pointer to the memory containing the value + * @param swiftArena the arena this object belongs to. When the arena goes out of scope, this value is destroyed. + */ + private Date(long selfPointer, SwiftArena swiftArena) { + SwiftObjects.requireNonZero(selfPointer, "selfPointer"); + this.selfPointer = selfPointer; + + // Only register once we have fully initialized the object since this will need the object pointer. + swiftArena.register(this); + } // printConcreteType(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:230 + + /** + * Assume that the passed {@code long} represents a memory address of a {@link Date}. + *

+ * Warnings: + *

    + *
  • No checks are performed about the compatibility of the pointed at memory and the actual Date types.
  • + *
  • This operation does not copy, or retain, the pointed at pointer, so its lifetime must be ensured manually to be valid when wrapping.
  • + *
+ */ + public static Date wrapMemoryAddressUnsafe(long selfPointer, SwiftArena swiftArena) { + return new Date(selfPointer, swiftArena); + } + + public static Date wrapMemoryAddressUnsafe(long selfPointer) { + return new Date(selfPointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); + } + /** Pointer to the "self". */ + private final long selfPointer; + + /** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */ + private final AtomicBoolean $state$destroyed = new AtomicBoolean(false); + + public long $memoryAddress() { + return this.selfPointer; + } + + @Override + public AtomicBoolean $statusDestroyedFlag() { + return $state$destroyed; + } + + + // ==== -------------------------------------------------- + // Date.init + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public init(timeIntervalSince1970: Double) + * } + */ + public static Date init(double timeIntervalSince1970, SwiftArena swiftArena) { + return Date.wrapMemoryAddressUnsafe(Date.$init(timeIntervalSince1970), swiftArena); + } // printJavaBindingWrapperMethod(_:_:importedFunc:skipMethodBody:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:684 + private static native long $init(double timeIntervalSince1970); + + + // ==== -------------------------------------------------- + // getter:Date.timeIntervalSince1970 + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public var timeIntervalSince1970: Double + * } + */ + public double getTimeIntervalSince1970() { + return Date.$getTimeIntervalSince1970(this.$memoryAddress()); + } // printJavaBindingWrapperMethod(_:_:importedFunc:skipMethodBody:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:684 + private static native double $getTimeIntervalSince1970(long selfPointer); + + private static native long $typeMetadataAddressDowncall(); + @Override + public long $typeMetadataAddress() { + return Date.$typeMetadataAddressDowncall(); + } // printTypeMetadataAddressFunction(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:776 + + public String toString() { + return SwiftObjects.toString(this.$memoryAddress(), this.$typeMetadataAddress()); + } + + public String toDebugString() { + return SwiftObjects.toDebugString(this.$memoryAddress(), this.$typeMetadataAddress()); + } + + @Override + public Runnable $createDestroyFunction() { + long self$ = this.$memoryAddress(); + long selfType$ = this.$typeMetadataAddress(); + if (CallTraces.TRACE_DOWNCALLS) { + CallTraces.traceDowncall("Date.$createDestroyFunction", + "this", this, + "self", self$); + } + return new Runnable() { + @Override + public void run() { + if (CallTraces.TRACE_DOWNCALLS) { + CallTraces.traceDowncall("Date.$destroy", "self", self$); + } + SwiftObjects.destroy(self$, selfType$); + } + }; + } // printDestroyFunction(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:790 +} // printNominal(_:_:body:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:402 diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/SwiftJava.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/SwiftJava.java new file mode 100644 index 000000000..0a19e273c --- /dev/null +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/SwiftJava.java @@ -0,0 +1,20 @@ +// Generated by jextract-swift +// Swift module: SwiftJava + +package org.swift.swiftkit.core.generated; + +import org.swift.swiftkit.core.*; +import org.swift.swiftkit.core.util.*; +import org.swift.swiftkit.core.collections.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import org.swift.swiftkit.core.annotations.*; + +public final class SwiftJava { + static final String LIB_NAME = "SwiftJava"; + + static { + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); + System.loadLibrary(LIB_NAME); + } +} // printModuleClass(_:body:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:410 diff --git a/scripts/swiftkit-jni-generate-bindings.sh b/scripts/swiftkit-jni-generate-bindings.sh new file mode 100755 index 000000000..a83e97309 --- /dev/null +++ b/scripts/swiftkit-jni-generate-bindings.sh @@ -0,0 +1,50 @@ +#!/bin/bash +##===----------------------------------------------------------------------===## +## +## This source file is part of the Swift.org open source project +## +## Copyright (c) 2026 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 +## +##===----------------------------------------------------------------------===## + +# Regenerate FFM bindings for types in SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/generated/ +# +# Run from the swift-java repository root: +# ./scripts/swiftkit-ffm-generate-bindings.sh + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" + +JAVA_OUTPUT="${REPO_ROOT}/SwiftKitCore/src/main/java" +JAVA_PACKAGE="org.swift.swiftkit.core.generated" + +# Declare types to generate: SWIFT_MODULE INPUT_SWIFT_DIR OUTPUT_SWIFT_DIR +TYPES=( + "SwiftJava Sources/FakeFoundation Sources/SwiftJavaRuntimeSupport/generated" +) + +for entry in "${TYPES[@]}"; do + read -r MODULE INPUT_SWIFT OUTPUT_SWIFT <<< "$entry" + + echo "==> Generating ${INPUT_SWIFT}..." + + xcrun swift run swift-java jextract \ + --mode jni \ + --swift-module "$MODULE" \ + --input-swift "${REPO_ROOT}/${INPUT_SWIFT}" \ + --output-swift "${REPO_ROOT}/${OUTPUT_SWIFT}" \ + --output-java "$JAVA_OUTPUT" \ + --java-package "$JAVA_PACKAGE" + + echo " Swift thunks: ${OUTPUT_SWIFT}/" + echo " Java output: SwiftKitCore/src/main/java/$(echo "$JAVA_PACKAGE" | tr '.' '/')/" +done + +echo "==> Done." From cbd55ffc1a5bbd31b84b5103908d2c3516925448 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 3 Apr 2026 17:53:02 +0900 Subject: [PATCH 02/17] Add Date and Date wrapper by hand --- .../test/java/com/example/swift/DataTest.java | 2 +- Sources/FakeFoundation/Data.swift | 26 ---- Sources/FakeFoundation/Date.swift | 27 ---- ...t2JavaGenerator+JavaBindingsPrinting.swift | 131 ------------------ ...ISwift2JavaGenerator+JavaTranslation.swift | 14 +- .../JavaTypes/JavaType+JDK.swift | 12 +- .../Swift2JavaTranslator.swift | 33 ----- .../SwiftJavaMacros/ImplementsJavaMacro.swift | 2 +- .../Foundation/Data+JNI.swift | 67 +++++++++ .../Foundation/Date+JNI.swift | 47 +++++++ .../generated/Data+SwiftJava.swift | 55 -------- .../generated/DataProtocol+SwiftJava.swift | 19 --- .../generated/Date+SwiftJava.swift | 53 ------- .../generated/SwiftJavaModule+SwiftJava.swift | 12 -- .../core/{generated => foundation}/Data.java | 115 ++++++++++----- .../core/foundation/DataProtocol.java | 11 +- .../core/{generated => foundation}/Date.java | 109 +++++++++++---- .../swiftkit/core/generated/DataProtocol.java | 14 -- .../swiftkit/core/generated/SwiftJava.java | 20 --- 19 files changed, 298 insertions(+), 471 deletions(-) delete mode 100644 Sources/FakeFoundation/Data.swift delete mode 100644 Sources/FakeFoundation/Date.swift create mode 100644 Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift create mode 100644 Sources/SwiftJavaRuntimeSupport/Foundation/Date+JNI.swift delete mode 100644 Sources/SwiftJavaRuntimeSupport/generated/Data+SwiftJava.swift delete mode 100644 Sources/SwiftJavaRuntimeSupport/generated/DataProtocol+SwiftJava.swift delete mode 100644 Sources/SwiftJavaRuntimeSupport/generated/Date+SwiftJava.swift delete mode 100644 Sources/SwiftJavaRuntimeSupport/generated/SwiftJavaModule+SwiftJava.swift rename SwiftKitCore/src/main/java/org/swift/swiftkit/core/{generated => foundation}/Data.java (63%) rename Sources/FakeFoundation/DataProtocol.swift => SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/DataProtocol.java (76%) rename SwiftKitCore/src/main/java/org/swift/swiftkit/core/{generated => foundation}/Date.java (62%) delete mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/DataProtocol.java delete mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/SwiftJava.java diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DataTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DataTest.java index d947e7133..32bd4a98b 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DataTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DataTest.java @@ -16,7 +16,7 @@ import org.junit.jupiter.api.Test; import org.swift.swiftkit.core.SwiftArena; -import org.swift.swiftkit.core.generated.Data; +import org.swift.swiftkit.core.foundation.Data; import static org.junit.jupiter.api.Assertions.*; diff --git a/Sources/FakeFoundation/Data.swift b/Sources/FakeFoundation/Data.swift deleted file mode 100644 index 5fd160dd0..000000000 --- a/Sources/FakeFoundation/Data.swift +++ /dev/null @@ -1,26 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2026 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 - -public struct Data: DataProtocol { - public init(bytes: UnsafeRawPointer, count: Int) - public init(_ bytes: [UInt8]) - public var count: Int { get } - public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) -> Void) -} diff --git a/Sources/FakeFoundation/Date.swift b/Sources/FakeFoundation/Date.swift deleted file mode 100644 index ee18b8afc..000000000 --- a/Sources/FakeFoundation/Date.swift +++ /dev/null @@ -1,27 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2026 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 - -public struct Date { - /// The interval between the date object and 00:00:00 UTC on 1 January 1970. - public var timeIntervalSince1970: Double { get } - - /// Returns a `Date` initialized relative to 00:00:00 UTC on 1 January 1970 by a given number of seconds. - public init(timeIntervalSince1970: Double) -} diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 4581091b8..eaf34540b 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -314,8 +314,6 @@ extension JNISwift2JavaGenerator { printer.println() } - printSpecificTypeHelpers(&printer, decl) - printTypeMetadataAddressFunction(&printer, decl) printer.println() @@ -336,22 +334,6 @@ extension JNISwift2JavaGenerator { } } - /// Prints helpers for specific types like `Foundation.Date` - private func printSpecificTypeHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { - guard let knownType = decl.swiftNominal.knownTypeKind else { return } - - switch knownType { - case .foundationDate, .essentialsDate: - printFoundationDateHelpers(&printer, decl) - - case .foundationData, .essentialsData: - printFoundationDataHelpers(&printer, decl) - - default: - break - } - } - private func printHeader(_ printer: inout CodePrinter) { printer.print( """ @@ -832,117 +814,4 @@ extension JNISwift2JavaGenerator { } } } - - private func printFoundationDateHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { - printer.print( - """ - /** - * Converts this wrapped date to a Java {@link java.time.Instant}. - *

- * This method constructs the {@code Instant} using the underlying {@code double} value - * representing seconds since the Unix Epoch (January 1, 1970). - *

- * - * @return A {@code java.time.Instant} derived from the floating-point timestamp. - */ - public java.time.Instant toInstant() { - long seconds = (long) this.getTimeIntervalSince1970(); - long nanos = Math.round((this.getTimeIntervalSince1970() - seconds) * 1_000_000_000); - return java.time.Instant.ofEpochSecond(seconds, nanos); - } - """ - ) - printer.println() - printer.print( - """ - /** - * Initializes a Swift {@code Foundation.Date} from a Java {@link java.time.Instant}. - * - *

Warning: Precision Loss

- *

- * The input precision will be degraded. - *

- *

- * Java's {@code Instant} stores time with nanosecond precision (9 decimal places). - * However, this class stores time as a 64-bit floating-point value. - *

- *

- * This leaves enough capacity for microsecond precision (approx. 6 decimal places). - *

- *

- * Consequently, the last ~3 digits of the {@code Instant}'s nanosecond field will be - * truncated or subjected to rounding errors during conversion. - *

- * - * @param instant The source timestamp to convert. - * @return A date derived from the input instant with microsecond precision. - */ - public static Date fromInstant(java.time.Instant instant, SwiftArena swiftArena) { - Objects.requireNonNull(instant, "Instant cannot be null"); - double timeIntervalSince1970 = instant.getEpochSecond() + (instant.getNano() / 1_000_000_000.0); - return Date.init(timeIntervalSince1970, swiftArena); - } - """ - ) - } - - private func printFoundationDataHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { - printer.print( - """ - /** - * Creates a new Swift @{link Data} instance from a byte array. - * - * @param bytes The byte array to copy into the Data - * @param swiftArena The arena for memory management - * @return A new Data instance containing a copy of the bytes - */ - public static Data fromByteArray(byte[] bytes, SwiftArena swiftArena) { - Objects.requireNonNull(bytes, "bytes cannot be null"); - return Data.init(bytes, swiftArena); - } - """ - ) - - printer.print( - """ - /** - * Copies the contents of this Data to a new byte array. - * - * This is a relatively efficient implementation, which avoids native array copies, - * however it will still perform a copy of the data onto the JVM heap, so use this - * only when necessary. - * - *

When utmost performance is necessary, you may want to investigate the FFM mode - * of jextract which is able to map memory more efficiently. - * - * @return A byte array containing a copy of this Data's bytes - */ - public byte[] toByteArray() { - return $toByteArray(this.$memoryAddress()); - } - """ - ) - - printer.print( - """ - private static native byte[] $toByteArray(long selfPointer); - - /** - * Copies the contents of this Data to a new byte array. - * - * @deprecated Prefer using the `toByteArray` method as it is more performant. - * This implementation uses a naive conversion path from native bytes into jbytes - * and then copying them onto the jvm heap. - * - * @return A byte array containing a copy of this Data's bytes - */ - @Deprecated(forRemoval = true) - public byte[] toByteArrayIndirectCopy() { - return $toByteArrayIndirectCopy(this.$memoryAddress()); - } - - private static native byte[] $toByteArrayIndirectCopy(long selfPointer); - """ - ) - } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 8df7930f6..733cbac43 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -508,7 +508,7 @@ extension JNISwift2JavaGenerator { return TranslatedParameter( parameter: JavaParameter( name: parameterName, - type: .concrete(.swiftkitCoreGeneratedDate), + type: .concrete(.swiftkitCoreFoundationDate), annotations: parameterAnnotations, ), conversion: .valueMemoryAddress(.placeholder), @@ -518,7 +518,7 @@ extension JNISwift2JavaGenerator { return TranslatedParameter( parameter: JavaParameter( name: parameterName, - type: .concrete(.swiftkitCoreGeneratedData), + type: .concrete(.swiftkitCoreFoundationData), annotations: parameterAnnotations, ), conversion: .valueMemoryAddress(.placeholder), @@ -959,7 +959,7 @@ extension JNISwift2JavaGenerator { ) case .foundationDate, .essentialsDate: - let javaType = JavaType.swiftkitCoreGeneratedDate + let javaType = JavaType.swiftkitCoreFoundationDate return TranslatedResult( javaType: javaType, annotations: resultAnnotations, @@ -968,7 +968,7 @@ extension JNISwift2JavaGenerator { ) case .foundationData, .essentialsData: - let javaType = JavaType.swiftkitCoreGeneratedData + let javaType = JavaType.swiftkitCoreFoundationData return TranslatedResult( javaType: javaType, annotations: resultAnnotations, @@ -1118,13 +1118,13 @@ extension JNISwift2JavaGenerator { return .swiftSet(elementJavaType) case .foundationDate, .essentialsDate: - return .swiftkitCoreGeneratedDate + return .swiftkitCoreFoundationDate case .foundationData, .essentialsData: - return .swiftkitCoreGeneratedData + return .swiftkitCoreFoundationData case .foundationDataProtocol, .essentialsDataProtocol: - return .swiftkitCoreGeneratedDataProtocol + return .swiftkitCoreFoundationDataProtocol case .foundationUUID, .essentialsUUID: return .javaUtilUUID diff --git a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift index 56a6d65bf..32e01b26f 100644 --- a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift +++ b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift @@ -59,15 +59,15 @@ extension JavaType { .class(package: "java.util", name: "UUID") } - static var swiftkitCoreGeneratedDate: JavaType { - .class(package: "org.swift.swiftkit.core.generated", name: "Date") + static var swiftkitCoreFoundationDate: JavaType { + .class(package: "org.swift.swiftkit.core.foundation", name: "Date") } - static var swiftkitCoreGeneratedData: JavaType { - .class(package: "org.swift.swiftkit.core.generated", name: "Data") + static var swiftkitCoreFoundationData: JavaType { + .class(package: "org.swift.swiftkit.core.foundation", name: "Data") } - static var swiftkitCoreGeneratedDataProtocol: JavaType { - .class(package: "org.swift.swiftkit.core.generated", name: "DataProtocol") + static var swiftkitCoreFoundationDataProtocol: JavaType { + .class(package: "org.swift.swiftkit.core.foundation", name: "DataProtocol") } } diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift index d9952724c..51d100350 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift @@ -110,39 +110,6 @@ extension Swift2JavaTranslator { // Apply any specializations registered after their target types were visited visitor.applyPendingSpecializations() - -// self.visitFoundationDeclsIfNeeded(with: visitor) - } - - private func visitFoundationDeclsIfNeeded(with visitor: Swift2JavaVisitor) { - // 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, - sourceFilePath: "Foundation/FAKE_FOUNDATION_DATA.swift", - ) - visitor.visit( - nominalDecl: dataProtocolDecl.syntax!.asNominal!, - in: nil, - sourceFilePath: "Foundation/FAKE_FOUNDATION_DATAPROTOCOL.swift", - ) - } - } - - // Foundation.Date - if let dateDecl = self.symbolTable[.foundationDate] ?? self.symbolTable[.essentialsDate] { - if self.isUsing(where: { $0 == dateDecl }) { - visitor.visit( - nominalDecl: dateDecl.syntax!.asNominal!, - in: nil, - sourceFilePath: "Foundation/FAKE_FOUNDATION_DATE.swift", - ) - } - } } package func prepareForTranslation() { diff --git a/Sources/SwiftJavaMacros/ImplementsJavaMacro.swift b/Sources/SwiftJavaMacros/ImplementsJavaMacro.swift index 2770a490c..bfcde7b30 100644 --- a/Sources/SwiftJavaMacros/ImplementsJavaMacro.swift +++ b/Sources/SwiftJavaMacros/ImplementsJavaMacro.swift @@ -213,7 +213,7 @@ extension JavaImplementationMacro: PeerMacro { @used #endif @_cdecl(\(literal: cName)) - public func \(context.makeUniqueName(swiftName))(\(raw: cParameters.map{ $0.description }.joined(separator: ", ")))\(raw: cReturnType) { + public func \(context.makeUniqueName("\(swiftTypeName)_\(swiftName)"))(\(raw: cParameters.map{ $0.description }.joined(separator: ", ")))\(raw: cReturnType) { \(body) } """ diff --git a/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift new file mode 100644 index 000000000..aa391639f --- /dev/null +++ b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 SwiftJava +import SwiftJavaJNICore + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +@JavaImplementation("org.swift.swiftkit.core.foundation.Data") +extension Data { + @JavaMethod("$init") + static func _init(environment: UnsafeMutablePointer!, bytes: [UInt8]) -> Int64 { + let result$ = UnsafeMutablePointer.allocate(capacity: 1) + result$.initialize(to: Data(bytes)) + return Int64(Int(bitPattern: result$)) + } + + @JavaMethod("$getCount") + static func _getCount(environment: UnsafeMutablePointer!, selfPointer: Int64) -> Int64 { + let selfPointer$ = UnsafeMutablePointer(bitPattern: Int(selfPointer)) + guard let selfPointer$ else { + fatalError("selfPointer memory address was null in call to \(#function)!") + } + return Int64(selfPointer$.pointee.count) + } + + @JavaMethod("$toByteArray") + static func _toByteArray(environment: UnsafeMutablePointer!, selfPointer: Int64) -> [UInt8] { + let selfPointer$ = UnsafeMutablePointer(bitPattern: Int(selfPointer)) + guard let selfPointer$ else { + fatalError("selfPointer memory address was null in call to \(#function)!") + } + return selfPointer$.pointee.withUnsafeBytes { buffer in + return buffer.getJNIValue(in: environment) + } + } + + @JavaMethod("$toByteArrayIndirectCopy") + static func _toByteArrayIndirectCopy(environment: UnsafeMutablePointer!, selfPointer: Int64) -> [UInt8] { + let selfPointer$ = UnsafeMutablePointer(bitPattern: Int(selfPointer)) + guard let selfPointer$ else { + fatalError("selfPointer memory address was null in call to \(#function)!") + } + return [UInt8](selfPointer$.pointee) + } + + @JavaMethod("$typeMetadataAddressDowncall") + static func _typeMetadataAddressDowncall(environment: UnsafeMutablePointer!) -> Int64 { + let metadataPointer = unsafeBitCast(Data.self, to: UnsafeRawPointer.self) + return Int64(Int(bitPattern: metadataPointer)) + } +} diff --git a/Sources/SwiftJavaRuntimeSupport/Foundation/Date+JNI.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/Date+JNI.swift new file mode 100644 index 000000000..3c27cd7af --- /dev/null +++ b/Sources/SwiftJavaRuntimeSupport/Foundation/Date+JNI.swift @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 SwiftJava +import SwiftJavaJNICore + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +@JavaImplementation("org.swift.swiftkit.core.foundation.Date") +extension Date { + @JavaMethod("$init") + static func _init(environment: UnsafeMutablePointer!, timeIntervalSince1970: Double) -> Int64 { + let result$ = UnsafeMutablePointer.allocate(capacity: 1) + result$.initialize(to: Date(timeIntervalSince1970: timeIntervalSince1970)) + return Int64(Int(bitPattern: result$)) + } + + @JavaMethod("$getTimeIntervalSince1970") + static func _getTimeIntervalSince1970(environment: UnsafeMutablePointer!, selfPointer: Int64) -> Double { + let selfPointer$ = UnsafeMutablePointer(bitPattern: Int(selfPointer)) + guard let selfPointer$ else { + fatalError("selfPointer memory address was null in call to \(#function)!") + } + return selfPointer$.pointee.timeIntervalSince1970 + } + + @JavaMethod("$typeMetadataAddressDowncall") + static func _typeMetadataAddressDowncall(environment: UnsafeMutablePointer!) -> Int64 { + let metadataPointer = unsafeBitCast(Date.self, to: UnsafeRawPointer.self) + return Int64(Int(bitPattern: metadataPointer)) + } +} diff --git a/Sources/SwiftJavaRuntimeSupport/generated/Data+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/generated/Data+SwiftJava.swift deleted file mode 100644 index dbe1a8f05..000000000 --- a/Sources/SwiftJavaRuntimeSupport/generated/Data+SwiftJava.swift +++ /dev/null @@ -1,55 +0,0 @@ - -// ==== -------------------------------------------------- -// Thunks for Data - -// Generated by swift-java - -import SwiftJava -import SwiftJavaJNICore -import SwiftJavaRuntimeSupport - -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - -enum _JNI_Data { -} // printJNICache(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:166 - - -#if compiler(>=6.3) -@used -#endif -@_cdecl("Java_org_swift_swiftkit_core_generated_Data__00024init___3B") -public func Java_org_swift_swiftkit_core_generated_Data__00024init___3B(environment: UnsafeMutablePointer!, thisClass: jclass, bytes: jbyteArray?) -> jlong { - let result$ = UnsafeMutablePointer.allocate(capacity: 1) - result$.initialize(to: Data.init([UInt8](fromJNI: bytes, in: environment))) - let resultBits$ = Int64(Int(bitPattern: result$)) - return resultBits$.getJNILocalRefValue(in: environment) -} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:808 - - -#if compiler(>=6.3) -@used -#endif -@_cdecl("Java_org_swift_swiftkit_core_generated_Data__00024getCount__J") -public func Java_org_swift_swiftkit_core_generated_Data__00024getCount__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jlong { - assert(selfPointer != 0, "selfPointer memory address was null") - let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: environment)) - let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) - guard let selfPointer$ else { - fatalError("selfPointer memory address was null in call to \(#function)!") - } - return Int64(selfPointer$.pointee.count).getJNILocalRefValue(in: environment) -} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:808 - -#if compiler(>=6.3) -@used -#endif -@_cdecl("Java_org_swift_swiftkit_core_generated_Data__00024typeMetadataAddressDowncall__") -public func Java_org_swift_swiftkit_core_generated_Data__00024typeMetadataAddressDowncall__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { - let metadataPointer = unsafeBitCast(Data.self, to: UnsafeRawPointer.self) - return Int64(Int(bitPattern: metadataPointer)).getJNIValue(in: environment) -} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:808 - diff --git a/Sources/SwiftJavaRuntimeSupport/generated/DataProtocol+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/generated/DataProtocol+SwiftJava.swift deleted file mode 100644 index 3f7754f5e..000000000 --- a/Sources/SwiftJavaRuntimeSupport/generated/DataProtocol+SwiftJava.swift +++ /dev/null @@ -1,19 +0,0 @@ - -// ==== -------------------------------------------------- -// Thunks for DataProtocol - -// Generated by swift-java - -import SwiftJava -import SwiftJavaJNICore -import SwiftJavaRuntimeSupport - -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - -enum _JNI_DataProtocol { -} // printJNICache(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:166 - diff --git a/Sources/SwiftJavaRuntimeSupport/generated/Date+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/generated/Date+SwiftJava.swift deleted file mode 100644 index 838d8729e..000000000 --- a/Sources/SwiftJavaRuntimeSupport/generated/Date+SwiftJava.swift +++ /dev/null @@ -1,53 +0,0 @@ - -// ==== -------------------------------------------------- -// Thunks for Date - -// Generated by swift-java - -import SwiftJava -import SwiftJavaJNICore -import SwiftJavaRuntimeSupport - -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - -enum _JNI_Date { -} // printJNICache(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:166 - -#if compiler(>=6.3) -@used -#endif -@_cdecl("Java_org_swift_swiftkit_core_generated_Date__00024init__D") -public func Java_org_swift_swiftkit_core_generated_Date__00024init__D(environment: UnsafeMutablePointer!, thisClass: jclass, timeIntervalSince1970: jdouble) -> jlong { - let result$ = UnsafeMutablePointer.allocate(capacity: 1) - result$.initialize(to: Date.init(timeIntervalSince1970: Double(fromJNI: timeIntervalSince1970, in: environment))) - let resultBits$ = Int64(Int(bitPattern: result$)) - return resultBits$.getJNILocalRefValue(in: environment) -} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:808 - -#if compiler(>=6.3) -@used -#endif -@_cdecl("Java_org_swift_swiftkit_core_generated_Date__00024getTimeIntervalSince1970__J") -public func Java_org_swift_swiftkit_core_generated_Date__00024getTimeIntervalSince1970__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jdouble { - assert(selfPointer != 0, "selfPointer memory address was null") - let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: environment)) - let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) - guard let selfPointer$ else { - fatalError("selfPointer memory address was null in call to \(#function)!") - } - return selfPointer$.pointee.timeIntervalSince1970.getJNILocalRefValue(in: environment) -} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:808 - -#if compiler(>=6.3) -@used -#endif -@_cdecl("Java_org_swift_swiftkit_core_generated_Date__00024typeMetadataAddressDowncall__") -public func Java_org_swift_swiftkit_core_generated_Date__00024typeMetadataAddressDowncall__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { - let metadataPointer = unsafeBitCast(Date.self, to: UnsafeRawPointer.self) - return Int64(Int(bitPattern: metadataPointer)).getJNIValue(in: environment) -} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:808 - diff --git a/Sources/SwiftJavaRuntimeSupport/generated/SwiftJavaModule+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/generated/SwiftJavaModule+SwiftJava.swift deleted file mode 100644 index e24e89ffe..000000000 --- a/Sources/SwiftJavaRuntimeSupport/generated/SwiftJavaModule+SwiftJava.swift +++ /dev/null @@ -1,12 +0,0 @@ -// Generated by swift-java - -import SwiftJava -import SwiftJavaJNICore -import SwiftJavaRuntimeSupport - -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Data.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Data.java similarity index 63% rename from SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Data.java rename to SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Data.java index c6b0f3636..b4f58e69c 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Data.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Data.java @@ -1,23 +1,32 @@ -// Generated by jextract-swift -// Swift module: SwiftJava +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 org.swift.swiftkit.core.generated; +package org.swift.swiftkit.core.foundation; import org.swift.swiftkit.core.*; import org.swift.swiftkit.core.util.*; import org.swift.swiftkit.core.collections.*; -import java.util.*; + +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import org.swift.swiftkit.core.annotations.*; public final class Data implements JNISwiftInstance, DataProtocol { - static final String LIB_NAME = "SwiftJava"; - @SuppressWarnings("unused") private static final boolean INITIALIZED_LIBS = initializeLibs(); static boolean initializeLibs() { System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); - System.loadLibrary(LIB_NAME); return true; } /** @@ -29,11 +38,11 @@ static boolean initializeLibs() { private Data(long selfPointer, SwiftArena swiftArena) { SwiftObjects.requireNonZero(selfPointer, "selfPointer"); this.selfPointer = selfPointer; - + // Only register once we have fully initialized the object since this will need the object pointer. swiftArena.register(this); - } // printConcreteType(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:230 - + } + /** * Assume that the passed {@code long} represents a memory address of a {@link Data}. *

@@ -46,30 +55,30 @@ private Data(long selfPointer, SwiftArena swiftArena) { public static Data wrapMemoryAddressUnsafe(long selfPointer, SwiftArena swiftArena) { return new Data(selfPointer, swiftArena); } - + public static Data wrapMemoryAddressUnsafe(long selfPointer) { return new Data(selfPointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); } /** Pointer to the "self". */ private final long selfPointer; - + /** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */ private final AtomicBoolean $state$destroyed = new AtomicBoolean(false); - + public long $memoryAddress() { return this.selfPointer; } - + @Override public AtomicBoolean $statusDestroyedFlag() { return $state$destroyed; } - - - + + + // ==== -------------------------------------------------- // Data.init - + /** * Downcall to Swift: * {@snippet lang=swift : @@ -78,14 +87,14 @@ public static Data wrapMemoryAddressUnsafe(long selfPointer) { */ public static Data init(@Unsigned byte[] bytes, SwiftArena swiftArena) { return Data.wrapMemoryAddressUnsafe(Data.$init(Objects.requireNonNull(bytes, "bytes must not be null")), swiftArena); - } // printJavaBindingWrapperMethod(_:_:importedFunc:skipMethodBody:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:684 + } private static native long $init(byte[] bytes); - - - + + + // ==== -------------------------------------------------- // getter:Data.count - + /** * Downcall to Swift: * {@snippet lang=swift : @@ -94,23 +103,67 @@ public static Data init(@Unsigned byte[] bytes, SwiftArena swiftArena) { */ public long getCount() { return Data.$getCount(this.$memoryAddress()); - } // printJavaBindingWrapperMethod(_:_:importedFunc:skipMethodBody:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:684 + } private static native long $getCount(long selfPointer); - + + /** + * Creates a new Swift @{link Data} instance from a byte array. + * + * @param bytes The byte array to copy into the Data + * @param swiftArena The arena for memory management + * @return A new Data instance containing a copy of the bytes + */ + public static Data fromByteArray(byte[] bytes, SwiftArena swiftArena) { + Objects.requireNonNull(bytes, "bytes cannot be null"); + return Data.init(bytes, swiftArena); + } + + /** + * Copies the contents of this Data to a new byte array. + * + * This is a relatively efficient implementation, which avoids native array copies, + * however it will still perform a copy of the data onto the JVM heap, so use this + * only when necessary. + * + *

When utmost performance is necessary, you may want to investigate the FFM mode + * of jextract which is able to map memory more efficiently. + * + * @return A byte array containing a copy of this Data's bytes + */ + public byte[] toByteArray() { + return $toByteArray(this.$memoryAddress()); + } + private static native byte[] $toByteArray(long selfPointer); + + /** + * Copies the contents of this Data to a new byte array. + * + * @deprecated Prefer using the `toByteArray` method as it is more performant. + * This implementation uses a naive conversion path from native bytes into jbytes + * and then copying them onto the jvm heap. + * + * @return A byte array containing a copy of this Data's bytes + */ + @Deprecated(forRemoval = true) + public byte[] toByteArrayIndirectCopy() { + return $toByteArrayIndirectCopy(this.$memoryAddress()); + } + private static native byte[] $toByteArrayIndirectCopy(long selfPointer); + private static native long $typeMetadataAddressDowncall(); @Override public long $typeMetadataAddress() { return Data.$typeMetadataAddressDowncall(); - } // printTypeMetadataAddressFunction(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:776 - + } + public String toString() { return SwiftObjects.toString(this.$memoryAddress(), this.$typeMetadataAddress()); } - + public String toDebugString() { return SwiftObjects.toDebugString(this.$memoryAddress(), this.$typeMetadataAddress()); } - + @Override public Runnable $createDestroyFunction() { long self$ = this.$memoryAddress(); @@ -129,5 +182,5 @@ public void run() { SwiftObjects.destroy(self$, selfType$); } }; - } // printDestroyFunction(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:790 -} // printNominal(_:_:body:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:402 + } +} diff --git a/Sources/FakeFoundation/DataProtocol.swift b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/DataProtocol.java similarity index 76% rename from Sources/FakeFoundation/DataProtocol.swift rename to SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/DataProtocol.java index d5a02d423..f3240bc0d 100644 --- a/Sources/FakeFoundation/DataProtocol.swift +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/DataProtocol.java @@ -12,10 +12,9 @@ // //===----------------------------------------------------------------------===// -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif +package org.swift.swiftkit.core.foundation; -public protocol DataProtocol {} +import org.swift.swiftkit.core.JNISwiftInstance; + +public interface DataProtocol extends JNISwiftInstance { +} diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Date.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Date.java similarity index 62% rename from SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Date.java rename to SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Date.java index 1e32774b5..157fe4f81 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/Date.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Date.java @@ -1,23 +1,31 @@ -// Generated by jextract-swift -// Swift module: SwiftJava +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 org.swift.swiftkit.core.generated; +package org.swift.swiftkit.core.foundation; import org.swift.swiftkit.core.*; import org.swift.swiftkit.core.util.*; import org.swift.swiftkit.core.collections.*; -import java.util.*; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import org.swift.swiftkit.core.annotations.*; public final class Date implements JNISwiftInstance { - static final String LIB_NAME = "SwiftJava"; - @SuppressWarnings("unused") private static final boolean INITIALIZED_LIBS = initializeLibs(); static boolean initializeLibs() { System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); - System.loadLibrary(LIB_NAME); return true; } /** @@ -29,11 +37,11 @@ static boolean initializeLibs() { private Date(long selfPointer, SwiftArena swiftArena) { SwiftObjects.requireNonZero(selfPointer, "selfPointer"); this.selfPointer = selfPointer; - + // Only register once we have fully initialized the object since this will need the object pointer. swiftArena.register(this); - } // printConcreteType(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:230 - + } + /** * Assume that the passed {@code long} represents a memory address of a {@link Date}. *

@@ -46,29 +54,29 @@ private Date(long selfPointer, SwiftArena swiftArena) { public static Date wrapMemoryAddressUnsafe(long selfPointer, SwiftArena swiftArena) { return new Date(selfPointer, swiftArena); } - + public static Date wrapMemoryAddressUnsafe(long selfPointer) { return new Date(selfPointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); } /** Pointer to the "self". */ private final long selfPointer; - + /** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */ private final AtomicBoolean $state$destroyed = new AtomicBoolean(false); - + public long $memoryAddress() { return this.selfPointer; } - + @Override public AtomicBoolean $statusDestroyedFlag() { return $state$destroyed; } - - + + // ==== -------------------------------------------------- // Date.init - + /** * Downcall to Swift: * {@snippet lang=swift : @@ -77,13 +85,13 @@ public static Date wrapMemoryAddressUnsafe(long selfPointer) { */ public static Date init(double timeIntervalSince1970, SwiftArena swiftArena) { return Date.wrapMemoryAddressUnsafe(Date.$init(timeIntervalSince1970), swiftArena); - } // printJavaBindingWrapperMethod(_:_:importedFunc:skipMethodBody:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:684 + } private static native long $init(double timeIntervalSince1970); - - + + // ==== -------------------------------------------------- // getter:Date.timeIntervalSince1970 - + /** * Downcall to Swift: * {@snippet lang=swift : @@ -92,23 +100,66 @@ public static Date init(double timeIntervalSince1970, SwiftArena swiftArena) { */ public double getTimeIntervalSince1970() { return Date.$getTimeIntervalSince1970(this.$memoryAddress()); - } // printJavaBindingWrapperMethod(_:_:importedFunc:skipMethodBody:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:684 + } private static native double $getTimeIntervalSince1970(long selfPointer); - + + /** + * Converts this wrapped date to a Java {@link java.time.Instant}. + *

+ * This method constructs the {@code Instant} using the underlying {@code double} value + * representing seconds since the Unix Epoch (January 1, 1970). + *

+ * + * @return A {@code java.time.Instant} derived from the floating-point timestamp. + */ + public java.time.Instant toInstant() { + long seconds = (long) this.getTimeIntervalSince1970(); + long nanos = Math.round((this.getTimeIntervalSince1970() - seconds) * 1_000_000_000); + return java.time.Instant.ofEpochSecond(seconds, nanos); + } + + /** + * Initializes a Swift {@code Foundation.Date} from a Java {@link java.time.Instant}. + * + *

Warning: Precision Loss

+ *

+ * The input precision will be degraded. + *

+ *

+ * Java's {@code Instant} stores time with nanosecond precision (9 decimal places). + * However, this class stores time as a 64-bit floating-point value. + *

+ *

+ * This leaves enough capacity for microsecond precision (approx. 6 decimal places). + *

+ *

+ * Consequently, the last ~3 digits of the {@code Instant}'s nanosecond field will be + * truncated or subjected to rounding errors during conversion. + *

+ * + * @param instant The source timestamp to convert. + * @return A date derived from the input instant with microsecond precision. + */ + public static Date fromInstant(java.time.Instant instant, SwiftArena swiftArena) { + Objects.requireNonNull(instant, "Instant cannot be null"); + double timeIntervalSince1970 = instant.getEpochSecond() + (instant.getNano() / 1_000_000_000.0); + return Date.init(timeIntervalSince1970, swiftArena); + } + private static native long $typeMetadataAddressDowncall(); @Override public long $typeMetadataAddress() { return Date.$typeMetadataAddressDowncall(); - } // printTypeMetadataAddressFunction(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:776 - + } + public String toString() { return SwiftObjects.toString(this.$memoryAddress(), this.$typeMetadataAddress()); } - + public String toDebugString() { return SwiftObjects.toDebugString(this.$memoryAddress(), this.$typeMetadataAddress()); } - + @Override public Runnable $createDestroyFunction() { long self$ = this.$memoryAddress(); @@ -127,5 +178,5 @@ public void run() { SwiftObjects.destroy(self$, selfType$); } }; - } // printDestroyFunction(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:790 -} // printNominal(_:_:body:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:402 + } +} diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/DataProtocol.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/DataProtocol.java deleted file mode 100644 index 8e9c0b90f..000000000 --- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/DataProtocol.java +++ /dev/null @@ -1,14 +0,0 @@ -// Generated by jextract-swift -// Swift module: SwiftJava - -package org.swift.swiftkit.core.generated; - -import org.swift.swiftkit.core.*; -import org.swift.swiftkit.core.util.*; -import org.swift.swiftkit.core.collections.*; -import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; -import org.swift.swiftkit.core.annotations.*; - -public interface DataProtocol extends JNISwiftInstance { -} // printProtocol(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:154 diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/SwiftJava.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/SwiftJava.java deleted file mode 100644 index 0a19e273c..000000000 --- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/generated/SwiftJava.java +++ /dev/null @@ -1,20 +0,0 @@ -// Generated by jextract-swift -// Swift module: SwiftJava - -package org.swift.swiftkit.core.generated; - -import org.swift.swiftkit.core.*; -import org.swift.swiftkit.core.util.*; -import org.swift.swiftkit.core.collections.*; -import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; -import org.swift.swiftkit.core.annotations.*; - -public final class SwiftJava { - static final String LIB_NAME = "SwiftJava"; - - static { - System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); - System.loadLibrary(LIB_NAME); - } -} // printModuleClass(_:body:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:410 From ce6ec4cde3af1f209ea0c964457384a6558f739a Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 6 Apr 2026 12:56:23 +0900 Subject: [PATCH 03/17] implement without macro for jbytearray returning functions --- .../test/java/com/example/swift/DateTest.java | 3 +- .../java/com/example/swift/OptionalsTest.java | 5 +- ...ift2JavaGenerator+SwiftThunkPrinting.swift | 62 ------------------- .../Foundation/Data+JNI.swift | 55 ++++++++++------ 4 files changed, 40 insertions(+), 85 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DateTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DateTest.java index 9b13f37ec..36da11d9d 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DateTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/DateTest.java @@ -16,6 +16,7 @@ import org.junit.jupiter.api.Test; import org.swift.swiftkit.core.SwiftArena; +import org.swift.swiftkit.core.foundation.Date; import java.time.Instant; import java.time.temporal.ChronoUnit; @@ -59,4 +60,4 @@ void date_timeIntervalSince1970() { assertEquals(1000, date.getTimeIntervalSince1970()); } } -} \ No newline at end of file +} diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/OptionalsTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/OptionalsTest.java index 771cffc33..941d2a545 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/OptionalsTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/OptionalsTest.java @@ -14,9 +14,10 @@ package com.example.swift; -import com.example.swift.MySwiftLibrary; import org.junit.jupiter.api.Test; import org.swift.swiftkit.core.SwiftArena; +import org.swift.swiftkit.core.foundation.Data; +import org.swift.swiftkit.core.foundation.Date; import java.time.Instant; import java.util.Optional; @@ -143,4 +144,4 @@ void optionalThrows() { assertEquals("swiftError", exception.getMessage()); } } -} \ No newline at end of file +} diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 544b1b5ac..24c106c01 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -347,7 +347,6 @@ extension JNISwift2JavaGenerator { printer.println() } - printSpecificTypeThunks(&printer, type) printTypeMetadataAddressThunk(&printer, type) printer.println() } @@ -856,67 +855,6 @@ extension JNISwift2JavaGenerator { } } - /// Prints thunks for specific known types like Foundation.Date, Foundation.Data - private func printSpecificTypeThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) { - guard let knownType = type.swiftNominal.knownTypeKind else { return } - - switch knownType { - case .foundationData, .essentialsData: - printFoundationDataThunks(&printer, type) - printer.println() - - default: - break - } - } - - /// Prints Swift thunks for Foundation.Data helper methods - private func printFoundationDataThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) { - let selfPointerParam = JavaParameter(name: "selfPointer", type: .long) - let parentName = type.qualifiedName - - // Rebind the memory instead of converting, and set the memory directly using 'jniSetArrayRegion' from the buffer - printCDecl( - &printer, - javaMethodName: "$toByteArray", - parentName: type.effectiveJavaName, - parameters: [ - selfPointerParam - ], - resultType: .array(.byte), - ) { printer in - let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam) - - printer.print( - """ - return \(selfVar).pointee.withUnsafeBytes { buffer in - return buffer.getJNIValue(in: environment) - } - """ - ) - } - - // Legacy API, also to compare with as a baseline, we could remove it - printCDecl( - &printer, - javaMethodName: "$toByteArrayIndirectCopy", - parentName: type.effectiveJavaName, - parameters: [ - selfPointerParam - ], - resultType: .array(.byte), - ) { printer in - let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam) - - printer.print( - """ - // This is a double copy, we need to initialize the array and then copy into a JVM array in getJNIValue - return [UInt8](\(selfVar).pointee).getJNIValue(in: environment) - """ - ) - } - } - private func printFunctionOpenerCall(_ printer: inout CodePrinter, _ decl: ImportedFunc) { guard let translatedDecl = self.translatedDecl(for: decl) else { fatalError("Cannot print function opener for a function that can't be translated: \(decl)") diff --git a/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift index aa391639f..8110af8f3 100644 --- a/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift +++ b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift @@ -39,29 +39,44 @@ extension Data { return Int64(selfPointer$.pointee.count) } - @JavaMethod("$toByteArray") - static func _toByteArray(environment: UnsafeMutablePointer!, selfPointer: Int64) -> [UInt8] { - let selfPointer$ = UnsafeMutablePointer(bitPattern: Int(selfPointer)) - guard let selfPointer$ else { - fatalError("selfPointer memory address was null in call to \(#function)!") - } - return selfPointer$.pointee.withUnsafeBytes { buffer in - return buffer.getJNIValue(in: environment) - } - } - - @JavaMethod("$toByteArrayIndirectCopy") - static func _toByteArrayIndirectCopy(environment: UnsafeMutablePointer!, selfPointer: Int64) -> [UInt8] { - let selfPointer$ = UnsafeMutablePointer(bitPattern: Int(selfPointer)) - guard let selfPointer$ else { - fatalError("selfPointer memory address was null in call to \(#function)!") - } - return [UInt8](selfPointer$.pointee) - } - @JavaMethod("$typeMetadataAddressDowncall") static func _typeMetadataAddressDowncall(environment: UnsafeMutablePointer!) -> Int64 { let metadataPointer = unsafeBitCast(Data.self, to: UnsafeRawPointer.self) return Int64(Int(bitPattern: metadataPointer)) } } + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_foundation_Data__00024toByteArray__J") +public func Java_org_swift_swiftkit_core_foundation_Data__00024toByteArray__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jbyteArray? { + guard let env$ = environment else { + fatalError("Missing JNIEnv in downcall to \(#function)") + } + assert(selfPointer != 0, "selfPointer memory address was null") + let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: env$)) + guard let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) else { + fatalError("selfPointer memory address was null in call to \(#function)!") + } + return selfPointer$.pointee.withUnsafeBytes { buffer in + return buffer.getJNIValue(in: environment) + } +} + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_foundation_Data__00024toByteArrayIndirectCopy__J") +public func Java_org_swift_swiftkit_core_foundation_Data__00024toByteArrayIndirectCopy__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jbyteArray? { + guard let env$ = environment else { + fatalError("Missing JNIEnv in downcall to \(#function)") + } + assert(selfPointer != 0, "selfPointer memory address was null") + let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: env$)) + guard let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) else { + fatalError("selfPointer memory address was null in call to \(#function)!") + } + // This is a double copy, we need to initialize the array and then copy into a JVM array in getJNIValue + return [UInt8](selfPointer$.pointee).getJNIValue(in: environment) +} From cc176f8508465bf559f022b3f1fb1875ce5bd5ab Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 6 Apr 2026 15:41:46 +0900 Subject: [PATCH 04/17] Remove generate script --- scripts/swiftkit-jni-generate-bindings.sh | 50 ----------------------- 1 file changed, 50 deletions(-) delete mode 100755 scripts/swiftkit-jni-generate-bindings.sh diff --git a/scripts/swiftkit-jni-generate-bindings.sh b/scripts/swiftkit-jni-generate-bindings.sh deleted file mode 100755 index a83e97309..000000000 --- a/scripts/swiftkit-jni-generate-bindings.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the Swift.org open source project -## -## Copyright (c) 2026 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 -## -##===----------------------------------------------------------------------===## - -# Regenerate FFM bindings for types in SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/generated/ -# -# Run from the swift-java repository root: -# ./scripts/swiftkit-ffm-generate-bindings.sh - -set -euo pipefail - -REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" - -JAVA_OUTPUT="${REPO_ROOT}/SwiftKitCore/src/main/java" -JAVA_PACKAGE="org.swift.swiftkit.core.generated" - -# Declare types to generate: SWIFT_MODULE INPUT_SWIFT_DIR OUTPUT_SWIFT_DIR -TYPES=( - "SwiftJava Sources/FakeFoundation Sources/SwiftJavaRuntimeSupport/generated" -) - -for entry in "${TYPES[@]}"; do - read -r MODULE INPUT_SWIFT OUTPUT_SWIFT <<< "$entry" - - echo "==> Generating ${INPUT_SWIFT}..." - - xcrun swift run swift-java jextract \ - --mode jni \ - --swift-module "$MODULE" \ - --input-swift "${REPO_ROOT}/${INPUT_SWIFT}" \ - --output-swift "${REPO_ROOT}/${OUTPUT_SWIFT}" \ - --output-java "$JAVA_OUTPUT" \ - --java-package "$JAVA_PACKAGE" - - echo " Swift thunks: ${OUTPUT_SWIFT}/" - echo " Java output: SwiftKitCore/src/main/java/$(echo "$JAVA_PACKAGE" | tr '.' '/')/" -done - -echo "==> Done." From ae82915f9e1610e366557fceb0efb3eff23cf92e Mon Sep 17 00:00:00 2001 From: Iceman Date: Tue, 7 Apr 2026 17:38:11 +0900 Subject: [PATCH 05/17] Fix ffm java generated code --- .../com/example/swift/HelloJava2Swift.java | 3 +- ...FMSwift2JavaGenerator+FoundationData.swift | 166 ------- ...t2JavaGenerator+JavaBindingsPrinting.swift | 14 +- ...MSwift2JavaGenerator+JavaTranslation.swift | 48 ++- .../FFM/FFMSwift2JavaGenerator.swift | 17 - .../JavaTypes/JavaType+JDK.swift | 8 + .../swift/swiftkit/ffm/foundation/Data.java | 406 ++++++++++++++++++ .../swiftkit/ffm/foundation/DataProtocol.java | 75 ++++ 8 files changed, 543 insertions(+), 194 deletions(-) delete mode 100644 Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+FoundationData.swift create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/Data.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/DataProtocol.java diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftJavaExtractFFMSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java index 62c29482a..53fca07b6 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -22,6 +22,7 @@ import org.swift.swiftkit.core.SwiftLibraries; import org.swift.swiftkit.ffm.AllocatingSwiftArena; import org.swift.swiftkit.ffm.SwiftRuntime; +import org.swift.swiftkit.ffm.foundation.Data; import java.util.Optional; import java.util.OptionalLong; @@ -85,7 +86,7 @@ static void examples() { var origBytes = arena.allocateFrom("foobar"); var origDat = Data.init(origBytes, origBytes.byteSize(), arena); CallTraces.trace("origDat.count = " + origDat.getCount()); - + var retDat = MySwiftLibrary.globalReceiveReturnData(origDat, arena); retDat.withUnsafeBytes((retBytes) -> { var str = retBytes.getString(0); diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+FoundationData.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+FoundationData.swift deleted file mode 100644 index 7dd054f22..000000000 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+FoundationData.swift +++ /dev/null @@ -1,166 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2026 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 CodePrinting -import SwiftJavaConfigurationShared -import SwiftJavaJNICore -import SwiftSyntax -import SwiftSyntaxBuilder - -import struct Foundation.URL - -extension FFMSwift2JavaGenerator { - - /// Print Java helper methods for Foundation.Data type - package func printFoundationDataHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { - let typeName = decl.swiftNominal.name - let thunkNameCopyBytes = "swiftjava_\(swiftModuleName)_\(typeName)_copyBytes__" - - printer.printSeparator("\(typeName) helper methods") - - // This is primarily here for API parity with the JNI version and easier discovery - printer.print( - """ - /** - * Creates a new Swift {@link \(typeName)} instance from a byte array. - * - * @param bytes The byte array to copy into the \(typeName) - * @param arena The arena for memory management - * @return A new \(typeName) instance containing a copy of the bytes - */ - public static \(typeName) fromByteArray(byte[] bytes, AllocatingSwiftArena arena) { - Objects.requireNonNull(bytes, "bytes cannot be null"); - return \(typeName).init(bytes, arena); - } - """ - ) - - // TODO: Implement a fromByteBuffer as well - - // Print the descriptor class for copyBytes native call using the shared helper - let copyBytesCFunc = CFunction( - resultType: .void, - name: thunkNameCopyBytes, - parameters: [ - CParameter(name: "self", type: .qualified(const: true, volatile: false, type: .pointer(.void))), - CParameter(name: "destination", type: .pointer(.void)), - CParameter(name: "count", type: .integral(.ptrdiff_t)), - ], - isVariadic: false - ) - printJavaBindingDescriptorClass(&printer, copyBytesCFunc) - - printer.print( - """ - /** - * Copies the contents of this \(typeName) to a new {@link MemorySegment}. - * - * This is the most efficient way to access \(typeName) bytes from Java when you don't - * need a {@code byte[]}. The returned segment is valid for the lifetime of the arena. - * - *

Copy count: 1 (Swift Data -> MemorySegment) - * - * @param arena The arena to allocate the segment in - * @return A MemorySegment containing a copy of this \(typeName)'s bytes - */ - public MemorySegment toMemorySegment(AllocatingSwiftArena arena) { - $ensureAlive(); - long count = getCount(); - if (count == 0) return MemorySegment.NULL; - MemorySegment segment = arena.allocate(count); - \(thunkNameCopyBytes).call(this.$memorySegment(), segment, count); - return segment; - } - """ - ) - - printer.print( - """ - /** - * Copies the contents of this \(typeName) to a new {@link ByteBuffer}. - * - * The returned {@link java.nio.ByteBuffer} is a view over native memory and is valid for the - * lifetime of the arena. This avoids an additional copy to the Java heap. - * - *

Copy count: 1 (Swift Data -> native memory (managed by passed arena), then zero-copy view) - * - * @param arena The arena to allocate the underlying memory in - * @return A ByteBuffer view of the copied bytes - */ - public java.nio.ByteBuffer toByteBuffer(AllocatingSwiftArena arena) { - $ensureAlive(); - long count = getCount(); - if (count == 0) return java.nio.ByteBuffer.allocate(0); - MemorySegment segment = arena.allocate(count); - \(thunkNameCopyBytes).call(this.$memorySegment(), segment, count); - return segment.asByteBuffer(); - } - """ - ) - - printer.print( - """ - /** - * Copies the contents of this \(typeName) to a new byte array. - * The lifetime of the array is independent of the arena, the arena is just used for an intermediary copy. - * - *

Copy count: 2 (Swift Data -> MemorySegment -> byte[]) - * - *

For better performance when you can work with {@link MemorySegment} or - * {@link java.nio.ByteBuffer}, prefer {@link #toMemorySegment} or {@link #toByteBuffer}. - * - * @param arena The arena to use for temporary native memory allocation - * @return A byte array containing a copy of this \(typeName)'s bytes - */ - public byte[] toByteArray(AllocatingSwiftArena arena) { - $ensureAlive(); - long count = getCount(); - if (count == 0) return new byte[0]; - MemorySegment segment = arena.allocate(count); - \(thunkNameCopyBytes).call(this.$memorySegment(), segment, count); - return segment.toArray(ValueLayout.JAVA_BYTE); - } - """ - ) - - printer.print( - """ - /** - * Copies the contents of this \(typeName) to a new byte array. - * The lifetime of the array is independent of the arena, the arena is just used for an intermediary copy. - * - * This is a convenience method that creates a temporary arena for the copy. - * For repeated calls, prefer {@link #toByteArray(AllocatingSwiftArena)} to reuse an arena. - * - *

Copy count: 2 (Swift Data -> MemorySegment -> byte[]) - * - *

For better performance when you can work with {@link MemorySegment} or - * {@link java.nio.ByteBuffer}, prefer {@link #toMemorySegment} or {@link #toByteBuffer}. - * - * @return A byte array containing a copy of this \(typeName)'s bytes - */ - public byte[] toByteArray() { - $ensureAlive(); - long count = getCount(); - if (count == 0) return new byte[0]; - try (var arena = Arena.ofConfined()) { - MemorySegment segment = arena.allocate(count); - \(thunkNameCopyBytes).call(this.$memorySegment(), segment, count); - return segment.toArray(ValueLayout.JAVA_BYTE); - } - } - """ - ) - } -} diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift index cb7eac727..48cf4c331 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift @@ -456,6 +456,8 @@ extension FFMSwift2JavaGenerator { let arena = if let className = type.className, analysis.importedTypes[className] != nil + || type == .swiftkitFFMFoundationData + || type == .swiftkitFFMFoundationDataProtocol { // Use passed-in 'SwiftArena' for 'SwiftValue'. "swiftArena" @@ -619,8 +621,14 @@ extension FFMSwift2JavaGenerator { func renderMemoryLayoutValue(for javaType: JavaType) -> String { if let layout = ForeignValueLayout(javaType: javaType) { return layout.description - } else if case .class(package: _, name: let customClass, _) = javaType { - return ForeignValueLayout(customType: customClass).description + } else if case .class(let package, name: let customClass, _) = javaType { + let type = + if let package { + "\(package).\(customClass)" + } else { + customClass + } + return ForeignValueLayout(customType: type).description } else { fatalError("renderMemoryLayoutValue not supported for \(javaType)") } @@ -776,7 +784,7 @@ extension FFMSwift2JavaGenerator.JavaConversionStep { case .wrapMemoryAddressUnsafe(let inner, let javaType): let inner = inner.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall) - return "\(javaType.className!).wrapMemoryAddressUnsafe(\(inner), swiftArena)" + return "\(javaType).wrapMemoryAddressUnsafe(\(inner), swiftArena)" case .construct(let inner, let javaType): let inner = inner.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index c750fdc0a..decc8a547 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -464,7 +464,15 @@ extension FFMSwift2JavaGenerator { ) case .foundationData, .essentialsData: - break + return TranslatedParameter( + javaParameters: [ + JavaParameter( + name: parameterName, + type: .swiftkitFFMFoundationData + ) + ], + conversion: .swiftValueSelfSegment(.placeholder) + ) case .swiftJavaError: // SwiftJavaError is a class — treat as arbitrary nominal type below @@ -635,10 +643,28 @@ extension FFMSwift2JavaGenerator { case .nominal(let nominal): if let knownType = nominal.nominalTypeDecl.knownTypeKind { switch knownType { - case .foundationData, .foundationDataProtocol: - break - case .essentialsData, .essentialsDataProtocol: - break + case .foundationData, .essentialsData: + return TranslatedParameter( + javaParameters: [ + JavaParameter(name: parameterName, type: .class( + package: nil, + name: "Optional", + typeParameters: [.swiftkitFFMFoundationData] + )) + ], + conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false) + ) + case .foundationDataProtocol, .essentialsDataProtocol: + return TranslatedParameter( + javaParameters: [ + JavaParameter(name: parameterName, type: .class( + package: nil, + name: "Optional", + typeParameters: [.swiftkitFFMFoundationDataProtocol] + )) + ], + conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false) + ) default: throw JavaTranslationError.unhandledType(known: .optional(swiftType)) } @@ -647,7 +673,7 @@ extension FFMSwift2JavaGenerator { let translatedTy = try self.translate(swiftType: swiftType) return TranslatedParameter( javaParameters: [ - JavaParameter(name: parameterName, type: JavaType(className: "Optional<\(translatedTy.description)>")) + JavaParameter(name: parameterName, type: .class(package: nil, name: "Optional", typeParameters: [translatedTy])) ], conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false) ) @@ -750,7 +776,15 @@ extension FFMSwift2JavaGenerator { ) case .foundationData, .essentialsData: - break // Implemented as wrapper + let javaType: JavaType = .swiftkitFFMFoundationData + return TranslatedResult( + javaResultType: javaType, + annotations: resultAnnotations, + outParameters: [ + JavaParameter(name: "", type: javaType) + ], + conversion: .wrapMemoryAddressUnsafe(.placeholder, javaType) + ) case .unsafePointer, .unsafeMutablePointer: // FIXME: Implement diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 6f5f1b8c6..bf4ef1e80 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -328,9 +328,6 @@ extension FFMSwift2JavaGenerator { printFunctionDowncallMethods(&printer, funcDecl) } - // Special helper methods for known types (e.g. Data) - printSpecificTypeHelpers(&printer, decl) - if let printSpecialPostExtras = self.getSpecialNominalPostMembersPrinting(decl) { printSpecialPostExtras(&printer) } else { @@ -555,20 +552,6 @@ extension FFMSwift2JavaGenerator { ) } - /// Print special helper methods for known types like Foundation.Data - func printSpecificTypeHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { - guard let knownType = decl.swiftNominal.knownTypeKind else { - return - } - - switch knownType { - case .foundationData, .essentialsData: - printFoundationDataHelpers(&printer, decl) - default: - break - } - } - /// Print the `fetchDescription` static helper for SwiftJavaError. /// This calls the `errorDescription()` downcall to get the error message /// for the super constructor diff --git a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift index 32e01b26f..6790c5af9 100644 --- a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift +++ b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift @@ -70,4 +70,12 @@ extension JavaType { static var swiftkitCoreFoundationDataProtocol: JavaType { .class(package: "org.swift.swiftkit.core.foundation", name: "DataProtocol") } + + static var swiftkitFFMFoundationData: JavaType { + .class(package: "org.swift.swiftkit.ffm.foundation", name: "Data") + } + + static var swiftkitFFMFoundationDataProtocol: JavaType { + .class(package: "org.swift.swiftkit.ffm.foundation", name: "DataProtocol") + } } diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/Data.java b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/Data.java new file mode 100644 index 000000000..ffeaea2eb --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/Data.java @@ -0,0 +1,406 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 org.swift.swiftkit.ffm.foundation; + +import org.swift.swiftkit.core.*; +import org.swift.swiftkit.core.util.*; +import org.swift.swiftkit.ffm.*; +import org.swift.swiftkit.ffm.generated.*; +import org.swift.swiftkit.core.annotations.*; +import java.lang.foreign.*; +import java.lang.invoke.*; +import java.util.*; + +public final class Data extends FFMSwiftInstance implements SwiftValue { + static final String LIB_NAME = "SwiftRuntimeFunctions"; + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + @SuppressWarnings("unused") + private static final boolean INITIALIZED_LIBS = initializeLibs(); + static boolean initializeLibs() { + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_CORE); + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_JAVA); + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); + SwiftLibraries.loadLibraryWithFallbacks(LIB_NAME); + return true; + } + + public static final SwiftAnyType TYPE_METADATA = + new SwiftAnyType(SwiftRuntime.swiftjava.getType("SwiftRuntimeFunctions", "Data")); + public SwiftAnyType $swiftType() { + return TYPE_METADATA; + } + + public static final GroupLayout $LAYOUT = (GroupLayout) SwiftValueWitnessTable.layoutOfSwiftType(TYPE_METADATA.$memorySegment()); + public GroupLayout $layout() { + return $LAYOUT; + } + + private Data(MemorySegment segment, AllocatingSwiftArena arena) { + super(segment, arena); + } + + /** + * Assume that the passed {@code MemorySegment} represents a memory address of a {@link Data}. + *

+ * Warnings: + *

    + *
  • No checks are performed about the compatibility of the pointed at memory and the actual Data types.
  • + *
  • This operation does not copy, or retain, the pointed at pointer, so its lifetime must be ensured manually to be valid when wrapping.
  • + *
+ */ + public static Data wrapMemoryAddressUnsafe(MemorySegment selfPointer, AllocatingSwiftArena arena) { + return new Data(selfPointer, arena); + } + + // ==== -------------------------------------------------- + // Data.init + + /** + * {@snippet lang=c : + * void swiftjava_SwiftRuntimeFunctions_Data_init_bytes_count(const void *bytes, ptrdiff_t count, void *_result) + * } + */ + private static class swiftjava_SwiftRuntimeFunctions_Data_init_bytes_count { + private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + /* bytes: */SwiftValueLayout.SWIFT_POINTER, + /* count: */SwiftValueLayout.SWIFT_INT, + /* _result: */SwiftValueLayout.SWIFT_POINTER + ); + private static final MemorySegment ADDR = + SwiftRuntime.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_init_bytes_count"); + private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + public static void call(java.lang.foreign.MemorySegment bytes, long count, java.lang.foreign.MemorySegment _result) { + try { + if (CallTraces.TRACE_DOWNCALLS) { + CallTraces.traceDowncall(bytes, count, _result); + } + HANDLE.invokeExact(bytes, count, _result); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public init(bytes: UnsafeRawPointer, count: Int) + * } + */ + public static Data init(java.lang.foreign.MemorySegment bytes, long count, AllocatingSwiftArena swiftArena) throws SwiftIntegerOverflowException { + MemorySegment result$ = swiftArena.allocate(Data.$LAYOUT); + if (SwiftValueLayout.has32bitSwiftInt) { + if (count < Integer.MIN_VALUE || count > Integer.MAX_VALUE) { + throw new SwiftIntegerOverflowException("Parameter 'count' overflow: " + count); + } + } + swiftjava_SwiftRuntimeFunctions_Data_init_bytes_count.call(bytes, count, result$); + return Data.wrapMemoryAddressUnsafe(result$, swiftArena); + } + + // ==== -------------------------------------------------- + // Data.init + + /** + * {@snippet lang=c : + * void swiftjava_SwiftRuntimeFunctions_Data_init__(const void *bytes_pointer, ptrdiff_t bytes_count, void *_result) + * } + */ + private static class swiftjava_SwiftRuntimeFunctions_Data_init__ { + private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + /* bytes_pointer: */SwiftValueLayout.SWIFT_POINTER, + /* bytes_count: */SwiftValueLayout.SWIFT_INT, + /* _result: */SwiftValueLayout.SWIFT_POINTER + ); + private static final MemorySegment ADDR = + SwiftRuntime.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_init__"); + private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + public static void call(java.lang.foreign.MemorySegment bytes_pointer, long bytes_count, java.lang.foreign.MemorySegment _result) { + try { + if (CallTraces.TRACE_DOWNCALLS) { + CallTraces.traceDowncall(bytes_pointer, bytes_count, _result); + } + HANDLE.invokeExact(bytes_pointer, bytes_count, _result); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public init(_ bytes: [UInt8]) + * } + */ + public static Data init(@Unsigned byte[] bytes, AllocatingSwiftArena swiftArena) { + try(var arena$ = Arena.ofConfined()) { + MemorySegment result$ = swiftArena.allocate(Data.$LAYOUT); + swiftjava_SwiftRuntimeFunctions_Data_init__.call(arena$.allocateFrom(ValueLayout.JAVA_BYTE, bytes), bytes.length, result$); + return Data.wrapMemoryAddressUnsafe(result$, swiftArena); + } + } + + // ==== -------------------------------------------------- + // getter:Data.count + + /** + * {@snippet lang=c : + * ptrdiff_t swiftjava_SwiftRuntimeFunctions_Data_count$get(const void *self) + * } + */ + private static class swiftjava_SwiftRuntimeFunctions_Data_count$get { + private static final FunctionDescriptor DESC = FunctionDescriptor.of( + /* -> */SwiftValueLayout.SWIFT_INT, + /* self: */SwiftValueLayout.SWIFT_POINTER + ); + private static final MemorySegment ADDR = + SwiftRuntime.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_count$get"); + private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + public static long call(java.lang.foreign.MemorySegment self) { + try { + if (CallTraces.TRACE_DOWNCALLS) { + CallTraces.traceDowncall(self); + } + return (long) HANDLE.invokeExact(self); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public var count: Int + * } + */ + public long getCount() throws SwiftIntegerOverflowException { + $ensureAlive(); + long result$checked = swiftjava_SwiftRuntimeFunctions_Data_count$get.call(this.$memorySegment()); + if (SwiftValueLayout.has32bitSwiftInt) { + if (result$checked < Integer.MIN_VALUE || result$checked > Integer.MAX_VALUE) { + throw new SwiftIntegerOverflowException("Return value overflow: " + result$checked); + } + } + return result$checked; + } + + // ==== -------------------------------------------------- + // Data.withUnsafeBytes + + /** + * {@snippet lang=c : + * void swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__(void (*body)(const void *, ptrdiff_t), const void *self) + * } + */ + private static class swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__ { + private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + /* body: */SwiftValueLayout.SWIFT_POINTER, + /* self: */SwiftValueLayout.SWIFT_POINTER + ); + private static final MemorySegment ADDR = + SwiftRuntime.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__"); + private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + public static void call(java.lang.foreign.MemorySegment body, java.lang.foreign.MemorySegment self) { + try { + if (CallTraces.TRACE_DOWNCALLS) { + CallTraces.traceDowncall(body, self); + } + HANDLE.invokeExact(body, self); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + /** + * {snippet lang=c : + * void (*)(const void *, ptrdiff_t) + * } + */ + private static class $body { + @FunctionalInterface + public interface Function { + void apply(java.lang.foreign.MemorySegment _0, long _1); + } + private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + /* _0: */SwiftValueLayout.SWIFT_POINTER, + /* _1: */SwiftValueLayout.SWIFT_INT + ); + private static final MethodHandle HANDLE = SwiftRuntime.upcallHandle(Function.class, "apply", DESC); + private static MemorySegment toUpcallStub(Function fi, Arena arena) { + return Linker.nativeLinker().upcallStub(HANDLE.bindTo(fi), DESC, arena); + } + } + } + public static class withUnsafeBytes { + @FunctionalInterface + public interface body { + void apply(java.lang.foreign.MemorySegment _0); + } + private static MemorySegment $toUpcallStub(body fi, Arena arena) { + return swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__.$body.toUpcallStub((_0_pointer, _0_count) -> { + fi.apply(_0_pointer.reinterpret(_0_count)); + }, arena); + } + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) -> Void) + * } + */ + public void withUnsafeBytes(withUnsafeBytes.body body) { + $ensureAlive(); + try(var arena$ = Arena.ofConfined()) { + swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__.call(withUnsafeBytes.$toUpcallStub(body, arena$), this.$memorySegment()); + } + } + + // ==== -------------------------------------------------- + // Data helper methods + + /** + * Creates a new Swift {@link Data} instance from a byte array. + * + * @param bytes The byte array to copy into the Data + * @param arena The arena for memory management + * @return A new Data instance containing a copy of the bytes + */ + public static Data fromByteArray(byte[] bytes, AllocatingSwiftArena arena) { + Objects.requireNonNull(bytes, "bytes cannot be null"); + return Data.init(bytes, arena); + } + + /** + * {@snippet lang=c : + * void swiftjava_SwiftRuntimeFunctions_Data_copyBytes__(void *const self, void *destination, ptrdiff_t count) + * } + */ + private static class swiftjava_SwiftRuntimeFunctions_Data_copyBytes__ { + private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + /* self: */SwiftValueLayout.SWIFT_POINTER, + /* destination: */SwiftValueLayout.SWIFT_POINTER, + /* count: */SwiftValueLayout.SWIFT_INT + ); + private static final MemorySegment ADDR = + SwiftRuntime.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_copyBytes__"); + private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + public static void call(java.lang.foreign.MemorySegment self, java.lang.foreign.MemorySegment destination, long count) { + try { + if (CallTraces.TRACE_DOWNCALLS) { + CallTraces.traceDowncall(self, destination, count); + } + HANDLE.invokeExact(self, destination, count); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + + /** + * Copies the contents of this Data to a new {@link MemorySegment}. + * + * This is the most efficient way to access Data bytes from Java when you don't + * need a {@code byte[]}. The returned segment is valid for the lifetime of the arena. + * + *

Copy count: 1 (Swift Data -> MemorySegment) + * + * @param arena The arena to allocate the segment in + * @return A MemorySegment containing a copy of this Data's bytes + */ + public MemorySegment toMemorySegment(AllocatingSwiftArena arena) { + $ensureAlive(); + long count = getCount(); + if (count == 0) return MemorySegment.NULL; + MemorySegment segment = arena.allocate(count); + swiftjava_SwiftRuntimeFunctions_Data_copyBytes__.call(this.$memorySegment(), segment, count); + return segment; + } + + /** + * Copies the contents of this Data to a new {@link java.nio.ByteBuffer}. + * + * The returned {@link java.nio.ByteBuffer} is a view over native memory and is valid for the + * lifetime of the arena. This avoids an additional copy to the Java heap. + * + *

Copy count: 1 (Swift Data -> native memory (managed by passed arena), then zero-copy view) + * + * @param arena The arena to allocate the underlying memory in + * @return A ByteBuffer view of the copied bytes + */ + public java.nio.ByteBuffer toByteBuffer(AllocatingSwiftArena arena) { + $ensureAlive(); + long count = getCount(); + if (count == 0) return java.nio.ByteBuffer.allocate(0); + MemorySegment segment = arena.allocate(count); + swiftjava_SwiftRuntimeFunctions_Data_copyBytes__.call(this.$memorySegment(), segment, count); + return segment.asByteBuffer(); + } + + /** + * Copies the contents of this Data to a new byte array. + * The lifetime of the array is independent of the arena, the arena is just used for an intermediary copy. + * + *

Copy count: 2 (Swift Data -> MemorySegment -> byte[]) + * + *

For better performance when you can work with {@link MemorySegment} or + * {@link java.nio.ByteBuffer}, prefer {@link #toMemorySegment} or {@link #toByteBuffer}. + * + * @param arena The arena to use for temporary native memory allocation + * @return A byte array containing a copy of this Data's bytes + */ + public byte[] toByteArray(AllocatingSwiftArena arena) { + $ensureAlive(); + long count = getCount(); + if (count == 0) return new byte[0]; + MemorySegment segment = arena.allocate(count); + swiftjava_SwiftRuntimeFunctions_Data_copyBytes__.call(this.$memorySegment(), segment, count); + return segment.toArray(ValueLayout.JAVA_BYTE); + } + + /** + * Copies the contents of this Data to a new byte array. + * The lifetime of the array is independent of the arena, the arena is just used for an intermediary copy. + * + * This is a convenience method that creates a temporary arena for the copy. + * For repeated calls, prefer {@link #toByteArray(AllocatingSwiftArena)} to reuse an arena. + * + *

Copy count: 2 (Swift Data -> MemorySegment -> byte[]) + * + *

For better performance when you can work with {@link MemorySegment} or + * {@link java.nio.ByteBuffer}, prefer {@link #toMemorySegment} or {@link #toByteBuffer}. + * + * @return A byte array containing a copy of this Data's bytes + */ + public byte[] toByteArray() { + $ensureAlive(); + long count = getCount(); + if (count == 0) return new byte[0]; + try (var arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(count); + swiftjava_SwiftRuntimeFunctions_Data_copyBytes__.call(this.$memorySegment(), segment, count); + return segment.toArray(ValueLayout.JAVA_BYTE); + } + } + @Override + public String toString() { + return getClass().getSimpleName() + + "(" + + SwiftRuntime.nameOfSwiftType($swiftType().$memorySegment(), true) + + ")@" + + $memorySegment(); + } +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/DataProtocol.java b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/DataProtocol.java new file mode 100644 index 000000000..2881ac85e --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/DataProtocol.java @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 org.swift.swiftkit.ffm.foundation; + +import org.swift.swiftkit.core.*; +import org.swift.swiftkit.core.util.*; +import org.swift.swiftkit.ffm.*; +import org.swift.swiftkit.ffm.generated.*; +import org.swift.swiftkit.core.annotations.*; +import java.lang.foreign.*; +import java.lang.invoke.*; +import java.util.*; + +public final class DataProtocol extends FFMSwiftInstance implements SwiftValue { + static final String LIB_NAME = "SwiftRuntimeFunctions"; + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + @SuppressWarnings("unused") + private static final boolean INITIALIZED_LIBS = initializeLibs(); + static boolean initializeLibs() { + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_CORE); + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_JAVA); + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); + SwiftLibraries.loadLibraryWithFallbacks(LIB_NAME); + return true; + } + + public static final SwiftAnyType TYPE_METADATA = + new SwiftAnyType(SwiftRuntime.swiftjava.getType("SwiftRuntimeFunctions", "DataProtocol")); + public SwiftAnyType $swiftType() { + return TYPE_METADATA; + } + + public static final GroupLayout $LAYOUT = (GroupLayout) SwiftValueWitnessTable.layoutOfSwiftType(TYPE_METADATA.$memorySegment()); + public GroupLayout $layout() { + return $LAYOUT; + } + + private DataProtocol(MemorySegment segment, AllocatingSwiftArena arena) { + super(segment, arena); + } + + /** + * Assume that the passed {@code MemorySegment} represents a memory address of a {@link DataProtocol}. + *

+ * Warnings: + *

    + *
  • No checks are performed about the compatibility of the pointed at memory and the actual DataProtocol types.
  • + *
  • This operation does not copy, or retain, the pointed at pointer, so its lifetime must be ensured manually to be valid when wrapping.
  • + *
+ */ + public static DataProtocol wrapMemoryAddressUnsafe(MemorySegment selfPointer, AllocatingSwiftArena arena) { + return new DataProtocol(selfPointer, arena); + } + + @Override + public String toString() { + return getClass().getSimpleName() + + "(" + + SwiftRuntime.nameOfSwiftType($swiftType().$memorySegment(), true) + + ")@" + + $memorySegment(); + } +} From c42156ff4b4b530532f12c4cc5cc5fc66ae10cd5 Mon Sep 17 00:00:00 2001 From: Iceman Date: Tue, 7 Apr 2026 17:52:44 +0900 Subject: [PATCH 06/17] native implementation and fix test import --- .../swift/swiftkit/ffm/FFMDataBenchmark.java | 3 +- .../com/example/swift/DataImportTest.java | 3 +- .../com/example/swift/OptionalImportTest.java | 1 + .../foundation/Data+FFM.swift | 70 +++++++++++++++++++ 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 Sources/SwiftRuntimeFunctions/foundation/Data+FFM.swift diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/jmh/java/org/swift/swiftkit/ffm/FFMDataBenchmark.java b/Samples/SwiftJavaExtractFFMSampleApp/src/jmh/java/org/swift/swiftkit/ffm/FFMDataBenchmark.java index 39a64c7a9..2ce5950f2 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/jmh/java/org/swift/swiftkit/ffm/FFMDataBenchmark.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/jmh/java/org/swift/swiftkit/ffm/FFMDataBenchmark.java @@ -14,7 +14,6 @@ package org.swift.swiftkit.ffm; -import com.example.swift.Data; import com.example.swift.MySwiftLibrary; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.infra.Blackhole; @@ -79,7 +78,7 @@ public ByteBuffer ffm_data_withUnsafeBytes_asByteBuffer() { }); return buf.value; } - + @Benchmark public byte[] ffm_data_withUnsafeBytes_toArray() { Holder buf = new Holder<>(); diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/DataImportTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/DataImportTest.java index 82fb09464..69ed9db31 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/DataImportTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/DataImportTest.java @@ -16,6 +16,7 @@ import org.junit.jupiter.api.Test; import org.swift.swiftkit.ffm.AllocatingSwiftArena; +import org.swift.swiftkit.ffm.foundation.Data; import java.lang.foreign.ValueLayout; @@ -134,7 +135,7 @@ void test_Data_toByteBuffer_emptyData() { byte[] original = new byte[0]; var data = Data.fromByteArray(original, arena); var buffer = data.toByteBuffer(arena); - + assertEquals(0, buffer.capacity()); } } diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/OptionalImportTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/OptionalImportTest.java index 57e8dba61..306ce688c 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/OptionalImportTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/OptionalImportTest.java @@ -16,6 +16,7 @@ import org.junit.jupiter.api.Test; import org.swift.swiftkit.ffm.AllocatingSwiftArena; +import org.swift.swiftkit.ffm.foundation.Data; import java.util.Optional; import java.util.OptionalLong; diff --git a/Sources/SwiftRuntimeFunctions/foundation/Data+FFM.swift b/Sources/SwiftRuntimeFunctions/foundation/Data+FFM.swift new file mode 100644 index 000000000..157abe53e --- /dev/null +++ b/Sources/SwiftRuntimeFunctions/foundation/Data+FFM.swift @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 + + +// ==== -------------------------------------------------- +// Thunks for Data + +@_cdecl("swiftjava_getType_SwiftRuntimeFunctions_Data") +public func swiftjava_getType_SwiftRuntimeFunctions_Data() -> UnsafeMutableRawPointer /* Any.Type */ { + return unsafeBitCast(Data.self, to: UnsafeMutableRawPointer.self) +} + +@_cdecl("swiftjava_SwiftRuntimeFunctions_Data_init_bytes_count") +public func swiftjava_SwiftRuntimeFunctions_Data_init_bytes_count(_ bytes: UnsafeRawPointer, _ count: Int, _ _result: UnsafeMutableRawPointer) { + _result.assumingMemoryBound(to: Data.self).initialize(to: Data(bytes: bytes, count: count)) +} + +@_cdecl("swiftjava_SwiftRuntimeFunctions_Data_init__") +public func swiftjava_SwiftRuntimeFunctions_Data_init__(_ bytes_pointer: UnsafeRawPointer, _ bytes_count: Int, _ _result: UnsafeMutableRawPointer) { + _result.assumingMemoryBound(to: Data.self).initialize(to: Data([UInt8](UnsafeRawBufferPointer(start: bytes_pointer, count: bytes_count)))) +} + +@_cdecl("swiftjava_SwiftRuntimeFunctions_Data_count$get") +public func swiftjava_SwiftRuntimeFunctions_Data_count$get(_ self: UnsafeRawPointer) -> Int { + return self.assumingMemoryBound(to: Data.self).pointee.count +} + +@_cdecl("swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__") +public func swiftjava_SwiftRuntimeFunctions_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) + }) +} + +@_cdecl("swiftjava_SwiftRuntimeFunctions_Data_copyBytes__") +public func swiftjava_SwiftRuntimeFunctions_Data_copyBytes__( + selfPointer: UnsafeRawPointer, + destinationPointer: UnsafeMutableRawPointer, + count: Int +) { + let data = selfPointer.assumingMemoryBound(to: Data.self).pointee + data.withUnsafeBytes { buffer in + destinationPointer.copyMemory(from: buffer.baseAddress!, byteCount: count) + } +} + +// ==== -------------------------------------------------- +// Thunks for DataProtocol + +@_cdecl("swiftjava_getType_SwiftRuntimeFunctions_DataProtocol") +public func swiftjava_getType_SwiftRuntimeFunctions_DataProtocol() -> UnsafeMutableRawPointer /* Any.Type */ { + return unsafeBitCast((any DataProtocol).self, to: UnsafeMutableRawPointer.self) +} From afae76e66ce8a779bc5955f68155252278bb313a Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 8 Apr 2026 10:22:08 +0900 Subject: [PATCH 07/17] Fix test cases --- .../JExtractSwiftTests/DataImportTests.swift | 253 +----------------- Tests/JExtractSwiftTests/DateTests.swift | 149 +++-------- .../JNI/JNIDictionaryTest.swift | 16 +- Tests/JExtractSwiftTests/JNI/JNISetTest.swift | 12 +- .../OptionalImportTests.swift | 2 +- .../SwiftSymbolTableTests.swift | 8 +- 6 files changed, 58 insertions(+), 382 deletions(-) diff --git a/Tests/JExtractSwiftTests/DataImportTests.swift b/Tests/JExtractSwiftTests/DataImportTests.swift index e67a3db87..c2ce81ca5 100644 --- a/Tests/JExtractSwiftTests/DataImportTests.swift +++ b/Tests/JExtractSwiftTests/DataImportTests.swift @@ -95,36 +95,6 @@ final class DataImportTests { _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) - }) - } - """, ] ) } @@ -174,7 +144,7 @@ final class DataImportTests { * public func receiveData(dat: Data) * } */ - public static void receiveData(Data dat) { + public static void receiveData(org.swift.swiftkit.ffm.foundation.Data dat) { swiftjava_SwiftModule_receiveData_dat.call(dat.$memorySegment()); } """, @@ -212,178 +182,10 @@ final class DataImportTests { * public func returnData() -> Data * } */ - public static Data returnData(AllocatingSwiftArena swiftArena) { - MemorySegment result$ = swiftArena.allocate(Data.$LAYOUT); + public static org.swift.swiftkit.ffm.foundation.Data returnData(AllocatingSwiftArena swiftArena) { + MemorySegment result$ = swiftArena.allocate(org.swift.swiftkit.ffm.foundation.Data.$LAYOUT); swiftjava_SwiftModule_returnData.call(result$); - return Data.wrapMemoryAddressUnsafe(result$, swiftArena); - } - """, - - """ - /** - * {@snippet lang=c : - * void swiftjava_SwiftModule_Data_init_bytes_count(const void *bytes, ptrdiff_t count, void *_result) - * } - */ - private static class swiftjava_SwiftModule_Data_init_bytes_count { - private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - /* bytes: */SwiftValueLayout.SWIFT_POINTER, - /* count: */SwiftValueLayout.SWIFT_INT, - /* _result: */SwiftValueLayout.SWIFT_POINTER - ); - private static final MemorySegment ADDR = - SwiftModule.findOrThrow("swiftjava_SwiftModule_Data_init_bytes_count"); - private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - public static void call(java.lang.foreign.MemorySegment bytes, long count, java.lang.foreign.MemorySegment _result) { - try { - if (CallTraces.TRACE_DOWNCALLS) { - CallTraces.traceDowncall(bytes, count, _result); - } - HANDLE.invokeExact(bytes, count, _result); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - } - """, - - """ - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public init(bytes: UnsafeRawPointer, count: Int) - * } - */ - public static Data init(java.lang.foreign.MemorySegment bytes, long count, AllocatingSwiftArena swiftArena) throws SwiftIntegerOverflowException { - MemorySegment result$ = swiftArena.allocate(Data.$LAYOUT); - if (SwiftValueLayout.has32bitSwiftInt) { - if (count < Integer.MIN_VALUE || count > Integer.MAX_VALUE) { - throw new SwiftIntegerOverflowException("Parameter 'count' overflow: " + count); - } - } - swiftjava_SwiftModule_Data_init_bytes_count.call(bytes, count, result$); - return Data.wrapMemoryAddressUnsafe(result$, swiftArena); - } - """, - - """ - /** - * {@snippet lang=c : - * ptrdiff_t swiftjava_SwiftModule_Data_count$get(const void *self) - * } - */ - private static class swiftjava_SwiftModule_Data_count$get { - private static final FunctionDescriptor DESC = FunctionDescriptor.of( - /* -> */SwiftValueLayout.SWIFT_INT, - /* self: */SwiftValueLayout.SWIFT_POINTER - ); - private static final MemorySegment ADDR = - SwiftModule.findOrThrow("swiftjava_SwiftModule_Data_count$get"); - private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - public static long call(java.lang.foreign.MemorySegment self) { - try { - if (CallTraces.TRACE_DOWNCALLS) { - CallTraces.traceDowncall(self); - } - return (long) HANDLE.invokeExact(self); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - } - """, - - """ - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public var count: Int - * } - */ - public long getCount() throws SwiftIntegerOverflowException { - $ensureAlive(); - long result$checked = swiftjava_SwiftModule_Data_count$get.call(this.$memorySegment()); - if (SwiftValueLayout.has32bitSwiftInt) { - if (result$checked < Integer.MIN_VALUE || result$checked > Integer.MAX_VALUE) { - throw new SwiftIntegerOverflowException("Return value overflow: " + result$checked); - } - } - return result$checked; - } - """, - - """ - /** - * {@snippet lang=c : - * void swiftjava_SwiftModule_Data_withUnsafeBytes__(void (*body)(const void *, ptrdiff_t), const void *self) - * } - */ - private static class swiftjava_SwiftModule_Data_withUnsafeBytes__ { - private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - /* body: */SwiftValueLayout.SWIFT_POINTER, - /* self: */SwiftValueLayout.SWIFT_POINTER - ); - private static final MemorySegment ADDR = - SwiftModule.findOrThrow("swiftjava_SwiftModule_Data_withUnsafeBytes__"); - private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - public static void call(java.lang.foreign.MemorySegment body, java.lang.foreign.MemorySegment self) { - try { - if (CallTraces.TRACE_DOWNCALLS) { - CallTraces.traceDowncall(body, self); - } - HANDLE.invokeExact(body, self); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - /** - * {snippet lang=c : - * void (*)(const void *, ptrdiff_t) - * } - */ - private static class $body { - @FunctionalInterface - public interface Function { - void apply(java.lang.foreign.MemorySegment _0, long _1); - } - private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - /* _0: */SwiftValueLayout.SWIFT_POINTER, - /* _1: */SwiftValueLayout.SWIFT_INT - ); - private static final MethodHandle HANDLE = SwiftRuntime.upcallHandle(Function.class, "apply", DESC); - private static MemorySegment toUpcallStub(Function fi, Arena arena) { - return Linker.nativeLinker().upcallStub(HANDLE.bindTo(fi), DESC, arena); - } - } - } - """, - - """ - public static class withUnsafeBytes { - @FunctionalInterface - public interface body { - void apply(java.lang.foreign.MemorySegment _0); - } - private static MemorySegment $toUpcallStub(body fi, Arena arena) { - return swiftjava_SwiftModule_Data_withUnsafeBytes__.$body.toUpcallStub((_0_pointer, _0_count) -> { - fi.apply(_0_pointer.reinterpret(_0_count)); - }, arena); - } - } - """, - - """ - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) -> Void) - * } - */ - public void withUnsafeBytes(withUnsafeBytes.body body) { - $ensureAlive(); - try(var arena$ = Arena.ofConfined()) { - swiftjava_SwiftModule_Data_withUnsafeBytes__.call(withUnsafeBytes.$toUpcallStub(body, arena$), this.$memorySegment()); - } + return org.swift.swiftkit.ffm.foundation.Data.wrapMemoryAddressUnsafe(result$, swiftArena); } """, ] @@ -413,11 +215,6 @@ final class DataImportTests { 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") - """, ] ) } @@ -470,15 +267,10 @@ final class DataImportTests { * public func receiveDataProtocol(dat: some DataProtocol, dat2: T?) * } */ - public static void receiveDataProtocol(Data dat, Optional dat2) { + public static void receiveDataProtocol(org.swift.swiftkit.ffm.foundation.Data dat, Optional dat2) { swiftjava_SwiftModule_receiveDataProtocol_dat_dat2.call(dat.$memorySegment(), SwiftRuntime.toOptionalSegmentInstance(dat2)); } """, - - // Just to make sure 'Data' is imported. - """ - public final class Data extends FFMSwiftInstance implements SwiftValue { - """, ] ) } @@ -499,7 +291,7 @@ final class DataImportTests { detectChunkByInitialLines: 1, expectedChunks: [ """ - public static void acceptData(Data data) { + public static void acceptData(org.swift.swiftkit.core.foundation.Data data) { SwiftModule.$acceptData(data.$memoryAddress()); } """ @@ -533,7 +325,7 @@ final class DataImportTests { .java, expectedChunks: [ """ - public static Data returnData(SwiftArena swiftArena) { + public static org.swift.swiftkit.core.foundation.Data returnData(SwiftArena swiftArena) { """ ] ) @@ -551,33 +343,6 @@ final class DataImportTests { ) } - @Test("Import Data: JNI Data class") - func data_jni_class() throws { - let text = """ - import Foundation - public func f() -> Data - """ - - try assertOutput( - input: text, - .jni, - .java, - detectChunkByInitialLines: 1, - expectedChunks: [ - "public final class Data implements JNISwiftInstance, DataProtocol {", - "public long getCount() {", - - "public static Data fromByteArray(byte[] bytes, SwiftArena swiftArena) {", - - "public byte[] toByteArray() {", - "private static native byte[] $toByteArray(long selfPointer);", - - "public byte[] toByteArrayIndirectCopy() {", - "private static native byte[] $toByteArrayIndirectCopy(long selfPointer);", - ] - ) - } - // ==== ----------------------------------------------------------------------- // MARK: JNI DataProtocol generic parameter @@ -599,7 +364,7 @@ final class DataImportTests { detectChunkByInitialLines: 2, expectedChunks: [ """ - public static MyResult processData(D data, SwiftArena swiftArena) { + public static MyResult processData(D data, SwiftArena swiftArena) { """ ] ) @@ -620,7 +385,7 @@ final class DataImportTests { detectChunkByInitialLines: 2, expectedChunks: [ """ - public static boolean verify(D1 first, D2 second) { + public static boolean verify(D1 first, D2 second) { """ ] ) diff --git a/Tests/JExtractSwiftTests/DateTests.swift b/Tests/JExtractSwiftTests/DateTests.swift index bf5e9ef8e..13a1e59f1 100644 --- a/Tests/JExtractSwiftTests/DateTests.swift +++ b/Tests/JExtractSwiftTests/DateTests.swift @@ -17,34 +17,8 @@ import SwiftJavaConfigurationShared import Testing struct DateTests { - @Test( - "Import: accept Date", - arguments: [ - ( - JExtractGenerationMode.jni, - /* expected Java chunks */ - [ - """ - public static void acceptDate(Date date) { - SwiftModule.$acceptDate(date.$memoryAddress()); - } - """ - ], - /* expected Swift chunks */ - [ - """ - @_cdecl("Java_com_example_swift_SwiftModule__00024acceptDate__J") - public func Java_com_example_swift_SwiftModule__00024acceptDate__J(environment: UnsafeMutablePointer!, thisClass: jclass, date: jlong) { - """ - ], - ) - ] - ) - func func_accept_date( - mode: JExtractGenerationMode, - expectedJavaChunks: [String], - expectedSwiftChunks: [String] - ) throws { + @Test("Import: accept Date") + func func_accept_date() throws { let text = """ import Foundation @@ -54,47 +28,34 @@ struct DateTests { try assertOutput( input: text, - mode, + .jni, .java, detectChunkByInitialLines: 1, - expectedChunks: expectedJavaChunks + expectedChunks: [ + """ + public static void acceptDate(org.swift.swiftkit.core.foundation.Date date) { + SwiftModule.$acceptDate(date.$memoryAddress()); + } + """ + ] ) try assertOutput( input: text, - mode, + .jni, .swift, detectChunkByInitialLines: 1, - expectedChunks: expectedSwiftChunks + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024acceptDate__J") + public func Java_com_example_swift_SwiftModule__00024acceptDate__J(environment: UnsafeMutablePointer!, thisClass: jclass, date: jlong) { + """ + ] ) } - @Test( - "Import: return Date", - arguments: [ - ( - JExtractGenerationMode.jni, - /* expected Java chunks */ - [ - """ - public static Date returnDate(SwiftArena swiftArena) { - """ - ], - /* expected Swift chunks */ - [ - """ - @_cdecl("Java_com_example_swift_SwiftModule__00024returnDate__") - public func Java_com_example_swift_SwiftModule__00024returnDate__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { - """ - ] - ) - ] - ) - func func_return_Date( - mode: JExtractGenerationMode, - expectedJavaChunks: [String], - expectedSwiftChunks: [String] - ) throws { + @Test("Import: return Date") + func func_return_Date() throws { let text = """ import Foundation @@ -103,75 +64,25 @@ struct DateTests { try assertOutput( input: text, - mode, + .jni, .java, - expectedChunks: expectedJavaChunks - ) - - try assertOutput( - input: text, - mode, - .swift, - expectedChunks: expectedSwiftChunks - ) - } - - @Test( - "Import: Date type", - arguments: [ - ( - JExtractGenerationMode.jni, - /* expected Java chunks */ - [ + expectedChunks: [ """ - public final class Date implements JNISwiftInstance { - """, + public static org.swift.swiftkit.core.foundation.Date returnDate(SwiftArena swiftArena) { """ - public static Date init(double timeIntervalSince1970, SwiftArena swiftArena) { - """, - """ - public double getTimeIntervalSince1970() { - """, - """ - public static Date fromInstant(java.time.Instant instant, SwiftArena swiftArena) { - """, - """ - public java.time.Instant toInstant() { - """, - ], - /* expected Swift chunks */ - [ - """ - @_cdecl("Java_com_example_swift_Date__00024init__D") - public func Java_com_example_swift_Date__00024init__D(environment: UnsafeMutablePointer!, thisClass: jclass, timeIntervalSince1970: jdouble) -> jlong { - """, - """ - @_cdecl("Java_com_example_swift_Date__00024getTimeIntervalSince1970__J") - public func Java_com_example_swift_Date__00024getTimeIntervalSince1970__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jdouble { - """, - ] - ) - ] - ) - func date_class(mode: JExtractGenerationMode, expectedJavaChunks: [String], expectedSwiftChunks: [String]) throws { - let text = - """ - import Foundation - public func f() -> Date - """ - - try assertOutput( - input: text, - mode, - .java, - expectedChunks: expectedJavaChunks + ], ) try assertOutput( input: text, - mode, + .jni, .swift, - expectedChunks: expectedSwiftChunks + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024returnDate__") + public func Java_com_example_swift_SwiftModule__00024returnDate__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { + """ + ] ) } } diff --git a/Tests/JExtractSwiftTests/JNI/JNIDictionaryTest.swift b/Tests/JExtractSwiftTests/JNI/JNIDictionaryTest.swift index 0690ee01d..ebb8ed163 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIDictionaryTest.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIDictionaryTest.swift @@ -28,7 +28,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return org.swift.swiftkit.core.collections.SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """, """ @@ -104,7 +104,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(org.swift.swiftkit.core.collections.SwiftDictionaryMap dict, SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(dict, "dict must not be null").$memoryAddress()), swiftArena); + return org.swift.swiftkit.core.collections.SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(dict, "dict must not be null").$memoryAddress()), swiftArena); } """, """ @@ -142,7 +142,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(org.swift.swiftkit.core.collections.SwiftDictionaryMap dict, SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(dict, "dict must not be null").$memoryAddress()), swiftArena); + return org.swift.swiftkit.core.collections.SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(dict, "dict must not be null").$memoryAddress()), swiftArena); } """, """ @@ -183,7 +183,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return org.swift.swiftkit.core.collections.SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -200,7 +200,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return org.swift.swiftkit.core.collections.SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -217,7 +217,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return org.swift.swiftkit.core.collections.SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -234,7 +234,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return org.swift.swiftkit.core.collections.SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -295,7 +295,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(org.swift.swiftkit.core.collections.SwiftDictionaryMap dict, java.lang.String key, long value, SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(dict, "dict must not be null").$memoryAddress(), key, value), swiftArena); + return org.swift.swiftkit.core.collections.SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(dict, "dict must not be null").$memoryAddress(), key, value), swiftArena); } """, """ diff --git a/Tests/JExtractSwiftTests/JNI/JNISetTest.swift b/Tests/JExtractSwiftTests/JNI/JNISetTest.swift index e654344d2..8adec8d58 100644 --- a/Tests/JExtractSwiftTests/JNI/JNISetTest.swift +++ b/Tests/JExtractSwiftTests/JNI/JNISetTest.swift @@ -28,7 +28,7 @@ struct JNISetTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftSet f(SwiftArena swiftArena) { - return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return org.swift.swiftkit.core.collections.SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """, """ @@ -104,7 +104,7 @@ struct JNISetTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftSet f(org.swift.swiftkit.core.collections.SwiftSet set, SwiftArena swiftArena) { - return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(set, "set must not be null").$memoryAddress()), swiftArena); + return org.swift.swiftkit.core.collections.SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(set, "set must not be null").$memoryAddress()), swiftArena); } """, """ @@ -145,7 +145,7 @@ struct JNISetTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftSet f(SwiftArena swiftArena) { - return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return org.swift.swiftkit.core.collections.SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -162,7 +162,7 @@ struct JNISetTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftSet f(SwiftArena swiftArena) { - return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return org.swift.swiftkit.core.collections.SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -179,7 +179,7 @@ struct JNISetTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftSet f(SwiftArena swiftArena) { - return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return org.swift.swiftkit.core.collections.SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -240,7 +240,7 @@ struct JNISetTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftSet f(org.swift.swiftkit.core.collections.SwiftSet set, java.lang.String element, SwiftArena swiftArena) { - return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(set, "set must not be null").$memoryAddress(), element), swiftArena); + return org.swift.swiftkit.core.collections.SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(set, "set must not be null").$memoryAddress(), element), swiftArena); } """, """ diff --git a/Tests/JExtractSwiftTests/OptionalImportTests.swift b/Tests/JExtractSwiftTests/OptionalImportTests.swift index fc169fe53..ea20272e3 100644 --- a/Tests/JExtractSwiftTests/OptionalImportTests.swift +++ b/Tests/JExtractSwiftTests/OptionalImportTests.swift @@ -146,7 +146,7 @@ final class OptionalImportTests { * public func receiveOptionalDataProto(_ arg: (some DataProtocol)?) * } */ - public static void receiveOptionalDataProto(Optional arg) { + public static void receiveOptionalDataProto(Optional arg) { swiftjava_SwiftModule_receiveOptionalDataProto__.call(SwiftRuntime.toOptionalSegmentInstance(arg)); } """, diff --git a/Tests/JExtractSwiftTests/SwiftSymbolTableTests.swift b/Tests/JExtractSwiftTests/SwiftSymbolTableTests.swift index bdf00de18..a2b1fc9e3 100644 --- a/Tests/JExtractSwiftTests/SwiftSymbolTableTests.swift +++ b/Tests/JExtractSwiftTests/SwiftSymbolTableTests.swift @@ -52,8 +52,8 @@ struct SwiftSymbolTableSuite { #expect(symbolTable.lookupType("Z", parent: nil) == nil) } - @Test(arguments: [JExtractGenerationMode.jni, .ffm]) - func resolveSelfModuleName(mode: JExtractGenerationMode) throws { + @Test + func resolveSelfModuleName() throws { try assertOutput( input: """ import Foundation @@ -62,13 +62,13 @@ struct SwiftSymbolTableSuite { public func fullyQualifiedType() -> MyModule.MyValue public func fullyQualifiedType2() -> Foundation.Data """, - mode, + .jni, .java, swiftModuleName: "MyModule", detectChunkByInitialLines: 1, expectedChunks: [ "public static MyValue fullyQualifiedType(", - "public static Data fullyQualifiedType2(", + "public static org.swift.swiftkit.core.foundation.Data fullyQualifiedType2(", ], ) } From 9a6f3edd12e3d650e0f54d1a0f16d3849c955b25 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 8 Apr 2026 10:33:14 +0900 Subject: [PATCH 08/17] Fix benchmark code imports --- .../src/jmh/java/org/swift/swiftkit/ffm/FFMDataBenchmark.java | 1 + .../src/jmh/java/com/example/swift/JNIDataBenchmark.java | 1 + 2 files changed, 2 insertions(+) diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/jmh/java/org/swift/swiftkit/ffm/FFMDataBenchmark.java b/Samples/SwiftJavaExtractFFMSampleApp/src/jmh/java/org/swift/swiftkit/ffm/FFMDataBenchmark.java index 2ce5950f2..6cac287ff 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/jmh/java/org/swift/swiftkit/ffm/FFMDataBenchmark.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/jmh/java/org/swift/swiftkit/ffm/FFMDataBenchmark.java @@ -17,6 +17,7 @@ import com.example.swift.MySwiftLibrary; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.infra.Blackhole; +import org.swift.swiftkit.ffm.foundation.Data; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/jmh/java/com/example/swift/JNIDataBenchmark.java b/Samples/SwiftJavaExtractJNISampleApp/src/jmh/java/com/example/swift/JNIDataBenchmark.java index 283f1f7c0..52b1d16b8 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/jmh/java/com/example/swift/JNIDataBenchmark.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/jmh/java/com/example/swift/JNIDataBenchmark.java @@ -18,6 +18,7 @@ import org.openjdk.jmh.infra.Blackhole; import org.swift.swiftkit.core.ClosableSwiftArena; import org.swift.swiftkit.core.SwiftArena; +import org.swift.swiftkit.core.foundation.Data; import java.util.concurrent.TimeUnit; From d9547652882b001a3e898b1f418441abd2f95010 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 8 Apr 2026 11:02:15 +0900 Subject: [PATCH 09/17] swift format --- ...MSwift2JavaGenerator+JavaTranslation.swift | 26 +++++++++------ .../Foundation/Data+JNI.swift | 8 +++-- .../foundation/Data+FFM.swift | 8 ++--- Tests/JExtractSwiftTests/DateTests.swift | 32 +++++++++---------- 4 files changed, 42 insertions(+), 32 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index decc8a547..d4387c234 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -646,22 +646,28 @@ extension FFMSwift2JavaGenerator { case .foundationData, .essentialsData: return TranslatedParameter( javaParameters: [ - JavaParameter(name: parameterName, type: .class( - package: nil, - name: "Optional", - typeParameters: [.swiftkitFFMFoundationData] - )) + JavaParameter( + name: parameterName, + type: .class( + package: nil, + name: "Optional", + typeParameters: [.swiftkitFFMFoundationData] + ) + ) ], conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false) ) case .foundationDataProtocol, .essentialsDataProtocol: return TranslatedParameter( javaParameters: [ - JavaParameter(name: parameterName, type: .class( - package: nil, - name: "Optional", - typeParameters: [.swiftkitFFMFoundationDataProtocol] - )) + JavaParameter( + name: parameterName, + type: .class( + package: nil, + name: "Optional", + typeParameters: [.swiftkitFFMFoundationDataProtocol] + ) + ) ], conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false) ) diff --git a/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift index 8110af8f3..2451ccd0b 100644 --- a/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift +++ b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift @@ -60,7 +60,7 @@ public func Java_org_swift_swiftkit_core_foundation_Data__00024toByteArray__J(en fatalError("selfPointer memory address was null in call to \(#function)!") } return selfPointer$.pointee.withUnsafeBytes { buffer in - return buffer.getJNIValue(in: environment) + buffer.getJNIValue(in: environment) } } @@ -68,7 +68,11 @@ public func Java_org_swift_swiftkit_core_foundation_Data__00024toByteArray__J(en @used #endif @_cdecl("Java_org_swift_swiftkit_core_foundation_Data__00024toByteArrayIndirectCopy__J") -public func Java_org_swift_swiftkit_core_foundation_Data__00024toByteArrayIndirectCopy__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jbyteArray? { +public func Java_org_swift_swiftkit_core_foundation_Data__00024toByteArrayIndirectCopy__J( + environment: UnsafeMutablePointer!, + thisClass: jclass, + selfPointer: jlong +) -> jbyteArray? { guard let env$ = environment else { fatalError("Missing JNIEnv in downcall to \(#function)") } diff --git a/Sources/SwiftRuntimeFunctions/foundation/Data+FFM.swift b/Sources/SwiftRuntimeFunctions/foundation/Data+FFM.swift index 157abe53e..b7e2b6ab5 100644 --- a/Sources/SwiftRuntimeFunctions/foundation/Data+FFM.swift +++ b/Sources/SwiftRuntimeFunctions/foundation/Data+FFM.swift @@ -24,7 +24,7 @@ import Foundation @_cdecl("swiftjava_getType_SwiftRuntimeFunctions_Data") public func swiftjava_getType_SwiftRuntimeFunctions_Data() -> UnsafeMutableRawPointer /* Any.Type */ { - return unsafeBitCast(Data.self, to: UnsafeMutableRawPointer.self) + unsafeBitCast(Data.self, to: UnsafeMutableRawPointer.self) } @_cdecl("swiftjava_SwiftRuntimeFunctions_Data_init_bytes_count") @@ -39,13 +39,13 @@ public func swiftjava_SwiftRuntimeFunctions_Data_init__(_ bytes_pointer: UnsafeR @_cdecl("swiftjava_SwiftRuntimeFunctions_Data_count$get") public func swiftjava_SwiftRuntimeFunctions_Data_count$get(_ self: UnsafeRawPointer) -> Int { - return self.assumingMemoryBound(to: Data.self).pointee.count + self.assumingMemoryBound(to: Data.self).pointee.count } @_cdecl("swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__") public func swiftjava_SwiftRuntimeFunctions_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) + body(_0.baseAddress, _0.count) }) } @@ -66,5 +66,5 @@ public func swiftjava_SwiftRuntimeFunctions_Data_copyBytes__( @_cdecl("swiftjava_getType_SwiftRuntimeFunctions_DataProtocol") public func swiftjava_getType_SwiftRuntimeFunctions_DataProtocol() -> UnsafeMutableRawPointer /* Any.Type */ { - return unsafeBitCast((any DataProtocol).self, to: UnsafeMutableRawPointer.self) + unsafeBitCast((any DataProtocol).self, to: UnsafeMutableRawPointer.self) } diff --git a/Tests/JExtractSwiftTests/DateTests.swift b/Tests/JExtractSwiftTests/DateTests.swift index 13a1e59f1..7e694640c 100644 --- a/Tests/JExtractSwiftTests/DateTests.swift +++ b/Tests/JExtractSwiftTests/DateTests.swift @@ -32,11 +32,11 @@ struct DateTests { .java, detectChunkByInitialLines: 1, expectedChunks: [ - """ - public static void acceptDate(org.swift.swiftkit.core.foundation.Date date) { - SwiftModule.$acceptDate(date.$memoryAddress()); - } - """ + """ + public static void acceptDate(org.swift.swiftkit.core.foundation.Date date) { + SwiftModule.$acceptDate(date.$memoryAddress()); + } + """ ] ) @@ -46,10 +46,10 @@ struct DateTests { .swift, detectChunkByInitialLines: 1, expectedChunks: [ - """ - @_cdecl("Java_com_example_swift_SwiftModule__00024acceptDate__J") - public func Java_com_example_swift_SwiftModule__00024acceptDate__J(environment: UnsafeMutablePointer!, thisClass: jclass, date: jlong) { - """ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024acceptDate__J") + public func Java_com_example_swift_SwiftModule__00024acceptDate__J(environment: UnsafeMutablePointer!, thisClass: jclass, date: jlong) { + """ ] ) } @@ -67,9 +67,9 @@ struct DateTests { .jni, .java, expectedChunks: [ - """ - public static org.swift.swiftkit.core.foundation.Date returnDate(SwiftArena swiftArena) { - """ + """ + public static org.swift.swiftkit.core.foundation.Date returnDate(SwiftArena swiftArena) { + """ ], ) @@ -78,10 +78,10 @@ struct DateTests { .jni, .swift, expectedChunks: [ - """ - @_cdecl("Java_com_example_swift_SwiftModule__00024returnDate__") - public func Java_com_example_swift_SwiftModule__00024returnDate__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { - """ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024returnDate__") + public func Java_com_example_swift_SwiftModule__00024returnDate__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { + """ ] ) } From f377d1c2d69a276c7acb904a34b056c21b26141c Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 8 Apr 2026 11:20:03 +0900 Subject: [PATCH 10/17] simple collapling --- ...FFMSwift2JavaGenerator+JavaBindingsPrinting.swift | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift index 48cf4c331..1385894d3 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift @@ -621,14 +621,10 @@ extension FFMSwift2JavaGenerator { func renderMemoryLayoutValue(for javaType: JavaType) -> String { if let layout = ForeignValueLayout(javaType: javaType) { return layout.description - } else if case .class(let package, name: let customClass, _) = javaType { - let type = - if let package { - "\(package).\(customClass)" - } else { - customClass - } - return ForeignValueLayout(customType: type).description + } else if case .class(.some(let package), name: let customClass, _) = javaType { + return ForeignValueLayout(customType: "\(package).\(customClass)").description + } else if case .class(.none, name: let customClass, _) = javaType { + return ForeignValueLayout(customType: customClass).description } else { fatalError("renderMemoryLayoutValue not supported for \(javaType)") } From ab3e202e116c099080d7539b72a16e0e1a114e72 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 8 Apr 2026 11:43:33 +0900 Subject: [PATCH 11/17] Add JavaType.optional utility function --- .../FFMSwift2JavaGenerator+JavaTranslation.swift | 14 +++----------- .../JNISwift2JavaGenerator+JavaTranslation.swift | 8 ++++---- .../JExtractSwiftLib/JavaTypes/JavaType+JDK.swift | 5 +++++ Tests/JExtractSwiftTests/DataImportTests.swift | 2 +- Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift | 6 +++--- .../JNI/JNIGenericCombinationTests.swift | 4 ++-- .../JExtractSwiftTests/JNI/JNIOptionalTests.swift | 2 +- Tests/JExtractSwiftTests/OptionalImportTests.swift | 2 +- 8 files changed, 20 insertions(+), 23 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index d4387c234..e4363b7a4 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -648,11 +648,7 @@ extension FFMSwift2JavaGenerator { javaParameters: [ JavaParameter( name: parameterName, - type: .class( - package: nil, - name: "Optional", - typeParameters: [.swiftkitFFMFoundationData] - ) + type: .optional(.swiftkitFFMFoundationData) ) ], conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false) @@ -662,11 +658,7 @@ extension FFMSwift2JavaGenerator { javaParameters: [ JavaParameter( name: parameterName, - type: .class( - package: nil, - name: "Optional", - typeParameters: [.swiftkitFFMFoundationDataProtocol] - ) + type: .optional(.swiftkitFFMFoundationDataProtocol) ) ], conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false) @@ -679,7 +671,7 @@ extension FFMSwift2JavaGenerator { let translatedTy = try self.translate(swiftType: swiftType) return TranslatedParameter( javaParameters: [ - JavaParameter(name: parameterName, type: .class(package: nil, name: "Optional", typeParameters: [translatedTy])) + JavaParameter(name: parameterName, type: .optional(translatedTy)) ], conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false) ) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 733cbac43..f5ce801cc 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -176,7 +176,7 @@ extension JNISwift2JavaGenerator { ), parameters: [], resultType: TranslatedResult( - javaType: .class(package: nil, name: "Optional", typeParameters: [.class(package: nil, name: caseName)]), + javaType: .optional(.class(package: nil, name: caseName)), outParameters: conversions.flatMap(\.translated.outParameters), conversion: enumCase.parameters.isEmpty ? constructRecordConversion @@ -886,7 +886,7 @@ extension JNISwift2JavaGenerator { return TranslatedParameter( parameter: JavaParameter( name: parameterName, - type: .class(package: nil, name: "Optional", typeParameters: [javaType]), + type: .optional(javaType), annotations: parameterAnnotations, ), conversion: .method( @@ -1077,7 +1077,7 @@ extension JNISwift2JavaGenerator { genericParameters: genericParameters, genericRequirements: genericRequirements, ) - return .class(package: "java.util", name: "Optional", typeParameters: [wrappedType]) + return .optional(wrappedType) case .array: guard let elementType = nominalType.genericArguments?.first else { @@ -1361,7 +1361,7 @@ extension JNISwift2JavaGenerator { .void } - let returnType = JavaType.class(package: nil, name: "Optional", typeParameters: [javaType]) + let returnType = JavaType.optional(javaType) return TranslatedResult( javaType: returnType, annotations: parameterAnnotations, diff --git a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift index 6790c5af9..246f8a8b1 100644 --- a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift +++ b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift @@ -45,6 +45,11 @@ extension JavaType { .class(package: "java.lang", name: "Object") } + /// The description of the type java.util.Optional.. + static func optional(_ T: JavaType) -> JavaType { + .class(package: "java.util", name: "Optional", typeParameters: [T]) + } + /// The description of the type java.util.concurrent.CompletableFuture static func completableFuture(_ T: JavaType) -> JavaType { .class(package: "java.util.concurrent", name: "CompletableFuture", typeParameters: [T.boxedType]) diff --git a/Tests/JExtractSwiftTests/DataImportTests.swift b/Tests/JExtractSwiftTests/DataImportTests.swift index c2ce81ca5..42149c5ee 100644 --- a/Tests/JExtractSwiftTests/DataImportTests.swift +++ b/Tests/JExtractSwiftTests/DataImportTests.swift @@ -267,7 +267,7 @@ final class DataImportTests { * public func receiveDataProtocol(dat: some DataProtocol, dat2: T?) * } */ - public static void receiveDataProtocol(org.swift.swiftkit.ffm.foundation.Data dat, Optional dat2) { + public static void receiveDataProtocol(org.swift.swiftkit.ffm.foundation.Data dat, java.util.Optional dat2) { swiftjava_SwiftModule_receiveDataProtocol_dat_dat2.call(dat.$memorySegment(), SwiftRuntime.toOptionalSegmentInstance(dat2)); } """, diff --git a/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift b/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift index e09c215ca..0f3db0ca0 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift @@ -268,7 +268,7 @@ struct JNIEnumTests { detectChunkByInitialLines: 1, expectedChunks: [ """ - public Optional getAsFirst() { + public java.util.Optional getAsFirst() { if (getDiscriminator() != Discriminator.FIRST) { return Optional.empty(); } @@ -276,7 +276,7 @@ struct JNIEnumTests { } """, """ - public Optional getAsSecond() { + public java.util.Optional getAsSecond() { if (getDiscriminator() != Discriminator.SECOND) { return Optional.empty(); } @@ -285,7 +285,7 @@ struct JNIEnumTests { } """, """ - public Optional getAsThird() { + public java.util.Optional getAsThird() { if (getDiscriminator() != Discriminator.THIRD) { return Optional.empty(); } diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift index 57651e1e1..45b4cb2ec 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift @@ -55,7 +55,7 @@ struct JNIGenericCombinationTests { detectChunkByInitialLines: 2, expectedChunks: [ """ - public static Optional> makeStringIDOptional(java.lang.String value, SwiftArena swiftArena) { + public static java.util.Optional> makeStringIDOptional(java.lang.String value, SwiftArena swiftArena) { byte[] result$_discriminator$ = new byte[1]; org.swift.swiftkit.core._OutSwiftGenericInstance resultWrapped$ = new org.swift.swiftkit.core._OutSwiftGenericInstance(); SwiftModule.$makeStringIDOptional(value, result$_discriminator$, resultWrapped$); @@ -113,7 +113,7 @@ struct JNIGenericCombinationTests { detectChunkByInitialLines: 2, expectedChunks: [ """ - public static void takeStringIDOptional(Optional> value) { + public static void takeStringIDOptional(java.util.Optional> value) { SwiftModule.$takeStringIDOptional(value.map(MyID::$memoryAddress).orElse(0L)); } """, diff --git a/Tests/JExtractSwiftTests/JNI/JNIOptionalTests.swift b/Tests/JExtractSwiftTests/JNI/JNIOptionalTests.swift index 728165bf8..d01c4b103 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIOptionalTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIOptionalTests.swift @@ -155,7 +155,7 @@ struct JNIOptionalTests { * public func optionalClass(_ arg: MyClass?) -> MyClass? * } */ - public static Optional optionalClass(Optional arg, SwiftArena swiftArena) { + public static java.util.Optional optionalClass(java.util.Optional arg, SwiftArena swiftArena) { byte[] result$_discriminator$ = new byte[1]; long result$ = SwiftModule.$optionalClass(arg.map(MyClass::$memoryAddress).orElse(0L), result$_discriminator$); return (result$_discriminator$[0] == 1) ? Optional.of(MyClass.wrapMemoryAddressUnsafe(result$, swiftArena)) : Optional.empty(); diff --git a/Tests/JExtractSwiftTests/OptionalImportTests.swift b/Tests/JExtractSwiftTests/OptionalImportTests.swift index ea20272e3..60473bd3a 100644 --- a/Tests/JExtractSwiftTests/OptionalImportTests.swift +++ b/Tests/JExtractSwiftTests/OptionalImportTests.swift @@ -146,7 +146,7 @@ final class OptionalImportTests { * public func receiveOptionalDataProto(_ arg: (some DataProtocol)?) * } */ - public static void receiveOptionalDataProto(Optional arg) { + public static void receiveOptionalDataProto(java.util.Optional arg) { swiftjava_SwiftModule_receiveOptionalDataProto__.call(SwiftRuntime.toOptionalSegmentInstance(arg)); } """, From c4eaf38f1be62e23bdcc45bc8bd810267e4d8c27 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 8 Apr 2026 12:50:02 +0900 Subject: [PATCH 12/17] Make --single-type available in JNI --- ...ift2JavaGenerator+SwiftThunkPrinting.swift | 1 + ...t2JavaGenerator+JavaBindingsPrinting.swift | 35 ++++++++++++------- ...ift2JavaGenerator+SwiftThunkPrinting.swift | 35 ++++++++++++------- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift index fe7372759..40cab0646 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift @@ -71,6 +71,7 @@ extension FFMSwift2JavaGenerator { // === All types // We have to write all types to their corresponding output file that matches the file they were declared in, // because otherwise SwiftPM plugins will not pick up files apropriately -- we expect 1 output +SwiftJava.swift file for every input. + let filteredTypes: [String: ImportedNominalType] if let singleType = config.singleType { filteredTypes = self.analysis.importedTypes.filter { $0.key == singleType } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 833aabbcd..cef345ca7 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -42,13 +42,21 @@ extension JNISwift2JavaGenerator { } package func writeExportedJavaSources(_ printer: inout CodePrinter) throws { - let importedTypes = analysis.importedTypes.sorted(by: { (lhs, rhs) in lhs.key < rhs.key }) + let typesToExport: [(key: String, value: ImportedNominalType)] + if let singleType = config.singleType { + typesToExport = analysis.importedTypes + .filter { $0.key == singleType } + .sorted(by: { $0.key < $1.key }) + } else { + typesToExport = analysis.importedTypes + .sorted(by: { $0.key < $1.key }) + } var exportedFileNames: OrderedSet = [] // Each parent type goes into its own file // any nested types are printed inside the body as `static class` - for (_, ty) in importedTypes.filter({ _, type in type.parent == nil }) { + for (_, ty) in typesToExport.filter({ _, type in type.parent == nil }) { let filename = "\(ty.effectiveJavaSimpleName).java" logger.debug("Printing contents: \(filename)") printImportedNominal(&printer, ty) @@ -63,17 +71,20 @@ extension JNISwift2JavaGenerator { } } - let filename = "\(self.swiftModuleName).java" - logger.trace("Printing module class: \(filename)") - printModule(&printer) + // Skip the module-level .swift file when generating for a single type + if config.singleType == nil { + let filename = "\(self.swiftModuleName).java" + logger.trace("Printing module class: \(filename)") + printModule(&printer) - if let outputFile = try printer.writeContents( - outputDirectory: javaOutputDirectory, - javaPackagePath: javaPackagePath, - filename: filename, - ) { - exportedFileNames.append(outputFile.path(percentEncoded: false)) - logger.info("[swift-java] Generated: \(self.swiftModuleName).java (at \(outputFile))") + if let outputFile = try printer.writeContents( + outputDirectory: javaOutputDirectory, + javaPackagePath: javaPackagePath, + filename: filename, + ) { + exportedFileNames.append(outputFile.path(percentEncoded: false)) + logger.info("[swift-java] Generated: \(self.swiftModuleName).java (at \(outputFile))") + } } // Write java sources list file diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 24c106c01..a47486708 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -57,24 +57,35 @@ extension JNISwift2JavaGenerator { let moduleFilename = "\(moduleFilenameBase).swift" do { - logger.trace("Printing swift module class: \(moduleFilename)") - - try printGlobalSwiftThunkSources(&printer) - - if let outputFile = try printer.writeContents( - outputDirectory: self.swiftOutputDirectory, - javaPackagePath: nil, - filename: moduleFilename, - ) { - logger.info("Generated: \(moduleFilenameBase.bold).swift (at \(outputFile.absoluteString))") - self.expectedOutputSwiftFileNames.remove(moduleFilename) + // Skip the module-level .swift file when generating for a single type + if config.singleType == nil { + logger.trace("Printing swift module class: \(moduleFilename)") + + try printGlobalSwiftThunkSources(&printer) + + if let outputFile = try printer.writeContents( + outputDirectory: self.swiftOutputDirectory, + javaPackagePath: nil, + filename: moduleFilename, + ) { + logger.info("Generated: \(moduleFilenameBase.bold).swift (at \(outputFile.absoluteString))") + self.expectedOutputSwiftFileNames.remove(moduleFilename) + } } // === All types // We have to write all types to their corresponding output file that matches the file they were declared in, // because otherwise SwiftPM plugins will not pick up files apropriately -- we expect 1 output +SwiftJava.swift file for every input. + + let filteredTypes: [String: ImportedNominalType] + if let singleType = config.singleType { + filteredTypes = self.analysis.importedTypes.filter { $0.key == singleType } + } else { + filteredTypes = self.analysis.importedTypes + } + for group: (key: String, value: [Dictionary.Element]) in Dictionary( - grouping: self.analysis.importedTypes, + grouping: filteredTypes, by: { $0.value.sourceFilePath }, ) { logger.warning("Writing types in file group: \(group.key): \(group.value.map(\.key))") From a040322287ff5094acba92adb88282fe82f34c92 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 8 Apr 2026 12:50:10 +0900 Subject: [PATCH 13/17] Reuse the generation script --- Sources/FakeFoundation/Data.swift | 26 +++ Sources/FakeFoundation/Date.swift | 27 +++ ...FMSwift2JavaGenerator+FoundationData.swift | 166 ++++++++++++++++++ .../FFM/FFMSwift2JavaGenerator.swift | 17 ++ ...t2JavaGenerator+JavaBindingsPrinting.swift | 131 ++++++++++++++ ...ift2JavaGenerator+SwiftThunkPrinting.swift | 62 +++++++ scripts/swiftkit-jni-generate-bindings.sh | 52 ++++++ 7 files changed, 481 insertions(+) create mode 100644 Sources/FakeFoundation/Data.swift create mode 100644 Sources/FakeFoundation/Date.swift create mode 100644 Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+FoundationData.swift create mode 100755 scripts/swiftkit-jni-generate-bindings.sh diff --git a/Sources/FakeFoundation/Data.swift b/Sources/FakeFoundation/Data.swift new file mode 100644 index 000000000..5fd160dd0 --- /dev/null +++ b/Sources/FakeFoundation/Data.swift @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 + +public struct Data: DataProtocol { + public init(bytes: UnsafeRawPointer, count: Int) + public init(_ bytes: [UInt8]) + public var count: Int { get } + public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) -> Void) +} diff --git a/Sources/FakeFoundation/Date.swift b/Sources/FakeFoundation/Date.swift new file mode 100644 index 000000000..ee18b8afc --- /dev/null +++ b/Sources/FakeFoundation/Date.swift @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 + +public struct Date { + /// The interval between the date object and 00:00:00 UTC on 1 January 1970. + public var timeIntervalSince1970: Double { get } + + /// Returns a `Date` initialized relative to 00:00:00 UTC on 1 January 1970 by a given number of seconds. + public init(timeIntervalSince1970: Double) +} diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+FoundationData.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+FoundationData.swift new file mode 100644 index 000000000..7dd054f22 --- /dev/null +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+FoundationData.swift @@ -0,0 +1,166 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 CodePrinting +import SwiftJavaConfigurationShared +import SwiftJavaJNICore +import SwiftSyntax +import SwiftSyntaxBuilder + +import struct Foundation.URL + +extension FFMSwift2JavaGenerator { + + /// Print Java helper methods for Foundation.Data type + package func printFoundationDataHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { + let typeName = decl.swiftNominal.name + let thunkNameCopyBytes = "swiftjava_\(swiftModuleName)_\(typeName)_copyBytes__" + + printer.printSeparator("\(typeName) helper methods") + + // This is primarily here for API parity with the JNI version and easier discovery + printer.print( + """ + /** + * Creates a new Swift {@link \(typeName)} instance from a byte array. + * + * @param bytes The byte array to copy into the \(typeName) + * @param arena The arena for memory management + * @return A new \(typeName) instance containing a copy of the bytes + */ + public static \(typeName) fromByteArray(byte[] bytes, AllocatingSwiftArena arena) { + Objects.requireNonNull(bytes, "bytes cannot be null"); + return \(typeName).init(bytes, arena); + } + """ + ) + + // TODO: Implement a fromByteBuffer as well + + // Print the descriptor class for copyBytes native call using the shared helper + let copyBytesCFunc = CFunction( + resultType: .void, + name: thunkNameCopyBytes, + parameters: [ + CParameter(name: "self", type: .qualified(const: true, volatile: false, type: .pointer(.void))), + CParameter(name: "destination", type: .pointer(.void)), + CParameter(name: "count", type: .integral(.ptrdiff_t)), + ], + isVariadic: false + ) + printJavaBindingDescriptorClass(&printer, copyBytesCFunc) + + printer.print( + """ + /** + * Copies the contents of this \(typeName) to a new {@link MemorySegment}. + * + * This is the most efficient way to access \(typeName) bytes from Java when you don't + * need a {@code byte[]}. The returned segment is valid for the lifetime of the arena. + * + *

Copy count: 1 (Swift Data -> MemorySegment) + * + * @param arena The arena to allocate the segment in + * @return A MemorySegment containing a copy of this \(typeName)'s bytes + */ + public MemorySegment toMemorySegment(AllocatingSwiftArena arena) { + $ensureAlive(); + long count = getCount(); + if (count == 0) return MemorySegment.NULL; + MemorySegment segment = arena.allocate(count); + \(thunkNameCopyBytes).call(this.$memorySegment(), segment, count); + return segment; + } + """ + ) + + printer.print( + """ + /** + * Copies the contents of this \(typeName) to a new {@link ByteBuffer}. + * + * The returned {@link java.nio.ByteBuffer} is a view over native memory and is valid for the + * lifetime of the arena. This avoids an additional copy to the Java heap. + * + *

Copy count: 1 (Swift Data -> native memory (managed by passed arena), then zero-copy view) + * + * @param arena The arena to allocate the underlying memory in + * @return A ByteBuffer view of the copied bytes + */ + public java.nio.ByteBuffer toByteBuffer(AllocatingSwiftArena arena) { + $ensureAlive(); + long count = getCount(); + if (count == 0) return java.nio.ByteBuffer.allocate(0); + MemorySegment segment = arena.allocate(count); + \(thunkNameCopyBytes).call(this.$memorySegment(), segment, count); + return segment.asByteBuffer(); + } + """ + ) + + printer.print( + """ + /** + * Copies the contents of this \(typeName) to a new byte array. + * The lifetime of the array is independent of the arena, the arena is just used for an intermediary copy. + * + *

Copy count: 2 (Swift Data -> MemorySegment -> byte[]) + * + *

For better performance when you can work with {@link MemorySegment} or + * {@link java.nio.ByteBuffer}, prefer {@link #toMemorySegment} or {@link #toByteBuffer}. + * + * @param arena The arena to use for temporary native memory allocation + * @return A byte array containing a copy of this \(typeName)'s bytes + */ + public byte[] toByteArray(AllocatingSwiftArena arena) { + $ensureAlive(); + long count = getCount(); + if (count == 0) return new byte[0]; + MemorySegment segment = arena.allocate(count); + \(thunkNameCopyBytes).call(this.$memorySegment(), segment, count); + return segment.toArray(ValueLayout.JAVA_BYTE); + } + """ + ) + + printer.print( + """ + /** + * Copies the contents of this \(typeName) to a new byte array. + * The lifetime of the array is independent of the arena, the arena is just used for an intermediary copy. + * + * This is a convenience method that creates a temporary arena for the copy. + * For repeated calls, prefer {@link #toByteArray(AllocatingSwiftArena)} to reuse an arena. + * + *

Copy count: 2 (Swift Data -> MemorySegment -> byte[]) + * + *

For better performance when you can work with {@link MemorySegment} or + * {@link java.nio.ByteBuffer}, prefer {@link #toMemorySegment} or {@link #toByteBuffer}. + * + * @return A byte array containing a copy of this \(typeName)'s bytes + */ + public byte[] toByteArray() { + $ensureAlive(); + long count = getCount(); + if (count == 0) return new byte[0]; + try (var arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(count); + \(thunkNameCopyBytes).call(this.$memorySegment(), segment, count); + return segment.toArray(ValueLayout.JAVA_BYTE); + } + } + """ + ) + } +} diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 4572316c3..41c063048 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -328,6 +328,9 @@ extension FFMSwift2JavaGenerator { printFunctionDowncallMethods(&printer, funcDecl) } + // Special helper methods for known types (e.g. Data) + printSpecificTypeHelpers(&printer, decl) + if let printSpecialPostExtras = self.getSpecialNominalPostMembersPrinting(decl) { printSpecialPostExtras(&printer) } else { @@ -552,6 +555,20 @@ extension FFMSwift2JavaGenerator { ) } + /// Print special helper methods for known types like Foundation.Data + func printSpecificTypeHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { + guard let knownType = decl.swiftNominal.knownTypeKind else { + return + } + + switch knownType { + case .foundationData, .essentialsData: + printFoundationDataHelpers(&printer, decl) + default: + break + } + } + /// Print the `fetchDescription` static helper for SwiftJavaError. /// This calls the `errorDescription()` downcall to get the error message /// for the super constructor diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index cef345ca7..7bcbd13a5 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -325,6 +325,8 @@ extension JNISwift2JavaGenerator { printer.println() } + printSpecificTypeHelpers(&printer, decl) + printTypeMetadataAddressFunction(&printer, decl) printer.println() @@ -345,6 +347,22 @@ extension JNISwift2JavaGenerator { } } + /// Prints helpers for specific types like `Foundation.Date` + private func printSpecificTypeHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { + guard let knownType = decl.swiftNominal.knownTypeKind else { return } + + switch knownType { + case .foundationDate, .essentialsDate: + printFoundationDateHelpers(&printer, decl) + + case .foundationData, .essentialsData: + printFoundationDataHelpers(&printer, decl) + + default: + break + } + } + private func printHeader(_ printer: inout CodePrinter) { printer.print( """ @@ -825,4 +843,117 @@ extension JNISwift2JavaGenerator { } } } + + private func printFoundationDateHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { + printer.print( + """ + /** + * Converts this wrapped date to a Java {@link java.time.Instant}. + *

+ * This method constructs the {@code Instant} using the underlying {@code double} value + * representing seconds since the Unix Epoch (January 1, 1970). + *

+ * + * @return A {@code java.time.Instant} derived from the floating-point timestamp. + */ + public java.time.Instant toInstant() { + long seconds = (long) this.getTimeIntervalSince1970(); + long nanos = Math.round((this.getTimeIntervalSince1970() - seconds) * 1_000_000_000); + return java.time.Instant.ofEpochSecond(seconds, nanos); + } + """ + ) + printer.println() + printer.print( + """ + /** + * Initializes a Swift {@code Foundation.Date} from a Java {@link java.time.Instant}. + * + *

Warning: Precision Loss

+ *

+ * The input precision will be degraded. + *

+ *

+ * Java's {@code Instant} stores time with nanosecond precision (9 decimal places). + * However, this class stores time as a 64-bit floating-point value. + *

+ *

+ * This leaves enough capacity for microsecond precision (approx. 6 decimal places). + *

+ *

+ * Consequently, the last ~3 digits of the {@code Instant}'s nanosecond field will be + * truncated or subjected to rounding errors during conversion. + *

+ * + * @param instant The source timestamp to convert. + * @return A date derived from the input instant with microsecond precision. + */ + public static Date fromInstant(java.time.Instant instant, SwiftArena swiftArena) { + Objects.requireNonNull(instant, "Instant cannot be null"); + double timeIntervalSince1970 = instant.getEpochSecond() + (instant.getNano() / 1_000_000_000.0); + return Date.init(timeIntervalSince1970, swiftArena); + } + """ + ) + } + + private func printFoundationDataHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { + printer.print( + """ + /** + * Creates a new Swift @{link Data} instance from a byte array. + * + * @param bytes The byte array to copy into the Data + * @param swiftArena The arena for memory management + * @return A new Data instance containing a copy of the bytes + */ + public static Data fromByteArray(byte[] bytes, SwiftArena swiftArena) { + Objects.requireNonNull(bytes, "bytes cannot be null"); + return Data.init(bytes, swiftArena); + } + """ + ) + + printer.print( + """ + /** + * Copies the contents of this Data to a new byte array. + * + * This is a relatively efficient implementation, which avoids native array copies, + * however it will still perform a copy of the data onto the JVM heap, so use this + * only when necessary. + * + *

When utmost performance is necessary, you may want to investigate the FFM mode + * of jextract which is able to map memory more efficiently. + * + * @return A byte array containing a copy of this Data's bytes + */ + public byte[] toByteArray() { + return $toByteArray(this.$memoryAddress()); + } + """ + ) + + printer.print( + """ + private static native byte[] $toByteArray(long selfPointer); + + /** + * Copies the contents of this Data to a new byte array. + * + * @deprecated Prefer using the `toByteArray` method as it is more performant. + * This implementation uses a naive conversion path from native bytes into jbytes + * and then copying them onto the jvm heap. + * + * @return A byte array containing a copy of this Data's bytes + */ + @Deprecated(forRemoval = true) + public byte[] toByteArrayIndirectCopy() { + return $toByteArrayIndirectCopy(this.$memoryAddress()); + } + + private static native byte[] $toByteArrayIndirectCopy(long selfPointer); + """ + ) + } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index a47486708..233a95df9 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -358,6 +358,7 @@ extension JNISwift2JavaGenerator { printer.println() } + printSpecificTypeThunks(&printer, type) printTypeMetadataAddressThunk(&printer, type) printer.println() } @@ -866,6 +867,67 @@ extension JNISwift2JavaGenerator { } } + /// Prints thunks for specific known types like Foundation.Date, Foundation.Data + private func printSpecificTypeThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) { + guard let knownType = type.swiftNominal.knownTypeKind else { return } + + switch knownType { + case .foundationData, .essentialsData: + printFoundationDataThunks(&printer, type) + printer.println() + + default: + break + } + } + + /// Prints Swift thunks for Foundation.Data helper methods + private func printFoundationDataThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) { + let selfPointerParam = JavaParameter(name: "selfPointer", type: .long) + let parentName = type.qualifiedName + + // Rebind the memory instead of converting, and set the memory directly using 'jniSetArrayRegion' from the buffer + printCDecl( + &printer, + javaMethodName: "$toByteArray", + parentName: type.effectiveJavaName, + parameters: [ + selfPointerParam + ], + resultType: .array(.byte), + ) { printer in + let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam) + + printer.print( + """ + return \(selfVar).pointee.withUnsafeBytes { buffer in + return buffer.getJNIValue(in: environment) + } + """ + ) + } + + // Legacy API, also to compare with as a baseline, we could remove it + printCDecl( + &printer, + javaMethodName: "$toByteArrayIndirectCopy", + parentName: type.effectiveJavaName, + parameters: [ + selfPointerParam + ], + resultType: .array(.byte), + ) { printer in + let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam) + + printer.print( + """ + // This is a double copy, we need to initialize the array and then copy into a JVM array in getJNIValue + return [UInt8](\(selfVar).pointee).getJNIValue(in: environment) + """ + ) + } + } + private func printFunctionOpenerCall(_ printer: inout CodePrinter, _ decl: ImportedFunc) { guard let translatedDecl = self.translatedDecl(for: decl) else { fatalError("Cannot print function opener for a function that can't be translated: \(decl)") diff --git a/scripts/swiftkit-jni-generate-bindings.sh b/scripts/swiftkit-jni-generate-bindings.sh new file mode 100755 index 000000000..7af973949 --- /dev/null +++ b/scripts/swiftkit-jni-generate-bindings.sh @@ -0,0 +1,52 @@ +#!/bin/bash +##===----------------------------------------------------------------------===## +## +## This source file is part of the Swift.org open source project +## +## Copyright (c) 2026 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 +## +##===----------------------------------------------------------------------===## + +# Regenerate FFM bindings for types in SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/generated/ +# +# Run from the swift-java repository root: +# ./scripts/swiftkit-ffm-generate-bindings.sh + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" + +JAVA_OUTPUT="${REPO_ROOT}/SwiftKitCore/src/main/java" +JAVA_PACKAGE="org.swift.swiftkit.core.foundation" + +# Declare types to generate: SWIFT_MODULE SINGLE_TYPE INPUT_SWIFT_DIR OUTPUT_SWIFT_DIR +TYPES=( + "Foundation Data Sources/FakeFoundation Sources/SwiftJavaRuntimeSupport/foundation" + "Foundation Date Sources/FakeFoundation Sources/SwiftJavaRuntimeSupport/foundation" +) + +for entry in "${TYPES[@]}"; do + read -r MODULE SINGLE_TYPE INPUT_SWIFT OUTPUT_SWIFT <<< "$entry" + + echo "==> Generating ${INPUT_SWIFT} ${SINGLE_TYPE}..." + + xcrun swift run swift-java jextract \ + --mode jni \ + --single-type "$SINGLE_TYPE" \ + --swift-module "$MODULE" \ + --input-swift "${REPO_ROOT}/${INPUT_SWIFT}" \ + --output-swift "${REPO_ROOT}/${OUTPUT_SWIFT}" \ + --output-java "$JAVA_OUTPUT" \ + --java-package "$JAVA_PACKAGE" + + echo " Swift thunks: ${OUTPUT_SWIFT}/" + echo " Java output: SwiftKitCore/src/main/java/$(echo "$JAVA_PACKAGE" | tr '.' '/')/" +done + +echo "==> Done." From 8ee8976a0144e36786330a97a1a58de959890798 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 8 Apr 2026 15:17:29 +0900 Subject: [PATCH 14/17] Update generator logic for internal module --- Sources/FakeFoundation/DataProtocol.swift | 21 ++++ ...ift2JavaGenerator+SwiftThunkPrinting.swift | 30 +++-- .../FFM/FFMSwift2JavaGenerator.swift | 16 ++- ...t2JavaGenerator+JavaBindingsPrinting.swift | 20 ++-- ...ift2JavaGenerator+SwiftThunkPrinting.swift | 46 ++++---- .../Foundation/Data+JNI.swift | 86 --------------- .../Foundation/Data+SwiftJava.swift | 91 ++++++++++++++++ .../Foundation/DataProtocol+SwiftJava.swift | 17 +++ .../Foundation/Date+JNI.swift | 47 -------- .../Foundation/Date+SwiftJava.swift | 55 ++++++++++ .../{Data+FFM.swift => Data+SwiftJava.swift} | 23 +--- .../foundation/DataProtocol+SwiftJava.swift | 17 +++ .../generated/SwiftJavaError+SwiftJava.swift | 1 - .../swift/swiftkit/core/foundation/Data.java | 88 +++++++-------- .../core/foundation/DataProtocol.java | 24 ++-- .../swift/swiftkit/core/foundation/Date.java | 67 +++++------- .../swift/swiftkit/ffm/foundation/Data.java | 103 +++++++----------- .../swiftkit/ffm/foundation/DataProtocol.java | 31 ++---- .../generated/SwiftJavaErrorException.java | 2 +- scripts/swiftkit-ffm-generate-bindings.sh | 11 +- scripts/swiftkit-jni-generate-bindings.sh | 5 +- 21 files changed, 397 insertions(+), 404 deletions(-) create mode 100644 Sources/FakeFoundation/DataProtocol.swift delete mode 100644 Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift create mode 100644 Sources/SwiftJavaRuntimeSupport/Foundation/Data+SwiftJava.swift create mode 100644 Sources/SwiftJavaRuntimeSupport/Foundation/DataProtocol+SwiftJava.swift delete mode 100644 Sources/SwiftJavaRuntimeSupport/Foundation/Date+JNI.swift create mode 100644 Sources/SwiftJavaRuntimeSupport/Foundation/Date+SwiftJava.swift rename Sources/SwiftRuntimeFunctions/foundation/{Data+FFM.swift => Data+SwiftJava.swift} (70%) create mode 100644 Sources/SwiftRuntimeFunctions/foundation/DataProtocol+SwiftJava.swift diff --git a/Sources/FakeFoundation/DataProtocol.swift b/Sources/FakeFoundation/DataProtocol.swift new file mode 100644 index 000000000..d5a02d423 --- /dev/null +++ b/Sources/FakeFoundation/DataProtocol.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 + +public protocol DataProtocol {} diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift index 40cab0646..99b2640f3 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift @@ -93,14 +93,13 @@ extension FFMSwift2JavaGenerator { let filename = "\(inputFileName)".replacing(".swift", with: "+SwiftJava.swift") // Print file header before all type thunks - printer.print( - """ - // Generated by swift-java - - import SwiftRuntimeFunctions + printer.print("// Generated by swift-java") + printer.println() + if !group.value.allSatisfy({ $0.value.swiftNominal.moduleName == "SwiftRuntimeFunctions" }) { + printer.print("import SwiftRuntimeFunctions") + } + printer.println() - """ - ) self.lookupContext.symbolTable.printImportedModules(&printer) for ty in importedTypesForThisFile { @@ -264,16 +263,15 @@ struct SwiftThunkTranslator { /// Render special thunks for known types like Foundation.Data func renderSpecificTypeThunks(_ nominal: ImportedNominalType) -> [DeclSyntax] { - guard let knownType = nominal.swiftNominal.knownTypeKind else { - return [] - } - - switch knownType { - case .foundationData, .essentialsData: - return renderFoundationDataThunks(nominal) - default: - return [] + if nominal.swiftNominal.moduleName == "SwiftRuntimeFunctions" { + switch nominal.swiftNominal.qualifiedName { + case "Data": + return renderFoundationDataThunks(nominal) + default: + break + } } + return [] } /// Render Swift thunks for Foundation.Data helper methods diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 41c063048..8427208c3 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -557,15 +557,13 @@ extension FFMSwift2JavaGenerator { /// Print special helper methods for known types like Foundation.Data func printSpecificTypeHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { - guard let knownType = decl.swiftNominal.knownTypeKind else { - return - } - - switch knownType { - case .foundationData, .essentialsData: - printFoundationDataHelpers(&printer, decl) - default: - break + if decl.swiftNominal.moduleName == "SwiftRuntimeFunctions" { + switch decl.swiftNominal.qualifiedName { + case "Data": + printFoundationDataHelpers(&printer, decl) + default: + break + } } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 7bcbd13a5..ffee615f7 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -349,17 +349,15 @@ extension JNISwift2JavaGenerator { /// Prints helpers for specific types like `Foundation.Date` private func printSpecificTypeHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { - guard let knownType = decl.swiftNominal.knownTypeKind else { return } - - switch knownType { - case .foundationDate, .essentialsDate: - printFoundationDateHelpers(&printer, decl) - - case .foundationData, .essentialsData: - printFoundationDataHelpers(&printer, decl) - - default: - break + if decl.swiftNominal.moduleName == "SwiftJava" { + switch decl.swiftNominal.qualifiedName { + case "Date": + printFoundationDateHelpers(&printer, decl) + case "Data": + printFoundationDataHelpers(&printer, decl) + default: + break + } } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 233a95df9..1883eebd2 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -286,7 +286,7 @@ extension JNISwift2JavaGenerator { } private func printGlobalSwiftThunkSources(_ printer: inout CodePrinter) throws { - printHeader(&printer) + printHeader(&printer, nil) for decl in analysis.importedGlobalFuncs { printSwiftFunctionThunk(&printer, decl) @@ -300,7 +300,7 @@ extension JNISwift2JavaGenerator { } private func printNominalTypeThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) throws { - printHeader(&printer) + printHeader(&printer, type) printJNICache(&printer, type) printer.println() @@ -829,17 +829,20 @@ extension JNISwift2JavaGenerator { } } - private func printHeader(_ printer: inout CodePrinter) { - printer.print( - """ - // Generated by swift-java - - import SwiftJava - import SwiftJavaJNICore - import SwiftJavaRuntimeSupport - - """ - ) + private func printHeader(_ printer: inout CodePrinter, _ type: ImportedNominalType?) { + var imports = [ + "SwiftJava", + "SwiftJavaJNICore", + ] + if type?.swiftNominal.moduleName != "SwiftJavaRuntimeSupport" { + imports.append("SwiftJavaRuntimeSupport") + } + printer.print("// Generated by swift-java") + printer.println() + for package in imports { + printer.print("import \(package)") + } + printer.println() self.lookupContext.symbolTable.printImportedModules(&printer) } @@ -869,15 +872,14 @@ extension JNISwift2JavaGenerator { /// Prints thunks for specific known types like Foundation.Date, Foundation.Data private func printSpecificTypeThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) { - guard let knownType = type.swiftNominal.knownTypeKind else { return } - - switch knownType { - case .foundationData, .essentialsData: - printFoundationDataThunks(&printer, type) - printer.println() - - default: - break + if type.swiftNominal.moduleName == "SwiftJava" { + switch type.swiftNominal.qualifiedName { + case "Data": + printFoundationDataThunks(&printer, type) + printer.println() + default: + break + } } } diff --git a/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift deleted file mode 100644 index 2451ccd0b..000000000 --- a/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift +++ /dev/null @@ -1,86 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2026 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 SwiftJava -import SwiftJavaJNICore - -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - -@JavaImplementation("org.swift.swiftkit.core.foundation.Data") -extension Data { - @JavaMethod("$init") - static func _init(environment: UnsafeMutablePointer!, bytes: [UInt8]) -> Int64 { - let result$ = UnsafeMutablePointer.allocate(capacity: 1) - result$.initialize(to: Data(bytes)) - return Int64(Int(bitPattern: result$)) - } - - @JavaMethod("$getCount") - static func _getCount(environment: UnsafeMutablePointer!, selfPointer: Int64) -> Int64 { - let selfPointer$ = UnsafeMutablePointer(bitPattern: Int(selfPointer)) - guard let selfPointer$ else { - fatalError("selfPointer memory address was null in call to \(#function)!") - } - return Int64(selfPointer$.pointee.count) - } - - @JavaMethod("$typeMetadataAddressDowncall") - static func _typeMetadataAddressDowncall(environment: UnsafeMutablePointer!) -> Int64 { - let metadataPointer = unsafeBitCast(Data.self, to: UnsafeRawPointer.self) - return Int64(Int(bitPattern: metadataPointer)) - } -} - -#if compiler(>=6.3) -@used -#endif -@_cdecl("Java_org_swift_swiftkit_core_foundation_Data__00024toByteArray__J") -public func Java_org_swift_swiftkit_core_foundation_Data__00024toByteArray__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jbyteArray? { - guard let env$ = environment else { - fatalError("Missing JNIEnv in downcall to \(#function)") - } - assert(selfPointer != 0, "selfPointer memory address was null") - let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: env$)) - guard let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) else { - fatalError("selfPointer memory address was null in call to \(#function)!") - } - return selfPointer$.pointee.withUnsafeBytes { buffer in - buffer.getJNIValue(in: environment) - } -} - -#if compiler(>=6.3) -@used -#endif -@_cdecl("Java_org_swift_swiftkit_core_foundation_Data__00024toByteArrayIndirectCopy__J") -public func Java_org_swift_swiftkit_core_foundation_Data__00024toByteArrayIndirectCopy__J( - environment: UnsafeMutablePointer!, - thisClass: jclass, - selfPointer: jlong -) -> jbyteArray? { - guard let env$ = environment else { - fatalError("Missing JNIEnv in downcall to \(#function)") - } - assert(selfPointer != 0, "selfPointer memory address was null") - let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: env$)) - guard let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) else { - fatalError("selfPointer memory address was null in call to \(#function)!") - } - // This is a double copy, we need to initialize the array and then copy into a JVM array in getJNIValue - return [UInt8](selfPointer$.pointee).getJNIValue(in: environment) -} diff --git a/Sources/SwiftJavaRuntimeSupport/Foundation/Data+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+SwiftJava.swift new file mode 100644 index 000000000..1637eb1fc --- /dev/null +++ b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+SwiftJava.swift @@ -0,0 +1,91 @@ +// ==== -------------------------------------------------- +// Thunks for Data + +// Generated by swift-java + +import SwiftJava +import SwiftJavaJNICore +import SwiftJavaRuntimeSupport + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +enum _JNI_Data { +} // printJNICache(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:177 + + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_foundation_Data__00024init___3B") +public func Java_org_swift_swiftkit_core_foundation_Data__00024init___3B(environment: UnsafeMutablePointer!, thisClass: jclass, bytes: jbyteArray?) -> jlong { + let result$ = UnsafeMutablePointer.allocate(capacity: 1) + result$.initialize(to: Data.init([UInt8](fromJNI: bytes, in: environment))) + let resultBits$ = Int64(Int(bitPattern: result$)) + return resultBits$.getJNILocalRefValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:819 + + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_foundation_Data__00024getCount__J") +public func Java_org_swift_swiftkit_core_foundation_Data__00024getCount__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jlong { + assert(selfPointer != 0, "selfPointer memory address was null") + let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: environment)) + let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) + guard let selfPointer$ else { + fatalError("selfPointer memory address was null in call to \(#function)!") + } + return Int64(selfPointer$.pointee.count).getJNILocalRefValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:819 + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_foundation_Data__00024toByteArray__J") +public func Java_org_swift_swiftkit_core_foundation_Data__00024toByteArray__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jbyteArray? { + guard let env$ = environment else { + fatalError("Missing JNIEnv in downcall to \(#function)") + } + assert(selfPointer != 0, "selfPointer memory address was null") + let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: env$)) + guard let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) else { + fatalError("selfPointer memory address was null in call to \(#function)!") + } + return selfPointer$.pointee.withUnsafeBytes { buffer in + buffer.getJNIValue(in: environment) + } +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:819 +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_foundation_Data__00024toByteArrayIndirectCopy__J") +public func Java_org_swift_swiftkit_core_foundation_Data__00024toByteArrayIndirectCopy__J( + environment: UnsafeMutablePointer!, + thisClass: jclass, + selfPointer: jlong +) -> jbyteArray? { + guard let env$ = environment else { + fatalError("Missing JNIEnv in downcall to \(#function)") + } + assert(selfPointer != 0, "selfPointer memory address was null") + let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: env$)) + guard let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) else { + fatalError("selfPointer memory address was null in call to \(#function)!") + } + // This is a double copy, we need to initialize the array and then copy into a JVM array in getJNIValue + return [UInt8](selfPointer$.pointee).getJNIValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:819 + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_foundation_Data__00024typeMetadataAddressDowncall__") +public func Java_org_swift_swiftkit_core_foundation_Data__00024typeMetadataAddressDowncall__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { + let metadataPointer = unsafeBitCast(Data.self, to: UnsafeRawPointer.self) + return Int64(Int(bitPattern: metadataPointer)).getJNIValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:819 diff --git a/Sources/SwiftJavaRuntimeSupport/Foundation/DataProtocol+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/DataProtocol+SwiftJava.swift new file mode 100644 index 000000000..eb9dd1dad --- /dev/null +++ b/Sources/SwiftJavaRuntimeSupport/Foundation/DataProtocol+SwiftJava.swift @@ -0,0 +1,17 @@ +// ==== -------------------------------------------------- +// Thunks for DataProtocol + +// Generated by swift-java + +import SwiftJava +import SwiftJavaJNICore +import SwiftJavaRuntimeSupport + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +enum _JNI_DataProtocol { +} // printJNICache(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:177 diff --git a/Sources/SwiftJavaRuntimeSupport/Foundation/Date+JNI.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/Date+JNI.swift deleted file mode 100644 index 3c27cd7af..000000000 --- a/Sources/SwiftJavaRuntimeSupport/Foundation/Date+JNI.swift +++ /dev/null @@ -1,47 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2026 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 SwiftJava -import SwiftJavaJNICore - -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - -@JavaImplementation("org.swift.swiftkit.core.foundation.Date") -extension Date { - @JavaMethod("$init") - static func _init(environment: UnsafeMutablePointer!, timeIntervalSince1970: Double) -> Int64 { - let result$ = UnsafeMutablePointer.allocate(capacity: 1) - result$.initialize(to: Date(timeIntervalSince1970: timeIntervalSince1970)) - return Int64(Int(bitPattern: result$)) - } - - @JavaMethod("$getTimeIntervalSince1970") - static func _getTimeIntervalSince1970(environment: UnsafeMutablePointer!, selfPointer: Int64) -> Double { - let selfPointer$ = UnsafeMutablePointer(bitPattern: Int(selfPointer)) - guard let selfPointer$ else { - fatalError("selfPointer memory address was null in call to \(#function)!") - } - return selfPointer$.pointee.timeIntervalSince1970 - } - - @JavaMethod("$typeMetadataAddressDowncall") - static func _typeMetadataAddressDowncall(environment: UnsafeMutablePointer!) -> Int64 { - let metadataPointer = unsafeBitCast(Date.self, to: UnsafeRawPointer.self) - return Int64(Int(bitPattern: metadataPointer)) - } -} diff --git a/Sources/SwiftJavaRuntimeSupport/Foundation/Date+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/Date+SwiftJava.swift new file mode 100644 index 000000000..920167cbb --- /dev/null +++ b/Sources/SwiftJavaRuntimeSupport/Foundation/Date+SwiftJava.swift @@ -0,0 +1,55 @@ +// ==== -------------------------------------------------- +// Thunks for Date + +// Generated by swift-java + +import SwiftJava +import SwiftJavaJNICore +import SwiftJavaRuntimeSupport + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +enum _JNI_Date { +} // printJNICache(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:177 + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_foundation_Date__00024init__D") +public func Java_org_swift_swiftkit_core_foundation_Date__00024init__D(environment: UnsafeMutablePointer!, thisClass: jclass, timeIntervalSince1970: jdouble) -> jlong { + let result$ = UnsafeMutablePointer.allocate(capacity: 1) + result$.initialize(to: Date.init(timeIntervalSince1970: Double(fromJNI: timeIntervalSince1970, in: environment))) + let resultBits$ = Int64(Int(bitPattern: result$)) + return resultBits$.getJNILocalRefValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:819 + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_foundation_Date__00024getTimeIntervalSince1970__J") +public func Java_org_swift_swiftkit_core_foundation_Date__00024getTimeIntervalSince1970__J( + environment: UnsafeMutablePointer!, + thisClass: jclass, + selfPointer: jlong +) -> jdouble { + assert(selfPointer != 0, "selfPointer memory address was null") + let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: environment)) + let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) + guard let selfPointer$ else { + fatalError("selfPointer memory address was null in call to \(#function)!") + } + return selfPointer$.pointee.timeIntervalSince1970.getJNILocalRefValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:819 + +#if compiler(>=6.3) +@used +#endif +@_cdecl("Java_org_swift_swiftkit_core_foundation_Date__00024typeMetadataAddressDowncall__") +public func Java_org_swift_swiftkit_core_foundation_Date__00024typeMetadataAddressDowncall__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { + let metadataPointer = unsafeBitCast(Date.self, to: UnsafeRawPointer.self) + return Int64(Int(bitPattern: metadataPointer)).getJNIValue(in: environment) +} // printCDecl(_:javaMethodName:parentName:parameters:resultType:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+SwiftThunkPrinting.swift:819 diff --git a/Sources/SwiftRuntimeFunctions/foundation/Data+FFM.swift b/Sources/SwiftRuntimeFunctions/foundation/Data+SwiftJava.swift similarity index 70% rename from Sources/SwiftRuntimeFunctions/foundation/Data+FFM.swift rename to Sources/SwiftRuntimeFunctions/foundation/Data+SwiftJava.swift index b7e2b6ab5..2f9970b8a 100644 --- a/Sources/SwiftRuntimeFunctions/foundation/Data+FFM.swift +++ b/Sources/SwiftRuntimeFunctions/foundation/Data+SwiftJava.swift @@ -1,16 +1,5 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2026 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 -// -//===----------------------------------------------------------------------===// +// Generated by swift-java + #if canImport(FoundationEssentials) import FoundationEssentials @@ -60,11 +49,3 @@ public func swiftjava_SwiftRuntimeFunctions_Data_copyBytes__( destinationPointer.copyMemory(from: buffer.baseAddress!, byteCount: count) } } - -// ==== -------------------------------------------------- -// Thunks for DataProtocol - -@_cdecl("swiftjava_getType_SwiftRuntimeFunctions_DataProtocol") -public func swiftjava_getType_SwiftRuntimeFunctions_DataProtocol() -> UnsafeMutableRawPointer /* Any.Type */ { - unsafeBitCast((any DataProtocol).self, to: UnsafeMutableRawPointer.self) -} diff --git a/Sources/SwiftRuntimeFunctions/foundation/DataProtocol+SwiftJava.swift b/Sources/SwiftRuntimeFunctions/foundation/DataProtocol+SwiftJava.swift new file mode 100644 index 000000000..30db7ec84 --- /dev/null +++ b/Sources/SwiftRuntimeFunctions/foundation/DataProtocol+SwiftJava.swift @@ -0,0 +1,17 @@ +// Generated by swift-java + + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + + +// ==== -------------------------------------------------- +// Thunks for DataProtocol + +@_cdecl("swiftjava_getType_SwiftRuntimeFunctions_DataProtocol") +public func swiftjava_getType_SwiftRuntimeFunctions_DataProtocol() -> UnsafeMutableRawPointer /* Any.Type */ { + unsafeBitCast(DataProtocol.self, to: UnsafeMutableRawPointer.self) +} diff --git a/Sources/SwiftRuntimeFunctions/generated/SwiftJavaError+SwiftJava.swift b/Sources/SwiftRuntimeFunctions/generated/SwiftJavaError+SwiftJava.swift index bdc2d8494..3faaa80b5 100644 --- a/Sources/SwiftRuntimeFunctions/generated/SwiftJavaError+SwiftJava.swift +++ b/Sources/SwiftRuntimeFunctions/generated/SwiftJavaError+SwiftJava.swift @@ -1,6 +1,5 @@ // Generated by swift-java -import SwiftRuntimeFunctions // ==== -------------------------------------------------- // Thunks for SwiftJavaError diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Data.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Data.java index b4f58e69c..bd9afe7cb 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Data.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Data.java @@ -1,32 +1,23 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2026 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 -// -//===----------------------------------------------------------------------===// +// Generated by jextract-swift +// Swift module: SwiftJava package org.swift.swiftkit.core.foundation; import org.swift.swiftkit.core.*; import org.swift.swiftkit.core.util.*; import org.swift.swiftkit.core.collections.*; - -import java.util.Objects; +import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import org.swift.swiftkit.core.annotations.*; public final class Data implements JNISwiftInstance, DataProtocol { + static final String LIB_NAME = "SwiftJava"; + @SuppressWarnings("unused") private static final boolean INITIALIZED_LIBS = initializeLibs(); static boolean initializeLibs() { System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); + System.loadLibrary(LIB_NAME); return true; } /** @@ -38,11 +29,11 @@ static boolean initializeLibs() { private Data(long selfPointer, SwiftArena swiftArena) { SwiftObjects.requireNonZero(selfPointer, "selfPointer"); this.selfPointer = selfPointer; - + // Only register once we have fully initialized the object since this will need the object pointer. swiftArena.register(this); - } - + } // printConcreteType(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:241 + /** * Assume that the passed {@code long} represents a memory address of a {@link Data}. *

@@ -55,30 +46,30 @@ private Data(long selfPointer, SwiftArena swiftArena) { public static Data wrapMemoryAddressUnsafe(long selfPointer, SwiftArena swiftArena) { return new Data(selfPointer, swiftArena); } - + public static Data wrapMemoryAddressUnsafe(long selfPointer) { return new Data(selfPointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); } /** Pointer to the "self". */ private final long selfPointer; - + /** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */ private final AtomicBoolean $state$destroyed = new AtomicBoolean(false); - + public long $memoryAddress() { return this.selfPointer; } - + @Override public AtomicBoolean $statusDestroyedFlag() { return $state$destroyed; } - - - + + + // ==== -------------------------------------------------- // Data.init - + /** * Downcall to Swift: * {@snippet lang=swift : @@ -87,14 +78,14 @@ public static Data wrapMemoryAddressUnsafe(long selfPointer) { */ public static Data init(@Unsigned byte[] bytes, SwiftArena swiftArena) { return Data.wrapMemoryAddressUnsafe(Data.$init(Objects.requireNonNull(bytes, "bytes must not be null")), swiftArena); - } + } // printJavaBindingWrapperMethod(_:_:importedFunc:skipMethodBody:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:693 private static native long $init(byte[] bytes); - - - + + + // ==== -------------------------------------------------- // getter:Data.count - + /** * Downcall to Swift: * {@snippet lang=swift : @@ -103,9 +94,9 @@ public static Data init(@Unsigned byte[] bytes, SwiftArena swiftArena) { */ public long getCount() { return Data.$getCount(this.$memoryAddress()); - } + } // printJavaBindingWrapperMethod(_:_:importedFunc:skipMethodBody:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:693 private static native long $getCount(long selfPointer); - + /** * Creates a new Swift @{link Data} instance from a byte array. * @@ -117,16 +108,15 @@ public static Data fromByteArray(byte[] bytes, SwiftArena swiftArena) { Objects.requireNonNull(bytes, "bytes cannot be null"); return Data.init(bytes, swiftArena); } - /** * Copies the contents of this Data to a new byte array. - * + * * This is a relatively efficient implementation, which avoids native array copies, - * however it will still perform a copy of the data onto the JVM heap, so use this + * however it will still perform a copy of the data onto the JVM heap, so use this * only when necessary. - * + * *

When utmost performance is necessary, you may want to investigate the FFM mode - * of jextract which is able to map memory more efficiently. + * of jextract which is able to map memory more efficiently. * * @return A byte array containing a copy of this Data's bytes */ @@ -134,36 +124,36 @@ public byte[] toByteArray() { return $toByteArray(this.$memoryAddress()); } private static native byte[] $toByteArray(long selfPointer); - + /** * Copies the contents of this Data to a new byte array. - * + * * @deprecated Prefer using the `toByteArray` method as it is more performant. - * This implementation uses a naive conversion path from native bytes into jbytes + * This implementation uses a naive conversion path from native bytes into jbytes * and then copying them onto the jvm heap. - * + * * @return A byte array containing a copy of this Data's bytes */ @Deprecated(forRemoval = true) public byte[] toByteArrayIndirectCopy() { return $toByteArrayIndirectCopy(this.$memoryAddress()); } + private static native byte[] $toByteArrayIndirectCopy(long selfPointer); - private static native long $typeMetadataAddressDowncall(); @Override public long $typeMetadataAddress() { return Data.$typeMetadataAddressDowncall(); - } - + } // printTypeMetadataAddressFunction(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:785 + public String toString() { return SwiftObjects.toString(this.$memoryAddress(), this.$typeMetadataAddress()); } - + public String toDebugString() { return SwiftObjects.toDebugString(this.$memoryAddress(), this.$typeMetadataAddress()); } - + @Override public Runnable $createDestroyFunction() { long self$ = this.$memoryAddress(); @@ -182,5 +172,5 @@ public void run() { SwiftObjects.destroy(self$, selfType$); } }; - } -} + } // printDestroyFunction(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:799 +} // printNominal(_:_:body:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:411 diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/DataProtocol.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/DataProtocol.java index f3240bc0d..1a9f82f73 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/DataProtocol.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/DataProtocol.java @@ -1,20 +1,14 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2026 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 -// -//===----------------------------------------------------------------------===// +// Generated by jextract-swift +// Swift module: SwiftJava package org.swift.swiftkit.core.foundation; -import org.swift.swiftkit.core.JNISwiftInstance; +import org.swift.swiftkit.core.*; +import org.swift.swiftkit.core.util.*; +import org.swift.swiftkit.core.collections.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import org.swift.swiftkit.core.annotations.*; public interface DataProtocol extends JNISwiftInstance { -} +} // printProtocol(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:165 diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Date.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Date.java index 157fe4f81..59368c852 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Date.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Date.java @@ -1,31 +1,23 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2026 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 -// -//===----------------------------------------------------------------------===// +// Generated by jextract-swift +// Swift module: SwiftJava package org.swift.swiftkit.core.foundation; import org.swift.swiftkit.core.*; import org.swift.swiftkit.core.util.*; import org.swift.swiftkit.core.collections.*; -import java.util.Objects; +import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import org.swift.swiftkit.core.annotations.*; public final class Date implements JNISwiftInstance { + static final String LIB_NAME = "SwiftJava"; + @SuppressWarnings("unused") private static final boolean INITIALIZED_LIBS = initializeLibs(); static boolean initializeLibs() { System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); + System.loadLibrary(LIB_NAME); return true; } /** @@ -37,11 +29,11 @@ static boolean initializeLibs() { private Date(long selfPointer, SwiftArena swiftArena) { SwiftObjects.requireNonZero(selfPointer, "selfPointer"); this.selfPointer = selfPointer; - + // Only register once we have fully initialized the object since this will need the object pointer. swiftArena.register(this); - } - + } // printConcreteType(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:241 + /** * Assume that the passed {@code long} represents a memory address of a {@link Date}. *

@@ -54,29 +46,29 @@ private Date(long selfPointer, SwiftArena swiftArena) { public static Date wrapMemoryAddressUnsafe(long selfPointer, SwiftArena swiftArena) { return new Date(selfPointer, swiftArena); } - + public static Date wrapMemoryAddressUnsafe(long selfPointer) { return new Date(selfPointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); } /** Pointer to the "self". */ private final long selfPointer; - + /** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */ private final AtomicBoolean $state$destroyed = new AtomicBoolean(false); - + public long $memoryAddress() { return this.selfPointer; } - + @Override public AtomicBoolean $statusDestroyedFlag() { return $state$destroyed; } - - + + // ==== -------------------------------------------------- // Date.init - + /** * Downcall to Swift: * {@snippet lang=swift : @@ -85,13 +77,13 @@ public static Date wrapMemoryAddressUnsafe(long selfPointer) { */ public static Date init(double timeIntervalSince1970, SwiftArena swiftArena) { return Date.wrapMemoryAddressUnsafe(Date.$init(timeIntervalSince1970), swiftArena); - } + } // printJavaBindingWrapperMethod(_:_:importedFunc:skipMethodBody:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:693 private static native long $init(double timeIntervalSince1970); - - + + // ==== -------------------------------------------------- // getter:Date.timeIntervalSince1970 - + /** * Downcall to Swift: * {@snippet lang=swift : @@ -100,9 +92,9 @@ public static Date init(double timeIntervalSince1970, SwiftArena swiftArena) { */ public double getTimeIntervalSince1970() { return Date.$getTimeIntervalSince1970(this.$memoryAddress()); - } + } // printJavaBindingWrapperMethod(_:_:importedFunc:skipMethodBody:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:693 private static native double $getTimeIntervalSince1970(long selfPointer); - + /** * Converts this wrapped date to a Java {@link java.time.Instant}. *

@@ -117,7 +109,7 @@ public java.time.Instant toInstant() { long nanos = Math.round((this.getTimeIntervalSince1970() - seconds) * 1_000_000_000); return java.time.Instant.ofEpochSecond(seconds, nanos); } - + /** * Initializes a Swift {@code Foundation.Date} from a Java {@link java.time.Instant}. * @@ -145,21 +137,20 @@ public static Date fromInstant(java.time.Instant instant, SwiftArena swiftArena) double timeIntervalSince1970 = instant.getEpochSecond() + (instant.getNano() / 1_000_000_000.0); return Date.init(timeIntervalSince1970, swiftArena); } - private static native long $typeMetadataAddressDowncall(); @Override public long $typeMetadataAddress() { return Date.$typeMetadataAddressDowncall(); - } - + } // printTypeMetadataAddressFunction(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:785 + public String toString() { return SwiftObjects.toString(this.$memoryAddress(), this.$typeMetadataAddress()); } - + public String toDebugString() { return SwiftObjects.toDebugString(this.$memoryAddress(), this.$typeMetadataAddress()); } - + @Override public Runnable $createDestroyFunction() { long self$ = this.$memoryAddress(); @@ -178,5 +169,5 @@ public void run() { SwiftObjects.destroy(self$, selfType$); } }; - } -} + } // printDestroyFunction(_:_:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:799 +} // printNominal(_:_:body:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:411 diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/Data.java b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/Data.java index ffeaea2eb..04a9603e5 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/Data.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/Data.java @@ -1,16 +1,5 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2026 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 -// -//===----------------------------------------------------------------------===// +// Generated by jextract-swift +// Swift module: SwiftRuntimeFunctions package org.swift.swiftkit.ffm.foundation; @@ -22,6 +11,7 @@ import java.lang.foreign.*; import java.lang.invoke.*; import java.util.*; +import java.nio.charset.StandardCharsets; public final class Data extends FFMSwiftInstance implements SwiftValue { static final String LIB_NAME = "SwiftRuntimeFunctions"; @@ -35,22 +25,22 @@ static boolean initializeLibs() { SwiftLibraries.loadLibraryWithFallbacks(LIB_NAME); return true; } - + public static final SwiftAnyType TYPE_METADATA = new SwiftAnyType(SwiftRuntime.swiftjava.getType("SwiftRuntimeFunctions", "Data")); - public SwiftAnyType $swiftType() { + public final SwiftAnyType $swiftType() { return TYPE_METADATA; } - + public static final GroupLayout $LAYOUT = (GroupLayout) SwiftValueWitnessTable.layoutOfSwiftType(TYPE_METADATA.$memorySegment()); - public GroupLayout $layout() { + public final GroupLayout $layout() { return $LAYOUT; } - + private Data(MemorySegment segment, AllocatingSwiftArena arena) { super(segment, arena); } - + /** * Assume that the passed {@code MemorySegment} represents a memory address of a {@link Data}. *

@@ -63,10 +53,10 @@ private Data(MemorySegment segment, AllocatingSwiftArena arena) { public static Data wrapMemoryAddressUnsafe(MemorySegment selfPointer, AllocatingSwiftArena arena) { return new Data(selfPointer, arena); } - + // ==== -------------------------------------------------- // Data.init - + /** * {@snippet lang=c : * void swiftjava_SwiftRuntimeFunctions_Data_init_bytes_count(const void *bytes, ptrdiff_t count, void *_result) @@ -79,7 +69,7 @@ private static class swiftjava_SwiftRuntimeFunctions_Data_init_bytes_count { /* _result: */SwiftValueLayout.SWIFT_POINTER ); private static final MemorySegment ADDR = - SwiftRuntime.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_init_bytes_count"); + SwiftRuntimeFunctions.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_init_bytes_count"); private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); public static void call(java.lang.foreign.MemorySegment bytes, long count, java.lang.foreign.MemorySegment _result) { try { @@ -91,8 +81,7 @@ public static void call(java.lang.foreign.MemorySegment bytes, long count, java. throw new AssertionError("should not reach here", ex$); } } - } - + } // printJavaBindingDescriptorClass(_:_:symbolLookup:additionalContent:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:65 /** * Downcall to Swift: * {@snippet lang=swift : @@ -104,15 +93,15 @@ public static Data init(java.lang.foreign.MemorySegment bytes, long count, Alloc if (SwiftValueLayout.has32bitSwiftInt) { if (count < Integer.MIN_VALUE || count > Integer.MAX_VALUE) { throw new SwiftIntegerOverflowException("Parameter 'count' overflow: " + count); - } - } + } // printDowncall(_:_:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:506 + } // printDowncall(_:_:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:498 swiftjava_SwiftRuntimeFunctions_Data_init_bytes_count.call(bytes, count, result$); return Data.wrapMemoryAddressUnsafe(result$, swiftArena); - } - + } // printJavaBindingWrapperMethod(_:_:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:403 + // ==== -------------------------------------------------- // Data.init - + /** * {@snippet lang=c : * void swiftjava_SwiftRuntimeFunctions_Data_init__(const void *bytes_pointer, ptrdiff_t bytes_count, void *_result) @@ -125,7 +114,7 @@ private static class swiftjava_SwiftRuntimeFunctions_Data_init__ { /* _result: */SwiftValueLayout.SWIFT_POINTER ); private static final MemorySegment ADDR = - SwiftRuntime.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_init__"); + SwiftRuntimeFunctions.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_init__"); private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); public static void call(java.lang.foreign.MemorySegment bytes_pointer, long bytes_count, java.lang.foreign.MemorySegment _result) { try { @@ -137,8 +126,7 @@ public static void call(java.lang.foreign.MemorySegment bytes_pointer, long byte throw new AssertionError("should not reach here", ex$); } } - } - + } // printJavaBindingDescriptorClass(_:_:symbolLookup:additionalContent:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:65 /** * Downcall to Swift: * {@snippet lang=swift : @@ -151,11 +139,11 @@ public static Data init(@Unsigned byte[] bytes, AllocatingSwiftArena swiftArena) swiftjava_SwiftRuntimeFunctions_Data_init__.call(arena$.allocateFrom(ValueLayout.JAVA_BYTE, bytes), bytes.length, result$); return Data.wrapMemoryAddressUnsafe(result$, swiftArena); } - } - + } // printJavaBindingWrapperMethod(_:_:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:403 + // ==== -------------------------------------------------- // getter:Data.count - + /** * {@snippet lang=c : * ptrdiff_t swiftjava_SwiftRuntimeFunctions_Data_count$get(const void *self) @@ -167,7 +155,7 @@ private static class swiftjava_SwiftRuntimeFunctions_Data_count$get { /* self: */SwiftValueLayout.SWIFT_POINTER ); private static final MemorySegment ADDR = - SwiftRuntime.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_count$get"); + SwiftRuntimeFunctions.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_count$get"); private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); public static long call(java.lang.foreign.MemorySegment self) { try { @@ -179,8 +167,7 @@ public static long call(java.lang.foreign.MemorySegment self) { throw new AssertionError("should not reach here", ex$); } } - } - + } // printJavaBindingDescriptorClass(_:_:symbolLookup:additionalContent:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:65 /** * Downcall to Swift: * {@snippet lang=swift : @@ -193,14 +180,14 @@ public long getCount() throws SwiftIntegerOverflowException { if (SwiftValueLayout.has32bitSwiftInt) { if (result$checked < Integer.MIN_VALUE || result$checked > Integer.MAX_VALUE) { throw new SwiftIntegerOverflowException("Return value overflow: " + result$checked); - } - } + } // printReturnWithOverflowCheck(_:value:overflowCheck:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:603 + } // printReturnWithOverflowCheck(_:value:overflowCheck:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:602 return result$checked; - } - + } // printJavaBindingWrapperMethod(_:_:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:403 + // ==== -------------------------------------------------- // Data.withUnsafeBytes - + /** * {@snippet lang=c : * void swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__(void (*body)(const void *, ptrdiff_t), const void *self) @@ -212,7 +199,7 @@ private static class swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__ { /* self: */SwiftValueLayout.SWIFT_POINTER ); private static final MemorySegment ADDR = - SwiftRuntime.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__"); + SwiftRuntimeFunctions.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__"); private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); public static void call(java.lang.foreign.MemorySegment body, java.lang.foreign.MemorySegment self) { try { @@ -242,8 +229,8 @@ public interface Function { private static MemorySegment toUpcallStub(Function fi, Arena arena) { return Linker.nativeLinker().upcallStub(HANDLE.bindTo(fi), DESC, arena); } - } - } + } // printFunctionPointerParameterDescriptorClass(_:_:_:impl:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:223 + } // printJavaBindingDescriptorClass(_:_:symbolLookup:additionalContent:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:65 public static class withUnsafeBytes { @FunctionalInterface public interface body { @@ -253,9 +240,8 @@ public interface body { return swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__.$body.toUpcallStub((_0_pointer, _0_count) -> { fi.apply(_0_pointer.reinterpret(_0_count)); }, arena); - } - } - + } // printJavaBindingWrapperFunctionTypeHelper(_:_:_:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:327 + } // printJavaBindingWrapperHelperClass(_:_:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:280 /** * Downcall to Swift: * {@snippet lang=swift : @@ -267,11 +253,11 @@ public void withUnsafeBytes(withUnsafeBytes.body body) { try(var arena$ = Arena.ofConfined()) { swiftjava_SwiftRuntimeFunctions_Data_withUnsafeBytes__.call(withUnsafeBytes.$toUpcallStub(body, arena$), this.$memorySegment()); } - } - + } // printJavaBindingWrapperMethod(_:_:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:403 + // ==== -------------------------------------------------- // Data helper methods - + /** * Creates a new Swift {@link Data} instance from a byte array. * @@ -283,7 +269,6 @@ public static Data fromByteArray(byte[] bytes, AllocatingSwiftArena arena) { Objects.requireNonNull(bytes, "bytes cannot be null"); return Data.init(bytes, arena); } - /** * {@snippet lang=c : * void swiftjava_SwiftRuntimeFunctions_Data_copyBytes__(void *const self, void *destination, ptrdiff_t count) @@ -296,7 +281,7 @@ private static class swiftjava_SwiftRuntimeFunctions_Data_copyBytes__ { /* count: */SwiftValueLayout.SWIFT_INT ); private static final MemorySegment ADDR = - SwiftRuntime.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_copyBytes__"); + SwiftRuntimeFunctions.findOrThrow("swiftjava_SwiftRuntimeFunctions_Data_copyBytes__"); private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); public static void call(java.lang.foreign.MemorySegment self, java.lang.foreign.MemorySegment destination, long count) { try { @@ -308,8 +293,7 @@ public static void call(java.lang.foreign.MemorySegment self, java.lang.foreign. throw new AssertionError("should not reach here", ex$); } } - } - + } // printJavaBindingDescriptorClass(_:_:symbolLookup:additionalContent:) @ JExtractSwiftLib/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift:65 /** * Copies the contents of this Data to a new {@link MemorySegment}. * @@ -329,9 +313,8 @@ public MemorySegment toMemorySegment(AllocatingSwiftArena arena) { swiftjava_SwiftRuntimeFunctions_Data_copyBytes__.call(this.$memorySegment(), segment, count); return segment; } - /** - * Copies the contents of this Data to a new {@link java.nio.ByteBuffer}. + * Copies the contents of this Data to a new {@link ByteBuffer}. * * The returned {@link java.nio.ByteBuffer} is a view over native memory and is valid for the * lifetime of the arena. This avoids an additional copy to the Java heap. @@ -349,7 +332,6 @@ public java.nio.ByteBuffer toByteBuffer(AllocatingSwiftArena arena) { swiftjava_SwiftRuntimeFunctions_Data_copyBytes__.call(this.$memorySegment(), segment, count); return segment.asByteBuffer(); } - /** * Copies the contents of this Data to a new byte array. * The lifetime of the array is independent of the arena, the arena is just used for an intermediary copy. @@ -370,7 +352,6 @@ public byte[] toByteArray(AllocatingSwiftArena arena) { swiftjava_SwiftRuntimeFunctions_Data_copyBytes__.call(this.$memorySegment(), segment, count); return segment.toArray(ValueLayout.JAVA_BYTE); } - /** * Copies the contents of this Data to a new byte array. * The lifetime of the array is independent of the arena, the arena is just used for an intermediary copy. @@ -403,4 +384,4 @@ public String toString() { + ")@" + $memorySegment(); } -} +} // printNominal(_:_:body:) @ JExtractSwiftLib/FFMSwift2JavaGenerator.swift:395 diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/DataProtocol.java b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/DataProtocol.java index 2881ac85e..8d16ef379 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/DataProtocol.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/DataProtocol.java @@ -1,16 +1,5 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2026 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 -// -//===----------------------------------------------------------------------===// +// Generated by jextract-swift +// Swift module: SwiftRuntimeFunctions package org.swift.swiftkit.ffm.foundation; @@ -22,6 +11,7 @@ import java.lang.foreign.*; import java.lang.invoke.*; import java.util.*; +import java.nio.charset.StandardCharsets; public final class DataProtocol extends FFMSwiftInstance implements SwiftValue { static final String LIB_NAME = "SwiftRuntimeFunctions"; @@ -35,22 +25,22 @@ static boolean initializeLibs() { SwiftLibraries.loadLibraryWithFallbacks(LIB_NAME); return true; } - + public static final SwiftAnyType TYPE_METADATA = new SwiftAnyType(SwiftRuntime.swiftjava.getType("SwiftRuntimeFunctions", "DataProtocol")); - public SwiftAnyType $swiftType() { + public final SwiftAnyType $swiftType() { return TYPE_METADATA; } - + public static final GroupLayout $LAYOUT = (GroupLayout) SwiftValueWitnessTable.layoutOfSwiftType(TYPE_METADATA.$memorySegment()); - public GroupLayout $layout() { + public final GroupLayout $layout() { return $LAYOUT; } - + private DataProtocol(MemorySegment segment, AllocatingSwiftArena arena) { super(segment, arena); } - + /** * Assume that the passed {@code MemorySegment} represents a memory address of a {@link DataProtocol}. *

@@ -63,7 +53,6 @@ private DataProtocol(MemorySegment segment, AllocatingSwiftArena arena) { public static DataProtocol wrapMemoryAddressUnsafe(MemorySegment selfPointer, AllocatingSwiftArena arena) { return new DataProtocol(selfPointer, arena); } - @Override public String toString() { return getClass().getSimpleName() @@ -72,4 +61,4 @@ public String toString() { + ")@" + $memorySegment(); } -} +} // printNominal(_:_:body:) @ JExtractSwiftLib/FFMSwift2JavaGenerator.swift:395 diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/generated/SwiftJavaErrorException.java b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/generated/SwiftJavaErrorException.java index ce276caa7..aff2eccee 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/generated/SwiftJavaErrorException.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/generated/SwiftJavaErrorException.java @@ -127,4 +127,4 @@ private static String fetchDescription(MemorySegment errorPointer) { return "Swift error (address: 0x" + Long.toHexString(errorPointer.address()) + ")"; } } -} // printNominal(_:_:body:) @ JExtractSwiftLib/FFMSwift2JavaGenerator.swift:385 +} // printNominal(_:_:body:) @ JExtractSwiftLib/FFMSwift2JavaGenerator.swift:395 diff --git a/scripts/swiftkit-ffm-generate-bindings.sh b/scripts/swiftkit-ffm-generate-bindings.sh index 9f706a77c..43cb277e9 100755 --- a/scripts/swiftkit-ffm-generate-bindings.sh +++ b/scripts/swiftkit-ffm-generate-bindings.sh @@ -23,15 +23,18 @@ set -euo pipefail REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" JAVA_OUTPUT="${REPO_ROOT}/SwiftKitFFM/src/main/java" -JAVA_PACKAGE="org.swift.swiftkit.ffm.generated" +PACKAGE_GENERATED="org.swift.swiftkit.ffm.generated" +PACKAGE_FOUNDATION="org.swift.swiftkit.ffm.foundation" -# Declare types to generate: SWIFT_MODULE SINGLE_TYPE INPUT_SWIFT_DIR OUTPUT_SWIFT_DIR +# Declare types to generate: SWIFT_MODULE SINGLE_TYPE JAVA_PACKAGE INPUT_SWIFT_DIR OUTPUT_SWIFT_DIR TYPES=( - "SwiftRuntimeFunctions SwiftJavaError Sources/SwiftRuntimeFunctions Sources/SwiftRuntimeFunctions/generated" + "SwiftRuntimeFunctions SwiftJavaError ${PACKAGE_GENERATED} Sources/SwiftRuntimeFunctions Sources/SwiftRuntimeFunctions/generated" + "SwiftRuntimeFunctions Data ${PACKAGE_FOUNDATION} Sources/FakeFoundation Sources/SwiftRuntimeFunctions/foundation" + "SwiftRuntimeFunctions DataProtocol ${PACKAGE_FOUNDATION} Sources/FakeFoundation Sources/SwiftRuntimeFunctions/foundation" ) for entry in "${TYPES[@]}"; do - read -r MODULE SINGLE_TYPE INPUT_SWIFT OUTPUT_SWIFT <<< "$entry" + read -r MODULE SINGLE_TYPE JAVA_PACKAGE INPUT_SWIFT OUTPUT_SWIFT <<< "$entry" echo "==> Generating ${SINGLE_TYPE} (module: ${MODULE})..." diff --git a/scripts/swiftkit-jni-generate-bindings.sh b/scripts/swiftkit-jni-generate-bindings.sh index 7af973949..285350b64 100755 --- a/scripts/swiftkit-jni-generate-bindings.sh +++ b/scripts/swiftkit-jni-generate-bindings.sh @@ -27,8 +27,9 @@ JAVA_PACKAGE="org.swift.swiftkit.core.foundation" # Declare types to generate: SWIFT_MODULE SINGLE_TYPE INPUT_SWIFT_DIR OUTPUT_SWIFT_DIR TYPES=( - "Foundation Data Sources/FakeFoundation Sources/SwiftJavaRuntimeSupport/foundation" - "Foundation Date Sources/FakeFoundation Sources/SwiftJavaRuntimeSupport/foundation" + "SwiftJava Data Sources/FakeFoundation Sources/SwiftJavaRuntimeSupport/foundation" + "SwiftJava DataProtocol Sources/FakeFoundation Sources/SwiftJavaRuntimeSupport/foundation" + "SwiftJava Date Sources/FakeFoundation Sources/SwiftJavaRuntimeSupport/foundation" ) for entry in "${TYPES[@]}"; do From baecaf202f453c66215bda0730665280db65b796 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 8 Apr 2026 15:47:38 +0900 Subject: [PATCH 15/17] add public accessor --- Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift | 2 +- .../swift/swiftkit/ffm/generated/SwiftRuntimeFunctions.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 8427208c3..65a3d9d65 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -448,7 +448,7 @@ extension FFMSwift2JavaGenerator { printer.print( """ - static MemorySegment findOrThrow(String symbol) { + public static MemorySegment findOrThrow(String symbol) { return SYMBOL_LOOKUP.find(symbol) .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); } diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/generated/SwiftRuntimeFunctions.java b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/generated/SwiftRuntimeFunctions.java index ec27f2a29..ff7765dfb 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/generated/SwiftRuntimeFunctions.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/generated/SwiftRuntimeFunctions.java @@ -17,14 +17,14 @@ public final class SwiftRuntimeFunctions { private SwiftRuntimeFunctions() { // Should not be called directly } - + // Static enum to force initialization private static enum Initializer { FORCE; // Refer to this to force outer Class initialization (and static{} blocks to trigger) } static final String LIB_NAME = "SwiftRuntimeFunctions"; static final Arena LIBRARY_ARENA = Arena.ofAuto(); - static MemorySegment findOrThrow(String symbol) { + public static MemorySegment findOrThrow(String symbol) { return SYMBOL_LOOKUP.find(symbol) .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); } @@ -49,7 +49,7 @@ private static SymbolLookup getSymbolLookup() { SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); SwiftLibraries.loadLibraryWithFallbacks(LIB_NAME); } - + if (PlatformUtils.isMacOS()) { return SymbolLookup.libraryLookup(System.mapLibraryName(LIB_NAME), LIBRARY_ARENA) .or(SymbolLookup.loaderLookup()) From ee1ce478685922d02508aefe56c396e97d291ef3 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 8 Apr 2026 16:37:03 +0900 Subject: [PATCH 16/17] fix stripping import target module name --- .../JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift | 2 +- Sources/SwiftJavaRuntimeSupport/Foundation/Data+SwiftJava.swift | 1 - .../Foundation/DataProtocol+SwiftJava.swift | 1 - Sources/SwiftJavaRuntimeSupport/Foundation/Date+SwiftJava.swift | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 1883eebd2..fdf1db9f3 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -834,7 +834,7 @@ extension JNISwift2JavaGenerator { "SwiftJava", "SwiftJavaJNICore", ] - if type?.swiftNominal.moduleName != "SwiftJavaRuntimeSupport" { + if !(type?.swiftNominal.moduleName == "SwiftJava") { imports.append("SwiftJavaRuntimeSupport") } printer.print("// Generated by swift-java") diff --git a/Sources/SwiftJavaRuntimeSupport/Foundation/Data+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+SwiftJava.swift index 1637eb1fc..3e8c20d68 100644 --- a/Sources/SwiftJavaRuntimeSupport/Foundation/Data+SwiftJava.swift +++ b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+SwiftJava.swift @@ -5,7 +5,6 @@ import SwiftJava import SwiftJavaJNICore -import SwiftJavaRuntimeSupport #if canImport(FoundationEssentials) import FoundationEssentials diff --git a/Sources/SwiftJavaRuntimeSupport/Foundation/DataProtocol+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/DataProtocol+SwiftJava.swift index eb9dd1dad..ad62ac0a0 100644 --- a/Sources/SwiftJavaRuntimeSupport/Foundation/DataProtocol+SwiftJava.swift +++ b/Sources/SwiftJavaRuntimeSupport/Foundation/DataProtocol+SwiftJava.swift @@ -5,7 +5,6 @@ import SwiftJava import SwiftJavaJNICore -import SwiftJavaRuntimeSupport #if canImport(FoundationEssentials) import FoundationEssentials diff --git a/Sources/SwiftJavaRuntimeSupport/Foundation/Date+SwiftJava.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/Date+SwiftJava.swift index 920167cbb..6a240919a 100644 --- a/Sources/SwiftJavaRuntimeSupport/Foundation/Date+SwiftJava.swift +++ b/Sources/SwiftJavaRuntimeSupport/Foundation/Date+SwiftJava.swift @@ -5,7 +5,6 @@ import SwiftJava import SwiftJavaJNICore -import SwiftJavaRuntimeSupport #if canImport(FoundationEssentials) import FoundationEssentials From ac26e217fed14e3874ff8189ed0e43774c8f9c64 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 8 Apr 2026 17:22:50 +0900 Subject: [PATCH 17/17] Update .licenseignore --- .licenseignore | 4 ++++ .../{foundation => Foundation}/Data+SwiftJava.swift | 0 .../{foundation => Foundation}/DataProtocol+SwiftJava.swift | 0 3 files changed, 4 insertions(+) rename Sources/SwiftRuntimeFunctions/{foundation => Foundation}/Data+SwiftJava.swift (100%) rename Sources/SwiftRuntimeFunctions/{foundation => Foundation}/DataProtocol+SwiftJava.swift (100%) diff --git a/.licenseignore b/.licenseignore index a49ab625d..b8009e6df 100644 --- a/.licenseignore +++ b/.licenseignore @@ -52,3 +52,7 @@ Samples/JavaDependencySampleApp/gradle Sources/_Subprocess/** Sources/_SubprocessCShims/** Samples/gradle +Sources/SwiftJavaRuntimeSupport/Foundation/* +Sources/SwiftRuntimeFunctions/Foundation/* +SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/* +SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/foundation/* diff --git a/Sources/SwiftRuntimeFunctions/foundation/Data+SwiftJava.swift b/Sources/SwiftRuntimeFunctions/Foundation/Data+SwiftJava.swift similarity index 100% rename from Sources/SwiftRuntimeFunctions/foundation/Data+SwiftJava.swift rename to Sources/SwiftRuntimeFunctions/Foundation/Data+SwiftJava.swift diff --git a/Sources/SwiftRuntimeFunctions/foundation/DataProtocol+SwiftJava.swift b/Sources/SwiftRuntimeFunctions/Foundation/DataProtocol+SwiftJava.swift similarity index 100% rename from Sources/SwiftRuntimeFunctions/foundation/DataProtocol+SwiftJava.swift rename to Sources/SwiftRuntimeFunctions/Foundation/DataProtocol+SwiftJava.swift