runtime: AOT+LLVM throws for `Enum.GetUnderlyingType()` on Android in .NET 7

Description

Our Mono.Android-Tests project crashes on startup with:

05-05 15:19:34.429  4929  4929 F mono-rt : [ERROR] FATAL UNHANDLED EXCEPTION: System.ArgumentException: Type provided must be an Enum. Arg_ParamName_Name, enumType
05-05 15:19:34.429  4929  4929 F mono-rt :    at System.Enum.InternalGetUnderlyingType(RuntimeType enumType)
05-05 15:19:34.429  4929  4929 F mono-rt :    at System.RuntimeType.GetEnumUnderlyingType()
05-05 15:19:34.429  4929  4929 F mono-rt :    at System.Enum.GetUnderlyingType(Type enumType)
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniRuntime.JniTypeManager.GetUnderlyingType(Type , Int32& )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniRuntime.JniTypeManager.GetTypeSignature(Type )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniPeerMembers.JniInstanceMethods..ctor(Type )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String , Type , JniArgumentValue* )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Lang.Error..ctor(String )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Android.Runtime.JavaProxyThrowable..ctor(Exception )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Lang.Throwable.FromException(Exception )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Android.Runtime.AndroidEnvironment.UnhandledException(Exception e)
05-05 15:19:34.429  4929  4929 F mono-rt :    at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PPL_V(_JniMarshal_PPL_V , IntPtr , IntPtr , IntPtr )

This looks oddly familiar to a related issue in mono/mono: https://github.com/xamarin/xamarin-android/issues/1842

Looking at the underlying code, I think it throws here:

https://github.com/xamarin/java.interop/blob/61cdb40dcc8b33d400aef3fd12e0aeee72f71e35/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs#L151-L152

Reproduction Steps

Build a .NET 7 Release app with -p:EnableLLVM=true.

Expected behavior

Use of -p:EnableLLVM=true on Android doesn’t crash on startup.

Actual behavior

Use of -p:EnableLLVM=true crashes on startup on Android.

Regression?

This project appears to be working in .NET 6.

Known Workarounds

Disable LLVM.

Configuration

.NET 7.0.100-preview.4.22174.1 c7afae6936bf80239aa93bfd3b6f68513d9876d4 .NET runtime 7.0.0-preview.4.22172.7 c5d40c9e703fd257db1b26ef4fd1399bbae73ab0

Other information

No response

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 18 (17 by maintainers)

Commits related to this issue

Most upvoted comments

What’s “odd” about this stack trace is that it makes no sense.

It starts with the JavaProxyThrowable constructor, which chains to the Java.Lang.Error constructor, which does:

var __r = _members.InstanceMethods.StartCreateInstance (__id, ((object) this).GetType (), __args);

i.e. the System.Type instance will be typeof(JavaProxyThrowable).

We then hit JniPeerMembers.JniInstanceMethods.StartCreateInstance(): https://github.com/xamarin/java.interop/blob/3fcce7466324c6564874fe275eac222a8970078c/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs#L97-L112

which calls GetConstructorsForType(): https://github.com/xamarin/java.interop/blob/3fcce7466324c6564874fe275eac222a8970078c/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs#L70-L82

  • declaringType is typeof(JavaProxyThrowable).
  • DeclaringType is typeof(Error)
  • We thus hit new JniInstanceMethods(typeof(JavaProxyThrowable))

This brings us to the JniInstaceMethods(Type) constructor: https://github.com/xamarin/java.interop/blob/3fcce7466324c6564874fe275eac222a8970078c/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs#L17-L29

Which calls GetTypeSignature(typeof(JavaProxyThrowable)): https://github.com/xamarin/java.interop/blob/3fcce7466324c6564874fe275eac222a8970078c/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs#L51

Which calls GetUnderlyingType(typeof(JavaProxyThrowable), out int _): https://github.com/xamarin/java.interop/blob/3fcce7466324c6564874fe275eac222a8970078c/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs#L140-L155

Which is where things make no sense: statically, we know type is typeof(JavaProxyThrowable), yet here:

https://github.com/xamarin/java.interop/blob/3fcce7466324c6564874fe275eac222a8970078c/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs#L151-L152

typeof(JavaProxyThrowable).IsEnum must be true in order for us to hit Enum.GetUnderlyingType(typeof(JavaProxyThrowable)), but it shouldn’t be true!

Thus, throwing the ArgumentException is correct behavior.

The bug is that typeof(JavaProxyThrowable).IsEnum is true when it should be false!

(Or AOT+LLVM inlining mumble something?)

Let’s just close it, and I can reopen if we hit this again.

@lambdageek this particular issue, I haven’t seen last time I tested LLVM. Is it possible some change since May solved this one?

I’ve more recently hit these two:

@SamMonoRT this issue is only happening in .NET 7.

It looks like the same test is passing in .NET 6.