Skip to content

Commit 44ac341

Browse files
committed
add support for composite types
1 parent 6827730 commit 44ac341

File tree

11 files changed

+137
-53
lines changed

11 files changed

+137
-53
lines changed

Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/ConcreteSomeProtocol.swift renamed to Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/ConcreteProtocolAB.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
public class ConcreteSomeProtocol: SomeProtocol {
15+
public class ConcreteProtocolAB: ProtocolA, ProtocolB {
1616
public let constant: Int64
1717
public var mutable: Int64 = 0
1818

1919
public func name() -> String {
20-
return "ConcreteSomeProtocol"
20+
return "ConcreteProtocolAB"
2121
}
2222

2323
public init(constant: Int64) {

Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/SomeProtocol.swift renamed to Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/ProtocolA.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
public protocol SomeProtocol {
15+
public protocol ProtocolA {
1616
var constant: Int64 { get }
1717
var mutable: Int64 { get set }
1818

1919
func name() -> String
2020
}
2121

22-
public func takeProtocol(_ proto1: some SomeProtocol, _ proto2: some SomeProtocol) -> Int64 {
22+
public func takeProtocol(_ proto1: any ProtocolA, _ proto2: some ProtocolA) -> Int64 {
2323
return proto1.constant + proto2.constant
2424
}
2525

26-
public func takeGenericProtocol<First: SomeProtocol, Second: SomeProtocol>(_ proto1: First, _ proto2: Second) -> Int64 {
26+
public func takeGenericProtocol<First: ProtocolA, Second: ProtocolA>(_ proto1: First, _ proto2: Second) -> Int64 {
2727
return proto1.constant + proto2.constant
2828
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
public protocol ProtocolB {
16+
var constant: Int64 { get }
17+
}
18+
19+
public func takeCombinedProtocol(_ proto: some ProtocolA & ProtocolB) -> Int64 {
20+
return proto.constant + proto.constant
21+
}

Samples/JExtractJNISampleApp/src/test/java/com/example/swift/SomeProtocolTest.java renamed to Samples/JExtractJNISampleApp/src/test/java/com/example/swift/ProtocolTest.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,21 @@
1919

2020
import static org.junit.jupiter.api.Assertions.*;
2121

22-
public class SomeProtocolTest {
22+
public class ProtocolTest {
2323
@Test
2424
void takeProtocol() {
2525
try (var arena = SwiftArena.ofConfined()) {
26-
ConcreteSomeProtocol proto1 = ConcreteSomeProtocol.init(10, arena);
27-
ConcreteSomeProtocol proto2 = ConcreteSomeProtocol.init(20, arena);
26+
ConcreteProtocolAB proto1 = ConcreteProtocolAB.init(10, arena);
27+
ConcreteProtocolAB proto2 = ConcreteProtocolAB.init(20, arena);
2828
assertEquals(30, MySwiftLibrary.takeProtocol(proto1, proto2));
2929
}
3030
}
31+
32+
@Test
33+
void takeCombinedProtocol() {
34+
try (var arena = SwiftArena.ofConfined()) {
35+
ConcreteProtocolAB proto1 = ConcreteProtocolAB.init(10, arena);
36+
assertEquals(20, MySwiftLibrary.takeCombinedProtocol(proto1));
37+
}
38+
}
3139
}

Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ extension CType {
6868
case .optional(let wrapped) where wrapped.isPointer:
6969
try self.init(cdeclType: wrapped)
7070

71-
case .genericParameter, .metatype, .optional, .tuple, .opaque, .existential:
71+
case .genericParameter, .metatype, .optional, .tuple, .opaque, .existential, .composite:
7272
throw CDeclToCLoweringError.invalidCDeclType(cdeclType)
7373
}
7474
}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,9 @@ struct CdeclLowering {
344344

345345
case .optional(let wrapped):
346346
return try lowerOptionalParameter(wrapped, convention: convention, parameterName: parameterName, genericParameters: genericParameters, genericRequirements: genericRequirements)
347+
348+
case .composite:
349+
throw LoweringError.unhandledType(type)
347350
}
348351
}
349352

@@ -412,7 +415,7 @@ struct CdeclLowering {
412415
}
413416
throw LoweringError.unhandledType(.optional(wrappedType))
414417

415-
case .function, .metatype, .optional:
418+
case .function, .metatype, .optional, .composite:
416419
throw LoweringError.unhandledType(.optional(wrappedType))
417420
}
418421
}
@@ -513,7 +516,7 @@ struct CdeclLowering {
513516
// Custom types are not supported yet.
514517
throw LoweringError.unhandledType(type)
515518

516-
case .genericParameter, .function, .metatype, .optional, .tuple, .existential, .opaque:
519+
case .genericParameter, .function, .metatype, .optional, .tuple, .existential, .opaque, .composite:
517520
// TODO: Implement
518521
throw LoweringError.unhandledType(type)
519522
}
@@ -667,7 +670,7 @@ struct CdeclLowering {
667670
conversion: .tupleExplode(conversions, name: outParameterName)
668671
)
669672

670-
case .genericParameter, .function, .optional, .existential, .opaque:
673+
case .genericParameter, .function, .optional, .existential, .opaque, .composite:
671674
throw LoweringError.unhandledType(type)
672675
}
673676
}

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,9 @@ extension FFMSwift2JavaGenerator {
472472
genericParameters: genericParameters,
473473
genericRequirements: genericRequirements
474474
)
475+
476+
case .composite:
477+
throw JavaTranslationError.unhandledType(swiftType)
475478
}
476479
}
477480

