Skip to content

Commit

Permalink
[Mono.Android] JNIEnv.FindClass() uses `JniEnvironment.Types.FindCl…
Browse files Browse the repository at this point in the history
…ass()` (#9769)

Context: xamarin/monodroid@ed984a3
Context: dotnet/java-interop#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)
  • Loading branch information
jonathanpeppers authored Feb 8, 2025
1 parent a9299a2 commit aba2726
Show file tree
Hide file tree
Showing 4 changed files with 2 additions and 51 deletions.
47 changes: 2 additions & 45 deletions src/Mono.Android/Android.Runtime/JNIEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,52 +280,9 @@ public static IntPtr FindClass (System.Type type)
}
}

const int nameBufferLength = 1024;
[ThreadStatic] static char[]? nameBuffer;

static unsafe IntPtr BinaryName (string classname)
public static IntPtr FindClass (string classname)
{
int index = classname.IndexOf ('/');

if (index == -1)
return NewString (classname);

int length = classname.Length;
if (length > nameBufferLength)
return NewString (classname.Replace ('/', '.'));

if (nameBuffer == null)
nameBuffer = new char[nameBufferLength];

fixed (char* src = classname, dst = nameBuffer) {
char* src_ptr = src;
char* dst_ptr = dst;
char* end_ptr = src + length;
while (src_ptr < end_ptr) {
*dst_ptr = (*src_ptr == '/') ? '.' : *src_ptr;
src_ptr++;
dst_ptr++;
}
}
return NewString (nameBuffer, length);
}

public unsafe static IntPtr FindClass (string classname)
{
JniObjectReference local_ref;

IntPtr native_str = BinaryName (classname);
try {
JniArgumentValue* parameters = stackalloc JniArgumentValue [3] {
new JniArgumentValue (native_str),
new JniArgumentValue (true),
new JniArgumentValue (JNIEnvInit.java_class_loader),
};
local_ref = JniEnvironment.StaticMethods.CallStaticObjectMethod (Java.Lang.Class.Members.JniPeerType.PeerReference, JNIEnvInit.mid_Class_forName!, parameters);
} finally {
DeleteLocalRef (native_str);
}

JniObjectReference local_ref = JniEnvironment.Types.FindClass (classname);
IntPtr global_ref = NewGlobalRef (local_ref.Handle);
JniObjectReference.Dispose (ref local_ref);
return global_ref;
Expand Down
4 changes: 0 additions & 4 deletions src/Mono.Android/Android.Runtime/JNIEnvInit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ internal struct JnienvInitializeArgs {
public IntPtr grefLoader;
public IntPtr Loader_loadClass;
public IntPtr grefClass; // TODO: remove, not needed anymore
public IntPtr Class_forName;
public uint logCategories;
public int version; // TODO: remove, not needed anymore
public int grefGcThreshold;
Expand All @@ -46,7 +45,6 @@ internal struct JnienvInitializeArgs {
internal static IntPtr grefIGCUserPeer_class;
internal static IntPtr grefGCUserPeerable_class;
internal static IntPtr java_class_loader;
internal static JniMethodInfo? mid_Class_forName;

internal static JniRuntime? androidRuntime;

Expand Down Expand Up @@ -100,8 +98,6 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args)
MarshalMethodsEnabled = args->marshalMethodsEnabled;
java_class_loader = args->grefLoader;

mid_Class_forName = new JniMethodInfo (args->Class_forName, isStatic: true);

BoundExceptionType = (BoundExceptionType)args->ioExceptionType;
androidRuntime = new AndroidRuntime (args->env, args->javaVm, args->grefLoader, args->Loader_loadClass, args->jniAddNativeMethodRegistrationAttributePresent != 0);
ValueManager = androidRuntime.ValueManager;
Expand Down
1 change: 0 additions & 1 deletion src/native/mono/monodroid/monodroid-glue-internal.hh
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ namespace xamarin::android::internal
jobject grefLoader;
jmethodID Loader_loadClass;
jclass grefClass;
jmethodID Class_forName;
unsigned int logCategories;
int version;
int grefGcThreshold;
Expand Down
1 change: 0 additions & 1 deletion src/native/mono/monodroid/monodroid-glue.cc
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,6 @@ MonodroidRuntime::init_android_runtime (JNIEnv *env, jclass runtimeClass, jobjec

init.grefClass = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "java_lang_Class", true);
Class_getName = env->GetMethodID (init.grefClass, "getName", "()Ljava/lang/String;");
init.Class_forName = env->GetStaticMethodID (init.grefClass, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");

MonoAssembly *mono_android_assembly;

Expand Down

0 comments on commit aba2726

Please sign in to comment.