Commit aa5e597
[JNIEnv] Add support for Kotlin's unsigned types. (#4054)
Fixes: dotnet/java-interop#525
Fixes: dotnet/java-interop#543
Context: dotnet/java-interop@476597b...4565369
Context: dotnet/java-interop@71afce5
Context: https://github.com/Kotlin/KEEP/blob/13b67668ccc5b4741ecc37d0dd050fd77227c035/proposals/unsigned-types.md
Context: https://kotlinlang.org/docs/reference/basic-types.html#unsigned-integers
Bumps to xamarin/Java.Interop/master@45653697
* dotnet/java-interop@4565369: [generator] Improve generic type lookup (#552)
* dotnet/java-interop@71afce5: [generator] Support Kotlin's unsigned types (#539)
* dotnet/java-interop@f26bc27: [Java.Interop] use Dictionary<string, *>(StringComparer.Ordinal) (#550)
Kotlin has experimental (!) support for unsigned types, providing the
following value types:
* `kotlin.UByte`: marshals as a Java `byte`/JNI `B`;
equivalent to C# `byte`
* `kotlin.UInt`: marshals as an Java `int`/JNI `I`;
equivalent to C# `uint`
* `kotlin.ULong`: marshals as a Java `long`/JNI `J`;
equivalent to C# `ulong`
* `kotlin.UShort`: marshals as a Java `short`/JNI `S`;
equivalent to C# `ushort`
Kotlin also provides arrays of these types:
* `kotlin.UByteArray`: marshals as a Java `byte[]`/JNI `[B`;
equivalent to C# `byte[]`,
* `kotlin.UIntArray`: marshals as an Java `int[]`/JNI `[I`;
equivalent to C# `uint[]`.
* `kotlin.ULongArray`: marshals as a Java `long[]`/JNI `[J`;
equivalent to C# `ulong[]`.
* `kotlin.UShortArray`: marshals as a Java `short[]`/JNI `[S`;
equivalent to C# `ushort[]`.
Kotlin methods which contain unsigned types are "mangled", containing
characters which cannot be part of a valid Java identifier. As such,
they cannot be invoked from Java code.
They *can* be invoked via JNI.
To bind these, we have two options:
1. We could ignore all members which use unsigned types, or
2. We could present a "Java-esque" view of the methods.
(1) is potentially problematic, as it means that abstract classes and
interfaces which contain them could become unbindable, making life
more complicated than is necessarily ideal.
(2) is viable, but ugly.
Consider this Kotlin type:
// Kotlin
package example;
public open class ExampleBase {
public fun foo(value : UInt) {}
}
Run `javap` on the resulting output, and we observe:
Compiled from "hello.kt"
public class example.ExampleBase {
public final void foo-WZ4Q5Ns(int);
public example.ExampleBase();
}
We could bind `example.ExampleBase.foo-WZ4Q5Ns(int)` in C# by
replacing invalid characters with `_`, e.g.:
// C# Binding
partial class ExampleBase : Java.Lang.Object {
[Register ("foo-WZ4Q5Ns", …)]
public void Foo_WZ4Q5Ns (int value) {…}
}
...but that would be *really* ugly.
We could instead just take everything before the `-` as the method
name for the C# method name -- resulting in `ExampleBase.Foo()` --
which results in a friendlier name from C#, but opens us up to
"collisions" with method overloads:
// Kotlin
public open class ExampleBase2 {
public fun foo(value : Int) {}
public fun foo(value : UInt) {}
}
The C# `ExampleBase` type can't bind *both* `ExampleBase2.foo()`
methods as `Foo(int)`.
The chosen solution is to "more natively" support Kotlin unsigned
types, which allows reasonable disambiguation:
// C# binding
partial class ExampleBase2 : Java.Lang.Object {
[Register ("foo", …)]
public void Foo (int value) {…}
[Register ("foo-WZ4Q5Ns", …)]
public void Foo (uint value) {…}
}
Java.Interop added support for emitting C# binding code which supports
Kotlin unsigned types.
For `kotlin.UByte`, `kotlin.UInt`, `kotlin.ULong`, and `kotlin.UShort`,
no additional runtime changes are needed to support the binding.
The `*Array` counterparts *do* require additional runtime changes.
Consider this Kotlin type:
// Kotlin
class UnsignedInstanceMethods {
public open fun uintArrayInstanceMethod (value: UIntArray) : UIntArray {
return value;
}
}
The binding for this method would be:
// C# binding
partial class UnsignedInstanceMethods {
[Register ("uintArrayInstanceMethod--ajY-9A", "([I)[I", "")]
public unsafe uint[] UintArrayInstanceMethod (uint[] value)
{
const string __id = "uintArrayInstanceMethod--ajY-9A.([I)[I";
IntPtr native_value = JNIEnv.NewArray ((int[])(object)value);
try {
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
__args [0] = new JniArgumentValue (native_value);
var __rm = _members.InstanceMethods.InvokeNonvirtualObjectMethod (__id, this, __args);
return (uint[]) JNIEnv.GetArray (__rm.Handle, JniHandleOwnership.TransferLocalRef, typeof (uint));
} finally {
if (value != null) {
JNIEnv.DeleteLocalRef (native_value);
}
}
}
}
The problem is the `JNIEnv.GetArray(…, typeof(uint))` invocation.
This eventually hits various dictionaries within `JNIEnv`, which
requires additional support so that `uint` can be marshaled properly.
Previous versions of Xamarin.Android do not contain support for
marshaling arrays of unsigned types.
As such, support for using Kotlin bindings which contain unsigned
types will require Xamarin.Android 10.2.0 and later.
~~ Installer ~~
Add `protobuf-net.dll` to our installers, as it is required to read
the metadata that Kotlin puts within `.class` files.
~~ Unit Tests ~~
On-device tests for calling Kotlin code that uses unsigned types have
been added.
As we don't currently have a way of acquiring the Kotlin compiler for
builds, the test `.jar` and Kotlin's
`org.jetbrains.kotlin.kotlin-stdlib.jar` are copied into the repo.
They are *not* redistributed.
The source of the test `.jar` is provided for future use.
~~ Warnings ~~
Kotlin unsigned types are still experimental. If Kotlin changes how
they are represented in Java bytecode, bindings which use them will
break.
Methods which use unsigned types *cannot* be virtual, nor can they
be used in bound interfaces. This is because Java source code is
currently used as an "intermediary" for Java <-> Managed transitions,
and Java cannot override Kotlin "mangled" methods. If the virtual
method is declared on a class, it will be bound as a non-`virtual`
method. If the method is on an interface, it will be bound, but the
interface will not actually be implementable; a
[XA4213 build-time error][0] will be generated.
[0]: dotnet/java-interop@3bf53331 parent 46eeb3a commit aa5e597
File tree
9 files changed
+213
-1
lines changed- build-tools/installers
- external
- src/Mono.Android/Android.Runtime
- tests/CodeGen-Binding
- Xamarin.Android.JcwGen-Tests
- Xamarin.Android.McwGen-Tests
- Jars
9 files changed
+213
-1
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
188 | 188 | | |
189 | 189 | | |
190 | 190 | | |
| 191 | + | |
191 | 192 | | |
192 | 193 | | |
193 | 194 | | |
| |||
Submodule Java.Interop updated from 476597b to 4565369
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1123 | 1123 | | |
1124 | 1124 | | |
1125 | 1125 | | |
| 1126 | + | |
| 1127 | + | |
| 1128 | + | |
| 1129 | + | |
| 1130 | + | |
1126 | 1131 | | |
1127 | 1132 | | |
1128 | 1133 | | |
1129 | 1134 | | |
1130 | 1135 | | |
| 1136 | + | |
| 1137 | + | |
| 1138 | + | |
| 1139 | + | |
| 1140 | + | |
1131 | 1141 | | |
1132 | 1142 | | |
1133 | 1143 | | |
1134 | 1144 | | |
1135 | 1145 | | |
| 1146 | + | |
| 1147 | + | |
| 1148 | + | |
| 1149 | + | |
| 1150 | + | |
1136 | 1151 | | |
1137 | 1152 | | |
1138 | 1153 | | |
| |||
1592 | 1607 | | |
1593 | 1608 | | |
1594 | 1609 | | |
| 1610 | + | |
| 1611 | + | |
| 1612 | + | |
| 1613 | + | |
| 1614 | + | |
| 1615 | + | |
| 1616 | + | |
| 1617 | + | |
| 1618 | + | |
| 1619 | + | |
| 1620 | + | |
| 1621 | + | |
| 1622 | + | |
| 1623 | + | |
| 1624 | + | |
| 1625 | + | |
| 1626 | + | |
| 1627 | + | |
| 1628 | + | |
| 1629 | + | |
| 1630 | + | |
| 1631 | + | |
| 1632 | + | |
1595 | 1633 | | |
1596 | 1634 | | |
1597 | 1635 | | |
| |||
Lines changed: 92 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
| 55 | + | |
55 | 56 | | |
56 | 57 | | |
57 | 58 | | |
| |||
Binary file not shown.
Lines changed: 74 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
Binary file not shown.
Lines changed: 6 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
134 | 134 | | |
135 | 135 | | |
136 | 136 | | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
137 | 143 | | |
138 | 144 | | |
139 | 145 | | |
| |||
0 commit comments