@@ -691,7 +694,7 @@ extension FFMSwift2JavaGenerator {
691694
// TODO: Implement.
692695
throw JavaTranslationError.unhandledType(swiftType)
693696

694-
case .genericParameter, .optional, .function, .existential, .opaque:
697+
case .genericParameter, .optional, .function, .existential, .opaque, .composite:
695698
throw JavaTranslationError.unhandledType(swiftType)
696699
}
697700

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ extension JNISwift2JavaGenerator {
440440
javaGenericName: "$T\(parameterPosition)"
441441
)
442442

443-
case .metatype, .tuple, .genericParameter:
443+
case .metatype, .tuple, .genericParameter, .composite:
444444
throw JavaTranslationError.unsupportedSwiftType(swiftType)
445445
}
446446
}
@@ -464,28 +464,45 @@ extension JNISwift2JavaGenerator {
464464
parameterName: String,
465465
javaGenericName: String
466466
) throws -> TranslatedParameter {
467-
let parameterAnnotations: [JavaAnnotation] = getTypeAnnotations(swiftType: protocolType, config: config)
468-
469467
switch protocolType {
470-
case .nominal(let nominalType):
471-
let nominalTypeName = nominalType.nominalTypeDecl.name
472-
let protocolType = JavaType.class(package: nil, name: nominalTypeName)
468+
case .nominal:
469+
return try translateProtocolParameter(protocolTypes: [protocolType], parameterName: parameterName, javaGenericName: javaGenericName)
473470

474-
// We assume this is a JExtract class.
475-
return TranslatedParameter(
476-
parameter: JavaParameter(
477-
name: parameterName,
478-
type: .generic(name: javaGenericName, extends: [protocolType]),
479-
annotations: parameterAnnotations
480-
),
481-
conversion: .commaSeparated([.valueMemoryAddress(.placeholder), .typeMetadataAddress(.placeholder)])
482-
)
471+
case .composite(let types):
472+
return try translateProtocolParameter(protocolTypes: types, parameterName: parameterName, javaGenericName: javaGenericName)
483473

484474
default:
485475
throw JavaTranslationError.unsupportedSwiftType(protocolType)
486476
}
487477
}
488478

