Skip to content

Commit 870e182

Browse files
committed
[JExtract/FFM] Lowering Optional parameters
1 parent 17210b7 commit 870e182

File tree

7 files changed

+116
-9
lines changed

7 files changed

+116
-9
lines changed

Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ extension SwiftKnownTypeDeclKind {
125125
.qualified(const: true, volatile: false, type: .void)
126126
)
127127
case .void: .void
128-
case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data, .dataProtocol:
128+
case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data, .dataProtocol, .optional:
129129
nil
130130
}
131131
}

Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,12 @@ struct CdeclLowering {
253253
]
254254
))
255255

256+
case .optional:
257+
guard let genericArgs = type.asNominalType?.genericArguments, genericArgs.count == 1 else {
258+
throw LoweringError.unhandledType(type)
259+
}
260+
return try lowerOptionalParameter(genericArgs[0], convention: convention, parameterName: parameterName)
261+
256262
case .string:
257263
// 'String' is passed in by C string. i.e. 'UnsafePointer<Int8>' ('const uint8_t *')
258264
if knownType == .string {
@@ -336,8 +342,79 @@ struct CdeclLowering {
336342
}
337343
throw LoweringError.unhandledType(type)
338344

339-
case .optional:
340-
throw LoweringError.unhandledType(type)
345+
case .optional(let wrapped):
346+
return try lowerOptionalParameter(wrapped, convention: convention, parameterName: parameterName)
347+
}
348+
}
349+
350+
/// Lower a Swift Optional to cdecl function type.
351+
///
352+
/// - Parameters:
353+
/// - fn: the Swift function type to lower.
354+
func lowerOptionalParameter(
355+
_ wrappedType: SwiftType,
356+
convention: SwiftParameterConvention,
357+
parameterName: String
358+
) throws -> LoweredParameter {
359+
// If there is a 1:1 mapping between this Swift type and a C type, lower it to 'UnsafePointer<T>?'
360+
if let _ = try? CType(cdeclType: wrappedType) {
361+
return LoweredParameter(
362+
cdeclParameters: [
363+
SwiftParameter(convention: .byValue, parameterName: parameterName, type: .optional(knownTypes.unsafePointer(wrappedType)))
364+
],
365+
conversion: .pointee(.optionalChain(.placeholder))
366+
)
367+
}
368+
369+
switch wrappedType {
370+
case .nominal(let nominal):
371+
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
372+
switch knownType {
373+
case .data:
374+
break
375+
case .unsafeRawPointer, .unsafeMutableRawPointer:
376+
throw LoweringError.unhandledType(.optional(wrappedType))
377+
case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
378+
throw LoweringError.unhandledType(.optional(wrappedType))
379+
case .unsafePointer, .unsafeMutablePointer:
380+
throw LoweringError.unhandledType(.optional(wrappedType))
381+
case .unsafeBufferPointer, .unsafeMutableBufferPointer:
382+
throw LoweringError.unhandledType(.optional(wrappedType))
383+
case .void, .string:
384+
throw LoweringError.unhandledType(.optional(wrappedType))
385+
case .dataProtocol:
386+
throw LoweringError.unhandledType(.optional(wrappedType))
387+
default:
388+
// Unreachable? Should be handled by `CType(cdeclType:)` lowering above.
389+
throw LoweringError.unhandledType(.optional(wrappedType))
390+
}
391+
}
392+
393+
// Lower arbitrary nominal to `UnsafeRawPointer?`
394+
return LoweredParameter(
395+
cdeclParameters: [
396+
SwiftParameter(convention: .byValue, parameterName: parameterName, type: .optional(knownTypes.unsafeRawPointer))
397+
],
398+
conversion: .pointee(.typedPointer(.optionalChain(.placeholder), swiftType: wrappedType))
399+
)
400+
401+
case .existential(let proto), .opaque(let proto):
402+
if
403+
let knownProtocol = proto.asNominalTypeDeclaration?.knownTypeKind,
404+
let concreteTy = knownTypes.representativeType(of: knownProtocol)
405+
{
406+
return try lowerOptionalParameter(concreteTy, convention: convention, parameterName: parameterName)
407+
}
408+
throw LoweringError.unhandledType(.optional(wrappedType))
409+
410+
case .tuple(let tuple):
411+
if tuple.count == 1 {
412+
return try lowerOptionalParameter(tuple[0], convention: convention, parameterName: parameterName)
413+
}
414+
throw LoweringError.unhandledType(.optional(wrappedType))
415+
416+
case .function, .metatype, .optional:
417+
throw LoweringError.unhandledType(.optional(wrappedType))
341418
}
342419
}
343420

@@ -527,13 +604,13 @@ struct CdeclLowering {
527604
case .void:
528605
return LoweredResult(cdeclResultType: .void, cdeclOutParameters: [], conversion: .placeholder)
529606

530-
case .string:
531-
// Returning string is not supported at this point.
532-
throw LoweringError.unhandledType(type)
533-
534607
case .data:
535608
break
536609

610+
case .string, .optional:
611+
// Not supported at this point.
612+
throw LoweringError.unhandledType(type)
613+
537614
default:
538615
// Unreachable? Should be handled by `CType(cdeclType:)` lowering above.
539616
throw LoweringError.unhandledType(type)

Sources/JExtractSwiftLib/FFM/ConversionStep.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ enum ConversionStep: Equatable {
6262

6363
indirect case member(ConversionStep, member: String)
6464

65+
indirect case optionalChain(ConversionStep)
66+
6567
/// Count the number of times that the placeholder occurs within this
6668
/// conversion step.
6769
var placeholderCount: Int {
@@ -71,7 +73,7 @@ enum ConversionStep: Equatable {
7173
.typedPointer(let inner, swiftType: _),
7274
.unsafeCastPointer(let inner, swiftType: _),
7375
.populatePointer(name: _, assumingType: _, to: let inner),
74-
.member(let inner, member: _):
76+
.member(let inner, member: _), .optionalChain(let inner):
7577
inner.placeholderCount
7678
case .initialize(_, arguments: let arguments):
7779
arguments.reduce(0) { $0 + $1.argument.placeholderCount }
@@ -175,6 +177,10 @@ enum ConversionStep: Equatable {
175177
}
176178
return nil
177179

180+
case .optionalChain(let step):
181+
let inner = step.asExprSyntax(placeholder: placeholder, bodyItems: &bodyItems)
182+
return ExprSyntax(OptionalChainingExprSyntax(expression: inner!))
183+
178184
case .closureLowering(let parameterSteps, let resultStep):
179185
var body: [CodeBlockItemSyntax] = []
180186

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ extension JNISwift2JavaGenerator {
9999
.unsafeRawPointer, .unsafeMutableRawPointer,
100100
.unsafePointer, .unsafeMutablePointer,
101101
.unsafeRawBufferPointer, .unsafeMutableRawBufferPointer,
102-
.unsafeBufferPointer, .unsafeMutableBufferPointer, .data, .dataProtocol:
102+
.unsafeBufferPointer, .unsafeMutableBufferPointer, .optional, .data, .dataProtocol:
103103
nil
104104
}
105105
}

Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ private let swiftSourceFile: SourceFileSyntax = """
7676
public struct UnsafeBufferPointer<Element> {}
7777
public struct UnsafeMutableBufferPointer<Element> {}
7878
79+
public struct Optional<Wrapped> {}
80+
7981
// FIXME: Support 'typealias Void = ()'
8082
public struct Void {}
8183

Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ enum SwiftKnownTypeDeclKind: String, Hashable {
3636
case unsafeMutablePointer = "Swift.UnsafeMutablePointer"
3737
case unsafeBufferPointer = "Swift.UnsafeBufferPointer"
3838
case unsafeMutableBufferPointer = "Swift.UnsafeMutableBufferPointer"
39+
case optional = "Swift.Optional"
3940
case void = "Swift.Void"
4041
case string = "Swift.String"
4142

Tests/JExtractSwiftTests/FunctionLoweringTests.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,27 @@ final class FunctionLoweringTests {
383383
)
384384
}
385385

386+
@Test("Lowering optional primitives")
387+
func lowerOptionalParameters() throws {
388+
try assertLoweredFunction(
389+
"""
390+
func fn(a1: Optional<Int>, a2: Int?, a3: Point?, a4: (some DataProtocol)?)
391+
""",
392+
sourceFile: """
393+
import Foundation
394+
struct Point {}
395+
""",
396+
expectedCDecl:"""
397+
@_cdecl("c_fn")
398+
public func c_fn(_ a1: UnsafePointer<Int>?, _ a2: UnsafePointer<Int>?, _ a3: UnsafeRawPointer?, _ a4: UnsafeRawPointer?) {
399+
fn(a1: a1?.pointee, a2: a2?.pointee, a3: a3?.assumingMemoryBound(to: Point.self).pointee, a4: a4?.assumingMemoryBound(to: Data.self).pointee)
400+
}
401+
""",
402+
expectedCFunction: """
403+
void c_fn(const ptrdiff_t *a1, const ptrdiff_t *a2, const void *a3, const void *a4)
404+
""")
405+
}
406+
386407
@Test("Lowering read accessor")
387408
func lowerGlobalReadAccessor() throws {
388409
try assertLoweredVariableAccessor(

0 commit comments

Comments
 (0)