@@ -16,6 +16,9 @@ import JavaTypes
1616import SwiftSyntax
1717
1818extension Swift2JavaTranslator {
19+ /// Lower the given function declaration to a C-compatible entrypoint,
20+ /// providing all of the mappings between the parameter and result types
21+ /// of the original function and its `@_cdecl` counterpart.
1922 @_spi ( Testing)
2023 public func lowerFunctionSignature(
2124 _ decl: FunctionDeclSyntax ,
@@ -145,8 +148,12 @@ extension Swift2JavaTranslator {
145148 let mutable = ( convention == . inout)
146149 let loweringStep : LoweringStep
147150 switch nominal. nominalTypeDecl. kind {
148- case . actor , . class: loweringStep = . passDirectly( parameterName)
149- case . enum, . struct, . protocol: loweringStep = . passIndirectly( parameterName)
151+ case . actor , . class:
152+ loweringStep =
153+ . unsafeCastPointer( . passDirectly( parameterName) , swiftType: type)
154+ case . enum, . struct, . protocol:
155+ loweringStep =
156+ . passIndirectly( . pointee( . typedPointer( . passDirectly( parameterName) , swiftType: type) ) )
150157 }
151158
152159 return LoweredParameters (
@@ -173,7 +180,7 @@ extension Swift2JavaTranslator {
173180 try lowerParameter ( element, convention: convention, parameterName: name)
174181 }
175182 return LoweredParameters (
176- cdeclToOriginal: . tuplify( parameterNames . map { . passDirectly ( $0 ) } ) ,
183+ cdeclToOriginal: . tuplify( loweredElements . map { $0 . cdeclToOriginal } ) ,
177184 cdeclParameters: loweredElements. flatMap { $0. cdeclParameters } ,
178185 javaFFMParameters: loweredElements. flatMap { $0. javaFFMParameters }
179186 )
@@ -258,10 +265,9 @@ extension Swift2JavaTranslator {
258265 cdeclToOriginal = . passDirectly( parameterName)
259266
260267 case ( true , false ) :
261- // FIXME: Generic arguments, ugh
262- cdeclToOriginal = . suffixed(
263- . passDirectly( parameterName) ,
264- " .assumingMemoryBound(to: \( nominal. genericArguments![ 0 ] ) .self) "
268+ cdeclToOriginal = . typedPointer(
269+ . passDirectly( parameterName + " _pointer " ) ,
270+ swiftType: nominal. genericArguments![ 0 ]
265271 )
266272
267273 case ( false , true ) :
@@ -275,9 +281,9 @@ extension Swift2JavaTranslator {
275281 type,
276282 arguments: [
277283 LabeledArgument ( label: " start " ,
278- argument: . suffixed (
284+ argument: . typedPointer (
279285 . passDirectly( parameterName + " _pointer " ) ,
280- " .assumingMemoryBound(to: \( nominal. genericArguments![ 0 ] ) .self " ) ) ,
286+ swiftType : nominal. genericArguments![ 0 ] ) ) ,
281287 LabeledArgument ( label: " count " ,
282288 argument: . passDirectly( parameterName + " _count " ) )
283289 ]
@@ -338,30 +344,113 @@ struct LabeledArgument<Element> {
338344
339345extension LabeledArgument : Equatable where Element: Equatable { }
340346
341- /// How to lower the Swift parameter
347+ /// Describes the transformation needed to take the parameters of a thunk
348+ /// and map them to the corresponding parameter (or result value) of the
349+ /// original function.
342350enum LoweringStep : Equatable {
351+ /// A direct reference to a parameter of the thunk.
343352 case passDirectly( String )
344- case passIndirectly( String )
345- indirect case suffixed( LoweringStep , String )
353+
354+ /// Cast the pointer described by the lowering step to the given
355+ /// Swift type using `unsafeBitCast(_:to:)`.
356+ indirect case unsafeCastPointer( LoweringStep , swiftType: SwiftType )
357+
358+ /// Assume at the untyped pointer described by the lowering step to the
359+ /// given type, using `assumingMemoryBound(to:).`
360+ indirect case typedPointer( LoweringStep , swiftType: SwiftType )
361+
362+ /// The thing to which the pointer typed, which is the `pointee` property
363+ /// of the `Unsafe(Mutable)Pointer` types in Swift.
364+ indirect case pointee( LoweringStep )
365+
366+ /// Pass this value indirectly, via & for explicit `inout` parameters.
367+ indirect case passIndirectly( LoweringStep )
368+
369+ /// Initialize a value of the given Swift type with the set of labeled
370+ /// arguments.
346371 case initialize( SwiftType , arguments: [ LabeledArgument < LoweringStep > ] )
372+
373+ /// Produce a tuple with the given elements.
374+ ///
375+ /// This is used for exploding Swift tuple arguments into multiple
376+ /// elements, recursively. Note that this always produces unlabeled
377+ /// tuples, which Swift will convert to the labeled tuple form.
347378 case tuplify( [ LoweringStep ] )
348379}
349380
350381struct LoweredParameters : Equatable {
351- /// The steps needed to get from the @_cdecl parameter to the original function
382+ /// The steps needed to get from the @_cdecl parameters to the original function
352383 /// parameter.
353384 var cdeclToOriginal : LoweringStep
354385
355386 /// The lowering of the parameters at the C level in Swift.
356387 var cdeclParameters : [ SwiftParameter ]
357388
358- /// The lowerung of the parmaeters at the C level as expressed for Java's
389+ /// The lowering of the parameters at the C level as expressed for Java's
359390 /// foreign function and memory interface.
360391 ///
361392 /// The elements in this array match up with those of 'cdeclParameters'.
362393 var javaFFMParameters : [ ForeignValueLayout ]
363394}
364395
396+ extension LoweredParameters {
397+ /// Produce an expression that computes the argument for this parameter
398+ /// when calling the original function from the cdecl entrypoint.
399+ func cdeclToOriginalArgumentExpr( isSelf: Bool ) -> ExprSyntax {
400+ cdeclToOriginal. asExprSyntax ( isSelf: isSelf)
401+ }
402+ }
403+
404+ extension LoweringStep {
405+ func asExprSyntax( isSelf: Bool ) -> ExprSyntax {
406+ switch self {
407+ case . passDirectly( let rawArgument) :
408+ return " \( raw: rawArgument) "
409+
410+ case . unsafeCastPointer( let step, swiftType: let swiftType) :
411+ let untypedExpr = step. asExprSyntax ( isSelf: false )
412+ return " unsafeBitCast( \( untypedExpr) , to: \( swiftType. metatypeReferenceExprSyntax) ) "
413+
414+ case . typedPointer( let step, swiftType: let type) :
415+ let untypedExpr = step. asExprSyntax ( isSelf: isSelf)
416+ return " \( untypedExpr) .assumingMemoryBound(to: \( type. metatypeReferenceExprSyntax) ) "
417+
418+ case . pointee( let step) :
419+ let untypedExpr = step. asExprSyntax ( isSelf: isSelf)
420+ return " \( untypedExpr) .pointee "
421+
422+ case . passIndirectly( let step) :
423+ let innerExpr = step. asExprSyntax ( isSelf: false )
424+ return isSelf ? innerExpr : " & \( innerExpr) "
425+
426+ case . initialize( let type, arguments: let arguments) :
427+ let renderedArguments : [ String ] = arguments. map { labeledArgument in
428+ let renderedArg = labeledArgument. argument. asExprSyntax ( isSelf: false )
429+ if let argmentLabel = labeledArgument. label {
430+ return " \( argmentLabel) : \( renderedArg. description) "
431+ } else {
432+ return renderedArg. description
433+ }
434+ }
435+
436+ // FIXME: Should be able to use structured initializers here instead
437+ // of splatting out text.
438+ let renderedArgumentList = renderedArguments. joined ( separator: " , " )
439+ return " \( raw: type. description) ( \( raw: renderedArgumentList) ) "
440+
441+ case . tuplify( let elements) :
442+ let renderedElements : [ String ] = elements. map { element in
443+ element. asExprSyntax ( isSelf: false ) . description
444+ }
445+
446+ // FIXME: Should be able to use structured initializers here instead
447+ // of splatting out text.
448+ let renderedElementList = renderedElements. joined ( separator: " , " )
449+ return " ( \( raw: renderedElementList) ) "
450+ }
451+ }
452+ }
453+
365454enum LoweringError : Error {
366455 case inoutNotSupported( SwiftType )
367456 case unhandledType( SwiftType )
@@ -375,3 +464,74 @@ public struct LoweredFunctionSignature: Equatable {
375464 var parameters : [ LoweredParameters ]
376465 var result : LoweredParameters
377466}
467+
468+ extension LoweredFunctionSignature {
469+ /// Produce the `@_cdecl` thunk for this lowered function signature that will
470+ /// call into the original function.
471+ @_spi ( Testing)
472+ public func cdeclThunk( cName: String , inputFunction: FunctionDeclSyntax ) -> FunctionDeclSyntax {
473+ var loweredCDecl = cdecl. createFunctionDecl ( cName)
474+
475+ // Add the @_cdecl attribute.
476+ let cdeclAttribute : AttributeSyntax = " @_cdecl( \( literal: cName) ) \n "
477+ loweredCDecl. attributes. append ( . attribute( cdeclAttribute) )
478+
479+ // Create the body.
480+
481+ // Lower "self", if there is one.
482+ let parametersToLower : ArraySlice < LoweredParameters >
483+ let cdeclToOriginalSelf : ExprSyntax ?
484+ if original. selfParameter != nil {
485+ cdeclToOriginalSelf = parameters [ 0 ] . cdeclToOriginalArgumentExpr ( isSelf: true )
486+ parametersToLower = parameters [ 1 ... ]
487+ } else {
488+ cdeclToOriginalSelf = nil
489+ parametersToLower = parameters [ ... ]
490+ }
491+
492+ // Lower the remaining arguments.
493+ // FIXME: Should be able to use structured initializers here instead
494+ // of splatting out text.
495+ let cdeclToOriginalArguments = zip ( parametersToLower, original. parameters) . map { lowering, originalParam in
496+ let cdeclToOriginalArg = lowering. cdeclToOriginalArgumentExpr ( isSelf: false )
497+ if let argumentLabel = originalParam. argumentLabel {
498+ return " \( argumentLabel) : \( cdeclToOriginalArg. description) "
499+ } else {
500+ return cdeclToOriginalArg. description
501+ }
502+ }
503+
504+ // Form the call expression.
505+ var callExpression : ExprSyntax = " \( inputFunction. name) ( \( raw: cdeclToOriginalArguments. joined ( separator: " , " ) ) ) "
506+ if let cdeclToOriginalSelf {
507+ callExpression = " \( cdeclToOriginalSelf) . \( callExpression) "
508+ }
509+
510+ // Handle the return.
511+ if cdecl. result. type. isVoid && original. result. type. isVoid {
512+ // Nothing to return.
513+ loweredCDecl. body = """
514+ {
515+ \( callExpression)
516+ }
517+ """
518+ } else if cdecl. result. type. isVoid {
519+ // Indirect return. This is a regular return in Swift that turns
520+ // into a
521+ loweredCDecl. body = """
522+ {
523+ \( result. cdeclToOriginalArgumentExpr ( isSelf: true ) ) = \( callExpression)
524+ }
525+ """
526+ } else {
527+ // Direct return.
528+ loweredCDecl. body = """
529+ {
530+ return \( callExpression)
531+ }
532+ """
533+ }
534+
535+ return loweredCDecl
536+ }
537+ }
0 commit comments