Skip to content

Commit 21cff45

Browse files
committed
[JExtract/FFM] Translate 'some DataProtocol' parameters to 'Data'
* Introduce `SwiftType.opaque(SwiftType)` and `.existential(SwiftType)` to represent `some` and `any` types respectively. * Introduce `SwiftKnownTypes.representativeType(of:)` to the hardcoded `DataProtocol` -> `Data` translation mapping.
1 parent c2f683e commit 21cff45

File tree

13 files changed

+225
-55
lines changed

13 files changed

+225
-55
lines changed

Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ public func withBuffer(body: (UnsafeRawBufferPointer) -> Void) {
6363
body(globalBuffer)
6464
}
6565

66+
public func globalReceiveSomeDataProtocol(data: some DataProtocol) {
67+
p(Array(data).description)
68+
}
69+
6670
// ==== Internal helpers
6771

6872
func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) {

Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@
2222
import org.swift.swiftkit.ffm.AllocatingSwiftArena;
2323
import org.swift.swiftkit.ffm.SwiftRuntime;
2424

25-
import java.lang.foreign.MemoryLayout;
26-
import java.lang.foreign.MemorySegment;
27-
import java.lang.foreign.ValueLayout;
28-
2925
public class HelloJava2Swift {
3026

3127
public static void main(String[] args) {
@@ -95,6 +91,12 @@ static void examples() {
9591
});
9692
}
9793

94+
try (var arena = AllocatingSwiftArena.ofConfined()) {
95+
var bytes = arena.allocateFrom("hello");
96+
var dat = Data.init(bytes, bytes.byteSize(), arena);
97+
MySwiftLibrary.globalReceiveSomeDataProtocol(dat);
98+
}
99+
98100

99101
System.out.println("DONE.");
100102
}

Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ extension CType {
2424
init(cdeclType: SwiftType) throws {
2525
switch cdeclType {
2626
case .nominal(let nominalType):
27-
if let knownType = nominalType.nominalTypeDecl.knownStandardLibraryType {
27+
if let knownType = nominalType.nominalTypeDecl.knownTypeKind {
2828
if let primitiveCType = knownType.primitiveCType {
2929
self = primitiveCType
3030
return
@@ -68,7 +68,7 @@ extension CType {
6868
case .optional(let wrapped) where wrapped.isPointer:
6969
try self.init(cdeclType: wrapped)
7070

71-
case .metatype, .optional, .tuple:
71+
case .metatype, .optional, .tuple, .opaque, .existential:
7272
throw CDeclToCLoweringError.invalidCDeclType(cdeclType)
7373
}
7474
}
@@ -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:
128+
case .unsafePointer, .unsafeMutablePointer, .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer, .unsafeBufferPointer, .unsafeMutableBufferPointer, .string, .data, .dataProtocol:
129129
nil
130130
}
131131
}

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ extension FFMSwift2JavaGenerator {
7777
struct CdeclLowering {
7878
var knownTypes: SwiftKnownTypes
7979

80+
init(knownTypes: SwiftKnownTypes) {
81+
self.knownTypes = knownTypes
82+
}
83+
8084
init(symbolTable: SwiftSymbolTable) {
8185
self.knownTypes = SwiftKnownTypes(symbolTable: symbolTable)
8286
}
@@ -165,7 +169,7 @@ struct CdeclLowering {
165169
)
166170

167171
case .nominal(let nominal):
168-
if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType {
172+
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
169173
if convention == .inout {
170174
// FIXME: Support non-trivial 'inout' for builtin types.
171175
throw LoweringError.inoutNotSupported(type)
@@ -320,6 +324,18 @@ struct CdeclLowering {
320324
conversion: conversion
321325
)
322326

327+
case .opaque(let proto), .existential(let proto):
328+
// If the protocol has a known representative implementation, e.g. `String` for `StringProtocol`
329+
// Translate it as the concrete type.
330+
// NOTE: This is a temporary workaround until we add support for generics.
331+
if
332+
let knownProtocol = proto.asNominalTypeDeclaration?.knownTypeKind,
333+
let concreteTy = knownTypes.representativeType(of: knownProtocol)
334+
{
335+
return try lowerParameter(concreteTy, convention: convention, parameterName: parameterName)
336+
}
337+
throw LoweringError.unhandledType(type)
338+
323339
case .optional:
324340
throw LoweringError.unhandledType(type)
325341
}
@@ -386,7 +402,7 @@ struct CdeclLowering {
386402

387403
switch type {
388404
case .nominal(let nominal):
389-
if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType {
405+
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
390406
switch knownType {
391407
case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
392408
// pointer buffers are lowered to (raw-pointer, count) pair.
@@ -421,7 +437,7 @@ struct CdeclLowering {
421437
// Custom types are not supported yet.
422438
throw LoweringError.unhandledType(type)
423439

424-
case .function, .metatype, .optional, .tuple:
440+
case .function, .metatype, .optional, .tuple, .existential, .opaque:
425441
// TODO: Implement
426442
throw LoweringError.unhandledType(type)
427443
}
@@ -454,7 +470,7 @@ struct CdeclLowering {
454470

455471
case .nominal(let nominal):
456472
// Types from the Swift standard library that we know about.
457-
if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType {
473+
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
458474
switch knownType {
459475
case .unsafePointer, .unsafeMutablePointer:
460476
// Typed pointers are lowered to corresponding raw forms.
@@ -575,7 +591,7 @@ struct CdeclLowering {
575591
conversion: .tupleExplode(conversions, name: outParameterName)
576592
)
577593

578-
case .function(_), .optional(_):
594+
case .function, .optional, .existential, .opaque:
579595
throw LoweringError.unhandledType(type)
580596
}
581597
}

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,16 @@ extension FFMSwift2JavaGenerator {
113113
}
114114

115115
struct JavaTranslation {
116-
var symbolTable: SwiftSymbolTable
116+
var knownTypes: SwiftKnownTypes
117+
118+
init(symbolTable: SwiftSymbolTable) {
119+
self.knownTypes = SwiftKnownTypes(symbolTable: symbolTable)
120+
}
117121

118122
func translate(
119123
_ decl: ImportedFunc
120124
) throws -> TranslatedFunctionDecl {
121-
let lowering = CdeclLowering(symbolTable: symbolTable)
125+
let lowering = CdeclLowering(knownTypes: knownTypes)
122126
let loweredSignature = try lowering.lowerFunctionSignature(decl.functionSignature)
123127

124128
// Name.
@@ -208,7 +212,7 @@ extension FFMSwift2JavaGenerator {
208212

209213
switch type {
210214
case .nominal(let nominal):
211-
if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType {
215+
if let knownType = nominal.nominalTypeDecl.knownTypeKind {
212216
switch knownType {
213217
case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
214218
return TranslatedParameter(
@@ -248,11 +252,12 @@ extension FFMSwift2JavaGenerator {
248252
// 'self'
249253
let selfParameter: TranslatedParameter?
250254
if case .instance(let swiftSelf) = swiftSignature.selfParameter {
251-
selfParameter = try self.translate(
252-
swiftParam: swiftSelf,
255+
selfParameter = try self.translateParameter(
256+
type: swiftSelf.type,
257+
convention: swiftSelf.convention,
258+
parameterName: swiftSelf.parameterName ?? "self",
253259
loweredParam: loweredFunctionSignature.selfParameter!,
254-
methodName: methodName,
255-
parameterName: swiftSelf.parameterName ?? "self"
260+
methodName: methodName
256261
)
257262
} else {
258263
selfParameter = nil
@@ -263,11 +268,12 @@ extension FFMSwift2JavaGenerator {
263268
.map { (idx, swiftParam) in
264269
let loweredParam = loweredFunctionSignature.parameters[idx]
265270
let parameterName = swiftParam.parameterName ?? "_\(idx)"
266-
return try self.translate(
267-
swiftParam: swiftParam,
271+
return try self.translateParameter(
272+
type: swiftParam.type,
273+
convention: swiftParam.convention,
274+
parameterName: parameterName,
268275
loweredParam: loweredParam,
269-
methodName: methodName,
270-
parameterName: parameterName
276+
methodName: methodName
271277
)
272278
}
273279

@@ -285,13 +291,13 @@ extension FFMSwift2JavaGenerator {
285291
}
286292

287293
/// Translate a Swift API parameter to the user-facing Java API parameter.
288-
func translate(
289-
swiftParam: SwiftParameter,
294+
func translateParameter(
295+
type swiftType: SwiftType,
296+
convention: SwiftParameterConvention,
297+
parameterName: String,
290298
loweredParam: LoweredParameter,
291-
methodName: String,
292-
parameterName: String
299+
methodName: String
293300
) throws -> TranslatedParameter {
294-
let swiftType = swiftParam.type
295301

296302
// If there is a 1:1 mapping between this Swift type and a C type, that can
297303
// be expressed as a Java primitive type.
@@ -319,8 +325,8 @@ extension FFMSwift2JavaGenerator {
319325
)
320326

321327
case .nominal(let swiftNominalType):
322-
if let knownType = swiftNominalType.nominalTypeDecl.knownStandardLibraryType {
323-
if swiftParam.convention == .inout {
328+
if let knownType = swiftNominalType.nominalTypeDecl.knownTypeKind {
329+
if convention == .inout {
324330
// FIXME: Support non-trivial 'inout' for builtin types.
325331
throw JavaTranslationError.inoutNotSupported(swiftType)
326332
}
@@ -388,6 +394,26 @@ extension FFMSwift2JavaGenerator {
388394
conversion: .call(.placeholder, function: "\(methodName).$toUpcallStub", withArena: true)
389395
)
390396

397+
case .existential(let proto), .opaque(let proto):
398+
// If the protocol has a known representative implementation, e.g. `String` for `StringProtocol`
399+
// Translate it as the concrete type.
400+
// NOTE: This is a temporary workaround until we add support for generics.
401+
if
402+
let knownProtocol = proto.asNominalTypeDeclaration?.knownTypeKind,
403+
let concreteTy = knownTypes.representativeType(of: knownProtocol)
404+
{
405+
return try translateParameter(
406+
type: concreteTy,
407+
convention: convention,
408+
parameterName: parameterName,
409+
loweredParam: loweredParam,
410+
methodName: methodName
411+
)
412+
}
413+
414+
// Otherwise, not supported yet.
415+
throw JavaTranslationError.unhandledType(swiftType)
416+
391417
case .optional:
392418
throw JavaTranslationError.unhandledType(swiftType)
393419
}
@@ -422,7 +448,7 @@ extension FFMSwift2JavaGenerator {
422448
)
423449

424450
case .nominal(let swiftNominalType):
425-
if let knownType = swiftNominalType.nominalTypeDecl.knownStandardLibraryType {
451+
if let knownType = swiftNominalType.nominalTypeDecl.knownTypeKind {
426452
switch knownType {
427453
case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer:
428454
return TranslatedResult(
@@ -476,7 +502,7 @@ extension FFMSwift2JavaGenerator {
476502
// TODO: Implement.
477503
throw JavaTranslationError.unhandledType(swiftType)
478504

479-
case .optional, .function:
505+
case .optional, .function, .existential, .opaque:
480506
throw JavaTranslationError.unhandledType(swiftType)
481507
}
482508

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ extension JNISwift2JavaGenerator {
6666
func translate(swiftType: SwiftType) -> JavaType {
6767
switch swiftType {
6868
case .nominal(let nominalType):
69-
if let knownType = nominalType.nominalTypeDecl.knownStandardLibraryType {
69+
if let knownType = nominalType.nominalTypeDecl.knownTypeKind {
7070
guard let javaType = translate(knownType: knownType) else {
7171
fatalError("unsupported known type: \(knownType)")
7272
}
@@ -78,7 +78,7 @@ extension JNISwift2JavaGenerator {
7878
case .tuple([]):
7979
return .void
8080

81-
case .metatype, .optional, .tuple, .function:
81+
case .metatype, .optional, .tuple, .function, .existential, .opaque:
8282
fatalError("unsupported type: \(self)")
8383
}
8484
}
@@ -99,10 +99,8 @@ extension JNISwift2JavaGenerator {
9999
.unsafeRawPointer, .unsafeMutableRawPointer,
100100
.unsafePointer, .unsafeMutablePointer,
101101
.unsafeRawBufferPointer, .unsafeMutableRawBufferPointer,
102-
.unsafeBufferPointer, .unsafeMutableBufferPointer:
102+
.unsafeBufferPointer, .unsafeMutableBufferPointer, .data, .dataProtocol:
103103
nil
104-
case .data:
105-
fatalError("unimplemented")
106104
}
107105
}
108106
}

Sources/JExtractSwiftLib/Swift2JavaTranslator.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ extension Swift2JavaTranslator {
103103
// If any API uses 'Foundation.Data', import 'Data' as if it's declared in
104104
// this module.
105105
if let dataDecl = self.symbolTable[.data] {
106-
if self.isUsing(dataDecl) {
106+
let dataProtocolDecl = self.symbolTable[.dataProtocol]!
107+
if self.isUsing(where: { $0 == dataDecl || $0 == dataProtocolDecl }) {
107108
visitor.visit(nominalDecl: dataDecl.syntax!.asNominal!, in: nil)
108109
}
109110
}
@@ -117,12 +118,13 @@ extension Swift2JavaTranslator {
117118
)
118119
}
119120

120-
/// Check if any of the imported decls uses the specified nominal declaration.
121-
func isUsing(_ decl: SwiftNominalTypeDeclaration) -> Bool {
121+
/// Check if any of the imported decls uses a nominal declaration that satisfies
122+
/// the given predicate.
123+
func isUsing(where predicate: (SwiftNominalTypeDeclaration) -> Bool) -> Bool {
122124
func check(_ type: SwiftType) -> Bool {
123125
switch type {
124126
case .nominal(let nominal):
125-
return nominal.nominalTypeDecl == decl
127+
return predicate(nominal.nominalTypeDecl)
126128
case .optional(let ty):
127129
return check(ty)
128130
case .tuple(let tuple):
@@ -131,6 +133,8 @@ extension Swift2JavaTranslator {
131133
return check(fn.resultType) || fn.parameters.contains(where: { check($0.type) })
132134
case .metatype(let ty):
133135
return check(ty)
136+
case .existential(let ty), .opaque(let ty):
137+
return check(ty)
134138
}
135139
}
136140

Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownModules.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ private let swiftSourceFile: SourceFileSyntax = """
8686
"""
8787

8888
private let foundationSourceFile: SourceFileSyntax = """
89-
public struct Data {
89+
public protocol DataProtocol {}
90+
91+
public struct Data: DataProtocol {
9092
public init(bytes: UnsafeRawPointer, count: Int)
9193
public var count: Int { get }
9294
public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) -> Void)

Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ enum SwiftKnownTypeDeclKind: String, Hashable {
3939
case void = "Swift.Void"
4040
case string = "Swift.String"
4141

42+
case dataProtocol = "Foundation.DataProtocol"
4243
case data = "Foundation.Data"
4344

4445
var moduleAndName: (module: String, name: String) {

Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ struct SwiftKnownTypes {
3535
var unsafeRawPointer: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.unsafeRawPointer])) }
3636
var unsafeMutableRawPointer: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.unsafeMutableRawPointer])) }
3737

38+
var dataProtocol: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.dataProtocol])) }
39+
var data: SwiftType { .nominal(SwiftNominalType(nominalTypeDecl: symbolTable[.data])) }
40+
3841
func unsafePointer(_ pointeeType: SwiftType) -> SwiftType {
3942
.nominal(
4043
SwiftNominalType(
@@ -70,4 +73,13 @@ struct SwiftKnownTypes {
7073
)
7174
)
7275
}
76+
77+
/// Returns the known representative concrete type if there is one for the
78+
/// given protocol kind. E.g. `String` for `StringProtocol`
79+
func representativeType(of knownProtocol: SwiftKnownTypeDeclKind) -> SwiftType? {
80+
switch knownProtocol {
81+
case .dataProtocol: return self.data
82+
default: return nil
83+
}
84+
}
7385
}

0 commit comments

Comments
 (0)