diff --git a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift index 1bcca4fd..cdd61c12 100644 --- a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -68,6 +68,23 @@ public func globalReceiveSomeDataProtocol(data: some DataProtocol) -> Int { return data.count } +public func globalReceiveOptional(o1: Int?, o2: (some DataProtocol)?) -> Int { + switch (o1, o2) { + case (nil, nil): + p(", ") + return 0 + case (let v1?, nil): + p("\(v1), ") + return 1 + case (nil, let v2?): + p(", \(v2)") + return 2 + case (let v1?, let v2?): + p("\(v1), \(v2)") + return 3 + } +} + // ==== Internal helpers func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) { diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java index 821b228e..3b083cc0 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -22,6 +22,9 @@ import org.swift.swiftkit.ffm.AllocatingSwiftArena; import org.swift.swiftkit.ffm.SwiftRuntime; +import java.util.Optional; +import java.util.OptionalLong; + public class HelloJava2Swift { public static void main(String[] args) { @@ -95,6 +98,7 @@ static void examples() { var bytes = arena.allocateFrom("hello"); var dat = Data.init(bytes, bytes.byteSize(), arena); MySwiftLibrary.globalReceiveSomeDataProtocol(dat); + MySwiftLibrary.globalReceiveOptional(OptionalLong.of(12), Optional.of(dat)); } diff --git a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/OptionalImportTest.java b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/OptionalImportTest.java new file mode 100644 index 00000000..57e8dba6 --- /dev/null +++ b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/OptionalImportTest.java @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package com.example.swift; + +import org.junit.jupiter.api.Test; +import org.swift.swiftkit.ffm.AllocatingSwiftArena; + +import java.util.Optional; +import java.util.OptionalLong; + +import static org.junit.jupiter.api.Assertions.*; + +public class OptionalImportTest { + @Test + void test_Optional_receive() { + try (var arena = AllocatingSwiftArena.ofConfined()) { + var origBytes = arena.allocateFrom("foobar"); + var data = Data.init(origBytes, origBytes.byteSize(), arena); + assertEquals(0, MySwiftLibrary.globalReceiveOptional(OptionalLong.empty(), Optional.empty())); + assertEquals(3, MySwiftLibrary.globalReceiveOptional(OptionalLong.of(12), Optional.of(data))); + } + } +} diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift index 4e44e74b..c2bb53ab 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift @@ -125,7 +125,7 @@ extension SwiftKnownTypeDeclKind { .qualified(const: true, volatile: false, type: .void) ) case .void: .void - case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data, .dataProtocol: + case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data, .dataProtocol, .optional: nil } } diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift index 0c6c707c..bb994164 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift @@ -194,7 +194,7 @@ struct CdeclLowering { ) case .unsafeBufferPointer, .unsafeMutableBufferPointer: - guard let genericArgs = type.asNominalType?.genericArguments, genericArgs.count == 1 else { + guard let genericArgs = nominal.genericArguments, genericArgs.count == 1 else { throw LoweringError.unhandledType(type) } // Typed pointers are lowered to (raw-pointer, count) pair. @@ -253,6 +253,12 @@ struct CdeclLowering { ] )) + case .optional: + guard let genericArgs = nominal.genericArguments, genericArgs.count == 1 else { + throw LoweringError.unhandledType(type) + } + return try lowerOptionalParameter(genericArgs[0], convention: convention, parameterName: parameterName) + case .string: // 'String' is passed in by C string. i.e. 'UnsafePointer' ('const uint8_t *') if knownType == .string { @@ -336,8 +342,79 @@ struct CdeclLowering { } throw LoweringError.unhandledType(type) - case .optional: - throw LoweringError.unhandledType(type) + case .optional(let wrapped): + return try lowerOptionalParameter(wrapped, convention: convention, parameterName: parameterName) + } + } + + /// Lower a Swift Optional to cdecl function type. + /// + /// - Parameters: + /// - fn: the Swift function type to lower. + func lowerOptionalParameter( + _ wrappedType: SwiftType, + convention: SwiftParameterConvention, + parameterName: String + ) throws -> LoweredParameter { + // If there is a 1:1 mapping between this Swift type and a C type, lower it to 'UnsafePointer?' + if let _ = try? CType(cdeclType: wrappedType) { + return LoweredParameter( + cdeclParameters: [ + SwiftParameter(convention: .byValue, parameterName: parameterName, type: .optional(knownTypes.unsafePointer(wrappedType))) + ], + conversion: .pointee(.optionalChain(.placeholder)) + ) + } + + switch wrappedType { + case .nominal(let nominal): + if let knownType = nominal.nominalTypeDecl.knownTypeKind { + switch knownType { + case .data: + break + case .unsafeRawPointer, .unsafeMutableRawPointer: + throw LoweringError.unhandledType(.optional(wrappedType)) + case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer: + throw LoweringError.unhandledType(.optional(wrappedType)) + case .unsafePointer, .unsafeMutablePointer: + throw LoweringError.unhandledType(.optional(wrappedType)) + case .unsafeBufferPointer, .unsafeMutableBufferPointer: + throw LoweringError.unhandledType(.optional(wrappedType)) + case .void, .string: + throw LoweringError.unhandledType(.optional(wrappedType)) + case .dataProtocol: + throw LoweringError.unhandledType(.optional(wrappedType)) + default: + // Unreachable? Should be handled by `CType(cdeclType:)` lowering above. + throw LoweringError.unhandledType(.optional(wrappedType)) + } + } + + // Lower arbitrary nominal to `UnsafeRawPointer?` + return LoweredParameter( + cdeclParameters: [ + SwiftParameter(convention: .byValue, parameterName: parameterName, type: .optional(knownTypes.unsafeRawPointer)) + ], + conversion: .pointee(.typedPointer(.optionalChain(.placeholder), swiftType: wrappedType)) + ) + + case .existential(let proto), .opaque(let proto): + if + let knownProtocol = proto.asNominalTypeDeclaration?.knownTypeKind, + let concreteTy = knownTypes.representativeType(of: knownProtocol) + { + return try lowerOptionalParameter(concreteTy, convention: convention, parameterName: parameterName) + } + throw LoweringError.unhandledType(.optional(wrappedType)) + + case .tuple(let tuple): + if tuple.count == 1 { + return try lowerOptionalParameter(tuple[0], convention: convention, parameterName: parameterName) + } + throw LoweringError.unhandledType(.optional(wrappedType)) + + case .function, .metatype, .optional: + throw LoweringError.unhandledType(.optional(wrappedType)) } } @@ -527,13 +604,13 @@ struct CdeclLowering { case .void: return LoweredResult(cdeclResultType: .void, cdeclOutParameters: [], conversion: .placeholder) - case .string: - // Returning string is not supported at this point. - throw LoweringError.unhandledType(type) - case .data: break + case .string, .optional: + // Not supported at this point. + throw LoweringError.unhandledType(type) + default: // Unreachable? Should be handled by `CType(cdeclType:)` lowering above. throw LoweringError.unhandledType(type) diff --git a/Sources/JExtractSwiftLib/FFM/ConversionStep.swift b/Sources/JExtractSwiftLib/FFM/ConversionStep.swift index c7fa53e3..656d9dd9 100644 --- a/Sources/JExtractSwiftLib/FFM/ConversionStep.swift +++ b/Sources/JExtractSwiftLib/FFM/ConversionStep.swift @@ -62,6 +62,8 @@ enum ConversionStep: Equatable { indirect case member(ConversionStep, member: String) + indirect case optionalChain(ConversionStep) + /// Count the number of times that the placeholder occurs within this /// conversion step. var placeholderCount: Int { @@ -71,7 +73,7 @@ enum ConversionStep: Equatable { .typedPointer(let inner, swiftType: _), .unsafeCastPointer(let inner, swiftType: _), .populatePointer(name: _, assumingType: _, to: let inner), - .member(let inner, member: _): + .member(let inner, member: _), .optionalChain(let inner): inner.placeholderCount case .initialize(_, arguments: let arguments): arguments.reduce(0) { $0 + $1.argument.placeholderCount } @@ -175,6 +177,10 @@ enum ConversionStep: Equatable { } return nil + case .optionalChain(let step): + let inner = step.asExprSyntax(placeholder: placeholder, bodyItems: &bodyItems) + return ExprSyntax(OptionalChainingExprSyntax(expression: inner!)) + case .closureLowering(let parameterSteps, let resultStep): var body: [CodeBlockItemSyntax] = [] diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index db706a91..19ae7fa5 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -238,7 +238,6 @@ extension FFMSwift2JavaGenerator { throw JavaTranslationError.unhandledType(type) } - /// Translate a Swift API signature to the user-facing Java API signature. /// /// Note that the result signature is for the high-level Java API, not the @@ -349,6 +348,12 @@ extension FFMSwift2JavaGenerator { ]) ) + case .optional: + guard let genericArgs = swiftNominalType.genericArguments, genericArgs.count == 1 else { + throw JavaTranslationError.unhandledType(swiftType) + } + return try translateOptionalParameter(wrappedType: genericArgs[0], convention: convention, parameterName: parameterName, loweredParam: loweredParam, methodName: methodName) + case .string: return TranslatedParameter( javaParameters: [ @@ -414,8 +419,81 @@ extension FFMSwift2JavaGenerator { // Otherwise, not supported yet. throw JavaTranslationError.unhandledType(swiftType) - case .optional: - throw JavaTranslationError.unhandledType(swiftType) + case .optional(let wrapped): + return try translateOptionalParameter(wrappedType: wrapped, convention: convention, parameterName: parameterName, loweredParam: loweredParam, methodName: methodName) + } + } + + /// Translate an Optional Swift API parameter to the user-facing Java API parameter. + func translateOptionalParameter( + wrappedType swiftType: SwiftType, + convention: SwiftParameterConvention, + parameterName: String, + loweredParam: LoweredParameter, + methodName: String + ) throws -> TranslatedParameter { + // If there is a 1:1 mapping between this Swift type and a C type, that can + // be expressed as a Java primitive type. + if let cType = try? CType(cdeclType: swiftType) { + var (translatedClass, lowerFunc) = switch cType.javaType { + case .int: ("OptionalInt", "toOptionalSegmentInt") + case .long: ("OptionalLong", "toOptionalSegmentLong") + case .double: ("OptionalDouble", "toOptionalSegmentDouble") + case .boolean: ("Optional", "toOptionalSegmentBoolean") + case .byte: ("Optional", "toOptionalSegmentByte") + case .char: ("Optional", "toOptionalSegmentCharacter") + case .short: ("Optional", "toOptionalSegmentShort") + case .float: ("Optional", "toOptionalSegmentFloat") + default: + throw JavaTranslationError.unhandledType(.optional(swiftType)) + } + return TranslatedParameter( + javaParameters: [ + JavaParameter(name: parameterName, type: JavaType(className: translatedClass)) + ], + conversion: .call(.placeholder, function: "SwiftRuntime.\(lowerFunc)", withArena: true) + ) + } + + switch swiftType { + case .nominal(let nominal): + if let knownType = nominal.nominalTypeDecl.knownTypeKind { + switch knownType { + case .data, .dataProtocol: + break + default: + throw JavaTranslationError.unhandledType(.optional(swiftType)) + } + } + + let translatedTy = try self.translate(swiftType: swiftType) + return TranslatedParameter( + javaParameters: [ + JavaParameter(name: parameterName, type: JavaType(className: "Optional<\(translatedTy.description)>")) + ], + conversion: .call(.placeholder, function: "SwiftRuntime.toOptionalSegmentInstance", withArena: false) + ) + case .existential(let proto), .opaque(let proto): + if + let knownProtocol = proto.asNominalTypeDeclaration?.knownTypeKind, + let concreteTy = knownTypes.representativeType(of: knownProtocol) + { + return try translateOptionalParameter( + wrappedType: concreteTy, + convention: convention, + parameterName: parameterName, + loweredParam: loweredParam, + methodName: methodName + ) + } + throw JavaTranslationError.unhandledType(.optional(swiftType)) + case .tuple(let tuple): + if tuple.count == 1 { + return try translateOptionalParameter(wrappedType: tuple[0], convention: convention, parameterName: parameterName, loweredParam: loweredParam, methodName: methodName) + } + throw JavaTranslationError.unhandledType(.optional(swiftType)) + default: + throw JavaTranslationError.unhandledType(.optional(swiftType)) } } @@ -585,7 +663,6 @@ extension FFMSwift2JavaGenerator.TranslatedFunctionSignature { } } - extension CType { /// Map lowered C type to Java type for FFM binding. var javaType: JavaType { diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 6cf80d9b..a24dabb1 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -101,9 +101,7 @@ extension FFMSwift2JavaGenerator { // Necessary for native calls and type mapping "java.lang.foreign.*", "java.lang.invoke.*", - "java.util.Arrays", - "java.util.stream.Collectors", - "java.util.concurrent.atomic.*", + "java.util.*", "java.nio.charset.StandardCharsets", ] } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index f006b4a8..b520f16e 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -99,7 +99,7 @@ extension JNISwift2JavaGenerator { .unsafeRawPointer, .unsafeMutableRawPointer, .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, - .unsafeBufferPointer, .unsafeMutableBufferPointer, .data, .dataProtocol: + .unsafeBufferPointer, .unsafeMutableBufferPointer, .optional, .data, .dataProtocol: nil } } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift index 47abb7f0..938d0c4a 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift @@ -76,6 +76,8 @@ private let swiftSourceFile: SourceFileSyntax = """ public struct UnsafeBufferPointer {} public struct UnsafeMutableBufferPointer {} + public struct Optional {} + // FIXME: Support 'typealias Void = ()' public struct Void {} diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift index d6af5920..11ff25c4 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift @@ -36,6 +36,7 @@ enum SwiftKnownTypeDeclKind: String, Hashable { case unsafeMutablePointer = "Swift.UnsafeMutablePointer" case unsafeBufferPointer = "Swift.UnsafeBufferPointer" case unsafeMutableBufferPointer = "Swift.UnsafeMutableBufferPointer" + case optional = "Swift.Optional" case void = "Swift.Void" case string = "Swift.String" diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/SwiftRuntime.java b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/SwiftRuntime.java index f734bbef..dc18b445 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/SwiftRuntime.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/SwiftRuntime.java @@ -14,14 +14,14 @@ package org.swift.swiftkit.ffm; +import org.swift.swiftkit.core.SwiftInstance; import org.swift.swiftkit.core.util.PlatformUtils; import java.lang.foreign.*; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; -import java.util.Arrays; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; import static org.swift.swiftkit.core.util.StringUtils.stripPrefix; @@ -425,6 +425,42 @@ public static MemorySegment toCString(String str, Arena arena) { return arena.allocateFrom(str); } + public static MemorySegment toOptionalSegmentInt(OptionalInt opt, Arena arena) { + return opt.isPresent() ? arena.allocateFrom(ValueLayout.JAVA_INT, opt.getAsInt()) : MemorySegment.NULL; + } + + public static MemorySegment toOptionalSegmentLong(OptionalLong opt, Arena arena) { + return opt.isPresent() ? arena.allocateFrom(ValueLayout.JAVA_LONG, opt.getAsLong()) : MemorySegment.NULL; + } + + public static MemorySegment toOptionalSegmentDouble(OptionalDouble opt, Arena arena) { + return opt.isPresent() ? arena.allocateFrom(ValueLayout.JAVA_DOUBLE, opt.getAsDouble()) : MemorySegment.NULL; + } + + public static MemorySegment toOptionalSegmentBoolean(Optional opt, Arena arena) { + return opt.map(val -> arena.allocateFrom(ValueLayout.JAVA_BYTE, (byte) (val ? 1 : 0))).orElse(MemorySegment.NULL); + } + + public static MemorySegment toOptionalSegmentByte(Optional opt, Arena arena) { + return opt.map(val -> arena.allocateFrom(ValueLayout.JAVA_BYTE, val)).orElse(MemorySegment.NULL); + } + + public static MemorySegment toOptionalSegmentCharacter(Optional opt, Arena arena) { + return opt.map(val -> arena.allocateFrom(ValueLayout.JAVA_CHAR, val)).orElse(MemorySegment.NULL); + } + + public static MemorySegment toOptionalSegmentShort(Optional opt, Arena arena) { + return opt.map(val -> arena.allocateFrom(ValueLayout.JAVA_SHORT, val)).orElse(MemorySegment.NULL); + } + + public static MemorySegment toOptionalSegmentFloat(Optional opt, Arena arena) { + return opt.map(val -> arena.allocateFrom(ValueLayout.JAVA_FLOAT, val)).orElse(MemorySegment.NULL); + } + + public static MemorySegment toOptionalSegmentInstance(Optional opt) { + return opt.map(instance -> instance.$memorySegment()).orElse(MemorySegment.NULL); + } + private static class swift_getTypeName { /** diff --git a/Tests/JExtractSwiftTests/FunctionLoweringTests.swift b/Tests/JExtractSwiftTests/FunctionLoweringTests.swift index 9422bbdb..8419ec61 100644 --- a/Tests/JExtractSwiftTests/FunctionLoweringTests.swift +++ b/Tests/JExtractSwiftTests/FunctionLoweringTests.swift @@ -383,6 +383,27 @@ final class FunctionLoweringTests { ) } + @Test("Lowering optional primitives") + func lowerOptionalParameters() throws { + try assertLoweredFunction( + """ + func fn(a1: Optional, a2: Int?, a3: Point?, a4: (some DataProtocol)?) + """, + sourceFile: """ + import Foundation + struct Point {} + """, + expectedCDecl:""" + @_cdecl("c_fn") + public func c_fn(_ a1: UnsafePointer?, _ a2: UnsafePointer?, _ a3: UnsafeRawPointer?, _ a4: UnsafeRawPointer?) { + fn(a1: a1?.pointee, a2: a2?.pointee, a3: a3?.assumingMemoryBound(to: Point.self).pointee, a4: a4?.assumingMemoryBound(to: Data.self).pointee) + } + """, + expectedCFunction: """ + void c_fn(const ptrdiff_t *a1, const ptrdiff_t *a2, const void *a3, const void *a4) + """) + } + @Test("Lowering read accessor") func lowerGlobalReadAccessor() throws { try assertLoweredVariableAccessor( diff --git a/Tests/JExtractSwiftTests/OptionalImportTests.swift b/Tests/JExtractSwiftTests/OptionalImportTests.swift new file mode 100644 index 00000000..756bb3d9 --- /dev/null +++ b/Tests/JExtractSwiftTests/OptionalImportTests.swift @@ -0,0 +1,156 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import JExtractSwiftLib +import Testing + +final class OptionalImportTests { + let interfaceFile = + """ + import Foundation + + public func receiveOptionalIntSugar(_ arg: Int?) + public func receiveOptionalIntExplicit(_ arg: Optional) + public func receiveOptionalDataProto(_ arg: (some DataProtocol)?)) + """ + + + @Test("Import Optionals: JavaBindings") + func data_javaBindings() throws { + + try assertOutput( + input: interfaceFile, .ffm, .java, + expectedChunks: [ + """ + /** + * {@snippet lang=c : + * void swiftjava_SwiftModule_receiveOptionalIntSugar__(const ptrdiff_t *arg) + * } + */ + private static class swiftjava_SwiftModule_receiveOptionalIntSugar__ { + private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + /* arg: */SwiftValueLayout.SWIFT_POINTER + ); + private static final MemorySegment ADDR = + SwiftModule.findOrThrow("swiftjava_SwiftModule_receiveOptionalIntSugar__"); + private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + public static void call(java.lang.foreign.MemorySegment arg) { + try { + if (SwiftRuntime.TRACE_DOWNCALLS) { + SwiftRuntime.traceDowncall(arg); + } + HANDLE.invokeExact(arg); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + """, + + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func receiveOptionalIntSugar(_ arg: Int?) + * } + */ + public static void receiveOptionalIntSugar(OptionalLong arg) { + try(var arena$ = Arena.ofConfined()) { + swiftjava_SwiftModule_receiveOptionalIntSugar__.call(SwiftRuntime.toOptionalSegmentLong(arg, arena$)); + } + } + """, + + """ + /** + * {@snippet lang=c : + * void swiftjava_SwiftModule_receiveOptionalIntExplicit__(const ptrdiff_t *arg) + * } + */ + private static class swiftjava_SwiftModule_receiveOptionalIntExplicit__ { + private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + /* arg: */SwiftValueLayout.SWIFT_POINTER + ); + private static final MemorySegment ADDR = + SwiftModule.findOrThrow("swiftjava_SwiftModule_receiveOptionalIntExplicit__"); + private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + public static void call(java.lang.foreign.MemorySegment arg) { + try { + if (SwiftRuntime.TRACE_DOWNCALLS) { + SwiftRuntime.traceDowncall(arg); + } + HANDLE.invokeExact(arg); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + """, + + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func receiveOptionalIntExplicit(_ arg: Optional) + * } + */ + public static void receiveOptionalIntExplicit(OptionalLong arg) { + try(var arena$ = Arena.ofConfined()) { + swiftjava_SwiftModule_receiveOptionalIntExplicit__.call(SwiftRuntime.toOptionalSegmentLong(arg, arena$)); + } + } + """, + + + """ + /** + * {@snippet lang=c : + * void swiftjava_SwiftModule_receiveOptionalDataProto__(const void *arg) + * } + */ + private static class swiftjava_SwiftModule_receiveOptionalDataProto__ { + private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + /* arg: */SwiftValueLayout.SWIFT_POINTER + ); + private static final MemorySegment ADDR = + SwiftModule.findOrThrow("swiftjava_SwiftModule_receiveOptionalDataProto__"); + private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + public static void call(java.lang.foreign.MemorySegment arg) { + try { + if (SwiftRuntime.TRACE_DOWNCALLS) { + SwiftRuntime.traceDowncall(arg); + } + HANDLE.invokeExact(arg); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + } + """, + + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func receiveOptionalDataProto(_ arg: (some DataProtocol)?) + * } + */ + public static void receiveOptionalDataProto(Optional arg) { + swiftjava_SwiftModule_receiveOptionalDataProto__.call(SwiftRuntime.toOptionalSegmentInstance(arg)); + } + """, + ] + ) + } +}