Skip to content

Commit 7cba57b

Browse files
committed
add support for optionals as parameters
1 parent 11dcbed commit 7cba57b

File tree

6 files changed

+304
-51
lines changed

6 files changed

+304
-51
lines changed

Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftClass.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,20 @@ public class MySwiftClass {
9090
public func addXWithJavaLong(_ other: JavaLong) -> Int64 {
9191
return self.x + other.longValue()
9292
}
93+
94+
public func optionalMethod(input: Optional<Int32>) -> Int64 {
95+
if let input {
96+
return Int64(input)
97+
} else {
98+
return 0
99+
}
100+
}
101+
102+
public func optionalMethodClass(input: MySwiftClass?) -> Bool {
103+
if let input {
104+
return true
105+
} else {
106+
return false
107+
}
108+
}
93109
}

Samples/JExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
import org.junit.jupiter.api.Test;
1818
import org.swift.swiftkit.core.ConfinedSwiftMemorySession;
1919

20+
import java.util.Optional;
21+
import java.util.OptionalInt;
22+
2023
import static org.junit.jupiter.api.Assertions.*;
2124

2225
public class MySwiftClassTest {
@@ -148,4 +151,23 @@ void addXWithJavaLong() {
148151
assertEquals(70, c1.addXWithJavaLong(javaLong));
149152
}
150153
}
154+
155+
@Test
156+
void optionalMethod() {
157+
try (var arena = new ConfinedSwiftMemorySession()) {
158+
MySwiftClass c1 = MySwiftClass.init(20, 10, arena);
159+
assertEquals(0, c1.optionalMethod(OptionalInt.empty()));
160+
assertEquals(50, c1.optionalMethod(OptionalInt.of(50)));
161+
}
162+
}
163+
164+
@Test
165+
void optionalMethodClass() {
166+
try (var arena = new ConfinedSwiftMemorySession()) {
167+
MySwiftClass c1 = MySwiftClass.init(20, 10, arena);
168+
MySwiftClass c2 = MySwiftClass.init(50, 10, arena);
169+
assertFalse(c1.optionalMethodClass(Optional.empty()));
170+
assertTrue(c1.optionalMethodClass(Optional.of(c2)));
171+
}
172+
}
151173
}

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ extension JNISwift2JavaGenerator {
2020
static let defaultJavaImports: Array<String> = [
2121
"org.swift.swiftkit.core.*",
2222
"org.swift.swiftkit.core.util.*",
23+
"java.util.*"
2324
]
2425
}
2526

@@ -270,7 +271,11 @@ extension JNISwift2JavaGenerator {
270271
if let selfParameter = nativeSignature.selfParameter {
271272
parameters.append(selfParameter)
272273
}
273-
let renderedParameters = parameters.map { "\($0.javaType) \($0.name)"}.joined(separator: ", ")
274+
let renderedParameters = parameters.flatMap {
275+
$0.parameters.map { javaParameter in
276+
"\(javaParameter.type) \(javaParameter.name)"
277+
}
278+
}.joined(separator: ", ")
274279

275280
printer.print("private static native \(resultType) \(translatedDecl.nativeFunctionName)(\(renderedParameters));")
276281
}

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift

Lines changed: 126 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,26 @@ extension JNISwift2JavaGenerator {
168168
let nominalTypeName = nominalType.nominalTypeDecl.name
169169

170170
if let knownType = nominalType.nominalTypeDecl.knownTypeKind {
171-
guard let javaType = JNISwift2JavaGenerator.translate(knownType: knownType) else {
172-
throw JavaTranslationError.unsupportedSwiftType(swiftType)
171+
switch knownType {
172+
case .optional:
173+
guard let genericArgs = nominalType.genericArguments, genericArgs.count == 1 else {
174+
throw JavaTranslationError.unsupportedSwiftType(swiftType)
175+
}
176+
return try translateOptionalParameter(
177+
wrappedType: genericArgs[0],
178+
parameterName: parameterName
179+
)
180+
181+
default:
182+
guard let javaType = JNISwift2JavaGenerator.translate(knownType: knownType) else {
183+
throw JavaTranslationError.unsupportedSwiftType(swiftType)
184+
}
185+
186+
return TranslatedParameter(
187+
parameter: JavaParameter(name: parameterName, type: javaType),
188+
conversion: .placeholder
189+
)
173190
}
174-
175-
return TranslatedParameter(
176-
parameter: JavaParameter(name: parameterName, type: javaType),
177-
conversion: .placeholder
178-
)
179191
}
180192

181193
if nominalType.isJavaKitWrapper {
@@ -216,7 +228,73 @@ extension JNISwift2JavaGenerator {
216228
conversion: .placeholder
217229
)
218230

219-
case .metatype, .optional, .tuple, .existential, .opaque:
231+
case .optional(let wrapped):
232+
return try translateOptionalParameter(
233+
wrappedType: wrapped,
234+
parameterName: parameterName
235+
)
236+
237+
case .metatype, .tuple, .existential, .opaque:
238+
throw JavaTranslationError.unsupportedSwiftType(swiftType)
239+
}
240+
}
241+
242+
func translateOptionalParameter(
243+
wrappedType swiftType: SwiftType,
244+
parameterName: String
245+
) throws -> TranslatedParameter {
246+
switch swiftType {
247+
case .nominal(let nominalType):
248+
let nominalTypeName = nominalType.nominalTypeDecl.name
249+
250+
if let knownType = nominalType.nominalTypeDecl.knownTypeKind {
251+
guard let javaType = JNISwift2JavaGenerator.translate(knownType: knownType) else {
252+
throw JavaTranslationError.unsupportedSwiftType(swiftType)
253+
}
254+
255+
let (translatedClass, orElseValue) = switch javaType {
256+
case .boolean: ("Optional<Boolean>", "false")
257+
case .byte: ("Optional<Byte>", "0")
258+
case .char: ("Optional<Character>", "0")
259+
case .short: ("Optional<Short>", "0")
260+
case .int: ("OptionalInt", "0")
261+
case .long: ("OptionalLong", "0")
262+
case .float: ("Optional<Float>", "0")
263+
case .double: ("OptionalDouble", "0")
264+
case .javaLangString: ("Optional<String>", #""""#)
265+
default:
266+
throw JavaTranslationError.unsupportedSwiftType(swiftType)
267+
}
268+
269+
return TranslatedParameter(
270+
parameter: JavaParameter(
271+
name: parameterName,
272+
type: JavaType(className: translatedClass)
273+
),
274+
conversion: .commaSeparated([
275+
.isOptionalPresent,
276+
.method(.placeholder, function: "orElse", arguments: [.constant(orElseValue)])
277+
])
278+
)
279+
}
280+
281+
guard !nominalType.isJavaKitWrapper else {
282+
throw JavaTranslationError.unsupportedSwiftType(swiftType)
283+
}
284+
285+
// Assume JExtract imported class
286+
return TranslatedParameter(
287+
parameter: JavaParameter(
288+
name: parameterName,
289+
type: .class(package: nil, name: "Optional<\(nominalTypeName)>")
290+
),
291+
conversion: .method(
292+
.method(.placeholder, function: "map", arguments: [.constant("\(nominalType)::$memoryAddress")]),
293+
function: "orElse",
294+
arguments: [.constant("0L")]
295+
)
296+
)
297+
default:
220298
throw JavaTranslationError.unsupportedSwiftType(swiftType)
221299
}
222300
}
@@ -337,41 +415,77 @@ extension JNISwift2JavaGenerator {
337415
/// The value being converted
338416
case placeholder
339417

418+
case constant(String)
419+
420+
// The input exploded into components.
421+
case explodedName(component: String)
422+
423+
// Convert the results of the inner steps to a comma separated list.
424+
indirect case commaSeparated([JavaNativeConversionStep])
425+
340426
/// `value.$memoryAddress()`
341427
indirect case valueMemoryAddress(JavaNativeConversionStep)
342428

343429
/// Call `new \(Type)(\(placeholder), swiftArena$)`
344430
indirect case constructSwiftValue(JavaNativeConversionStep, JavaType)
345431

432+
indirect case method(JavaNativeConversionStep, function: String, arguments: [JavaNativeConversionStep])
433+
434+
case isOptionalPresent
435+
346436
/// Returns the conversion string applied to the placeholder.
347437
func render(_ printer: inout CodePrinter, _ placeholder: String) -> String {
348438
// NOTE: 'printer' is used if the conversion wants to cause side-effects.
349439
// E.g. storing a temporary values into a variable.
350440
switch self {
351441
case .placeholder:
352442
return placeholder
353-
443+
444+
case .constant(let value):
445+
return value
446+
447+
case .explodedName(let component):
448+
return "\(placeholder)_\(component)"
449+
450+
case .commaSeparated(let list):
451+
return list.map({ $0.render(&printer, placeholder)}).joined(separator: ", ")
452+
354453
case .valueMemoryAddress:
355454
return "\(placeholder).$memoryAddress()"
356-
455+
357456
case .constructSwiftValue(let inner, let javaType):
358457
let inner = inner.render(&printer, placeholder)
359458
return "new \(javaType.className!)(\(inner), swiftArena$)"
360-
459+
460+
case .isOptionalPresent:
461+
return "(byte) (\(placeholder).isPresent() ? 1 : 0)"
462+
463+
case .method(let inner, let methodName, let arguments):
464+
let inner = inner.render(&printer, placeholder)
465+
let args = arguments.map { $0.render(&printer, placeholder) }
466+
let argsStr = args.joined(separator: ", ")
467+
return "\(inner).\(methodName)(\(argsStr))"
468+
361469
}
362470
}
363471

364472
/// Whether the conversion uses SwiftArena.
365473
var requiresSwiftArena: Bool {
366474
switch self {
367-
case .placeholder:
475+
case .placeholder, .constant, .explodedName, .isOptionalPresent:
368476
return false
369477

370478
case .constructSwiftValue:
371479
return true
372480

373481
case .valueMemoryAddress(let inner):
374482
return inner.requiresSwiftArena
483+
484+
case .commaSeparated(let list):
485+
return list.contains(where: { $0.requiresSwiftArena })
486+
487+
case .method(let inner, _, _):
488+
return inner.requiresSwiftArena
375489
}
376490
}
377491
}

0 commit comments

Comments
 (0)