Skip to content

Commit 9b53e69

Browse files
committed
add support for associated values
1 parent 31b9712 commit 9b53e69

13 files changed

+149
-12
lines changed

Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/Vehicle.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
public enum Vehicle {
16-
case car
1716
case bicycle
17+
case car(String)
18+
case motorbike(String, horsePower: Int64)
1819
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 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+
package com.example.swift;
16+
17+
import org.junit.jupiter.api.Test;
18+
import org.swift.swiftkit.core.ConfinedSwiftMemorySession;
19+
20+
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
import static org.junit.jupiter.api.Assertions.assertNotNull;
22+
23+
public class VehicleTest {
24+
@Test
25+
void bicycle() {
26+
try (var arena = new ConfinedSwiftMemorySession()) {
27+
Vehicle vehicle = Vehicle.bicycle(arena);
28+
assertNotNull(vehicle);
29+
}
30+
}
31+
32+
@Test
33+
void car() {
34+
try (var arena = new ConfinedSwiftMemorySession()) {
35+
Vehicle vehicle = Vehicle.car("Porsche 911", arena);
36+
assertNotNull(vehicle);
37+
}
38+
}
39+
40+
@Test
41+
void motorbike() {
42+
try (var arena = new ConfinedSwiftMemorySession()) {
43+
Vehicle vehicle = Vehicle.motorbike("Yamaha", 750, arena);
44+
assertNotNull(vehicle);
45+
}
46+
}
47+
}

Sources/JExtractSwiftLib/Convenience/SwiftSyntax+Extensions.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ extension DeclSyntaxProtocol {
253253
}
254254
))
255255
.triviaSanitizedDescription
256+
case .enumCaseDecl(let node):
257+
node.triviaSanitizedDescription
256258
default:
257259
fatalError("unimplemented \(self.kind)")
258260
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,10 @@ extension LoweredFunctionSignature {
835835
case .setter:
836836
assert(paramExprs.count == 1)
837837
resultExpr = "\(callee) = \(paramExprs[0])"
838+
839+
case .enumCase:
840+
// This should not be called, but let's fatalError.
841+
fatalError("Enum cases are not supported with FFM.")
838842
}
839843

