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..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 @@ -14,10 +14,10 @@ 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; +import org.swift.swiftkit.ffm.foundation.Data; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; @@ -79,7 +79,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/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/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/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; 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..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,6 +16,7 @@ import org.junit.jupiter.api.Test; import org.swift.swiftkit.core.SwiftArena; +import org.swift.swiftkit.core.foundation.Data; import static org.junit.jupiter.api.Assertions.*; 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/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..1385894d3 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,7 +621,9 @@ 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 { + } 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)") @@ -776,7 +780,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..e4363b7a4 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,26 @@ 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: .optional(.swiftkitFFMFoundationData) + ) + ], + conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false) + ) + case .foundationDataProtocol, .essentialsDataProtocol: + return TranslatedParameter( + javaParameters: [ + JavaParameter( + name: parameterName, + type: .optional(.swiftkitFFMFoundationDataProtocol) + ) + ], + conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false) + ) default: throw JavaTranslationError.unhandledType(known: .optional(swiftType)) } @@ -647,7 +671,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: .optional(translatedTy)) ], conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false) ) @@ -750,7 +774,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 41c063048..4572316c3 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/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index e6b3c71b8..833aabbcd 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( """ @@ -616,7 +598,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 { @@ -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 69455ba69..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 @@ -505,10 +505,24 @@ extension JNISwift2JavaGenerator { ) case .foundationDate, .essentialsDate: - break // Handled as wrapped struct + return TranslatedParameter( + parameter: JavaParameter( + name: parameterName, + type: .concrete(.swiftkitCoreFoundationDate), + annotations: parameterAnnotations, + ), + conversion: .valueMemoryAddress(.placeholder), + ) case .foundationData, .essentialsData: - break // Handled as wrapped struct + return TranslatedParameter( + parameter: JavaParameter( + name: parameterName, + type: .concrete(.swiftkitCoreFoundationData), + annotations: parameterAnnotations, + ), + conversion: .valueMemoryAddress(.placeholder), + ) case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer: return TranslatedParameter( @@ -872,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( @@ -945,12 +959,22 @@ extension JNISwift2JavaGenerator { ) case .foundationDate, .essentialsDate: - // Handled as wrapped struct - break + let javaType = JavaType.swiftkitCoreFoundationDate + return TranslatedResult( + javaType: javaType, + annotations: resultAnnotations, + outParameters: [], + conversion: .wrapMemoryAddressUnsafe(.placeholder, javaType), + ) case .foundationData, .essentialsData: - // Handled as wrapped struct - break + let javaType = JavaType.swiftkitCoreFoundationData + return TranslatedResult( + javaType: javaType, + annotations: resultAnnotations, + outParameters: [], + conversion: .wrapMemoryAddressUnsafe(.placeholder, javaType), + ) case .foundationUUID, .essentialsUUID: return TranslatedResult( @@ -1053,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 { @@ -1094,13 +1118,13 @@ extension JNISwift2JavaGenerator { return .swiftSet(elementJavaType) case .foundationDate, .essentialsDate: - return .class(package: nil, name: "Date") + return .swiftkitCoreFoundationDate case .foundationData, .essentialsData: - return .class(package: nil, name: "Data") + return .swiftkitCoreFoundationData case .foundationDataProtocol, .essentialsDataProtocol: - return .class(package: nil, name: "DataProtocol") + return .swiftkitCoreFoundationDataProtocol 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] = [] @@ -1338,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, @@ -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/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/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift index 34cb7f458..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]) @@ -59,4 +64,23 @@ extension JavaType { .class(package: "java.util", name: "UUID") } + static var swiftkitCoreFoundationDate: JavaType { + .class(package: "org.swift.swiftkit.core.foundation", name: "Date") + } + + static var swiftkitCoreFoundationData: JavaType { + .class(package: "org.swift.swiftkit.core.foundation", name: "Data") + } + + 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/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift index 106247a48..5ac2384d7 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift @@ -127,39 +127,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/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift new file mode 100644 index 000000000..2451ccd0b --- /dev/null +++ b/Sources/SwiftJavaRuntimeSupport/Foundation/Data+JNI.swift @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// 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/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/SwiftRuntimeFunctions/foundation/Data+FFM.swift b/Sources/SwiftRuntimeFunctions/foundation/Data+FFM.swift new file mode 100644 index 000000000..b7e2b6ab5 --- /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 */ { + 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 { + 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 + 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 */ { + unsafeBitCast((any DataProtocol).self, to: UnsafeMutableRawPointer.self) +} 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 new file mode 100644 index 000000000..b4f58e69c --- /dev/null +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Data.java @@ -0,0 +1,186 @@ +//===----------------------------------------------------------------------===// +// +// 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.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.concurrent.atomic.AtomicBoolean; +import org.swift.swiftkit.core.annotations.*; + +public final class Data implements JNISwiftInstance, DataProtocol { + @SuppressWarnings("unused") + private static final boolean INITIALIZED_LIBS = initializeLibs(); + static boolean initializeLibs() { + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); + 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); + } + + /** + * 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); + } + 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()); + } + 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(); + } + + 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$); + } + }; + } +} 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 new file mode 100644 index 000000000..f3240bc0d --- /dev/null +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/DataProtocol.java @@ -0,0 +1,20 @@ +//===----------------------------------------------------------------------===// +// +// 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.foundation; + +import org.swift.swiftkit.core.JNISwiftInstance; + +public interface DataProtocol extends JNISwiftInstance { +} 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 new file mode 100644 index 000000000..157fe4f81 --- /dev/null +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/foundation/Date.java @@ -0,0 +1,182 @@ +//===----------------------------------------------------------------------===// +// +// 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.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.concurrent.atomic.AtomicBoolean; +import org.swift.swiftkit.core.annotations.*; + +public final class Date implements JNISwiftInstance { + @SuppressWarnings("unused") + private static final boolean INITIALIZED_LIBS = initializeLibs(); + static boolean initializeLibs() { + System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); + 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); + } + + /** + * 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); + } + 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()); + } + 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(); + } + + 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$); + } + }; + } +} 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(); + } +} diff --git a/Tests/JExtractSwiftTests/DataImportTests.swift b/Tests/JExtractSwiftTests/DataImportTests.swift index e67a3db87..42149c5ee 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, java.util.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..7e694640c 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 + expectedChunks: [ + """ + public static org.swift.swiftkit.core.foundation.Date returnDate(SwiftArena swiftArena) { + """ + ], ) try assertOutput( input: text, - mode, + .jni, .swift, - expectedChunks: expectedSwiftChunks - ) - } - - @Test( - "Import: Date type", - arguments: [ - ( - JExtractGenerationMode.jni, - /* expected Java chunks */ - [ - """ - public final class Date implements JNISwiftInstance { - """, - """ - 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, - .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/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/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..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)); } """, 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(", ], ) }