Skip to content

Commit ad2e222

Browse files
committed
add documentation for unsigned types
1 parent bc1b0cf commit ad2e222

File tree

3 files changed

+67
-6
lines changed

3 files changed

+67
-6
lines changed

Samples/SwiftKitSampleApp/src/test/java/com/example/swift/UnsignedNumbersTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
package com.example.swift;
1616

1717
import org.junit.jupiter.api.Test;
18-
import org.swift.swiftkit.core.primitives.*;
1918
import org.swift.swiftkit.ffm.AllocatingSwiftArena;
2019

2120
public class UnsignedNumbersTest {

Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ SwiftJava's `swift-java jextract` tool automates generating Java bindings from S
4545
4646

4747
| Swift Feature | FFM | JNI |
48-
|--------------------------------------------------------------------------------------| -------- |-----|
48+
|--------------------------------------------------------------------------------------|----------|-----|
4949
| Initializers: `class`, `struct` |||
5050
| Optional Initializers / Throwing Initializers |||
5151
| Deinitializers: `class`, `struct` |||
@@ -67,7 +67,7 @@ SwiftJava's `swift-java jextract` tool automates generating Java bindings from S
6767
| Primitive types: `Bool`, `Int`, `Int8`, `Int16`, `Int32`, `Int64`, `Float`, `Double` |||
6868
| Parameters: JavaKit wrapped types `JavaLong`, `JavaInteger` |||
6969
| Return values: JavaKit wrapped types `JavaLong`, `JavaInteger` |||
70-
| Unsigned primitive types: `UInt`, `UInt8`, `UInt16`, `UInt32`, `UInt64` | | |
70+
| Unsigned primitive types: `UInt`, `UInt8`, `UInt16`, `UInt32`, `UInt64` | ✅ * | ✅ * |
7171
| String (with copying data) |||
7272
| Variadic parameters: `T...` |||
7373
| Parametrer packs / Variadic generics |||
@@ -76,14 +76,14 @@ SwiftJava's `swift-java jextract` tool automates generating Java bindings from S
7676
| Operators: `+`, `-`, user defined |||
7777
| Subscripts: `subscript()` |||
7878
| Equatable |||
79-
| Pointers: `UnsafeRawPointer`, UnsafeBufferPointer (?) | 🟡 ||
79+
| Pointers: `UnsafeRawPointer`, UnsafeBufferPointer (?) | 🟡 ||
8080
| Nested types: `struct Hello { struct World {} }` |||
8181
| Inheritance: `class Caplin: Capybara` |||
8282
| Non-escaping `Void` closures: `func callMe(maybe: () -> ())` |||
8383
| Non-escaping closures with primitive arguments/results: `func callMe(maybe: (Int) -> (Double))` |||
8484
| Non-escaping closures with object arguments/results: `func callMe(maybe: (JavaObj) -> (JavaObj))` |||
8585
| `@escaping` closures: `func callMe(_: @escaping () -> ())` |||
86-
| Swift type extensions: `extension String { func uppercased() }` | 🟡 | 🟡 |
86+
| Swift type extensions: `extension String { func uppercased() }` | 🟡 | 🟡 |
8787
| Swift macros (maybe) |||
8888
| Result builders |||
8989
| Automatic Reference Counting of class types / lifetime safety |||
@@ -94,3 +94,65 @@ SwiftJava's `swift-java jextract` tool automates generating Java bindings from S
9494
| | | |
9595

9696
> tip: The list of features may be incomplete, please file an issue if something is unclear or should be clarified in this table.
97+
98+
## Detailed feature support discussion
99+
100+
### Unsigned integers
101+
102+
### Java <-> Swift Type mapping
103+
104+
Java does not support unsigned numbers (other than the 16-bit wide `char`), and therefore mapping Swift's (and C)
105+
unsigned integer types is somewhat problematic.
106+
107+
SwiftJava's jextract mode, similar to OpenJDK jextract, does extract unsigned types from native code to Java
108+
as their bit-width equivalents. This is potentially dangerous because values larger than the `MAX_VALUE` of a given
109+
*signed* type in Java, e.g. `200` stored in an `UInt8` in Swift, would be interpreted as a `byte` of value `-56`,
110+
because Java's `byte` type is _signed_.
111+
112+
#### Unsigned numbers mode: annotate (default)
113+
114+
Because in many situations the data represented by such numbers is merely passed along, and not interpreted by Java,
115+
this may be safe to pass along. However, interpreting unsigned values incorrectly like this can lead to subtle mistakes
116+
on the Java side.
117+
118+
| Swift type | Java type |
119+
|------------|-----------|
120+
| `Int8` | `byte` |
121+
| `UInt8` | `byte` ⚠️ |
122+
| `Int16` | `short` |
123+
| `UInt16` | `char` |
124+
| `Int32` | `int` |
125+
| `UInt32` | `int` ⚠️ |
126+
| `Int64` | `long` |
127+
| `UInt64` | `long` ⚠️ |
128+
| `Float` | `float` |
129+
| `Double` | `double` |
130+
131+
#### Unsigned numbers mode: wrap-guava
132+
133+
You can configure `jextract` (in FFM mode) to instead import unsigned values as their unsigned type-safe representations
134+
as offered by the Guava library: `UnsignedLong` or `UnsignedInt`. To enable this mode pass the `--unsigned-numbers wrap-guava`
135+
command line option, or set the corresponding configuration value in `swift-java.config` (TODO).
136+
137+
This approach is type-safe, however it incurs a performance penalty for allocating a wrapper class for every
138+
unsigned integer parameter passed to and from native Swift functions.
139+
140+
SwiftJava _does not_ vendor or provide the Guava library as a dependency, and when using this mode
141+
you are expected to add a Guava dependency to your Java project.
142+
143+
> You can read more about the unsigned integers support
144+
145+
| Swift type | Java type |
146+
|------------|--------------------------------------------------------|
147+
| `Int8` | `byte` |
148+
| `UInt8` | `com.google.common.primitives.UnsignedInteger` (class) |
149+
| `Int16` | `short` |
150+
| `UInt16` | `char` |
151+
| `Int32` | `int` |
152+
| `UInt32` | `com.google.common.primitives.UnsignedInteger` (class)️ |
153+
| `Int64` | `long` |
154+
| `UInt64` | `com.google.common.primitives.UnsignedLong` (class) |
155+
| `Float` | `float` |
156+
| `Double` | `double` |
157+
158+
> Note: The `wrap-guava` mode is currently only available in FFM mode of jextract.

Sources/SwiftJavaTool/Commands/JExtractCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ extension SwiftJava {
6161
@Flag(help: "Some build systems require an output to be present when it was 'expected', even if empty. This is used by the JExtractSwiftPlugin build plugin, but otherwise should not be necessary.")
6262
var writeEmptyFiles: Bool = false
6363

64-
@Option(help: "The mode of generation to use for the output files. Used with jextract mode.")
64+
@Option(help: "The mode of generation to use for the output files. Used with jextract mode. By default, unsigned Swift types are imported as their bit-width compatible signed Java counterparts, and annotated using the '@Unsigned' annotation. You may choose the 'wrap-guava' mode in order to import types as class wrapper types (`UnsignedInteger` et al) defined by the Google Guava library's `com.google.common.primitives' package. that ensure complete type-safety with regards to unsigned values, however they incur an allocation and performance overhead.")
6565
var unsignedNumbers: JExtractUnsignedIntegerMode = .default
6666

6767
@Option(

0 commit comments

Comments
 (0)