Skip to content

Support Kotlin's unsigned types #539

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 7, 2020
Merged

Support Kotlin's unsigned types #539

merged 5 commits into from
Jan 7, 2020

Conversation

jpobst
Copy link
Contributor

@jpobst jpobst commented Dec 12, 2019

XA companion PR: dotnet/android#4054

This adds support for Kotlin's unsigned types (uint, ushort, ulong, ubyte). In order to do this, we pretend that they are native Java types and just translate a few places where we need to tell Java the real type.

Class Parse

When we read the Kotlin metadata in the Java bytecode, if we come across one of these types we store it as an additional KotlinType that we can use later. When we are generating the xml we check this additional flag and if it's one of our types we emit it instead of the native Java type. For example:

<method
  abstract="false"
  deprecated="not deprecated"
  final="false"
  name="unsignedAbstractMethod-WZ4Q5Ns"
  native="false"
  return="uint"
  jni-return="I"
  static="false"
  synchronized="false"
  visibility="public"
  bridge="false"
  synthetic="false"
  jni-signature="(I)I">
  <parameter
    name="value"
    type="uint"
    jni-type="I" />
</method>

ApiXmlAdjuster

Update JavaTypeReference to contain unsigned types:

UInt = new JavaTypeReference ("uint");
UShort = new JavaTypeReference ("ushort");
ULong = new JavaTypeReference ("ulong");
UByte = new JavaTypeReference ("ubyte");

Generator

generator has the 4 new types added to the SymbolTable as SimpleSymbols:

AddType (new SimpleSymbol ("0", "uint", "uint", "I", returnCast: "(uint)"));
AddType (new SimpleSymbol ("0", "ushort", "ushort", "S", returnCast: "(ushort)"));
AddType (new SimpleSymbol ("0", "ulong", "ulong", "J", returnCast: "(ulong)"));
AddType (new SimpleSymbol ("0", "ubyte", "byte", "B", returnCast: "(byte)"));

There are 2 fixups we have to make because we use GetIntValue (...), etc. instead of having unsigned versions:

  • Override name of which method to call, ie: GetIntValue instead of GetUintValue. (link)
  • Cast the int value returned to uint. This is accomplished via the new ReturnCast property on ISymbol. (link)

@jpobst jpobst force-pushed the unsigned-types branch 4 times, most recently from a1528b4 to 1e178fd Compare December 17, 2019 15:11
@jonpryor
Copy link
Contributor

Incredibly important meta-question: given that Kotlin unsigned types are EXPERIMENTAL, should we special-case things at all at this time? Or should we wait for Kotlin to stabilize things?

// Handle erasure of Kotlin unsigned types
if (jvmType == "I" && kotlinClass == "kotlin/UInt;")
return "uint";
if (jvmType == "[I" && kotlinClass == "kotlin/UIntArray;")
Copy link
Contributor

