@@ -16,6 +16,9 @@ import JavaTypes
16
16
import SwiftSyntax
17
17
18
18
extension 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.
19
22
@_spi ( Testing)
20
23
public func lowerFunctionSignature(
21
24
_ decl: FunctionDeclSyntax ,
@@ -145,8 +148,12 @@ extension Swift2JavaTranslator {
145
148
let mutable = ( convention == . inout)
146
149
let loweringStep : LoweringStep
147
150
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) ) )
150
157
}
151
158
152
159
return LoweredParameters (
@@ -173,7 +180,7 @@ extension Swift2JavaTranslator {
173
180
try lowerParameter ( element, convention: convention, parameterName: name)
174
181
}
175
182
return LoweredParameters (
176
- cdeclToOriginal: . tuplify( parameterNames . map { . passDirectly ( $0 ) } ) ,
183
+ cdeclToOriginal: . tuplify( loweredElements . map { $0 . cdeclToOriginal } ) ,
177
184
cdeclParameters: loweredElements. flatMap { $0. cdeclParameters } ,
178
185
javaFFMParameters: loweredElements. flatMap { $0. javaFFMParameters }
179
186
)
@@ -258,10 +265,9 @@ extension Swift2JavaTranslator {
258
265
cdeclToOriginal = . passDirectly( parameterName)
259
266
260
267
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 ]
265
271
)
266
272
267
273
case ( false , true ) :
@@ -275,9 +281,9 @@ extension Swift2JavaTranslator {
275
281
type,
276
282
arguments: [
277
283
LabeledArgument ( label: " start " ,
278
- argument: . suffixed (
284
+ argument: . typedPointer (
279
285
. passDirectly( parameterName + " _pointer " ) ,
280
- " .assumingMemoryBound(to: \( nominal. genericArguments![ 0 ] ) .self " ) ) ,
286
+ swiftType : nominal. genericArguments![ 0 ] ) ) ,
281
287
LabeledArgument ( label: " count " ,
282
288
argument: . passDirectly( parameterName + " _count " ) )
283
289
]
@@ -338,30 +344,113 @@ struct LabeledArgument<Element> {
338
344
339
345
extension LabeledArgument : Equatable where Element: Equatable { }
340
346
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.
342
350
enum LoweringStep : Equatable {
351
+ /// A direct reference to a parameter of the thunk.
343
352
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.
346
371
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.
347
378
case tuplify( [ LoweringStep ] )
348
379
}
349
380
350
381
struct 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
352
383
/// parameter.
353
384
var cdeclToOriginal : LoweringStep
354
385
355
386
/// The lowering of the parameters at the C level in Swift.
356
387
var cdeclParameters : [ SwiftParameter ]
357
388
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
359
390
/// foreign function and memory interface.
360
391
///
361
392
/// The elements in this array match up with those of 'cdeclParameters'.
362
393
var javaFFMParameters : [ ForeignValueLayout ]
363
394
}
364
395
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
+
365
454
enum LoweringError : Error {
366
455
case inoutNotSupported( SwiftType )
367
456
case unhandledType( SwiftType )
@@ -375,3 +464,74 @@ public struct LoweredFunctionSignature: Equatable {
375
464
var parameters : [ LoweredParameters ]
376
465
var result : LoweredParameters
377
466
}
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