diff --git a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift index 2bd1905c..8b6066cf 100644 --- a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -47,6 +47,12 @@ public func globalCallMeRunnable(run: () -> ()) { run() } +public func globalReceiveRawBuffer(buf: UnsafeRawBufferPointer) -> Int { + return buf.count +} + +public var globalBuffer: UnsafeRawBufferPointer = UnsafeRawBufferPointer(UnsafeMutableRawBufferPointer.allocate(byteCount: 124, alignment: 1)) + // ==== 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 69dfbdb3..f3073201 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -45,6 +45,8 @@ static void examples() { SwiftKit.trace("running runnable"); }); + SwiftKit.trace("getGlobalBuffer().byteSize()=" + MySwiftLibrary.getGlobalBuffer().byteSize()); + // Example of using an arena; MyClass.deinit is run at end of scope try (var arena = SwiftArena.ofConfined()) { MySwiftClass obj = MySwiftClass.init(2222, 7777, arena); diff --git a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift index 96afd331..1c9477f1 100644 --- a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift +++ b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift @@ -41,6 +41,10 @@ public func globalCallMeRunnable(run: () -> ()) { run() } +public func globalReceiveRawBuffer(buf: UnsafeRawBufferPointer) -> Int { + return buf.count +} + public class MySwiftClass { public var len: Int diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift index c52bf7db..eb27e5f6 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift @@ -65,6 +65,9 @@ extension CType { case .tuple([]): self = .void + case .optional(let wrapped) where wrapped.isPointer: + try self.init(cdeclType: wrapped) + case .metatype, .optional, .tuple: throw CDeclToCLoweringError.invalidCDeclType(cdeclType) } @@ -122,7 +125,7 @@ extension SwiftStandardLibraryTypeKind { .qualified(const: true, volatile: false, type: .void) ) case .void: .void - case .unsafePointer, .unsafeMutablePointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string: + case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string: nil } } diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift index 061dc997..ff04a598 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift @@ -215,6 +215,35 @@ struct CdeclLowering { ) ) + case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer: + // pointer buffers are lowered to (raw-pointer, count) pair. + let isMutable = knownType == .unsafeMutableRawBufferPointer + return LoweredParameter( + cdeclParameters: [ + SwiftParameter( + convention: .byValue, + parameterName: "\(parameterName)_pointer", + type: .optional(isMutable ? knownTypes.unsafeMutableRawPointer : knownTypes.unsafeRawPointer) + ), + SwiftParameter( + convention: .byValue, parameterName: "\(parameterName)_count", + type: knownTypes.int + ) + ], + conversion: .initialize( + type, + arguments: [ + LabeledArgument( + label: "start", + argument: .explodedComponent(.placeholder, component: "pointer") + ), + LabeledArgument( + label: "count", + argument: .explodedComponent(.placeholder, component: "count") + ) + ] + )) + case .string: // 'String' is passed in by C string. i.e. 'UnsafePointer' ('const uint8_t *') if knownType == .string { @@ -377,6 +406,37 @@ struct CdeclLowering { outParameterName: outParameterName ) + case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer: + // pointer buffers are lowered to (raw-pointer, count) pair. + let isMutable = knownType == .unsafeMutableRawBufferPointer + return LoweredResult( + cdeclResultType: .void, + cdeclOutParameters: [ + SwiftParameter( + convention: .byValue, + parameterName: "\(outParameterName)_pointer", + type: knownTypes.unsafeMutablePointer( + .optional(isMutable ? knownTypes.unsafeMutableRawPointer : knownTypes.unsafeRawPointer) + ) + ), + SwiftParameter( + convention: .byValue, + parameterName: "\(outParameterName)_count", + type: knownTypes.unsafeMutablePointer(knownTypes.int) + ), + ], + conversion: .aggregate([ + .populatePointer( + name: "\(outParameterName)_pointer", + to: .member(.placeholder, member: "baseAddress") + ), + .populatePointer( + name: "\(outParameterName)_count", + to: .member(.placeholder, member: "count") + ) + ], name: outParameterName) + ) + case .void: return LoweredResult(cdeclResultType: .void, cdeclOutParameters: [], conversion: .placeholder) diff --git a/Sources/JExtractSwiftLib/FFM/ConversionStep.swift b/Sources/JExtractSwiftLib/FFM/ConversionStep.swift index 31d731bc..315acc60 100644 --- a/Sources/JExtractSwiftLib/FFM/ConversionStep.swift +++ b/Sources/JExtractSwiftLib/FFM/ConversionStep.swift @@ -52,9 +52,14 @@ enum ConversionStep: Equatable { /// Initialize mutable raw pointer with a typed value. indirect case populatePointer(name: String, assumingType: SwiftType? = nil, to: ConversionStep) - /// Perform multiple conversions, but discard the result. + /// Perform multiple conversions for each tuple input elements, but discard the result. case tupleExplode([ConversionStep], name: String?) + /// Perform multiple conversions using the same input. + case aggregate([ConversionStep], name: String?) + + indirect case member(ConversionStep, member: String) + /// Count the number of times that the placeholder occurs within this /// conversion step. var placeholderCount: Int { @@ -63,13 +68,14 @@ enum ConversionStep: Equatable { .pointee(let inner), .typedPointer(let inner, swiftType: _), .unsafeCastPointer(let inner, swiftType: _), - .populatePointer(name: _, assumingType: _, to: let inner): + .populatePointer(name: _, assumingType: _, to: let inner), + .member(let inner, member: _): inner.placeholderCount case .initialize(_, arguments: let arguments): arguments.reduce(0) { $0 + $1.argument.placeholderCount } case .placeholder, .tupleExplode: 1 - case .tuplify(let elements): + case .tuplify(let elements), .aggregate(let elements, _): elements.reduce(0) { $0 + $1.placeholderCount } } } @@ -140,6 +146,25 @@ enum ConversionStep: Equatable { } } return nil + + case .member(let step, let member): + let inner = step.asExprSyntax(placeholder: placeholder, bodyItems: &bodyItems) + return "\(inner).\(raw: member)" + + case .aggregate(let steps, let name): + let toExplode: String + if let name { + bodyItems.append("let \(raw: name) = \(raw: placeholder)") + toExplode = name + } else { + toExplode = placeholder + } + for step in steps { + if let result = step.asExprSyntax(placeholder: toExplode, bodyItems: &bodyItems) { + bodyItems.append(CodeBlockItemSyntax(item: .expr(result))) + } + } + return nil } } } diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift index c7c6631a..ba88869d 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift @@ -365,7 +365,7 @@ extension FFMSwift2JavaGenerator { "arena$" } - let varName = "_result" + outParameter.name + let varName = outParameter.name.isEmpty ? "_result" : "_result_" + outParameter.name printer.print( "MemorySegment \(varName) = \(arena).allocate(\(memoryLayout));" @@ -419,32 +419,35 @@ extension JavaConversionStep { /// Whether the conversion uses SwiftArena. var requiresSwiftArena: Bool { switch self { - case .pass, .swiftValueSelfSegment, .construct, .cast, .call, .method: + case .placeholder, .constant, .readOutParameter: return false case .constructSwiftValue: return true + + case .call(let inner, _, _), .cast(let inner, _), .construct(let inner, _), + .method(let inner, _, _, _), .swiftValueSelfSegment(let inner): + return inner.requiresSwiftArena + + case .commaSeparated(let list): + return list.contains(where: { $0.requiresSwiftArena }) } } /// Whether the conversion uses temporary Arena. var requiresTemporaryArena: Bool { switch self { - case .pass, .swiftValueSelfSegment, .construct, .constructSwiftValue, .cast: + case .placeholder, .constant: return false - case .call(_, let withArena), .method(_, _, let withArena): - return withArena - } - } - - /// Whether if the result evaluation is trivial. - /// - /// If this is false, it's advised to store it to a variable if it's used multiple times - var isTrivial: Bool { - switch self { - case .pass, .swiftValueSelfSegment: + case .readOutParameter: return true - case .cast, .construct, .constructSwiftValue, .call, .method: - return false + case .cast(let inner, _), .construct(let inner, _), .constructSwiftValue(let inner, _), .swiftValueSelfSegment(let inner): + return inner.requiresSwiftArena + case .call(let inner, _, let withArena): + return withArena || inner.requiresTemporaryArena + case .method(let inner, _, let args, let withArena): + return withArena || inner.requiresTemporaryArena || args.contains(where: { $0.requiresTemporaryArena }) + case .commaSeparated(let list): + return list.contains(where: { $0.requiresTemporaryArena }) } } @@ -453,28 +456,43 @@ extension JavaConversionStep { // NOTE: 'printer' is used if the conversion wants to cause side-effects. // E.g. storing a temporary values into a variable. switch self { - case .pass: + case .placeholder: return placeholder case .swiftValueSelfSegment: return "\(placeholder).$memorySegment()" - case .call(let function, let withArena): + case .call(let inner, let function, let withArena): + let inner = inner.render(&printer, placeholder) let arenaArg = withArena ? ", arena$" : "" - return "\(function)(\(placeholder)\(arenaArg))" + return "\(function)(\(inner)\(arenaArg))" + + case .method(let inner, let methodName, let arguments, let withArena): + let inner = inner.render(&printer, placeholder) + let args = arguments.map { $0.render(&printer, placeholder) } + let argsStr = (args + (withArena ? ["arena$"] : [])).joined(separator: " ,") + return "\(inner).\(methodName)(\(argsStr))" + + case .constructSwiftValue(let inner, let javaType): + let inner = inner.render(&printer, placeholder) + return "new \(javaType.className!)(\(inner), swiftArena$)" + + case .construct(let inner, let javaType): + let inner = inner.render(&printer, placeholder) + return "new \(javaType)(\(inner))" - case .method(let methodName, let arguments, let withArena): - let argsStr = (arguments + (withArena ? ["arena$"] : [])).joined(separator: " ,") - return "\(placeholder).\(methodName)(\(argsStr))" + case .cast(let inner, let javaType): + let inner = inner.render(&printer, placeholder) + return "(\(javaType)) \(inner)" - case .constructSwiftValue(let javaType): - return "new \(javaType.className!)(\(placeholder), swiftArena$)" + case .commaSeparated(let list): + return list.map({ $0.render(&printer, placeholder)}).joined(separator: ", ") - case .construct(let javaType): - return "new \(javaType)(\(placeholder))" + case .constant(let value): + return value - case .cast(let javaType): - return "(\(javaType)) \(placeholder)" + case .readOutParameter(let javaType, let name): + return "\(placeholder)_\(name).get(\(ForeignValueLayout(javaType: javaType)!), 0)" } } } diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index 47616030..6f17158b 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -116,7 +116,7 @@ struct TranslatedFunctionType { /// Whether or not this functional interface with C ABI compatible. var isCompatibleWithC: Bool { - result.conversion.isPass && parameters.allSatisfy(\.conversion.isPass) + result.conversion.isPlaceholder && parameters.allSatisfy(\.conversion.isPlaceholder) } } @@ -200,7 +200,7 @@ struct JavaTranslation { javaParameters: [ JavaParameter(type: cType.javaType, name: paramName) ], - conversion: .pass + conversion: .placeholder ) translatedParams.append(translatedParam) continue @@ -215,7 +215,7 @@ struct JavaTranslation { let transltedResult = TranslatedResult( javaResultType: resultCType.javaType, outParameters: [], - conversion: .pass + conversion: .placeholder ) return TranslatedFunctionType( @@ -294,7 +294,7 @@ struct JavaTranslation { name: parameterName ) ], - conversion: .pass + conversion: .placeholder ) } @@ -307,7 +307,7 @@ struct JavaTranslation { type: JavaType.class(package: "org.swift.swiftkit", name: "SwiftAnyType"), name: parameterName) ], - conversion: .swiftValueSelfSegment + conversion: .swiftValueSelfSegment(.placeholder) ) case .nominal(let swiftNominalType): @@ -324,6 +324,17 @@ struct JavaTranslation { // FIXME: Implement throw JavaTranslationError.unhandledType(swiftType) + case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer: + return TranslatedParameter( + javaParameters: [ + JavaParameter(type: .javaForeignMemorySegment, name: parameterName), + ], + conversion: .commaSeparated([ + .placeholder, + .method(.placeholder, methodName: "byteSize", arguments: [], withArena: false) + ]) + ) + case .string: return TranslatedParameter( javaParameters: [ @@ -332,7 +343,7 @@ struct JavaTranslation { name: parameterName ) ], - conversion: .call(function: "SwiftKit.toCString", withArena: true) + conversion: .call(.placeholder, function: "SwiftKit.toCString", withArena: true) ) default: @@ -352,7 +363,7 @@ struct JavaTranslation { name: parameterName ) ], - conversion: .swiftValueSelfSegment + conversion: .swiftValueSelfSegment(.placeholder) ) case .tuple: @@ -366,7 +377,7 @@ struct JavaTranslation { type: JavaType.class(package: nil, name: "\(methodName).\(parameterName)"), name: parameterName) ], - conversion: .call(function: "\(methodName).$toUpcallStub", withArena: true) + conversion: .call(.placeholder, function: "\(methodName).$toUpcallStub", withArena: true) ) case .optional: @@ -388,7 +399,7 @@ struct JavaTranslation { return TranslatedResult( javaResultType: javaType, outParameters: [], - conversion: .pass + conversion: .placeholder ) } @@ -399,12 +410,29 @@ struct JavaTranslation { return TranslatedResult( javaResultType: javaType, outParameters: [], - conversion: .construct(javaType) + conversion: .construct(.placeholder, javaType) ) case .nominal(let swiftNominalType): if let knownType = swiftNominalType.nominalTypeDecl.knownStandardLibraryType { switch knownType { + case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer: + return TranslatedResult( + javaResultType: .javaForeignMemorySegment, + outParameters: [ + JavaParameter(type: .javaForeignMemorySegment, name: "pointer"), + JavaParameter(type: .long, name: "count"), + ], + conversion: .method( + .readOutParameter(.javaForeignMemorySegment, component: "pointer"), + methodName: "reinterpret", + arguments: [ + .readOutParameter(.long, component: "count") + ], + withArena: false + ) + ) + case .unsafePointer, .unsafeMutablePointer: // FIXME: Implement throw JavaTranslationError.unhandledType(swiftType) @@ -430,7 +458,7 @@ struct JavaTranslation { outParameters: [ JavaParameter(type: javaType, name: "") ], - conversion: .constructSwiftValue(javaType) + conversion: .constructSwiftValue(.placeholder, javaType) ) case .tuple: @@ -456,30 +484,39 @@ struct JavaTranslation { /// Describes how to convert values between Java types and FFM types. enum JavaConversionStep { // Pass through. - case pass + case placeholder + + // A fixed value + case constant(String) // 'value.$memorySegment()' - case swiftValueSelfSegment + indirect case swiftValueSelfSegment(JavaConversionStep) // call specified function using the placeholder as arguments. // If `withArena` is true, `arena$` argument is added. - case call(function: String, withArena: Bool) + indirect case call(JavaConversionStep, function: String, withArena: Bool) // Apply a method on the placeholder. // If `withArena` is true, `arena$` argument is added. - case method(methodName: String, arguments: [String] = [], withArena: Bool) + indirect case method(JavaConversionStep, methodName: String, arguments: [JavaConversionStep] = [], withArena: Bool) // Call 'new \(Type)(\(placeholder), swiftArena$)'. - case constructSwiftValue(JavaType) + indirect case constructSwiftValue(JavaConversionStep, JavaType) // Construct the type using the placeholder as arguments. - case construct(JavaType) + indirect case construct(JavaConversionStep, JavaType) // Casting the placeholder to the certain type. - case cast(JavaType) + indirect case cast(JavaConversionStep, JavaType) + + // Convert the results of the inner steps to a comma separated list. + indirect case commaSeparated([JavaConversionStep]) + + // Refer an exploded argument suffixed with `_\(name)`. + indirect case readOutParameter(JavaType, component: String) - var isPass: Bool { - return if case .pass = self { true } else { false } + var isPlaceholder: Bool { + return if case .placeholder = self { true } else { false } } } diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 036ee31e..4a65f7cc 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -138,7 +138,12 @@ extension FFMSwift2JavaGenerator { printImports(&printer) printModuleClass(&printer) { printer in - // TODO: print all "static" methods + + for decl in analysis.importedGlobalVariables { + self.log.trace("Print imported decl: \(decl)") + printFunctionDowncallMethods(&printer, decl) + } + for decl in analysis.importedGlobalFuncs { self.log.trace("Print imported decl: \(decl)") printFunctionDowncallMethods(&printer, decl) diff --git a/Sources/JExtractSwiftLib/FFM/ForeignValueLayouts.swift b/Sources/JExtractSwiftLib/FFM/ForeignValueLayouts.swift index 7cf1d4df..3784a75a 100644 --- a/Sources/JExtractSwiftLib/FFM/ForeignValueLayouts.swift +++ b/Sources/JExtractSwiftLib/FFM/ForeignValueLayouts.swift @@ -43,6 +43,7 @@ public struct ForeignValueLayout: CustomStringConvertible, Equatable { case .long: self = .SwiftInt64 case .float: self = .SwiftFloat case .double: self = .SwiftDouble + case .javaForeignMemorySegment: self = .SwiftPointer case .array, .class, .void: return nil } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift index 946223ae..65b17aa6 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift @@ -248,7 +248,12 @@ extension SwiftStandardLibraryTypeKind { case .double: .double case .void: .void case .string: .javaLangString - case .uint, .uint8, .uint32, .uint64, .unsafeRawPointer, .unsafeMutableRawPointer, .unsafePointer, .unsafeMutablePointer, .unsafeBufferPointer, .unsafeMutableBufferPointer: nil + case .uint, .uint8, .uint32, .uint64, + .unsafeRawPointer, .unsafeMutableRawPointer, + .unsafePointer, .unsafeMutablePointer, + .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, + .unsafeBufferPointer, .unsafeMutableBufferPointer: + nil } } } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftStandardLibraryTypeDecls.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftStandardLibraryTypeDecls.swift index 51e1adcf..5678b2bb 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftStandardLibraryTypeDecls.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftStandardLibraryTypeDecls.swift @@ -30,6 +30,8 @@ enum SwiftStandardLibraryTypeKind: String, Hashable, CaseIterable { case double = "Double" case unsafeRawPointer = "UnsafeRawPointer" case unsafeMutableRawPointer = "UnsafeMutableRawPointer" + case unsafeRawBufferPointer = "UnsafeRawBufferPointer" + case unsafeMutableRawBufferPointer = "UnsafeMutableRawBufferPointer" case unsafePointer = "UnsafePointer" case unsafeMutablePointer = "UnsafeMutablePointer" case unsafeBufferPointer = "UnsafeBufferPointer" @@ -48,7 +50,8 @@ enum SwiftStandardLibraryTypeKind: String, Hashable, CaseIterable { switch self { case .bool, .double, .float, .int, .int8, .int16, .int32, .int64, .uint, .uint8, .uint16, .uint32, .uint64, .unsafeRawPointer, - .unsafeMutableRawPointer, .string, .void: + .unsafeMutableRawPointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, + .string, .void: false case .unsafePointer, .unsafeMutablePointer, .unsafeBufferPointer, @@ -56,6 +59,15 @@ enum SwiftStandardLibraryTypeKind: String, Hashable, CaseIterable { true } } + + var isPointer: Bool { + switch self { + case .unsafePointer, .unsafeMutablePointer, .unsafeRawPointer, .unsafeMutableRawPointer: + return true + default: + return false + } + } } /// Captures many types from the Swift standard library in their most basic diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift index 5b0d8961..335bd466 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift @@ -38,8 +38,7 @@ enum SwiftType: Equatable { asNominalType?.nominalTypeDecl } - /// Whether this is the "Void" type, which is actually an empty - /// tuple. + /// Whether this is the "Void" type, which is actually an empty tuple. var isVoid: Bool { switch self { case .tuple([]): @@ -51,6 +50,19 @@ enum SwiftType: Equatable { } } + /// Whether this is a pointer type. I.e 'Unsafe[Mutable][Raw]Pointer' + var isPointer: Bool { + switch self { + case .nominal(let nominal): + if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType { + return knownType.isPointer + } + default: + break + } + return false; + } + /// Reference type /// /// * Mutations don't require 'inout' convention. diff --git a/Tests/JExtractSwiftTests/FunctionLoweringTests.swift b/Tests/JExtractSwiftTests/FunctionLoweringTests.swift index b7855e43..c707732e 100644 --- a/Tests/JExtractSwiftTests/FunctionLoweringTests.swift +++ b/Tests/JExtractSwiftTests/FunctionLoweringTests.swift @@ -315,6 +315,24 @@ final class FunctionLoweringTests { ) } + @Test("Lowering UnsafeRawBufferPointer") + func lowerRawBufferPointer() throws { + try assertLoweredFunction( + """ + func swapRawBufferPointer(buffer: UnsafeRawBufferPointer) -> UnsafeMutableRawBufferPointer {} + """, + expectedCDecl: """ + @_cdecl("c_swapRawBufferPointer") + public func c_swapRawBufferPointer(_ buffer_pointer: UnsafeRawPointer?, _ buffer_count: Int, _ _result_pointer: UnsafeMutablePointer, _ _result_count: UnsafeMutablePointer) { + let _result = swapRawBufferPointer(buffer: UnsafeRawBufferPointer(start: buffer_pointer, count: buffer_count)) + _result_pointer.initialize(to: _result.baseAddress) + _result_count.initialize(to: _result.count) + } + """, + expectedCFunction: "void c_swapRawBufferPointer(const void *buffer_pointer, ptrdiff_t buffer_count, void **_result_pointer, ptrdiff_t *_result_count)" + ) + } + @Test("Lowering () -> Void type") func lowerSimpleClosureTypes() throws { try assertLoweredFunction(""" diff --git a/Tests/JExtractSwiftTests/MethodImportTests.swift b/Tests/JExtractSwiftTests/MethodImportTests.swift index cd2bc150..2405e0ac 100644 --- a/Tests/JExtractSwiftTests/MethodImportTests.swift +++ b/Tests/JExtractSwiftTests/MethodImportTests.swift @@ -40,6 +40,8 @@ final class MethodImportTests { ) public func globalReturnClass() -> MySwiftClass + + public func swapRawBufferPointer(buffer: UnsafeRawBufferPointer) -> UnsafeMutableRawBufferPointer extension MySwiftClass { public func helloMemberInExtension() @@ -228,6 +230,53 @@ final class MethodImportTests { ) } + @Test("Import: func swapRawBufferPointer(buffer: _)") + func func_globalSwapRawBufferPointer() throws { + let st = Swift2JavaTranslator( + swiftModuleName: "__FakeModule" + ) + st.log.logLevel = .error + + try st.analyze(file: "Fake.swift", text: class_interfaceFile) + + let funcDecl = st.importedGlobalFuncs.first { + $0.name == "swapRawBufferPointer" + }! + + let generator = FFMSwift2JavaGenerator( + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + + let output = CodePrinter.toString { printer in + generator.printJavaBindingWrapperMethod(&printer, funcDecl) + } + + assertOutput( + dump: true, + output, + expected: + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func swapRawBufferPointer(buffer: UnsafeRawBufferPointer) -> UnsafeMutableRawBufferPointer + * } + */ + public static java.lang.foreign.MemorySegment swapRawBufferPointer(java.lang.foreign.MemorySegment buffer) { + try(var arena$ = Arena.ofConfined()) { + MemorySegment _result_pointer = arena$.allocate(SwiftValueLayout.SWIFT_POINTER); + MemorySegment _result_count = arena$.allocate(SwiftValueLayout.SWIFT_INT64); + swiftjava___FakeModule_swapRawBufferPointer_buffer.call(buffer, buffer.byteSize(), _result_pointer, _result_count); + return _result_pointer.get(SwiftValueLayout.SWIFT_POINTER, 0).reinterpret(_result_count.get(SwiftValueLayout.SWIFT_INT64, 0)); + } + } + """ + ) + } + @Test func method_class_helloMemberFunction() throws { let st = Swift2JavaTranslator(