You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Context: dotnet/android#9747
Context: dotnet/android#9812
Context: 71afce5
Context: dotnet/android@aa5e597
Context: dotnet/android@f800c1a
In the ongoing epic to get MAUI running atop NativeAOT, we hit our
longstanding NativeAOT nemesis: a P/Invoke:
E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.InvalidProgramException: InvalidProgram_Specific, IntPtr Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(System.Type, Byte*)
E AndroidRuntime: at Internal.Runtime.TypeLoaderExceptionHelper.CreateInvalidProgramException(ExceptionStringID, String) + 0x4c
E AndroidRuntime: at Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(Type, Byte*) + 0x18
E AndroidRuntime: at Android.Runtime.JNIEnv.TypemapManagedToJava(Type) + 0x104
E AndroidRuntime: at Android.Runtime.JNIEnv.GetJniName(Type) + 0x1c
E AndroidRuntime: at Android.Runtime.JNIEnv.FindClass(Type) + 0x38
E AndroidRuntime: at Android.Runtime.JNIEnv.NewArray(IJavaObject[]) + 0x28
E AndroidRuntime: at Android.Runtime.JNIEnv.NewArray[T](T[]) + 0x94
E AndroidRuntime: at Android.Graphics.Drawables.LayerDrawable..ctor(Drawable[] layers) + 0xd4
E AndroidRuntime: at Microsoft.Maui.Platform.MauiRippleDrawableExtensions.UpdateMauiRippleDrawableBackground(View, Paint, IButtonStroke, Func`1, Func`1, Action) + 0x2ac
(`JNIEnv.monodroid_typemap_managed_to_java()` is P/Invoke. Why are
P/Invokes bad? See dotnet/android@f800c1a6.)
The reasonable fix/workaround: update `JNIEnv.FindClass(Type)` to
instead use `JniRuntime.JniTypeManager.GetTypeSignature(Type)`.
(Also known as "embrace more JniRuntime abstractions!".)
Unfortunately, this straightforward idea hits a minor schism between
the .NET for Android and builtin java-interop world orders:
How should Java `byte` be bound?
For starters, what *is* a [Java `byte`][0]?
> The values of the integral types are integers in the following ranges:
>
> * For `byte`, from -128 to 127, inclusive
The Java `byte` is *signed*! Because of that, and because
java-interop originated as a Second System Syndrome rebuild of
Xamarin.Android, *of course* java-interop bound Java `byte` as
`System.SByte`.
.NET for Android, though, bound Java `byte` as `System.Byte`.
This "minor" change meant that lots of unit tests started failing, e.g.
[`NetworkInterfacesTest.DotNetInterfacesShouldEqualJavaInterfaces()`][2]:
System.ArgumentException : Could not determine Java type corresponding to System.Byte[], System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e. Arg_ParamName_Name, type
at Android.Runtime.JNIEnv.FindClass(Type )
at Android.Runtime.JNIEnv.AssertCompatibleArrayTypes(IntPtr , Type )
at Android.Runtime.JNIEnv._GetArray(IntPtr , Type )
at Android.Runtime.JNIEnv.GetArray(IntPtr , JniHandleOwnership , Type )
at Java.Net.NetworkInterface.GetHardwareAddress()
at System.NetTests.NetworkInterfacesTest.GetInfos(IEnumeration interfaces)
at System.NetTests.NetworkInterfacesTest.DotNetInterfacesShouldEqualJavaInterfaces()
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object , BindingFlags )
Rephrased, `runtime.TypeManager.GetTypeSignature(typeof(byte[]))`
returned a "default" `JniTypeSignature` instance.
It's time to reduce the size of this schism.
Update `JniBuiltinMarshalers.GetBuiltInTypeSignature()` so that
`TypeCode.Byte` is treated as a synonym for `TypeCode.SByte`.
This is in fact all that's needed in order to add support for `byte[]`!
Repeat this exercise for all other unsigned types: `ushort`, `uint`,
and `ulong`, as Kotlin unsigned types require it; see also 71afce5
and dotnet/android@aa5e597eba. This fixes the exception:
System.InvalidCastException : Unable to cast from '[I' to '[Ljava/lang/Object;'.
at Android.Runtime.JNIEnv.AssertCompatibleArrayTypes(IntPtr , Type )
at Android.Runtime.JNIEnv._GetArray(IntPtr , Type )
at Android.Runtime.JNIEnv.GetArray(IntPtr , JniHandleOwnership , Type )
at Foo.UnsignedInstanceMethods.UintArrayInstanceMethod(UInt32[] value)
at Xamarin.Android.JcwGenTests.KotlinUnsignedTypesTests.TestUnsignedArrayTypeMembers()
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object , BindingFlags )
This is *not* all that's necessary to fix all dotnet/android tests.
Update `JniRuntime.JniTypeManager.GetTypeSignature()` and
`.GetTypeSignatures()` so that if the type is an open generic type
a `System.NotSupportedException` is thrown instead of a
`System.ArgumentException`. This fixes
[`Java.InteropTests.JnienvTest.NewOpenGenericTypeThrows()`][3].
Also, `JniBuiltinMarshalers.cs` has some hand-made changes, rendering
it out of sync with `JniBuiltinMarshalers.tt`. Update
`JniBuiltinMarshalers.tt` appropriately and regenerate it.
[0]: https://docs.oracle.com/javase/specs/jls/se21/html/jls-4.html#jls-4.2.1
[1]: https://github.com/dotnet/java-interop/blob/f30e420a1638dc013302e85dcf76642c10c26832/Documentation/Motivation.md
[2]: https://github.com/dotnet/android/blob/1b1f1452f6b05707418d6605c06e106e6a2a6381/tests/Mono.Android-Tests/Mono.Android-Tests/System.Net/NetworkInterfaces.cs#L107-L137
[3]: https://github.com/dotnet/android/blob/1b1f1452f6b05707418d6605c06e106e6a2a6381/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs#L107-L116
0 commit comments