Skip to content

Commit d0fcd4c

Browse files
pelekonktoso
andauthored
Support UInt handling in jextract JNI mode (#460)
Co-authored-by: pelekon <[email protected]> Co-authored-by: Konrad `ktoso` Malawski <[email protected]> Co-authored-by: Konrad Malawski <[email protected]>
1 parent f16496b commit d0fcd4c

File tree

18 files changed

+787
-30
lines changed

18 files changed

+787
-30
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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 enum EnumWithValueCases {
16+
case firstCase(UInt)
17+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 final class ObjectWithInts {
16+
public var normalInt: Int
17+
public var unsignedInt: UInt
18+
19+
public init(normalInt: Int, unsignedInt: UInt) {
20+
self.normalInt = normalInt
21+
self.unsignedInt = unsignedInt
22+
}
23+
24+
public func callMe(arg: UInt) -> UInt {
25+
return arg
26+
}
27+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
import org.swift.swiftkit.core.SwiftArena;
20+
21+
import java.util.Optional;
22+
23+
import static org.junit.jupiter.api.Assertions.*;
24+
25+
public class EnumWithValueCasesTest {
26+
@Test
27+
void fn() {
28+
try (var arena = SwiftArena.ofConfined()) {
29+
EnumWithValueCases e = EnumWithValueCases.firstCase(48, arena);
30+
EnumWithValueCases.FirstCase c = (EnumWithValueCases.FirstCase) e.getCase();
31+
assertNotNull(c);
32+
}
33+
}
34+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
import org.swift.swiftkit.core.SwiftArena;
20+
21+
import java.util.Optional;
22+
23+
import static org.junit.jupiter.api.Assertions.*;
24+
25+
public class ObjectWithIntsTest {
26+
@Test
27+
void init() {
28+
try (var arena = SwiftArena.ofConfined()) {
29+
ObjectWithInts obj = ObjectWithInts.init(-45, 45, arena);
30+
assertEquals(-45, obj.getNormalInt());
31+
assertEquals(45, obj.getUnsignedInt());
32+
}
33+
}
34+
35+
@Test
36+
void callMe() {
37+
try (var arena = SwiftArena.ofConfined()) {
38+
ObjectWithInts obj = ObjectWithInts.init(-45, 45, arena);
39+
assertEquals(66, obj.callMe(66));
40+
}
41+
}
42+
}

Sources/JExtractSwiftLib/JNI/JNIJavaTypeTranslator.swift

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,55 @@ enum JNIJavaTypeTranslator {
3232

3333
case .int64: return .long
3434
case .uint64: return .long
35+
36+
case .int, .uint: return .long
3537

3638
case .float: return .float
3739
case .double: return .double
3840
case .void: return .void
3941

4042
case .string: return .javaLangString
4143

42-
case .int, .uint, // FIXME: why not supported int/uint?
43-
.unsafeRawPointer, .unsafeMutableRawPointer,
44+
case .unsafeRawPointer, .unsafeMutableRawPointer,
4445
.unsafePointer, .unsafeMutablePointer,
4546
.unsafeRawBufferPointer, .unsafeMutableRawBufferPointer,
4647
.unsafeBufferPointer, .unsafeMutableBufferPointer,
4748
.optional, .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol, .array:
4849
return nil
4950
}
5051
}
52+
53+
static func indirectConversionStepSwiftType(for knownKind: SwiftKnownTypeDeclKind, from knownTypes: SwiftKnownTypes) -> SwiftType? {
54+
switch knownKind {
55+
case .int: knownTypes.int64
56+
case .uint: knownTypes.uint64
57+
58+
case .bool, .int8, .uint8, .int16, .uint16, .int32, .uint32, .int64, .uint64,
59+
.float, .double, .void, .string,
60+
.unsafeRawPointer, .unsafeMutableRawPointer,
61+
.unsafePointer, .unsafeMutablePointer,
62+
.unsafeRawBufferPointer, .unsafeMutableRawBufferPointer,
63+
.unsafeBufferPointer, .unsafeMutableBufferPointer,
64+
.optional, .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol,
65+
.array:
66+
nil
67+
}
68+
}
69+
70+
static func checkStep(for knownKind: SwiftKnownTypeDeclKind, from knownTypes: SwiftKnownTypes) -> JNISwift2JavaGenerator.NativeSwiftConversionCheck? {
71+
switch knownKind {
72+
case .int: .check32BitIntOverflow(typeWithMinAndMax: knownTypes.int32)
73+
case .uint: .check32BitIntOverflow(typeWithMinAndMax: knownTypes.uint32)
74+
75+
case .bool, .int8, .uint8, .int16, .uint16, .int32, .uint32, .int64, .uint64,
76+
.float, .double, .void, .string,
77+
.unsafeRawPointer, .unsafeMutableRawPointer,
78+
.unsafePointer, .unsafeMutablePointer,
79+
.unsafeRawBufferPointer, .unsafeMutableRawBufferPointer,
80+
.unsafeBufferPointer, .unsafeMutableBufferPointer,
81+
.optional, .foundationData, .foundationDataProtocol, .essentialsData, .essentialsDataProtocol,
82+
.array:
83+
nil
84+
}
85+
}
5186
}

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ extension JNISwift2JavaGenerator {
509509
let translatedSignature = translatedDecl.translatedFunctionSignature
510510
let resultType = translatedSignature.resultType.javaType
511511
var parameters = translatedDecl.translatedFunctionSignature.parameters.map { $0.parameter.renderParameter() }
512-
let throwsClause = translatedDecl.isThrowing && !translatedDecl.isAsync ? " throws Exception" : ""
512+
let throwsClause = translatedDecl.throwsClause()
513513

514514
let generics = translatedDecl.translatedFunctionSignature.parameters.reduce(into: [(String, [JavaType])]()) { generics, parameter in
515515
guard case .generic(let name, let extends) = parameter.parameter.type else {

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ extension JNISwift2JavaGenerator {
121121
.class(package: nil,name: caseName)
122122
)
123123
])
124+
var exceptions: [JavaExceptionType] = []
125+
126+
if enumCase.parameters.contains(where: \.type.isArchDependingInteger) {
127+
exceptions.append(.integerOverflow)
128+
}
129+
124130
let getAsCaseFunction = TranslatedFunctionDecl(
125131
name: getAsCaseName,
126132
isStatic: false,
@@ -144,12 +150,15 @@ extension JNISwift2JavaGenerator {
144150
javaType: .class(package: nil, name: "Optional<\(caseName)>"),
145151
outParameters: conversions.flatMap(\.translated.outParameters),
146152
conversion: enumCase.parameters.isEmpty ? constructRecordConversion : .aggregate(variable: ("$nativeParameters", nativeParametersType), [constructRecordConversion])
147-
)
153+
),
154+
exceptions: exceptions
148155
),
149156
nativeFunctionSignature: NativeFunctionSignature(
150157
selfParameter: NativeParameter(
151158
parameters: [JavaParameter(name: "self", type: .long)],
152-
conversion: .extractSwiftValue(.placeholder, swiftType: .nominal(enumCase.enumType), allowNil: false)
159+
conversion: .extractSwiftValue(.placeholder, swiftType: .nominal(enumCase.enumType), allowNil: false),
160+
indirectConversion: nil,
161+
conversionCheck: nil
153162
),
154163
parameters: [],
155164
result: NativeResult(
@@ -299,12 +308,19 @@ extension JNISwift2JavaGenerator {
299308
genericRequirements: functionSignature.genericRequirements
300309
)
301310

311+
var exceptions: [JavaExceptionType] = []
312+
313+
if functionSignature.parameters.contains(where: \.type.isArchDependingInteger) {
314+
exceptions.append(.integerOverflow)
315+
}
316+
302317
let resultType = try translate(swiftResult: functionSignature.result)
303318

304319
return TranslatedFunctionSignature(
305320
selfParameter: selfParameter,
306321
parameters: parameters,
307-
resultType: resultType
322+
resultType: resultType,
323+
exceptions: exceptions
308324
)
309325
}
310326

@@ -952,12 +968,22 @@ extension JNISwift2JavaGenerator {
952968
var annotations: [JavaAnnotation] {
953969
self.translatedFunctionSignature.annotations
954970
}
971+
972+
func throwsClause() -> String {
973+
guard !translatedFunctionSignature.exceptions.isEmpty else {
974+
return isThrowing && !isAsync ? " throws Exception" : ""
975+
}
976+
977+
let signatureExceptions = translatedFunctionSignature.exceptions.compactMap(\.type.className).joined(separator: ", ")
978+
return " throws \(signatureExceptions)\(isThrowing ? ", Exception" : "")"
979+
}
955980
}
956981

957982
struct TranslatedFunctionSignature {
958983
var selfParameter: TranslatedParameter?
959984
var parameters: [TranslatedParameter]
960985
var resultType: TranslatedResult
986+
var exceptions: [JavaExceptionType]
961987

962988
// if the result type implied any annotations,
963989
// propagate them onto the function the result is returned from

0 commit comments

Comments
 (0)