@@ -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 ,
@@ -124,9 +127,29 @@ extension Swift2JavaTranslator {
124
127
parameterName: String
125
128
) throws -> LoweredParameters {
126
129
switch type {
127
- case . function, . metatype , . optional:
130
+ case . function, . optional:
128
131
throw LoweringError . unhandledType ( type)
129
132
133
+ case . metatype( let instanceType) :
134
+ return LoweredParameters (
135
+ cdeclToOriginal: . unsafeCastPointer(
136
+ . passDirectly( parameterName) ,
137
+ swiftType: instanceType
138
+ ) ,
139
+ cdeclParameters: [
140
+ SwiftParameter (
141
+ convention: . byValue,
142
+ parameterName: parameterName,
143
+ type: . nominal(
144
+ SwiftNominalType (
145
+ nominalTypeDecl: swiftStdlibTypes. unsafeRawPointerDecl
146
+ )
147
+ )
148
+ )
149
+ ] ,
150
+ javaFFMParameters: [ . SwiftPointer]
151
+ )
152
+
130
153
case . nominal( let nominal) :
131
154
// Types from the Swift standard library that we know about.
132
155
if nominal. nominalTypeDecl. moduleName == " Swift " ,
@@ -145,8 +168,12 @@ extension Swift2JavaTranslator {
145
168
let mutable = ( convention == . inout)
146
169
let loweringStep : LoweringStep
147
170
switch nominal. nominalTypeDecl. kind {
148
- case . actor , . class: loweringStep = . passDirectly( parameterName)
149
- case . enum, . struct, . protocol: loweringStep = . passIndirectly( parameterName)
171
+ case . actor , . class:
172
+ loweringStep =
173
+ . unsafeCastPointer( . passDirectly( parameterName) , swiftType: type)
174
+ case . enum, . struct, . protocol:
175
+ loweringStep =
176
+ . passIndirectly( . pointee( . typedPointer( . passDirectly( parameterName) , swiftType: type) ) )
150
177
}
151
178
152
179
return LoweredParameters (
@@ -173,7 +200,7 @@ extension Swift2JavaTranslator {
173
200
try lowerParameter ( element, convention: convention, parameterName: name)
174
201
}
175
202
return LoweredParameters (
176
- cdeclToOriginal: . tuplify( parameterNames . map { . passDirectly ( $0 ) } ) ,
203
+ cdeclToOriginal: . tuplify( loweredElements . map { $0 . cdeclToOriginal } ) ,
177
204
cdeclParameters: loweredElements. flatMap { $0. cdeclParameters } ,
178
205
javaFFMParameters: loweredElements. flatMap { $0. javaFFMParameters }
179
206
)
@@ -258,10 +285,9 @@ extension Swift2JavaTranslator {
258
285
cdeclToOriginal = . passDirectly( parameterName)
259
286
260
287
case ( true , false ) :
261
- // FIXME: Generic arguments, ugh
262
- cdeclToOriginal = . suffixed(
263
- . passDirectly( parameterName) ,
264
- " .assumingMemoryBound(to: \( nominal. genericArguments![ 0 ] ) .self) "
288
+ cdeclToOriginal = . typedPointer(
289
+ . passDirectly( parameterName + " _pointer " ) ,
290
+ swiftType: nominal. genericArguments![ 0 ]
265
291
)
266
292
267
293
case ( false , true ) :
@@ -275,9 +301,9 @@ extension Swift2JavaTranslator {
275
301
type,
276
302
arguments: [
277
303
LabeledArgument ( label: " start " ,
278
- argument: . suffixed (
304
+ argument: . typedPointer (
279
305
. passDirectly( parameterName + " _pointer " ) ,
280
- " .assumingMemoryBound(to: \( nominal. genericArguments![ 0 ] ) .self " ) ) ,
306
+ swiftType : nominal. genericArguments![ 0 ] ) ) ,
281
307
LabeledArgument ( label: " count " ,
282
308
argument: . passDirectly( parameterName + " _count " ) )
283
309
]
@@ -338,30 +364,113 @@ struct LabeledArgument<Element> {
338
364
339
365
extension LabeledArgument : Equatable where Element: Equatable { }
340
366
341
- /// How to lower the Swift parameter
367
+ /// Describes the transformation needed to take the parameters of a thunk
368
+ /// and map them to the corresponding parameter (or result value) of the
369
+ /// original function.
342
370
enum LoweringStep : Equatable {
371
+ /// A direct reference to a parameter of the thunk.
343
372
case passDirectly( String )
344
- case passIndirectly( String )
345
- indirect case suffixed( LoweringStep , String )
373
+
374
+ /// Cast the pointer described by the lowering step to the given
375
+ /// Swift type using `unsafeBitCast(_:to:)`.
376
+ indirect case unsafeCastPointer( LoweringStep , swiftType: SwiftType )
377
+
378
+ /// Assume at the untyped pointer described by the lowering step to the
379
+ /// given type, using `assumingMemoryBound(to:).`
380
+ indirect case typedPointer( LoweringStep , swiftType: SwiftType )
381
+
382
+ /// The thing to which the pointer typed, which is the `pointee` property
383
+ /// of the `Unsafe(Mutable)Pointer` types in Swift.
384
+ indirect case pointee( LoweringStep )
385
+
386
+ /// Pass this value indirectly, via & for explicit `inout` parameters.
387
+ indirect case passIndirectly( LoweringStep )
388
+
389
+ /// Initialize a value of the given Swift type with the set of labeled
390
+ /// arguments.
346
391
case initialize( SwiftType , arguments: [ LabeledArgument < LoweringStep > ] )
392
+
393
+ /// Produce a tuple with the given elements.
394
+ ///
395
+ /// This is used for exploding Swift tuple arguments into multiple
396
+ /// elements, recursively. Note that this always produces unlabeled
397
+ /// tuples, which Swift will convert to the labeled tuple form.
347
398
case tuplify( [ LoweringStep ] )
348
399
}
349
400
350
401
struct LoweredParameters : Equatable {
351
- /// The steps needed to get from the @_cdecl parameter to the original function
402
+ /// The steps needed to get from the @_cdecl parameters to the original function
352
403
/// parameter.
353
404
var cdeclToOriginal : LoweringStep
354
405
355
406
/// The lowering of the parameters at the C level in Swift.
356
407
var cdeclParameters : [ SwiftParameter ]
357
408
358
- /// The lowerung of the parmaeters at the C level as expressed for Java's
409
+ /// The lowering of the parameters at the C level as expressed for Java's
359
410
/// foreign function and memory interface.
360
411
///
361
412
/// The elements in this array match up with those of 'cdeclParameters'.
362
413
var javaFFMParameters : [ ForeignValueLayout ]
363
414
}
364
415
416
+ extension LoweredParameters {
417
+ /// Produce an expression that computes the argument for this parameter
418
+ /// when calling the original function from the cdecl entrypoint.
419
+ func cdeclToOriginalArgumentExpr( isSelf: Bool ) -> ExprSyntax {
420
+ cdeclToOriginal. asExprSyntax ( isSelf: isSelf)
421
+ }
422
+ }
423
+
424
+ extension LoweringStep {
425
+ func asExprSyntax( isSelf: Bool ) -> ExprSyntax {
426
+ switch self {
427
+ case . passDirectly( let rawArgument) :
428
+ return " \( raw: rawArgument) "
429
+
430
+ case . unsafeCastPointer( let step, swiftType: let swiftType) :
431
+ let untypedExpr = step. asExprSyntax ( isSelf: false )
432
+ return " unsafeBitCast( \( untypedExpr) , to: \( swiftType. metatypeReferenceExprSyntax) ) "
433
+
434
+ case . typedPointer( let step, swiftType: let type) :
435
+ let untypedExpr = step. asExprSyntax ( isSelf: isSelf)
436
+ return " \( untypedExpr) .assumingMemoryBound(to: \( type. metatypeReferenceExprSyntax) ) "
437
+
438
+ case . pointee( let step) :
439
+ let untypedExpr = step. asExprSyntax ( isSelf: isSelf)
440
+ return " \( untypedExpr) .pointee "
441
+
442
+ case . passIndirectly( let step) :
443
+ let innerExpr = step. asExprSyntax ( isSelf: false )
444
+ return isSelf ? innerExpr : " & \( innerExpr) "
445
+
446
+ case . initialize( let type, arguments: let arguments) :
447
+ let renderedArguments : [ String ] = arguments. map { labeledArgument in
448
+ let renderedArg = labeledArgument. argument. asExprSyntax ( isSelf: false )
449
+ if let argmentLabel = labeledArgument. label {
450
+ return " \( argmentLabel) : \( renderedArg. description) "
451
+ } else {
452
+ return renderedArg. description
453
+ }
454
+ }
455
+
456
+ // FIXME: Should be able to use structured initializers here instead
457
+ // of splatting out text.
458
+ let renderedArgumentList = renderedArguments. joined ( separator: " , " )
459
+ return " \( raw: type. description) ( \( raw: renderedArgumentList) ) "
460
+
461
+ case . tuplify( let elements) :
462
+ let renderedElements : [ String ] = elements. map { element in
463
+ element. asExprSyntax ( isSelf: false ) . description
464
+ }
465
+
466
+ // FIXME: Should be able to use structured initializers here instead
467
+ // of splatting out text.
468
+ let renderedElementList = renderedElements. joined ( separator: " , " )
469
+ return " ( \( raw: renderedElementList) ) "
470
+ }
471
+ }
472
+ }
473
+
365
474
enum LoweringError : Error {
366
475
case inoutNotSupported( SwiftType )
367
476
case unhandledType( SwiftType )
@@ -375,3 +484,74 @@ public struct LoweredFunctionSignature: Equatable {
375
484
var parameters : [ LoweredParameters ]
376
485
var result : LoweredParameters
377
486
}
487
+
488
+ extension LoweredFunctionSignature {
489
+ /// Produce the `@_cdecl` thunk for this lowered function signature that will
490
+ /// call into the original function.
491
+ @_spi ( Testing)
492
+ public func cdeclThunk( cName: String , inputFunction: FunctionDeclSyntax ) -> FunctionDeclSyntax {
493
+ var loweredCDecl = cdecl. createFunctionDecl ( cName)
494
+
495
+ // Add the @_cdecl attribute.
496
+ let cdeclAttribute : AttributeSyntax = " @_cdecl( \( literal: cName) ) \n "
497
+ loweredCDecl. attributes. append ( . attribute( cdeclAttribute) )
498
+
499
+ // Create the body.
500
+
501
+ // Lower "self", if there is one.
502
+ let parametersToLower : ArraySlice < LoweredParameters >
503
+ let cdeclToOriginalSelf : ExprSyntax ?
504
+ if original. selfParameter != nil {
505
+ cdeclToOriginalSelf = parameters [ 0 ] . cdeclToOriginalArgumentExpr ( isSelf: true )
506
+ parametersToLower = parameters [ 1 ... ]
507
+ } else {
508
+ cdeclToOriginalSelf = nil
509
+ parametersToLower = parameters [ ... ]
510
+ }
511
+
512
+ // Lower the remaining arguments.
513
+ // FIXME: Should be able to use structured initializers here instead
514
+ // of splatting out text.
515
+ let cdeclToOriginalArguments = zip ( parametersToLower, original. parameters) . map { lowering, originalParam in
516
+ let cdeclToOriginalArg = lowering. cdeclToOriginalArgumentExpr ( isSelf: false )
517
+ if let argumentLabel = originalParam. argumentLabel {
518
+ return " \( argumentLabel) : \( cdeclToOriginalArg. description) "
519
+ } else {
520
+ return cdeclToOriginalArg. description
521
+ }
522
+ }
523
+
524
+ // Form the call expression.
525
+ var callExpression : ExprSyntax = " \( inputFunction. name) ( \( raw: cdeclToOriginalArguments. joined ( separator: " , " ) ) ) "
526
+ if let cdeclToOriginalSelf {
527
+ callExpression = " \( cdeclToOriginalSelf) . \( callExpression) "
528
+ }
529
+
530
+ // Handle the return.
531
+ if cdecl. result. type. isVoid && original. result. type. isVoid {
532
+ // Nothing to return.
533
+ loweredCDecl. body = """
534
+ {
535
+ \( callExpression)
536
+ }
537
+ """
538
+ } else if cdecl. result. type. isVoid {
539
+ // Indirect return. This is a regular return in Swift that turns
540
+ // into a
541
+ loweredCDecl. body = """
542
+ {
543
+ \( result. cdeclToOriginalArgumentExpr ( isSelf: true ) ) = \( callExpression)
544
+ }
545
+ """
546
+ } else {
547
+ // Direct return.
548
+ loweredCDecl. body = """
549
+ {
550
+ return \( callExpression)
551
+ }
552
+ """
553
+ }
554
+
555
+ return loweredCDecl
556
+ }
557
+ }
0 commit comments