479+
private func translateProtocolParameter(
480+
protocolTypes: [SwiftType],
481+
parameterName: String,
482+
javaGenericName: String
483+
) throws -> TranslatedParameter {
484+
let javaProtocolTypes = try protocolTypes.map {
485+
switch $0 {
486+
case .nominal(let nominalType):
487+
let nominalTypeName = nominalType.nominalTypeDecl.name
488+
return JavaType.class(package: nil, name: nominalTypeName)
489+
490+
default:
491+
throw JavaTranslationError.unsupportedSwiftType($0)
492+
}
493+
}
494+
495+
// We assume this is a JExtract class.
496+
return TranslatedParameter(
497+
parameter: JavaParameter(
498+
name: parameterName,
499+
type: .generic(name: javaGenericName, extends: javaProtocolTypes),
500+
annotations: []
501+
),
502+
conversion: .commaSeparated([.valueMemoryAddress(.placeholder), .typeMetadataAddress(.placeholder)])
503+
)
504+
}
505+
489506
func translateOptionalParameter(
490507
wrappedType swiftType: SwiftType,
491508
parameterName: String
@@ -604,7 +621,7 @@ extension JNISwift2JavaGenerator {
604621
case .optional(let wrapped):
605622
return try translateOptionalResult(wrappedType: wrapped, resultName: resultName)
606623

607-
case .metatype, .tuple, .function, .existential, .opaque, .genericParameter:
624+
case .metatype, .tuple, .function, .existential, .opaque, .genericParameter, .composite:
608625
throw JavaTranslationError.unsupportedSwiftType(swiftType)
609626
}
610627
}

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ extension JNISwift2JavaGenerator {
186186
parameterName: parameterName
187187
)
188188

189-
case .metatype, .tuple, .genericParameter:
189+
case .metatype, .tuple, .genericParameter, .composite:
190190
throw JavaTranslationError.unsupportedSwiftType(type)
191191
}
192192
}
@@ -198,24 +198,40 @@ extension JNISwift2JavaGenerator {
198198
switch protocolType {
199199
case .nominal(let nominalType):
200200
let protocolName = nominalType.nominalTypeDecl.qualifiedName
201+
return try translateProtocolParameter(protocolNames: [protocolName], parameterName: parameterName)
201202

202-
return NativeParameter(
203-
parameters: [
204-
JavaParameter(name: parameterName, type: .long),
205-
JavaParameter(name: "\(parameterName)_typeMetadataAddress", type: .long)
206-
],
207-
conversion: .extractSwiftProtocolValue(
208-
.placeholder,
209-
typeMetadataVariableName: .combinedName(component: "typeMetadataAddress"),
210-
protocolName: protocolName
211-
)
212-
)
203+
case .composite(let types):
204+
let protocolNames = try types.map {
205+
guard let nominalTypeName = $0.asNominalType?.nominalTypeDecl.qualifiedName else {
206+
throw JavaTranslationError.unsupportedSwiftType($0)
207+
}
208+
return nominalTypeName
209+
}
210+
211+
return try translateProtocolParameter(protocolNames: protocolNames, parameterName: parameterName)
213212

214213
default:
215214
throw JavaTranslationError.unsupportedSwiftType(protocolType)
216215
}
217216
}
218217

218+
private func translateProtocolParameter(
219+
protocolNames: [String],
220+
parameterName: String
221+
) throws -> NativeParameter {
222+
return NativeParameter(
223+
parameters: [
224+
JavaParameter(name: parameterName, type: .long),
225+
JavaParameter(name: "\(parameterName)_typeMetadataAddress", type: .long)
226+
],
227+
conversion: .extractSwiftProtocolValue(
228+
.placeholder,
229+
typeMetadataVariableName: .combinedName(component: "typeMetadataAddress"),
230+
protocolNames: protocolNames
231+
)
232+
)
233+
}
234+
219235
func translateOptionalParameter(
220236
wrappedType swiftType: SwiftType,
221237
parameterName: String
@@ -382,7 +398,7 @@ extension JNISwift2JavaGenerator {
382398
outParameters: []
383399
)
384400

385-
case .function, .metatype, .optional, .tuple, .existential, .opaque, .genericParameter:
401+
case .function, .metatype, .optional, .tuple, .existential, .opaque, .genericParameter, .composite:
386402
throw JavaTranslationError.unsupportedSwiftType(type)
387403
}
388404
}
@@ -411,7 +427,7 @@ extension JNISwift2JavaGenerator {
411427
// Custom types are not supported yet.
412428
throw JavaTranslationError.unsupportedSwiftType(type)
413429

414-
case .function, .metatype, .optional, .tuple, .existential, .opaque, .genericParameter:
430+
case .function, .metatype, .optional, .tuple, .existential, .opaque, .genericParameter, .composite:
415431
throw JavaTranslationError.unsupportedSwiftType(type)
416432
}
417433
}
@@ -463,7 +479,7 @@ extension JNISwift2JavaGenerator {
463479
case .optional(let wrapped):
464480
return try translateOptionalResult(wrappedType: wrapped, resultName: resultName)
465481

466-
case .metatype, .tuple, .function, .existential, .opaque, .genericParameter:
482+
case .metatype, .tuple, .function, .existential, .opaque, .genericParameter, .composite:
467483
throw JavaTranslationError.unsupportedSwiftType(swiftResult.type)
468484
}
469485
}
@@ -514,7 +530,7 @@ extension JNISwift2JavaGenerator {
514530
indirect case extractSwiftProtocolValue(
515531
NativeSwiftConversionStep,
516532
typeMetadataVariableName: NativeSwiftConversionStep,
517-
protocolName: String
533+
protocolNames: [String]
518534
)
519535

520536
/// Extracts a swift type at a pointer given by a long.
@@ -577,11 +593,13 @@ extension JNISwift2JavaGenerator {
577593
let inner = inner.render(&printer, placeholder)
578594
return "\(swiftType)(fromJNI: \(inner), in: environment!)"
579595

580-
case .extractSwiftProtocolValue(let inner, let typeMetadataVariableName, let protocolName):
596+
case .extractSwiftProtocolValue(let inner, let typeMetadataVariableName, let protocolNames):
581597
let inner = inner.render(&printer, placeholder)
582598
let typeMetadataVariableName = typeMetadataVariableName.render(&printer, placeholder)
583599
let existentialName = "\(inner)Existential$"
584600

601+
let compositeProtocolName = "(\(protocolNames.joined(separator: " & ")))"
602+
585603
// TODO: Remove the _openExistential when we decide to only support language mode v6+
586604
printer.print(
587605
"""
@@ -593,10 +611,10 @@ extension JNISwift2JavaGenerator {
593611
fatalError("\(inner) memory address was null")
594612
}
595613
#if hasFeature(ImplicitOpenExistentials)
596-
let \(existentialName) = \(inner)RawPointer$.load(as: \(inner)DynamicType$) as! any \(protocolName)
614+
let \(existentialName) = \(inner)RawPointer$.load(as: \(inner)DynamicType$) as! any \(compositeProtocolName)
597615
#else
598-
func \(inner)doLoad<Ty>(_ ty: Ty.Type) -> any \(protocolName) {
599-
\(inner)RawPointer$.load(as: ty) as! any \(protocolName)
616+
func \(inner)doLoad<Ty>(_ ty: Ty.Type) -> any \(compositeProtocolName) {
617+
\(inner)RawPointer$.load(as: ty) as! any \(compositeProtocolName)
600618
}
601619
let \(existentialName) = _openExistential(\(inner)DynamicType$, do: \(inner)doLoad)
602620
#endif

Sources/JExtractSwiftLib/Swift2JavaTranslator.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ extension Swift2JavaTranslator {
146146
return check(ty)
147147
case .existential(let ty), .opaque(let ty):
148148
return check(ty)
149+
case .composite(let types):
150+
return types.contains(where: check)
149151
case .genericParameter:
150152
return false
151153
}

0 commit comments

Comments
 (0)