diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index 7b6cb7e56..368d9d6a4 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -398,11 +398,19 @@ extension FFMSwift2JavaGenerator { } switch knownType { case .unsafePointer, .unsafeMutablePointer: - // FIXME: Implement - throw JavaTranslationError.unhandledType(swiftType) - case .unsafeBufferPointer, .unsafeMutableBufferPointer: - // FIXME: Implement - throw JavaTranslationError.unhandledType(swiftType) + return TranslatedParameter( + parameter: JavaParameter(name: parameterName, type: .javaForeignMemorySegment), + conversion: .placeholder + ) + case .unsafeBufferPointer(let element), .unsafeMutableBufferPointer(let element): + let elementLayout = try CType(cdeclType: element).foreignValueLayout + return TranslatedParameter( + parameter: JavaParameter(name: parameterName, type: .javaForeignMemorySegment), + conversion: .commaSeparated([ + .placeholder, + .constant("\(parameterName).byteSize() / \(elementLayout.description).byteSize()"), + ]) + ) case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer: return TranslatedParameter( @@ -739,11 +747,32 @@ extension FFMSwift2JavaGenerator { break // Implemented as wrapper case .unsafePointer, .unsafeMutablePointer: - // FIXME: Implement - throw JavaTranslationError.unhandledType(swiftType) - case .unsafeBufferPointer, .unsafeMutableBufferPointer: - // FIXME: Implement - throw JavaTranslationError.unhandledType(swiftType) + + return TranslatedResult( + javaResultType: .javaForeignMemorySegment, + annotations: resultAnnotations, + outParameters: [], + conversion: .placeholder + ) + case .unsafeBufferPointer(let element), .unsafeMutableBufferPointer(let element): + let elementLayout = try CType(cdeclType: element).foreignValueLayout + return TranslatedResult( + javaResultType: .javaForeignMemorySegment, + annotations: resultAnnotations, + outParameters: [ + JavaParameter(name: "pointer", type: .javaForeignMemorySegment), + JavaParameter(name: "count", type: .long), + ], + conversion: .method( + .readMemorySegment(.explodedName(component: "pointer"), as: .javaForeignMemorySegment), + methodName: "reinterpret", + arguments: [ + .constant("result$_count.get(SwiftValueLayout.SWIFT_INT64, 0) * \(elementLayout.description).byteSize()") + ], + withArena: false + ) + ) + case .string: return TranslatedResult( javaResultType: .javaLangString, diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index f3e6e5189..0c61d6e8c 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -456,6 +456,23 @@ extension JNISwift2JavaGenerator { case .foundationDate, .essentialsDate, .foundationData, .essentialsData, .foundationURL, .essentialsURL: break // Handled as wrapped struct + case .unsafePointer, .unsafeMutablePointer: + return TranslatedParameter( + parameter: JavaParameter( + name: parameterName, + type: .long + ), + conversion: .placeholder + ) + case .unsafeBufferPointer, .unsafeMutableBufferPointer: + return TranslatedParameter( + parameter: JavaParameter(name: parameterName, type: .swiftBufferPointer), + conversion: .commaSeparated([ + .member(.placeholder, field: "baseAddress"), + .member(.placeholder, field: "count"), + ]) + ) + case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer: return TranslatedParameter( @@ -935,6 +952,25 @@ extension JNISwift2JavaGenerator { ), ) + case .unsafePointer, .unsafeMutablePointer: + return TranslatedResult( + javaType: .long, + nativeJavaType: .long, + annotations: resultAnnotations, + outParameters: [], + conversion: .placeholder, + ) + + case .unsafeBufferPointer, .unsafeMutableBufferPointer: + return TranslatedResult( + javaType: .swiftBufferPointer, + nativeJavaType: .void, + annotations: resultAnnotations, + outParameters: [.init(name: resultName, type: .swiftBufferPointer, allocation: .new)], + conversion: .constant(resultName), + ) + //taenda todo: add support for UnsafeRawBufferPointer and UnsafeMutableRawBufferPointer + default: guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType.kind, config: self.config) else { throw JavaTranslationError.unsupportedSwiftType(swiftType) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index 8e644a7c7..62b5bd562 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -152,6 +152,35 @@ extension JNISwift2JavaGenerator { elementType: element, parameterName: parameterName ) + case .unsafePointer(let pointee), .unsafeMutablePointer(let pointee): + return NativeParameter( + parameters: [ + JavaParameter(name: parameterName, type: .long) + ], + conversion: .extractSwiftValue(.placeholder, swiftType: pointee), + indirectConversion: nil, + conversionCheck: nil + ) + + case .unsafeBufferPointer(let element), .unsafeMutableBufferPointer(let element): + let isMutable = knownType.kind == .unsafeMutableBufferPointer + let countParameterName = "\(parameterName)_count" + return NativeParameter( + parameters: [ + JavaParameter(name: parameterName, type: .long), + JavaParameter(name: countParameterName, type: .long), + ], + conversion: .constructUnsafeBufferPointer( + base: .extractSwiftValue(.placeholder, swiftType: element, allowNil: true), + count: .labelessInitializer( + .initFromJNI(.constant(countParameterName), swiftType: self.knownTypes.int64), + swiftType: self.knownTypes.int + ), + mutable: isMutable + ), + indirectConversion: nil, + conversionCheck: nil + ) case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer: let isMutable = knownType.kind == .unsafeMutableRawBufferPointer @@ -829,6 +858,25 @@ extension JNISwift2JavaGenerator { outParameters: [] ) + case .unsafePointer, .unsafeMutablePointer: + return NativeResult( + javaType: .long, + conversion: .getJNIValue(.convertToBitPattern(.placeholder)), + outParameters: [] + ) + + case .unsafeBufferPointer, .unsafeMutableBufferPointer: + return NativeResult( + javaType: .void, + conversion: .bufferPointerIndirectReturn(.placeholder, outArgumentName: resultName + "Out"), + outParameters: [JavaParameter(name: resultName + "Out", type: .swiftBufferPointer)] + ) + case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer: + return NativeResult( + javaType: .array(.byte), + conversion: .getJNIValue(.placeholder), + outParameters: [] + ) default: guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType.kind, config: self.config), javaType.implementsJavaValue @@ -1364,6 +1412,10 @@ extension JNISwift2JavaGenerator { /// of the `Unsafe(Mutable)Pointer` types in Swift. indirect case pointee(NativeSwiftConversionStep) + indirect case convertToBitPattern(NativeSwiftConversionStep) + + indirect case constructUnsafeBufferPointer(base: NativeSwiftConversionStep, count: NativeSwiftConversionStep, mutable: Bool) + indirect case closureLowering(parameters: [NativeParameter], result: NativeResult) /// Escaping closure lowering using the protocol infrastructure. @@ -1408,6 +1460,11 @@ extension JNISwift2JavaGenerator { outArgumentName: String ) + indirect case bufferPointerIndirectReturn( + NativeSwiftConversionStep, + outArgumentName: String + ) + indirect case constructor( _ swiftType: SwiftType, arguments: [(String?, NativeSwiftConversionStep)] = [] @@ -1629,6 +1686,10 @@ extension JNISwift2JavaGenerator { ) return bitsName + case .convertToBitPattern(let inner): + let inner = inner.render(&printer, placeholder) + return "Int64(Int(bitPattern: \(inner)))" + case .allocateExistentialValue(let inner, let name, let protocolTypes): let inner = inner.render(&printer, placeholder) let existentialType = SwiftKitPrinting.renderExistentialType(protocolTypes) @@ -1667,6 +1728,12 @@ extension JNISwift2JavaGenerator { let inner = inner.render(&printer, placeholder) return "\(inner).pointee" + case .constructUnsafeBufferPointer(let base, let count, let mutable): + let base = base.render(&printer, placeholder) + let count = count.render(&printer, placeholder) + let bufferTypeName: String = mutable ? "UnsafeMutableBufferPointer" : "UnsafeBufferPointer" + return "\(bufferTypeName)(start: \(base), count: \(count))" + case .closureLowering(let parameters, let nativeResult): var printer = SwiftPrinter() @@ -1876,6 +1943,20 @@ extension JNISwift2JavaGenerator { } return "" + case .bufferPointerIndirectReturn(let inner, let outArgumentName): + let inner = inner.render(&printer, placeholder) + printer.printBraceBlock("do") { printer in + printer.print( + """ + let baseAddressBits$ = Int64(Int(bitPattern: \(inner).baseAddress)) + environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache.SwiftBufferPointer.baseAddress, baseAddressBits$.getJNIValue(in: environment)) + let countBits$ = Int64(\(inner).count) + environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache.SwiftBufferPointer.count, countBits$.getJNIValue(in: environment)) + """ + ) + } + return "" + case .constructor(let swiftType, let arguments): let args = arguments.map { name, value in let value = value.render(&printer, placeholder) diff --git a/Sources/JExtractSwiftLib/JavaTypes/JavaType+SwiftKit.swift b/Sources/JExtractSwiftLib/JavaTypes/JavaType+SwiftKit.swift index 850768c25..1e402ee74 100644 --- a/Sources/JExtractSwiftLib/JavaTypes/JavaType+SwiftKit.swift +++ b/Sources/JExtractSwiftLib/JavaTypes/JavaType+SwiftKit.swift @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// + import SwiftJavaJNICore extension JavaType { @@ -21,4 +22,9 @@ extension JavaType { .class(package: "org.swift.swiftkit.core", name: "_OutSwiftGenericInstance") } + /// A base address and element count pair + static var swiftBufferPointer: JavaType { + .class(package: "org.swift.swiftkit.core", name: "SwiftBufferPointer") + } + } diff --git a/Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift b/Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift index b938def03..0a3088781 100644 --- a/Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift +++ b/Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift @@ -240,4 +240,33 @@ extension _JNIMethodIDCache { cache.fields[selfTypePointerField]! } } + + public enum SwiftBufferPointer { + private static let baseAddressField = Field( + name: "baseAddress", + signature: "J" + ) + + private static let countField = Field( + name: "count", + signature: "J" + ) + + private static let cache = _JNIMethodIDCache( + className: "org/swift/swiftkit/core/SwiftBufferPointer", + fields: [baseAddressField, countField] + ) + + public static var `class`: jclass { + cache.javaClass + } + + public static var baseAddress: jfieldID { + cache.fields[baseAddressField]! + } + + public static var count: jfieldID { + cache.fields[countField]! + } + } } diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftBufferPointer.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftBufferPointer.java new file mode 100644 index 000000000..ad05eb8d8 --- /dev/null +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftBufferPointer.java @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// 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; + +/** + * Corresponds to Swift's {@code UnsafeBufferPointer} and + * {@code UnsafeMutableBufferPointer}. + */ +public final class SwiftBufferPointer { + /** Address of the first element, as a raw pointer bit pattern. */ + private long baseAddress; + + /** Number of elements in the buffer, not a byte count. */ + private long count; + + public SwiftBufferPointer() { + this(0, 0); + } + + /** + * @param baseAddress address of the first element, as a raw pointer bit pattern + * @param count number of elements in the buffer + */ + public SwiftBufferPointer(long baseAddress, long count) { + this.baseAddress = baseAddress; + this.count = count; + } + + @Override + public boolean equals(Object other) { + if (this == other) return true; + if (!(other instanceof SwiftBufferPointer)) return false; + SwiftBufferPointer o = (SwiftBufferPointer) other; + return this.baseAddress == o.baseAddress && this.count == o.count; + } + + @Override + public int hashCode() { + return java.util.Objects.hash(baseAddress, count); + } + + @Override + public String toString() { + return "SwiftBufferPointer(baseAddress=0x" + Long.toHexString(baseAddress) + ", count=" + count + ")"; + } +} diff --git a/Tests/JExtractSwiftTests/JNI/JNIPointerTests.swift b/Tests/JExtractSwiftTests/JNI/JNIPointerTests.swift new file mode 100644 index 000000000..d2410864f --- /dev/null +++ b/Tests/JExtractSwiftTests/JNI/JNIPointerTests.swift @@ -0,0 +1,245 @@ +//===----------------------------------------------------------------------===// +// +// 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 JExtractSwiftLib +import Testing + +@Suite +struct JNIPointerTests { + let pointerSource = + """ + public struct MyStruct { + public var x: Int32 = 0 + } + + public func globalTakeUnsafePointer(p: UnsafePointer) {} + """ + + let bufferPointerSource = + """ + public func globalTakeUnsafeBufferPointer(buffer: UnsafeBufferPointer) {} + """ + + let returnPointerSource = + """ + public struct MyStruct { + public var x: Int32 = 0 + } + + public func globalReturnUnsafePointer() -> UnsafePointer { + fatalError() + } + """ + + let returnBufferPointerSource = + """ + public func globalReturnUnsafeBufferPointer() -> UnsafeBufferPointer { + fatalError() + } + """ + + let rawBufferPointerSource = + """ + public func globalTakeUnsafeRawBufferPointer(buffer: UnsafeRawBufferPointer) {} + """ + + let returnRawBufferPointerSource = + """ + public func globalReturnUnsafeRawBufferPointer() -> UnsafeRawBufferPointer { + fatalError() + } + """ + + @Test + func takeUnsafePointer_javaBindings() throws { + try assertOutput( + input: pointerSource, + .jni, + .java, + expectedChunks: [ + "public static void globalTakeUnsafePointer(long p) {", + "private static native void $globalTakeUnsafePointer(long p);", + ] + ) + } + + @Test + func takeUnsafePointer_swiftThunks() throws { + try assertOutput( + input: pointerSource, + .jni, + .swift, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024globalTakeUnsafePointer__J") + public func Java_com_example_swift_SwiftModule__00024globalTakeUnsafePointer__J(environment: UnsafeMutablePointer!, thisClass: jclass, p: jlong) { + assert(p != 0, "p memory address was null") + let pBits$ = Int(Int64(fromJNI: p, in: environment)) + let p$ = UnsafeMutablePointer(bitPattern: pBits$) + guard let p$ else { + fatalError("p memory address was null in call to \\(#function)!") + } + SwiftModule.globalTakeUnsafePointer(p: p$) + } + """ + ] + ) + } + + @Test + func takeUnsafeBufferPointer_javaBindings() throws { + try assertOutput( + input: bufferPointerSource, + .jni, + .java, + expectedChunks: [ + "public static void globalTakeUnsafeBufferPointer(org.swift.swiftkit.core.SwiftBufferPointer buffer) {", + "private static native void $globalTakeUnsafeBufferPointer(long buffer, long buffer_count);", + ] + ) + } + + @Test + func takeUnsafeBufferPointer_swiftThunks() throws { + try assertOutput( + input: bufferPointerSource, + .jni, + .swift, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024globalTakeUnsafeBufferPointer__JJ") + public func Java_com_example_swift_SwiftModule__00024globalTakeUnsafeBufferPointer__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, buffer: jlong, buffer_count: jlong) { + let bufferBits$ = Int(Int64(fromJNI: buffer, in: environment)) + let buffer$ = UnsafeMutablePointer(bitPattern: bufferBits$) + SwiftModule.globalTakeUnsafeBufferPointer(buffer: UnsafeBufferPointer(start: buffer$, count: Int(Int64(fromJNI: buffer_count, in: environment)))) + } + """ + ] + ) + } + + @Test + func returnUnsafePointer_javaBindings() throws { + try assertOutput( + input: returnPointerSource, + .jni, + .java, + expectedChunks: [ + "public static long globalReturnUnsafePointer() {", + "private static native long $globalReturnUnsafePointer();", + ] + ) + } + + @Test + func returnUnsafePointer_swiftThunks() throws { + try assertOutput( + input: returnPointerSource, + .jni, + .swift, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024globalReturnUnsafePointer__") + public func Java_com_example_swift_SwiftModule__00024globalReturnUnsafePointer__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { + return Int64(Int(bitPattern: SwiftModule.globalReturnUnsafePointer())).getJNILocalRefValue(in: environment) + } + """ + ] + ) + } + + @Test + func returnUnsafeBufferPointer_javaBindings() throws { + try assertOutput( + input: returnBufferPointerSource, + .jni, + .java, + expectedChunks: [ + "public static org.swift.swiftkit.core.SwiftBufferPointer globalReturnUnsafeBufferPointer() {", + "private static native void $globalReturnUnsafeBufferPointer(org.swift.swiftkit.core.SwiftBufferPointer resultOut);", + ] + ) + } + + @Test + func returnUnsafeBufferPointer_swiftThunks() throws { + try assertOutput( + input: returnBufferPointerSource, + .jni, + .swift, + expectedChunks: [ + """ + do { + let baseAddressBits$ = Int64(Int(bitPattern: SwiftModule.globalReturnUnsafeBufferPointer().baseAddress)) + environment.interface.SetLongField(environment, resultOut, _JNIMethodIDCache.SwiftBufferPointer.baseAddress, baseAddressBits$.getJNIValue(in: environment)) + let countBits$ = Int64(SwiftModule.globalReturnUnsafeBufferPointer().count) + environment.interface.SetLongField(environment, resultOut, _JNIMethodIDCache.SwiftBufferPointer.count, countBits$.getJNIValue(in: environment)) + } + """ + ] + ) + } + + @Test + func takeUnsafeRawBufferPointer_javaBindings() throws { + try assertOutput( + input: rawBufferPointerSource, + .jni, + .java, + expectedChunks: [ + "public static void globalTakeUnsafeRawBufferPointer(byte[] buffer) {", + "private static native void $globalTakeUnsafeRawBufferPointer(byte[] buffer);", + ] + ) + } + + @Test + func takeUnsafeRawBufferPointer_swiftThunks() throws { + try assertOutput( + dump: true, + input: rawBufferPointerSource, + .jni, + .swift, + expectedChunks: [ + "public func Java_com_example_swift_SwiftModule__00024globalTakeUnsafeRawBufferPointer__3B(environment: UnsafeMutablePointer!, thisClass: jclass, buffer: jbyteArray?) {" + ] + ) + } + + @Test + func returnUnsafeRawBufferPointer_javaBindings() throws { + try assertOutput( + dump: true, + input: returnRawBufferPointerSource, + .jni, + .java, + expectedChunks: [ + "public static byte[] globalReturnUnsafeRawBufferPointer() {" + ] + ) + } + + @Test + func returnUnsafeRawBufferPointer_swiftThunks() throws { + try assertOutput( + dump: true, + input: returnRawBufferPointerSource, + .jni, + .swift, + expectedChunks: [ + "public func Java_com_example_swift_SwiftModule__00024globalReturnUnsafeRawBufferPointer__() -> jbyteArray? {" + ] + ) + } +} diff --git a/Tests/JExtractSwiftTests/MethodImportTests.swift b/Tests/JExtractSwiftTests/MethodImportTests.swift index 137b7e566..f98dafcb0 100644 --- a/Tests/JExtractSwiftTests/MethodImportTests.swift +++ b/Tests/JExtractSwiftTests/MethodImportTests.swift @@ -49,6 +49,14 @@ final class MethodImportTests { public func swapRawBufferPointer(buffer: UnsafeRawBufferPointer) -> UnsafeMutableRawBufferPointer + public func globalTakeUnsafePointer(p: UnsafePointer) + + public func globalReturnUnsafePointer() -> UnsafePointer + + public func globalTakeUnsafeBufferPointer(buffer: UnsafeBufferPointer) + + public func globalReturnUnsafeBufferPointer() -> UnsafeBufferPointer + extension MySwiftClass { public func helloMemberInExtension() } @@ -300,6 +308,187 @@ final class MethodImportTests { ) } + @Test("Import: func globalTakeUnsafePointer(p: UnsafePointer)") + func func_globalTakeUnsafePointer() throws { + var config = Configuration() + config.swiftModule = "__FakeModule" + let st = makeSwiftJavaAnalyzer(config: config) + st.log.logLevel = .error + + try st.analyze(path: "Fake.swift", text: class_interfaceFile) + + let funcDecl = try #require( + st.extractedGlobalFuncs.first { + $0.name == "globalTakeUnsafePointer" + } + ) + + let generator = FFMSwift2JavaGenerator( + config: config, + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + + let output = JavaPrinter.toString { printer in + generator.printJavaBindingWrapperMethod(&printer, funcDecl) + } + + assertOutput( + output, + expected: + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func globalTakeUnsafePointer(p: UnsafePointer) + * } + */ + public static void globalTakeUnsafePointer(java.lang.foreign.MemorySegment p) { + swiftjava___FakeModule_globalTakeUnsafePointer_p.call(p); + } + """ + ) + } + + @Test("Import: func globalReturnUnsafePointer() -> UnsafePointer") + func func_globalReturnUnsafePointer() throws { + var config = Configuration() + config.swiftModule = "__FakeModule" + let st = makeSwiftJavaAnalyzer(config: config) + st.log.logLevel = .error + + try st.analyze(path: "Fake.swift", text: class_interfaceFile) + + let funcDecl = try #require( + st.extractedGlobalFuncs.first { + $0.name == "globalReturnUnsafePointer" + } + ) + + let generator = FFMSwift2JavaGenerator( + config: config, + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + + let output = JavaPrinter.toString { printer in + generator.printJavaBindingWrapperMethod(&printer, funcDecl) + } + + assertOutput( + output, + expected: + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func globalReturnUnsafePointer() -> UnsafePointer + * } + */ + public static java.lang.foreign.MemorySegment globalReturnUnsafePointer() { + return swiftjava___FakeModule_globalReturnUnsafePointer.call(); + } + """ + ) + } + + @Test("Import: func globalTakeUnsafeBufferPointer(buffer: UnsafeBufferPointer)") + func func_globalTakeUnsafeBufferPointer() throws { + var config = Configuration() + config.swiftModule = "__FakeModule" + let st = makeSwiftJavaAnalyzer(config: config) + st.log.logLevel = .error + + try st.analyze(path: "Fake.swift", text: class_interfaceFile) + + let funcDecl = try #require( + st.extractedGlobalFuncs.first { + $0.name == "globalTakeUnsafeBufferPointer" + } + ) + + let generator = FFMSwift2JavaGenerator( + config: config, + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + + let output = JavaPrinter.toString { printer in + generator.printJavaBindingWrapperMethod(&printer, funcDecl) + } + + assertOutput( + output, + expected: + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func globalTakeUnsafeBufferPointer(buffer: UnsafeBufferPointer) + * } + */ + public static void globalTakeUnsafeBufferPointer(java.lang.foreign.MemorySegment buffer) { + swiftjava___FakeModule_globalTakeUnsafeBufferPointer_buffer.call(buffer, buffer.byteSize() / SwiftValueLayout.SWIFT_INT32.byteSize()); + } + """ + ) + } + + @Test("Import: func globalReturnUnsafeBufferPointer() -> UnsafeBufferPointer") + func func_globalReturnUnsafeBufferPointer() throws { + var config = Configuration() + config.swiftModule = "__FakeModule" + let st = makeSwiftJavaAnalyzer(config: config) + st.log.logLevel = .error + + try st.analyze(path: "Fake.swift", text: class_interfaceFile) + + let funcDecl = try #require( + st.extractedGlobalFuncs.first { + $0.name == "globalReturnUnsafeBufferPointer" + } + ) + + let generator = FFMSwift2JavaGenerator( + config: config, + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + + let output = JavaPrinter.toString { printer in + generator.printJavaBindingWrapperMethod(&printer, funcDecl) + } + + assertOutput( + output, + expected: + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func globalReturnUnsafeBufferPointer() -> UnsafeBufferPointer + * } + */ + public static java.lang.foreign.MemorySegment globalReturnUnsafeBufferPointer() { + try(var arena$ = Arena.ofConfined()) { + MemorySegment result$_pointer = arena$.allocate(SwiftValueLayout.SWIFT_POINTER); + MemorySegment result$_count = arena$.allocate(SwiftValueLayout.SWIFT_INT64); + swiftjava___FakeModule_globalReturnUnsafeBufferPointer.call(result$_pointer, result$_count); + return result$_pointer.get(SwiftValueLayout.SWIFT_POINTER, 0).reinterpret(result$_count.get(SwiftValueLayout.SWIFT_INT64, 0) * SwiftValueLayout.SWIFT_INT32.byteSize()); + } + } + """ + ) + } + @Test func method_class_helloMemberFunction() throws { var config = Configuration()