840844
// Lower the result.

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ extension FFMSwift2JavaGenerator {
146146
let javaName = switch decl.apiKind {
147147
case .getter: decl.javaGetterName
148148
case .setter: decl.javaSetterName
149-
case .function, .initializer: decl.name
149+
case .function, .initializer, .enumCase: decl.name
150150
}
151151

152152
// Signature.

Sources/JExtractSwiftLib/ImportedDecls.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package enum SwiftAPIKind {
2222
case initializer
2323
case getter
2424
case setter
25+
case enumCase
2526
}
2627

2728
/// Describes a Swift nominal type (e.g., a class, struct, enum) that has been
@@ -54,16 +55,21 @@ public final class ImportedEnumCase: ImportedDecl, CustomStringConvertible {
5455
/// The enum parameters
5556
var parameters: [SwiftEnumCaseParameter]
5657

57-
init(name: String, parameters: [SwiftEnumCaseParameter]) {
58+
/// A function that represents the Swift static "initializer" for cases
59+
var caseFunction: ImportedFunc
60+
61+
init(name: String, parameters: [SwiftEnumCaseParameter], caseFunction: ImportedFunc) {
5862
self.name = name
5963
self.parameters = parameters
64+
self.caseFunction = caseFunction
6065
}
6166

6267
public var description: String {
6368
"""
6469
ImportedEnumCase {
6570
name: \(name),
66-
parameters: \(parameters)
71+
parameters: \(parameters),
72+
caseFunction: \(caseFunction)
6773
}
6874
"""
6975
}
@@ -136,6 +142,7 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible {
136142
let prefix = switch self.apiKind {
137143
case .getter: "getter:"
138144
case .setter: "setter:"
145+
case .enumCase: "case:"
139146
case .function, .initializer: ""
140147
}
141148

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,24 @@ extension JNISwift2JavaGenerator {
193193
}
194194

195195
private func printEnumHelpers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
196+
// TODO: Move this to seperate file +Enum?
197+
printEnumDiscriminator(&printer, decl)
198+
printer.println()
199+
printEnumStaticInitializers(&printer, decl)
200+
}
201+
202+
private func printEnumDiscriminator(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
196203
printer.printBraceBlock("public enum Discriminator") { printer in
197204
printer.print(
198205
decl.cases.map { $0.name.uppercased() }.joined(separator: ",\n")
199206
)
200207
}
208+
}
201209

202-
printer.println()
210+
private func printEnumStaticInitializers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
211+
for enumCase in decl.cases {
212+
printFunctionDowncallMethods(&printer, enumCase.caseFunction)
213+
}
203214
}
204215

205216
private func printFunctionDowncallMethods(

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ extension JNISwift2JavaGenerator {
6161
let javaName = switch decl.apiKind {
6262
case .getter: decl.javaGetterName
6363
case .setter: decl.javaSetterName
64-
case .function, .initializer: decl.name
64+
case .function, .initializer, .enumCase: decl.name
6565
}
6666

6767
// Swift -> Java
@@ -137,7 +137,7 @@ extension JNISwift2JavaGenerator {
137137
parentName: String
138138
) throws -> TranslatedFunctionSignature {
139139
let parameters = try functionSignature.parameters.enumerated().map { idx, param in
140-
let parameterName = param.parameterName ?? "arg\(idx))"
140+
let parameterName = param.parameterName ?? "arg\(idx)"
141141
return try translateParameter(swiftType: param.type, parameterName: parameterName, methodName: methodName, parentName: parentName)
142142
}
143143

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ extension JNISwift2JavaGenerator {
102102
printer.println()
103103
}
104104

105+
for enumCase in type.cases {
106+
printSwiftFunctionThunk(&printer, enumCase.caseFunction)
107+
printer.println()
108+
}
109+
105110
for method in type.methods {
106111
printSwiftFunctionThunk(&printer, method)
107112
printer.println()
@@ -190,6 +195,18 @@ extension JNISwift2JavaGenerator {
190195
.joined(separator: ", ")
191196
result = "\(tryClause)\(callee).\(decl.name)(\(downcallArguments))"
192197

198+
case .enumCase:
199+
let downcallArguments = zip(
200+
decl.functionSignature.parameters,
201+
arguments
202+
).map { originalParam, argument in
203+
let label = originalParam.argumentLabel.map { "\($0): " } ?? ""
204+
return "\(label)\(argument)"
205+
}
206+
207+
let associatedValues = !downcallArguments.isEmpty ? "(\(downcallArguments.joined(separator: ", ")))" : ""
208+
result = "\(callee).\(decl.name)\(associatedValues)"
209+
193210
case .getter:
194211
result = "\(tryClause)\(callee).\(decl.name)"
195212

Sources/JExtractSwiftLib/Swift2JavaVisitor.swift

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ final class Swift2JavaVisitor {
134134
}
135135

136136
func visit(enumCaseDecl node: EnumCaseDeclSyntax, in typeContext: ImportedNominalType?) {
137+
guard let typeContext else {
138+
self.log.info("Enum case must be within a current type; \(node)")
139+
return
140+
}
141+
137142
do {
138143
for caseElement in node.elements {
139144
self.log.debug("Import case \(caseElement.name) of enum \(node.qualifiedNameForDebug)")
@@ -142,12 +147,27 @@ final class Swift2JavaVisitor {
142147
try SwiftEnumCaseParameter($0, lookupContext: translator.lookupContext)
143148
}
144149

145-
let imported = ImportedEnumCase(
150+
let signature = try SwiftFunctionSignature(
151+
caseElement,
152+
enclosingType: typeContext.swiftType,
153+
lookupContext: translator.lookupContext
154+
)
155+
156+
let caseFunction = ImportedFunc(
157+
module: translator.swiftModuleName,
158+
swiftDecl: node,
159+
name: caseElement.name.text,
160+
apiKind: .enumCase,
161+
functionSignature: signature
162+
)
163+
164+
let importedCase = ImportedEnumCase(
146165
name: caseElement.name.text,
147-
parameters: parameters ?? []
166+
parameters: parameters ?? [],
167+
caseFunction: caseFunction
148168
)
149169

150-
typeContext?.cases.append(imported)
170+
typeContext.cases.append(importedCase)
151171
}
152172
} catch {
153173
self.log.debug("Failed to import: \(node.qualifiedNameForDebug); \(error)")

0 commit comments

Comments
 (0)