Skip to content
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

[Mono.Android] update JNIEnv.FindClass() to use JniEnvironment.Types.FindClass() #9769

Merged
merged 1 commit into from
Feb 8, 2025

Conversation

jonathanpeppers
Copy link
Member

This avoids the problem of JNIEnvInit.mid_Class_forName being null in a NativeAOT context:

02-07 14:05:25.954 30744 30744 E AndroidRuntime: Process: net.dot.hellonativeaot, PID: 30744
02-07 14:05:25.954 30744 30744 E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.TypeInitializationException: TypeInitialization_Type_NoTypeAvailable
02-07 14:05:25.954 30744 30744 E AndroidRuntime:  ---> System.ArgumentNullException: ArgumentNull_Generic Arg_ParamName_Name, method
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference, JniMethodInfo, JniArgumentValue*) + 0x1c8
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(String) + 0xb4
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Java.Lang.Class..cctor() + 0x7c
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0xb4
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    Exception_EndOfInnerExceptionStack
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0x160
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at System.Runtime.CompilerServices.ClassConstructorRunner.CheckStaticClassConstructionReturnNonGCStaticBase(StaticClassConstructionContext*, IntPtr) + 0x14
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(String) + 0xc0
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Android.App.Application.get_Context() + 0x50
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.Hosting.EssentialsExtensions.<>c.<UseEssentials>b__0_0(ILifecycleBuilder life) + 0x18
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.LifecycleEvents.LifecycleEventService..ctor(IEnumerable`1) + 0x94
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.LifecycleEvents.MauiAppHostBuilderExtensions.<>c.<ConfigureLifecycleEvents>b__0_0(IServiceProvider sp) + 0x3c
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite, RuntimeResolverContext) + 0x68
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier) + 0x180
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey, Func`2) + 0x11c
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier, ServiceProviderEngineScope) + 0x44
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type) + 0x48
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type) + 0x48
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider) + 0x3c
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.<GetLifecycleEventDelegates>d__3`1.MoveNext() + 0x38
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.InvokeLifecycleEvents[TDelegate](IServiceProvider, Action`1) + 0x68
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.MauiApplication.OnCreate() + 0xb0
02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Android.App.Application.n_OnCreate(IntPtr jnienv, IntPtr native__this) + 0xb0
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at my.MainApplication.n_onCreate(Native Method)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at my.MainApplication.onCreate(MainApplication.java:24)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1386)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7398)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2379)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:107)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:232)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.os.Looper.loop(Looper.java:317)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8592)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
02-07 14:05:25.954 30744 30744 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)

This also allows us to remove these that are now unused:

  • JnienvInitializeArgs.Class_forName
  • JNIEnv.BinaryName()

…es.FindClass()`

This avoids the problem of `JNIEnvInit.mid_Class_forName` being `null`
in a NativeAOT context:

    02-07 14:05:25.954 30744 30744 E AndroidRuntime: Process: net.dot.hellonativeaot, PID: 30744
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.TypeInitializationException: TypeInitialization_Type_NoTypeAvailable
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:  ---> System.ArgumentNullException: ArgumentNull_Generic Arg_ParamName_Name, method
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference, JniMethodInfo, JniArgumentValue*) + 0x1c8
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(String) + 0xb4
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Java.Lang.Class..cctor() + 0x7c
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0xb4
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    Exception_EndOfInnerExceptionStack
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0x160
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at System.Runtime.CompilerServices.ClassConstructorRunner.CheckStaticClassConstructionReturnNonGCStaticBase(StaticClassConstructionContext*, IntPtr) + 0x14
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(String) + 0xc0
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Android.App.Application.get_Context() + 0x50
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.Hosting.EssentialsExtensions.<>c.<UseEssentials>b__0_0(ILifecycleBuilder life) + 0x18
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.LifecycleEvents.LifecycleEventService..ctor(IEnumerable`1) + 0x94
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.LifecycleEvents.MauiAppHostBuilderExtensions.<>c.<ConfigureLifecycleEvents>b__0_0(IServiceProvider sp) + 0x3c
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite, RuntimeResolverContext) + 0x68
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier) + 0x180
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey, Func`2) + 0x11c
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier, ServiceProviderEngineScope) + 0x44
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type) + 0x48
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type) + 0x48
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider) + 0x3c
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.<GetLifecycleEventDelegates>d__3`1.MoveNext() + 0x38
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.InvokeLifecycleEvents[TDelegate](IServiceProvider, Action`1) + 0x68
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Microsoft.Maui.MauiApplication.OnCreate() + 0xb0
    02-07 14:05:25.954 30744 30744 E AndroidRuntime:    at Android.App.Application.n_OnCreate(IntPtr jnienv, IntPtr native__this) + 0xb0
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at my.MainApplication.n_onCreate(Native Method)
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at my.MainApplication.onCreate(MainApplication.java:24)
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1386)
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7398)
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2379)
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:107)
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:232)
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.os.Looper.loop(Looper.java:317)
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8592)
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
    02-07 14:05:25.954 30744 30744 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)

This also allows us to remove these that are now unused:

* `JnienvInitializeArgs.Class_forName`
* `JNIEnv.BinaryName()`
@jonathanpeppers jonathanpeppers marked this pull request as ready for review February 7, 2025 23:06
@jonpryor
Copy link
Member

jonpryor commented Feb 8, 2025

@jonathanpeppers asked:

Which isn't done yet in Java.Interop, is that important?

Good question! I forget.

This is the commit which migrated Mono.Android.dll to using Class.forName():

https://github.com/xamarin/monodroid/commit/ed984a3a0bfbe71f6499a7855c647b5bc2b28466

[Mono.Android] Switch JNIEnv.FindClass to use Class.forName

Previously, JNIEnv.FindClass(string) would defer to ClassLoader.loadClass to load a Java type.

Because loading some classes on Java is a bit of an arcane art, you have to use a special syntax for
primitive types. This syntax seemed to have been supported just fine on (Dalvik|ART) when using the
loadClass method but on a desktop java like the Oracle VM this is not the case anymore.

Instead, passing by the Class.forName static method seems to handle such case that involves primitive
types so this commit supply the necessary handles to JNIEnv from native and switch the FindClass method
to use them.

For instance, a type that used to trip the runtime beforehand with a ClassNotFoundException but doesn't
after this commit is '[I' (an array of primitive Java integer).

Note: this commit only fix the XA part, Java.Interop implements the same sort of type lookup and needs to
be ultimately updated to the same technique at a later stage.

This appears to have been for the Xamarin.Android Designer atop desktop Java, which is something we no longer support.

We may want to migrate to Class.forName() anyway, but this doesn't appear to be important right now.

@jonpryor
Copy link
Member

jonpryor commented Feb 8, 2025

Draft commit message:

Context: https://github.com/xamarin/monodroid/commit/ed984a3a0bfbe71f6499a7855c647b5bc2b28466
Context: https://github.com/dotnet/java-interop/issues/23

Update `JNIEnv.FindClass()` to use `JniEnvironment.Types.FindClass()`.

This avoids the problem of `JNIEnvInit.mid_Class_forName` being
`null` in a NativeAOT context:

	E AndroidRuntime: Process: net.dot.hellonativeaot, PID: 30744
	E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.TypeInitializationException: TypeInitialization_Type_NoTypeAvailable
	E AndroidRuntime:  ---> System.ArgumentNullException: ArgumentNull_Generic Arg_ParamName_Name, method
	E AndroidRuntime:    at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference, JniMethodInfo, JniArgumentValue*) + 0x1c8
	E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(String) + 0xb4
	E AndroidRuntime:    at Java.Lang.Class..cctor() + 0x7c
	E AndroidRuntime:    at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0xb4
	E AndroidRuntime:    Exception_EndOfInnerExceptionStack
	E AndroidRuntime:    at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0x160
	E AndroidRuntime:    at System.Runtime.CompilerServices.ClassConstructorRunner.CheckStaticClassConstructionReturnNonGCStaticBase(StaticClassConstructionContext*, IntPtr) + 0x14
	E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(String) + 0xc0
	E AndroidRuntime:    at Android.App.Application.get_Context() + 0x50
	E AndroidRuntime:    at Microsoft.Maui.Hosting.EssentialsExtensions.<>c.<UseEssentials>b__0_0(ILifecycleBuilder life) + 0x18
	E AndroidRuntime:    at Microsoft.Maui.LifecycleEvents.LifecycleEventService..ctor(IEnumerable`1) + 0x94
	E AndroidRuntime:    at Microsoft.Maui.LifecycleEvents.MauiAppHostBuilderExtensions.<>c.<ConfigureLifecycleEvents>b__0_0(IServiceProvider sp) + 0x3c
	E AndroidRuntime:    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite, RuntimeResolverContext) + 0x68
	E AndroidRuntime:    at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier) + 0x180
	E AndroidRuntime:    at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey, Func`2) + 0x11c
	E AndroidRuntime:    at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier, ServiceProviderEngineScope) + 0x44
	E AndroidRuntime:    at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type) + 0x48
	E AndroidRuntime:    at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type) + 0x48
	E AndroidRuntime:    at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider) + 0x3c
	E AndroidRuntime:    at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.<GetLifecycleEventDelegates>d__3`1.MoveNext() + 0x38
	E AndroidRuntime:    at Microsoft.Maui.LifecycleEvents.LifecycleEventServiceExtensions.InvokeLifecycleEvents[TDelegate](IServiceProvider, Action`1) + 0x68
	E AndroidRuntime:    at Microsoft.Maui.MauiApplication.OnCreate() + 0xb0
	E AndroidRuntime:    at Android.App.Application.n_OnCreate(IntPtr jnienv, IntPtr native__this) + 0xb0
	E AndroidRuntime: at my.MainApplication.n_onCreate(Native Method)
	E AndroidRuntime: at my.MainApplication.onCreate(MainApplication.java:24)
	E AndroidRuntime: at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1386)
	E AndroidRuntime: at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7398)
	E AndroidRuntime: at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
	E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2379)
	E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:107)
	E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:232)
	E AndroidRuntime: at android.os.Looper.loop(Looper.java:317)
	E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8592)
	E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
	E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
	E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)

This also allows us to remove these fields that are now unused:

  * `JnienvInitializeArgs.Class_forName`
  * `JNIEnv.BinaryName()`

TODO?  While `JNIEnv.FindClass()` was using [`Class.forName()`][0],
`JniEnvironment.Types.FindClass()` uses [`ClassLoader.loadClass()`][1].
`JNIEnv.FindClass()` originally used `ClassLoader.loadClass()`, but
was changed to use `Class.forName()` in xamarin/monodroid@ed984a3a as
part of supporting the Xamarin.Android Designer.

Updating `JNIEnv.FindClass()` to use `JniEnvironment.Types.FindClass()`
effectively reverts that original change.  This is "fine" for now --
the Xamarin.Android Designer is no longer supported -- but in the
future we may want to update `JniEnvironment.Types.FindClass()` to
use `Class.forName()`; see dotnet/java-interop#23.

[0]: https://developer.android.com/reference/java/lang/Class#forName(java.lang.String)
[1]: https://developer.android.com/reference/java/lang/ClassLoader#loadClass(java.lang.String)

@jonpryor jonpryor merged commit aba2726 into main Feb 8, 2025
58 checks passed
@jonpryor jonpryor deleted the dev/peppers/JNIEnv.FindClass branch February 8, 2025 18:16
@filipnavara
Copy link
Member

We may want to migrate to Class.forName() anyway, but this doesn't appear to be important right now.

The observable difference is that Class.forName runs static constructors. That could possibly be quite important I guess.

grendello added a commit that referenced this pull request Feb 10, 2025
* main:
  [Mono.Android] `JNIEnv.FindClass()` uses `JniEnvironment.Types.FindClass()` (#9769)
  [Mono.Android] `JavaList.Add` should allow duplicates. (#9751)
  Bump to dotnet/java-interop@6bc87e8b (#9761)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants