Skip to content

Commit cea8b92

Browse files
committed
add support for primitive types that cannot be widened
1 parent 04b2d9e commit cea8b92

File tree

6 files changed

+261
-29
lines changed

6 files changed

+261
-29
lines changed

Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/Optionals.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,23 @@ public func optionalShort(input: Optional<Int16>) -> Int16? {
3131
public func optionalInt(input: Optional<Int32>) -> Int32? {
3232
return input
3333
}
34+
35+
public func optionalLong(input: Optional<Int64>) -> Int64? {
36+
return input
37+
}
38+
39+
public func optionalFloat(input: Optional<Float>) -> Float? {
40+
return input
41+
}
42+
43+
public func optionalDouble(input: Optional<Double>) -> Double? {
44+
return input
45+
}
46+
47+
public func optionalString(input: Optional<String>) -> String? {
48+
return input
49+
}
50+
51+
public func optionalClass(input: Optional<MySwiftClass>) -> MySwiftClass? {
52+
return input
53+
}

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
package com.example.swift;
1616

1717
import org.junit.jupiter.api.Test;
18+
import org.swift.swiftkit.core.ConfinedSwiftMemorySession;
1819

1920
import java.util.Optional;
21+
import java.util.OptionalDouble;
2022
import java.util.OptionalInt;
23+
import java.util.OptionalLong;
2124

2225
import static org.junit.jupiter.api.Assertions.assertEquals;
2326
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -52,4 +55,37 @@ void optionalInt() {
5255
assertEquals(OptionalInt.empty(), MySwiftLibrary.optionalInt(OptionalInt.empty()));
5356
assertEquals(OptionalInt.of(999), MySwiftLibrary.optionalInt(OptionalInt.of(999)));
5457
}
58+
59+
@Test
60+
void optionalLong() {
61+
assertEquals(OptionalLong.empty(), MySwiftLibrary.optionalLong(OptionalLong.empty()));
62+
assertEquals(OptionalLong.of(999), MySwiftLibrary.optionalLong(OptionalLong.of(999)));
63+
}
64+
65+
@Test
66+
void optionalFloat() {
67+
assertEquals(Optional.empty(), MySwiftLibrary.optionalFloat(Optional.empty()));
68+
assertEquals(Optional.of(3.14f), MySwiftLibrary.optionalFloat(Optional.of(3.14f)));
69+
}
70+
71+
@Test
72+
void optionalDouble() {
73+
assertEquals(OptionalDouble.empty(), MySwiftLibrary.optionalDouble(OptionalDouble.empty()));
74+
assertEquals(OptionalDouble.of(2.718), MySwiftLibrary.optionalDouble(OptionalDouble.of(2.718)));
75+
}
76+
77+
@Test
78+
void optionalString() {
79+
assertEquals(Optional.empty(), MySwiftLibrary.optionalString(Optional.empty()));
80+
assertEquals(Optional.of("Hello Swift!"), MySwiftLibrary.optionalString(Optional.of("Hello Swift!")));
81+
}
82+
83+
@Test
84+
void optionalClass() {
85+
try (var arena = new ConfinedSwiftMemorySession()) {
86+
MySwiftClass c = MySwiftClass.init(arena);
87+
assertEquals(Optional.empty(), MySwiftLibrary.optionalClass(Optional.empty()));
88+
assertEquals(Optional.of(c), MySwiftLibrary.optionalClass(Optional.of(c)));
89+
}
90+
}
5591
}

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift

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

15+
import JavaTypes
1516

1617
// MARK: Defaults
1718

@@ -268,14 +269,14 @@ extension JNISwift2JavaGenerator {
268269
let translatedDecl = translatedDecl(for: decl)! // Will always call with valid decl
269270
let nativeSignature = translatedDecl.nativeFunctionSignature
270271
let resultType = nativeSignature.result.javaType
271-
var parameters = nativeSignature.parameters
272-
if let selfParameter = nativeSignature.selfParameter {
273-
parameters.append(selfParameter)
272+
var parameters = nativeSignature.parameters.flatMap(\.parameters)
273+
if let selfParameter = nativeSignature.selfParameter?.parameters {
274+
parameters += selfParameter
274275
}
275-
let renderedParameters = parameters.flatMap {
276-
$0.parameters.map { javaParameter in
276+
parameters += nativeSignature.result.outParameters
277+
278+
let renderedParameters = parameters.map { javaParameter in
277279
"\(javaParameter.type) \(javaParameter.name)"
278-
}
279280
}.joined(separator: ", ")
280281

281282
printer.print("private static native \(resultType) \(translatedDecl.nativeFunctionName)(\(renderedParameters));")
@@ -301,6 +302,12 @@ extension JNISwift2JavaGenerator {
301302
arguments.append(lowered)
302303
}
303304

305+
// Indirect return receivers
306+
for outParameter in translatedFunctionSignature.resultType.outParameters {
307+
printer.print("\(outParameter.type) \(outParameter.name) = \(outParameter.allocation.render());")
308+
arguments.append(outParameter.name)
309+
}
310+
304311
//=== Part 3: Downcall.
305312
// TODO: If we always generate a native method and a "public" method, we can actually choose our own thunk names
306313
// using the registry?
@@ -356,4 +363,13 @@ extension JNISwift2JavaGenerator {
356363
)
357364
}
358365
}
366+
367+
private func renderIndirectReturnAllocation(for javaType: JavaType) -> String {
368+
switch javaType {
369+
case .array(let nested):
370+
"new byte[]"
371+
default:
372+
fatalError("renderIndirectReturnAllocation not supported for \(javaType)")
373+
}
374+
}
359375
}

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,8 @@ extension JNISwift2JavaGenerator {
259259
case .short: ("Optional<Short>", "(short) 0")
260260
case .int: ("OptionalInt", "0")
261261
case .long: ("OptionalLong", "0L")
262-
case .float: ("Optional<Float>", "0")
263-
case .double: ("OptionalDouble", "0")
262+
case .float: ("Optional<Float>", "0f")
263+
case .double: ("OptionalDouble", "0.0")
264264
case .javaLangString: ("Optional<String>", #""""#)
265265
default:
266266
throw JavaTranslationError.unsupportedSwiftType(swiftType)
@@ -321,6 +321,7 @@ extension JNISwift2JavaGenerator {
321321

322322
return TranslatedResult(
323323
javaType: javaType,
324+
outParameters: [],
324325
conversion: .placeholder
325326
)
326327
}
@@ -334,11 +335,12 @@ extension JNISwift2JavaGenerator {
334335
let javaType = JavaType.class(package: nil, name: nominalType.nominalTypeDecl.name)
335336
return TranslatedResult(
336337
javaType: javaType,
338+
outParameters: [],
337339
conversion: .constructSwiftValue(.placeholder, javaType)
338340
)
339341

340342
case .tuple([]):
341-
return TranslatedResult(javaType: .void, conversion: .placeholder)
343+
return TranslatedResult(javaType: .void, outParameters: [], conversion: .placeholder)
342344

343345
case .optional(let wrapped):
344346
return try translateOptionalResult(wrappedType: wrapped)
@@ -379,6 +381,7 @@ extension JNISwift2JavaGenerator {
379381
if let nextIntergralTypeWithSpaceForByte = javaType.nextIntergralTypeWithSpaceForByte {
380382
return TranslatedResult(
381383
javaType: .class(package: nil, name: returnType),
384+
outParameters: [],
382385
conversion: .combinedValueToOptional(
383386
.placeholder,
384387
nextIntergralTypeWithSpaceForByte.java,
@@ -388,8 +391,28 @@ extension JNISwift2JavaGenerator {
388391
)
389392
)
390393
} else {
391-
// Otherwise, we are forced to use an array.
392-
fatalError()
394+
// Otherwise, we return the result as normal, but
395+
// use an indirect return for the discriminator.
396+
return TranslatedResult(
397+
javaType: .class(package: nil, name: returnType),
398+
outParameters: [
399+
OutParameter(name: "result_discriminator$", type: .array(.byte), allocation: .newArray(.byte, size: 1))
400+
],
401+
conversion: .aggregate(
402+
name: "result$",
403+
type: javaType,
404+
[
405+
.ternary(
406+
.equals(
407+
.subscriptOf(.constant("result_discriminator$"), arguments: [.constant("0")]),
408+
.constant("1")
409+
),
410+
thenExp: .method(.constant(staticCallee), function: "of", arguments: [.constant("result$")]),
411+
elseExp: .method(.constant(staticCallee), function: "empty")
412+
)
413+
]
414+
)
415+
)
393416
}
394417
}
395418

@@ -467,10 +490,33 @@ extension JNISwift2JavaGenerator {
467490
struct TranslatedResult {
468491
let javaType: JavaType
469492

493+
let outParameters: [OutParameter]
494+
470495
/// Represents how to convert the Java native result into a user-facing result.
471496
let conversion: JavaNativeConversionStep
472497
}
473498

499+
struct OutParameter {
500+
enum Allocation {
501+
case newArray(JavaType, size: Int)
502+
503+
func render() -> String {
504+
switch self {
505+
case .newArray(let javaType, let size):
506+
"new \(javaType)[\(size)]"
507+
}
508+
}
509+
}
510+
511+
let name: String
512+
let type: JavaType
513+
let allocation: Allocation
514+
515+
var javaParameter: JavaParameter {
516+
JavaParameter(name: self.name, type: self.type)
517+
}
518+
}
519+
474520
/// Represent a Swift closure type in the user facing Java API.
475521
///
476522
/// Closures are translated to named functional interfaces in Java.
@@ -500,12 +546,21 @@ extension JNISwift2JavaGenerator {
500546
/// Call `new \(Type)(\(placeholder), swiftArena$)`
501547
indirect case constructSwiftValue(JavaNativeConversionStep, JavaType)
502548

503-
indirect case method(JavaNativeConversionStep, function: String, arguments: [JavaNativeConversionStep])
549+
indirect case method(JavaNativeConversionStep, function: String, arguments: [JavaNativeConversionStep] = [])
504550

505551
case isOptionalPresent
506552

507553
indirect case combinedValueToOptional(JavaNativeConversionStep, JavaType, valueType: JavaType, valueSizeInBytes: Int, optionalType: String)
508554

555+
indirect case ternary(JavaNativeConversionStep, thenExp: JavaNativeConversionStep, elseExp: JavaNativeConversionStep)
556+
557+
indirect case equals(JavaNativeConversionStep, JavaNativeConversionStep)
558+
559+
indirect case subscriptOf(JavaNativeConversionStep, arguments: [JavaNativeConversionStep])
560+
561+
/// Perform multiple conversions using the same input.
562+
case aggregate(name: String, type: JavaType, [JavaNativeConversionStep])
563+
509564
/// Returns the conversion string applied to the placeholder.
510565
func render(_ printer: inout CodePrinter, _ placeholder: String) -> String {
511566
// NOTE: 'printer' is used if the conversion wants to cause side-effects.
@@ -555,6 +610,30 @@ extension JNISwift2JavaGenerator {
555610
}
556611

557612
return "discriminator$ == 1 ? \(optionalType).of(value$) : \(optionalType).empty()"
613+
614+
case .ternary(let cond, let thenExp, let elseExp):
615+
let cond = cond.render(&printer, placeholder)
616+
let thenExp = thenExp.render(&printer, placeholder)
617+
let elseExp = elseExp.render(&printer, placeholder)
618+
return "(\(cond)) ? \(thenExp) : \(elseExp)"
619+
620+
case .equals(let lhs, let rhs):
621+
let lhs = lhs.render(&printer, placeholder)
622+
let rhs = rhs.render(&printer, placeholder)
623+
return "\(lhs) == \(rhs)"
624+
625+
case .subscriptOf(let inner, let arguments):
626+
let inner = inner.render(&printer, placeholder)
627+
let arguments = arguments.map { $0.render(&printer, placeholder) }
628+
return "\(inner)[\(arguments.joined(separator: ", "))]"
629+
630+
case .aggregate(let name, let type, let steps):
631+
precondition(!steps.isEmpty, "Aggregate must contain steps")
632+
printer.print("\(type) \(name) = \(placeholder);")
633+
let steps = steps.map {
634+
$0.render(&printer, name)
635+
}
636+
return steps.last!
558637
}
559638
}
560639

@@ -578,6 +657,18 @@ extension JNISwift2JavaGenerator {
578657

579658
case .combinedValueToOptional(let inner, _, _, _, _):
580659
return inner.requiresSwiftArena
660+
661+
case .ternary(let cond, let thenExp, let elseExp):
662+
return cond.requiresSwiftArena || thenExp.requiresSwiftArena || elseExp.requiresSwiftArena
663+
664+
case .equals(let lhs, let rhs):
665+
return lhs.requiresSwiftArena || rhs.requiresSwiftArena
666+
667+
case .subscriptOf(let inner, _):
668+
return inner.requiresSwiftArena
669+
670+
case .aggregate(_, _, let steps):
671+
return steps.contains(where: \.requiresSwiftArena)
581672
}
582673
}
583674
}

0 commit comments

Comments
 (0)