Skip to content

Commit ac30ee4

Browse files
authored
Initial support for unsigned numbers in jextract (#333)
1 parent 7f24d62 commit ac30ee4

File tree

55 files changed

+1922
-268
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1922
-268
lines changed

.unacceptablelanguageignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ Sources/_Subprocess/Platforms/Subprocess+Darwin.swift
33
Sources/_Subprocess/Platforms/Subprocess+Linux.swift
44
Sources/_Subprocess/Platforms/Subprocess+Unix.swift
55
Sources/_Subprocess/Teardown.swift
6-
Sources/_Subprocess/Subprocess.swift
6+
Sources/_Subprocess/Subprocess.swift
7+
NOTICE.txt

Samples/JExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ public func globalTakeIntInt(i: Int64, j: Int64) {
4545
p("i:\(i), j:\(j)")
4646
}
4747

48+
public func echoUnsignedInt(i: UInt32, j: UInt64) -> UInt64 {
49+
p("i:\(i), j:\(j)")
50+
return UInt64(i) + j
51+
}
52+
4853
// ==== Internal helpers
4954

5055
func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) {

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,11 @@ void globalVariable() {
6161
MySwiftLibrary.setGlobalVariable(100);
6262
assertEquals(100, MySwiftLibrary.getGlobalVariable());
6363
}
64+
65+
@Test
66+
void globalUnsignedIntEcho() {
67+
int i = 12;
68+
long l = 1200;
69+
assertEquals(1212, MySwiftLibrary.echoUnsignedInt(12, 1200));
70+
}
6471
}

Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftClass.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,16 @@ public class MySwiftClass {
6060
public func makeRandomIntMethod() -> Int {
6161
return Int.random(in: 1..<256)
6262
}
63+
64+
public func takeUnsignedChar(arg: UInt16) {
65+
p("\(UInt32.self) = \(arg)")
66+
}
67+
68+
public func takeUnsignedInt(arg: UInt32) {
69+
p("\(UInt32.self) = \(arg)")
70+
}
71+
72+
public func takeUnsignedLong(arg: UInt64) {
73+
p("\(UInt64.self) = \(arg)")
74+
}
6375
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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 org.junit.jupiter.api.Test;
18+
import org.swift.swiftkit.ffm.AllocatingSwiftArena;
19+
20+
public class UnsignedNumbersTest {
21+
@Test
22+
void take_uint32() {
23+
try (var arena = AllocatingSwiftArena.ofConfined()) {
24+
var c = MySwiftClass.init(1, 2, arena);
25+
c.takeUnsignedInt(128);
26+
}
27+
}
28+
29+
@Test
30+
void take_uint64() {
31+
try (var arena = AllocatingSwiftArena.ofConfined()) {
32+
var c = MySwiftClass.init(1, 2, arena);
33+
c.takeUnsignedLong(Long.MAX_VALUE);
34+
}
35+
}
36+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
import JavaTypes
16+
import JavaKitConfigurationShared
17+
18+
/// Determine if the given type needs any extra annotations that should be included
19+
/// in Java sources when the corresponding Java type is rendered.
20+
func getTypeAnnotations(swiftType: SwiftType, config: Configuration) -> [JavaAnnotation] {
21+
if swiftType.isUnsignedInteger, config.effectiveUnsignedNumbersMode == .annotate {
22+
return [JavaAnnotation.unsigned]
23+
}
24+
25+
return []
26+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
import JavaKitConfigurationShared // TODO: this should become SwiftJavaConfigurationShared
16+
import JavaTypes // TODO: this should become SwiftJavaConfigurationShared
17+
18+
extension Configuration {
19+
public var effectiveUnsignedNumericsMode: UnsignedNumericsMode {
20+
switch effectiveUnsignedNumbersMode {
21+
case .annotate: .ignoreSign
22+
case .wrapGuava: .wrapUnsignedGuava
23+
}
24+
}
25+
}

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,16 @@ extension FFMSwift2JavaGenerator {
105105
var params: [String] = []
106106
var args: [String] = []
107107
for param in cFunc.parameters {
108-
// ! unwrapping because cdecl lowering guarantees the parameter named.
109-
params.append("\(param.type.javaType) \(param.name!)")
110-
args.append(param.name!)
108+
let name = param.name! // !-safe, because cdecl lowering guarantees the parameter named.
109+
110+
let annotationsStr =
111+
if param.type.javaType.parameterAnnotations.isEmpty {
112+
""
113+
} else {
114+
param.type.javaType.parameterAnnotations.map({$0.render()}).joined(separator: " ") + " "
115+
}
116+
params.append("\(annotationsStr)\(param.type.javaType) \(name)")
117+
args.append(name)
111118
}
112119
let paramsStr = params.joined(separator: ", ")
113120
let argsStr = args.joined(separator: ", ")
@@ -316,23 +323,21 @@ extension FFMSwift2JavaGenerator {
316323
let translatedSignature = translated.translatedSignature
317324
let returnTy = translatedSignature.result.javaResultType
318325

326+
var annotationsStr = translatedSignature.annotations.map({ $0.render() }).joined(separator: "\n")
327+
if !annotationsStr.isEmpty { annotationsStr += "\n" }
328+
319329
var paramDecls = translatedSignature.parameters
320330
.flatMap(\.javaParameters)
321-
.map { "\($0.type) \($0.name)" }
331+
.map { $0.renderParameter() }
322332
if translatedSignature.requiresSwiftArena {
323333
paramDecls.append("AllocatingSwiftArena swiftArena$")
324334
}
325335

326336
// TODO: we could copy the Swift method's documentation over here, that'd be great UX
337+
printDeclDocumentation(&printer, decl)
327338
printer.printBraceBlock(
328339
"""
329-
/**
330-
* Downcall to Swift:
331-
* {@snippet lang=swift :
332-
* \(decl.signatureString)
333-
* }
334-
*/
335-
\(modifiers) \(returnTy) \(methodName)(\(paramDecls.joined(separator: ", ")))
340+
\(annotationsStr)\(modifiers) \(returnTy) \(methodName)(\(paramDecls.joined(separator: ", ")))
336341
"""
337342
) { printer in
338343
if case .instance(_) = decl.functionSignature.selfParameter {
@@ -344,6 +349,19 @@ extension FFMSwift2JavaGenerator {
344349
}
345350
}
346351

352+
private func printDeclDocumentation(_ printer: inout CodePrinter, _ decl: ImportedFunc) {
353+
printer.print(
354+
"""
355+
/**
356+
* Downcall to Swift:
357+
* {@snippet lang=swift :
358+
* \(decl.signatureString)
359+
* }
360+
*/
361+
"""
362+
)
363+
}
364+
347365
/// Print the actual downcall to the Swift API.
348366
///
349367
/// This assumes that all the parameters are passed-in with appropriate names.

0 commit comments

Comments
 (0)