Skip to content

Commit 0c7b613

Browse files
authored
jextract/jni: Basic accept/return support for Swift tuples (#602)
* Prepare tuple types * Tuple support in jextract/jni * update tuple definitions
1 parent 6828bc8 commit 0c7b613

37 files changed

Lines changed: 2537 additions & 13 deletions
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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 func returnPair() -> (Int64, String) {
16+
(42, "hello")
17+
}
18+
19+
public func takePair(pair: (Int64, String)) -> String {
20+
"\(pair.0):\(pair.1)"
21+
}
22+
23+
public func labeledTuple() -> (x: Int32, y: Int32) {
24+
(x: 10, y: 20)
25+
}
26+
27+
public func echoTriple(triple: (Bool, Double, Int64)) -> (Bool, Double, Int64) {
28+
triple
29+
}
30+
31+
public func makeBigTuple() -> (
32+
Bool, Int8, Int16, UInt16,
33+
Int32, Int64, Float, Double,
34+
String, Bool, Int8, Int16,
35+
UInt16, Int32, Int64, Float
36+
) {
37+
(
38+
true, 1, 2, 3,
39+
4, 5, 6.0, 7.0,
40+
"eight", false, 9, 10,
41+
11, 12, 13, 14.0
42+
)
43+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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+
package com.example.swift;
16+
17+
import com.example.swift.MySwiftLibrary;
18+
import org.junit.jupiter.api.Test;
19+
import org.swift.swiftkit.core.tuple.Tuple2;
20+
import org.swift.swiftkit.core.tuple.Tuple3;
21+
import org.swift.swiftkit.core.tuple.Tuple16;
22+
23+
import static org.junit.jupiter.api.Assertions.*;
24+
25+
public class TupleTest {
26+
@Test
27+
void returnPair() {
28+
Tuple2<Long, String> result = MySwiftLibrary.returnPair();
29+
assertEquals(42L, result.$0);
30+
assertEquals("hello", result.$1);
31+
}
32+
33+
@Test
34+
void takePair() {
35+
String result = MySwiftLibrary.takePair(new Tuple2<>(99L, "world"));
36+
assertEquals("99:world", result);
37+
}
38+
39+
@Test
40+
void labeledTuple() {
41+
Tuple2<Integer, Integer> result = MySwiftLibrary.labeledTuple();
42+
assertEquals(10, result.$0);
43+
assertEquals(20, result.$1);
44+
}
45+
46+
@Test
47+
void echoTriple() {
48+
Tuple3<Boolean, Double, Long> input = new Tuple3<>(true, 3.14, 100L);
49+
Tuple3<Boolean, Double, Long> result = MySwiftLibrary.echoTriple(input);
50+
assertEquals(true, result.$0);
51+
assertEquals(3.14, result.$1, 0.001);
52+
assertEquals(100L, result.$2);
53+
}
54+
55+
@Test
56+
void makeBigTuple() {
57+
Tuple16<Boolean, Byte, Short, Character,
58+
Integer, Long, Float, Double,
59+
String, Boolean, Byte, Short,
60+
Character, Integer, Long, Float> result = MySwiftLibrary.makeBigTuple();
61+
assertEquals(true, result.$0);
62+
assertEquals((byte) 1, result.$1);
63+
assertEquals((short) 2, result.$2);
64+
assertEquals((char) 3, result.$3);
65+
assertEquals(4, result.$4);
66+
assertEquals(5L, result.$5);
67+
assertEquals(6.0f, result.$6);
68+
assertEquals(7.0, result.$7);
69+
assertEquals("eight", result.$8);
70+
assertEquals(false, result.$9);
71+
assertEquals((byte) 9, result.$10);
72+
assertEquals((short) 10, result.$11);
73+
assertEquals((char) 11, result.$12);
74+
assertEquals(12, result.$13);
75+
assertEquals(13L, result.$14);
76+
assertEquals(14.0f, result.$15);
77+
}
78+
}

Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,20 @@ extension JavaType {
102102
}
103103
}
104104

105+
var jniSetArrayRegionMethodName: String {
106+
switch self {
107+
case .boolean: "SetBooleanArrayRegion"
108+
case .byte: "SetByteArrayRegion"
109+
case .char: "SetCharArrayRegion"
110+
case .short: "SetShortArrayRegion"
111+
case .int: "SetIntArrayRegion"
112+
case .long: "SetLongArrayRegion"
113+
case .float: "SetFloatArrayRegion"
114+
case .double: "SetDoubleArrayRegion"
115+
default: fatalError("Set*ArrayRegion is only available for JNI primitive types, was: \(self)")
116+
}
117+
}
118+
105119
/// Returns whether this type returns `JavaValue` from SwiftJava
106120
var implementsJavaValue: Bool {
107121
switch self {
@@ -146,4 +160,22 @@ extension JavaType {
146160
false
147161
}
148162
}
163+
164+
/// The boxed class name for this type, suitable for use as a generic type argument.
165+
var boxedName: String {
166+
switch self {
167+
case .boolean: "Boolean"
168+
case .byte: "Byte"
169+
case .char: "Character"
170+
case .short: "Short"
171+
case .int: "Integer"
172+
case .long: "Long"
173+
case .float: "Float"
174+
case .double: "Double"
175+
case .void: "Void"
176+
case .javaLangString: "String"
177+
case .class(_, let name, _): name
178+
case .array: description
179+
}
180+
}
149181
}

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ struct CdeclLowering {
325325
case .tuple(let tuple):
326326
if tuple.count == 1 {
327327
return try lowerParameter(
328-
tuple[0],
328+
tuple[0].type,
329329
convention: convention,
330330
parameterName: parameterName,
331331
genericParameters: genericParameters,
@@ -341,7 +341,7 @@ struct CdeclLowering {
341341
// FIXME: Use tuple element label.
342342
let cdeclName = "\(parameterName)_\(idx)"
343343
let lowered = try lowerParameter(
344-
element,
344+
element.type,
345345
convention: convention,
346346
parameterName: cdeclName,
347347
genericParameters: genericParameters,
@@ -518,7 +518,7 @@ struct CdeclLowering {
518518
case .tuple(let tuple):
519519
if tuple.count == 1 {
520520
return try lowerOptionalParameter(
521-
tuple[0],
521+
tuple[0].type,
522522
convention: convention,
523523
parameterName: parameterName,
524524
genericParameters: genericParameters,
@@ -697,8 +697,8 @@ struct CdeclLowering {
697697
let isMutable = knownType == .unsafeMutableBufferPointer
698698
return try lowerResult(
699699
.tuple([
700-
isMutable ? knownTypes.unsafeMutableRawPointer : knownTypes.unsafeRawPointer,
701-
knownTypes.int,
700+
SwiftTupleElement(label: nil, type: isMutable ? knownTypes.unsafeMutableRawPointer : knownTypes.unsafeRawPointer),
701+
SwiftTupleElement(label: nil, type: knownTypes.int),
702702
]),
703703
outParameterName: outParameterName
704704
)
@@ -755,14 +755,14 @@ struct CdeclLowering {
755755

756756
case .tuple(let tuple):
757757
if tuple.count == 1 {
758-
return try lowerResult(tuple[0], outParameterName: outParameterName)
758+
return try lowerResult(tuple[0].type, outParameterName: outParameterName)
759759
}
760760

761761
var parameters: [SwiftParameter] = []
762762
var conversions: [ConversionStep] = []
763763
for (idx, element) in tuple.enumerated() {
764764
let outName = "\(outParameterName)_\(idx)"
765-
let lowered = try lowerResult(element, outParameterName: outName)
765+
let lowered = try lowerResult(element.type, outParameterName: outName)
766766

767767
// Convert direct return values to typed mutable pointers.
768768
// E.g. (Int8, Int8) is lowered to '_ result_0: UnsafePointer<Int8>, _ result_1: UnsafePointer<Int8>'

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ extension FFMSwift2JavaGenerator {
585585
case .tuple(let tuple):
586586
if tuple.count == 1 {
587587
return try translateOptionalParameter(
588-
wrappedType: tuple[0],
588+
wrappedType: tuple[0].type,
589589
convention: convention,
590590
parameterName: parameterName,
591591
loweredParam: loweredParam,

0 commit comments

Comments
 (0)