@jonpryor jonpryor Dec 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the "obvious question is obvious" department -- transcending into the "oh god what horrors does this provoke?!" department -- is it possible to be given a [[I or [[[I or...?

There is no kotlin.UIntArrayArray type. However, we "could" do:

	public open fun uintArray(value: Array<UIntArray>) {
	}

...which isn't actually special-cased by the compiler. Meaning it "leaks", with javap showing!

  public void uintArray(kotlin.UIntArray[]);

WTF is a kotlin.UIntArray type? (It's an actual type!)

So in some contexts, UIntArray becomes a JVM int[], and in other contexts it remains a UIntArray.

I'm still not sure how to mentally process this. I'm not sure what this should mean wrt binding at all.

@@ -0,0 +1,90 @@
// Metadata.xml XPath class reference: path="/api/package[@name='java.code']/class[@name='MyClass']"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a .txt file? It means we don't get C# highlighting on GitHub. :-(

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...because of SDK-style (short-form) .csproj project usage. If these were .cs files, they'd be auto-included into the .csproj build, which is not what we want.

@jonpryor
Copy link
Contributor

In part because of the "can we have a [[I pondering above, I'm less certain of the wisdom of tacking "unsigned types as unsigned types" at all.

Which puts things in a quandary.

The problem is this: suppose someone provides this Kotlin type:

package example;

public interface IAbstractUnsigned {
	public abstract fun m(value : UInt)
}

Corresponding Java declaration:

$ javap -cp . example/IAbstractUnsigned
public interface example.IAbstractUnsigned {
  public abstract void m-WZ4Q5Ns(int);
}

What do we do? What can we do?

See also: #525

We can bind this type:

namespace Example {
    public partial interface IAbstractUnsigned {
        [Register ("m-WZ4Q5Ns", "(I)V", "...")]
        void M(int value);  // ignoring int vs. uint for now..
    }
    internal partial class IAbstractUnsigned : Java.Lang.Object, IAbstractUnsigned {
        public void M(int value) {
            _members.InstanceMethods.InvokeAbstractObjectMethod ("(I)V", this, ...)
        }
    }
}

However, any binding cannot be implemented, so long as we use Java source code as an intermediary:

public class MyExample : Java.Lang.Object, Example.IAbstractUnsigned {
    public void M(int value) {}
}

This C# source will compile, but we cannot emit a Java Callable Wrapper for it.

We considered this to be acceptable, as the alternative was to invalidate the entire interface. At least with the above, if the Java library provides types which implement the interface or has methods which return the interface, those types & methods are usable.

Abstract types are a similar scenario: if the type contains only the abstract methods, if we removed the "invalid" abstract methods from the binding, subclasses would fail to generate JCWs in non-obvious ways.

Related: f3553f4

The problem is that use of unsigned types always introduces name mangling, leaving the question: how do we deal with unsigned types?

I'm less certain of the answer there. :-(


protected MyClass (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer) {}

public unsafe uint[] UIntProp {
Copy link
Contributor

@jonpryor jonpryor Dec 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong: we should never map "property-looking" methods to actual properties when the return type is an array. See e.g.

Looks like unsigned types are being treated differently from signed types, such that RetVal.IsArray is returning false for this array type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this test is actually irrelevant because I found out you cannot create a Kotlin property with an array type, but I definitely need to dig in and figure out what's going on with IsArray returning false. 👍

Copy link
Contributor Author

@jpobst jpobst Dec 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, IsArray is returning true like it should, this unit test just doesn't go through that logic. It is explicitly creating properties with array types rather than combining methods into a property.

https://github.com/xamarin/java.interop/blob/dd8a6a09a480700583b70976a71f0e6dd43ae892/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs#L85

@jpobst
Copy link
Contributor Author

jpobst commented Dec 20, 2019

Incredibly important meta-question: given that Kotlin unsigned types are EXPERIMENTAL, should we special-case things at all at this time? Or should we wait for Kotlin to stabilize things?

I think the issue we face is that we need to bind kotlin-stdlib.jar so that people can bind their libraries, and the stdlib already contains unsigned types.

  • If we leave them as-is and then attempt to fix it later once Kotlin considers them "stable" that's a break.
  • If we translate now and Kotlin changes them it's a break, but it's Kotlin's fault.
  • We could use metadata to remove them from the stdlib binding, preventing users from using them for now.

It looks like they were introduced as experimental in Kotlin 1.3 in Oct 2018 and they still exist, so my gut says they're probably not going to change at this point, but it is definitely a risk.

Copy link
Contributor Author

@jpobst jpobst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that use of unsigned types always introduces name mangling, leaving the question: how do we deal with unsigned types?

I feel like calling methods is probably ~95% of usage compared to overriding/implementing them but that's just a wild guess. We do technically have a (future) solution to this in the form of #535. I think the best we can do is use the warning in f3553f4 and public feedback to prioritize this future enhancement as needed.

…[]" so we do not do any Kotlin fixups on it.
@jpobst
Copy link
Contributor Author

jpobst commented Jan 6, 2020

We decided offline that Kotlin's unsigned types are probably pretty stable by now and are used by the Kotlin stdlib, so we will go ahead and commit this work. If Kotlin does change this feature in the future we will update and blame any breakage on them. 😁

@jonpryor jonpryor merged commit 71afce5 into master Jan 7, 2020
@jonpryor jonpryor deleted the unsigned-types branch January 7, 2020 03:28
jpobst added a commit that referenced this pull request Jan 7, 2020
Fixes: #525

Context: dotnet/android#4054
Context: https://github.com/Kotlin/KEEP/blob/13b67668ccc5b4741ecc37d0dd050fd77227c035/proposals/unsigned-types.md
Context: https://kotlinlang.org/docs/reference/basic-types.html#unsigned-integers

Another place where Kotlin makes use of "name mangling" -- see also
commit f3553f4 -- is in the use of unsigned types such as `UInt`.
At the JVM ABI level, Kotlin treats unsigned types as their signed
counterparts, e.g. `kotlin.UInt` is an `int` and `kotlin.UIntArray`
is an `int[]`:

	// Kotlin
	public class Example {
	  public fun value(value: UInt) : UInt {
	    return value
	  }
	  public fun array(value: UIntArray) : UIntArray {
	    return value
	  }
	}

	// `javap` output:
	public final class Example {
	  public final int value-WZ4Q5Ns(int);
	  public final int[] array--ajY-9A(int[]);
	}

Kotlin uses Java Annotations to determine whether a parameter or
return type is actually an unsigned type instead of a signed type.

Update `Xamarin.Android.Tools.Bytecode` and `generator` to bind e.g.:

  * `kotlin.UInt` as `System.UInt32`
  * `kotlin.UIntArray` as a `System.UInt32[]`

and likewise for the other unsigned types `ushort`, `ulong`, `ubyte`.

In order to do this, we pretend that they are native Java types and
just translate a few places where we need to tell Java the real type.

~~ Xamarin.Android.Tools.Bytecode / class-parse ~~

When we read the Kotlin metadata in the Java bytecode, if we come
across one of these types we store it within an additional
`FieldInfo.KotlinType` property that we can access later.  When we
are generating the XML we check this additional flag and if it's one
of our types we emit it instead of the native Java type.

For example:

	<method
	  abstract="false"
	  deprecated="not deprecated"
	  final="false"
	  name="unsignedAbstractMethod-WZ4Q5Ns"
	  native="false"
	  return="uint"
	  jni-return="I"
	  static="false"
	  synchronized="false"
	  visibility="public"
	  bridge="false"
	  synthetic="false"
	  jni-signature="(I)I">
	  <parameter
	    name="value"
	    type="uint"
	    jni-type="I" />
	</method>

Here we see that even though `@jni-return` is `I` -- meaning `int` --
the `@return` property is `uint`.  Likewise `parameter/@jni-type` and
`parameter/@type`.  The JNI ABI is `int`, but we bind in C# as `uint`.

~~ ApiXmlAdjuster ~~

Update `JavaTypeReference` to contain unsigned types:

	UInt   = new JavaTypeReference ("uint");
	UShort = new JavaTypeReference ("ushort");
	ULong  = new JavaTypeReference ("ulong");
	UByte  = new JavaTypeReference ("ubyte");

~~ generator ~~

`generator` has the 4 new types added to the `SymbolTable` as
`SimpleSymbols`:

	AddType (new SimpleSymbol ("0", "uint",    "uint",   "I", returnCast: "(uint)"));
	AddType (new SimpleSymbol ("0", "ushort",  "ushort", "S", returnCast: "(ushort)"));
	AddType (new SimpleSymbol ("0", "ulong",   "ulong",  "J", returnCast: "(ulong)"));
	AddType (new SimpleSymbol ("0", "ubyte",   "byte",   "B", returnCast: "(byte)"));

There are 2 fixups we have to make because we use `GetIntValue(...)`,
etc. instead of having unsigned versions:

  * Override name of which method to call, e.g.: `GetIntValue()`
    instead of `GetUintValue()`.
  * Cast the `int` value returned to `uint`.  This is accomplished
    via the new `ISymbol.ReturnCast` property.

~~ A Note On API Compatibility ~~

Bindings which use Kotlin Unsigned Types will *only* work on
Xamarin.Android 10.2.0 or later ("Visual Studio 16.5").

The problem is that while we *can* emit C# source code which will
*compile* against older versions of Xamarin.Android, if they use
arrays they will not *run* under older versions of Xamarin.Android.

For example, imagine this binding code for the above Kotlin
`Example.array()` method:

	// C# Binding of Example.array()
	partial class Example {
	  public unsafe uint[] Array(uint[] value)
	  {
	    const string      __id          = "array--ajY-9A.([I)[I";
	    IntPtr            native_value  = JNIEnv.NewArray ((int[]) (object) value); // Works!...ish?
	    JniArgumentValue* __args        = stackalloc JniArgumentValue [1];
	    __args [0]                      = new JniArgumentValue (native_value);

	    JniObjectReference r            = _members.InstanceMethods.InvokeVirtualIntMethod (__id, this, __args);
	    return (uint[]) JNIEnv.GetArray (r.Handle, JniHandleOwnership.DoNotTransfer, typeof (uint));
	  }
	}

That could conceivably *compile* against older Xamarin.Android
versions.  However, that cannot *run* against older Xamarin.Android
versions, as *eventually* `JNIEnv.GetArray()` will hit some
dictionaries to determine how to marshal `IntPtr` to a `uint[]`, at
which point things will fail because there is no such mapping *until*
Xamarin.Android 10.2.0.

We feel that a "hard" ABI requirement will have more "graceful"
failure conditions than a solution which doesn't add ABI requirements.
In this case, if you create a Kotlin library binding which exposes
unsigned types, attempting to build an app in Release configuration
against older Xamarin.Android versions will result in a linker error,
as the required `JNIEnv` methods will not be resolvable.

(cherry picked from commit 71afce5)
jpobst added a commit that referenced this pull request Jan 7, 2020
Fixes: #525

Context: dotnet/android#4054
Context: https://github.com/Kotlin/KEEP/blob/13b67668ccc5b4741ecc37d0dd050fd77227c035/proposals/unsigned-types.md
Context: https://kotlinlang.org/docs/reference/basic-types.html#unsigned-integers

Another place where Kotlin makes use of "name mangling" -- see also
commit f3553f4 -- is in the use of unsigned types such as `UInt`.
At the JVM ABI level, Kotlin treats unsigned types as their signed
counterparts, e.g. `kotlin.UInt` is an `int` and `kotlin.UIntArray`
is an `int[]`:

	// Kotlin
	public class Example {
	  public fun value(value: UInt) : UInt {
	    return value
	  }
	  public fun array(value: UIntArray) : UIntArray {
	    return value
	  }
	}

	// `javap` output:
	public final class Example {
	  public final int value-WZ4Q5Ns(int);
	  public final int[] array--ajY-9A(int[]);
	}

Kotlin uses Java Annotations to determine whether a parameter or
return type is actually an unsigned type instead of a signed type.

Update `Xamarin.Android.Tools.Bytecode` and `generator` to bind e.g.:

  * `kotlin.UInt` as `System.UInt32`
  * `kotlin.UIntArray` as a `System.UInt32[]`

and likewise for the other unsigned types `ushort`, `ulong`, `ubyte`.

In order to do this, we pretend that they are native Java types and
just translate a few places where we need to tell Java the real type.

~~ Xamarin.Android.Tools.Bytecode / class-parse ~~

When we read the Kotlin metadata in the Java bytecode, if we come
across one of these types we store it within an additional
`FieldInfo.KotlinType` property that we can access later.  When we
are generating the XML we check this additional flag and if it's one
of our types we emit it instead of the native Java type.

For example:

	<method
	  abstract="false"
	  deprecated="not deprecated"
	  final="false"
	  name="unsignedAbstractMethod-WZ4Q5Ns"
	  native="false"
	  return="uint"
	  jni-return="I"
	  static="false"
	  synchronized="false"
	  visibility="public"
	  bridge="false"
	  synthetic="false"
	  jni-signature="(I)I">
	  <parameter
	    name="value"
	    type="uint"
	    jni-type="I" />
	</method>

Here we see that even though `@jni-return` is `I` -- meaning `int` --
the `@return` property is `uint`.  Likewise `parameter/@jni-type` and
`parameter/@type`.  The JNI ABI is `int`, but we bind in C# as `uint`.

~~ ApiXmlAdjuster ~~

Update `JavaTypeReference` to contain unsigned types:

	UInt   = new JavaTypeReference ("uint");
	UShort = new JavaTypeReference ("ushort");
	ULong  = new JavaTypeReference ("ulong");
	UByte  = new JavaTypeReference ("ubyte");

~~ generator ~~

`generator` has the 4 new types added to the `SymbolTable` as
`SimpleSymbols`:

	AddType (new SimpleSymbol ("0", "uint",    "uint",   "I", returnCast: "(uint)"));
	AddType (new SimpleSymbol ("0", "ushort",  "ushort", "S", returnCast: "(ushort)"));
	AddType (new SimpleSymbol ("0", "ulong",   "ulong",  "J", returnCast: "(ulong)"));
	AddType (new SimpleSymbol ("0", "ubyte",   "byte",   "B", returnCast: "(byte)"));

There are 2 fixups we have to make because we use `GetIntValue(...)`,
etc. instead of having unsigned versions:

  * Override name of which method to call, e.g.: `GetIntValue()`
    instead of `GetUintValue()`.
  * Cast the `int` value returned to `uint`.  This is accomplished
    via the new `ISymbol.ReturnCast` property.

~~ A Note On API Compatibility ~~

Bindings which use Kotlin Unsigned Types will *only* work on
Xamarin.Android 10.2.0 or later ("Visual Studio 16.5").

The problem is that while we *can* emit C# source code which will
*compile* against older versions of Xamarin.Android, if they use
arrays they will not *run* under older versions of Xamarin.Android.

For example, imagine this binding code for the above Kotlin
`Example.array()` method:

	// C# Binding of Example.array()
	partial class Example {
	  public unsafe uint[] Array(uint[] value)
	  {
	    const string      __id          = "array--ajY-9A.([I)[I";
	    IntPtr            native_value  = JNIEnv.NewArray ((int[]) (object) value); // Works!...ish?
	    JniArgumentValue* __args        = stackalloc JniArgumentValue [1];
	    __args [0]                      = new JniArgumentValue (native_value);

	    JniObjectReference r            = _members.InstanceMethods.InvokeVirtualIntMethod (__id, this, __args);
	    return (uint[]) JNIEnv.GetArray (r.Handle, JniHandleOwnership.DoNotTransfer, typeof (uint));
	  }
	}

That could conceivably *compile* against older Xamarin.Android
versions.  However, that cannot *run* against older Xamarin.Android
versions, as *eventually* `JNIEnv.GetArray()` will hit some
dictionaries to determine how to marshal `IntPtr` to a `uint[]`, at
which point things will fail because there is no such mapping *until*
Xamarin.Android 10.2.0.

We feel that a "hard" ABI requirement will have more "graceful"
failure conditions than a solution which doesn't add ABI requirements.
In this case, if you create a Kotlin library binding which exposes
unsigned types, attempting to build an app in Release configuration
against older Xamarin.Android versions will result in a linker error,
as the required `JNIEnv` methods will not be resolvable.

(cherry picked from commit 71afce5)
jonpryor pushed a commit that referenced this pull request Jan 7, 2020
Fixes: #525

Context: dotnet/android#4054
Context: https://github.com/Kotlin/KEEP/blob/13b67668ccc5b4741ecc37d0dd050fd77227c035/proposals/unsigned-types.md
Context: https://kotlinlang.org/docs/reference/basic-types.html#unsigned-integers

Another place where Kotlin makes use of "name mangling" -- see also
commit f3553f4 -- is in the use of unsigned types such as `UInt`.
At the JVM ABI level, Kotlin treats unsigned types as their signed
counterparts, e.g. `kotlin.UInt` is an `int` and `kotlin.UIntArray`
is an `int[]`:

	// Kotlin
	public class Example {
	  public fun value(value: UInt) : UInt {
	    return value
	  }
	  public fun array(value: UIntArray) : UIntArray {
	    return value
	  }
	}

	// `javap` output:
	public final class Example {
	  public final int value-WZ4Q5Ns(int);
	  public final int[] array--ajY-9A(int[]);
	}

Kotlin uses Java Annotations to determine whether a parameter or
return type is actually an unsigned type instead of a signed type.

Update `Xamarin.Android.Tools.Bytecode` and `generator` to bind e.g.:

  * `kotlin.UInt` as `System.UInt32`
  * `kotlin.UIntArray` as a `System.UInt32[]`

and likewise for the other unsigned types `ushort`, `ulong`, `ubyte`.

In order to do this, we pretend that they are native Java types and
just translate a few places where we need to tell Java the real type.

~~ Xamarin.Android.Tools.Bytecode / class-parse ~~

When we read the Kotlin metadata in the Java bytecode, if we come
across one of these types we store it within an additional
`FieldInfo.KotlinType` property that we can access later.  When we
are generating the XML we check this additional flag and if it's one
of our types we emit it instead of the native Java type.

For example:

	<method
	  abstract="false"
	  deprecated="not deprecated"
	  final="false"
	  name="unsignedAbstractMethod-WZ4Q5Ns"
	  native="false"
	  return="uint"
	  jni-return="I"
	  static="false"
	  synchronized="false"
	  visibility="public"
	  bridge="false"
	  synthetic="false"
	  jni-signature="(I)I">
	  <parameter
	    name="value"
	    type="uint"
	    jni-type="I" />
	</method>

Here we see that even though `@jni-return` is `I` -- meaning `int` --
the `@return` property is `uint`.  Likewise `parameter/@jni-type` and
`parameter/@type`.  The JNI ABI is `int`, but we bind in C# as `uint`.

~~ ApiXmlAdjuster ~~

Update `JavaTypeReference` to contain unsigned types:

	UInt   = new JavaTypeReference ("uint");
	UShort = new JavaTypeReference ("ushort");
	ULong  = new JavaTypeReference ("ulong");
	UByte  = new JavaTypeReference ("ubyte");

~~ generator ~~

`generator` has the 4 new types added to the `SymbolTable` as
`SimpleSymbols`:

	AddType (new SimpleSymbol ("0", "uint",    "uint",   "I", returnCast: "(uint)"));
	AddType (new SimpleSymbol ("0", "ushort",  "ushort", "S", returnCast: "(ushort)"));
	AddType (new SimpleSymbol ("0", "ulong",   "ulong",  "J", returnCast: "(ulong)"));
	AddType (new SimpleSymbol ("0", "ubyte",   "byte",   "B", returnCast: "(byte)"));

There are 2 fixups we have to make because we use `GetIntValue(...)`,
etc. instead of having unsigned versions:

  * Override name of which method to call, e.g.: `GetIntValue()`
    instead of `GetUintValue()`.
  * Cast the `int` value returned to `uint`.  This is accomplished
    via the new `ISymbol.ReturnCast` property.

~~ A Note On API Compatibility ~~

Bindings which use Kotlin Unsigned Types will *only* work on
Xamarin.Android 10.2.0 or later ("Visual Studio 16.5").

The problem is that while we *can* emit C# source code which will
*compile* against older versions of Xamarin.Android, if they use
arrays they will not *run* under older versions of Xamarin.Android.

For example, imagine this binding code for the above Kotlin
`Example.array()` method:

	// C# Binding of Example.array()
	partial class Example {
	  public unsafe uint[] Array(uint[] value)
	  {
	    const string      __id          = "array--ajY-9A.([I)[I";
	    IntPtr            native_value  = JNIEnv.NewArray ((int[]) (object) value); // Works!...ish?
	    JniArgumentValue* __args        = stackalloc JniArgumentValue [1];
	    __args [0]                      = new JniArgumentValue (native_value);

	    JniObjectReference r            = _members.InstanceMethods.InvokeVirtualIntMethod (__id, this, __args);
	    return (uint[]) JNIEnv.GetArray (r.Handle, JniHandleOwnership.DoNotTransfer, typeof (uint));
	  }
	}

That could conceivably *compile* against older Xamarin.Android
versions.  However, that cannot *run* against older Xamarin.Android
versions, as *eventually* `JNIEnv.GetArray()` will hit some
dictionaries to determine how to marshal `IntPtr` to a `uint[]`, at
which point things will fail because there is no such mapping *until*
Xamarin.Android 10.2.0.

We feel that a "hard" ABI requirement will have more "graceful"
failure conditions than a solution which doesn't add ABI requirements.
In this case, if you create a Kotlin library binding which exposes
unsigned types, attempting to build an app in Release configuration
against older Xamarin.Android versions will result in a linker error,
as the required `JNIEnv` methods will not be resolvable.

(cherry picked from commit 71afce5)
@github-actions github-actions bot locked and limited conversation to collaborators Apr 13, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants