Skip to content

Commit f800c1a

Browse files
[Mono.Android] Java.Lang.Object.GetObject<T>() implementation (#9728)
Context: #9630 Context: #9716 Context: dotnet/java-interop@e288589 @jonathanpeppers attempted to prototype MAUI startup in a NativeAOT environment, which promptly crashes similar to: E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.DllNotFoundException: DllNotFound_Linux, xa-internal-api, E AndroidRuntime: dlopen failed: library "xa-internal-api" not found E AndroidRuntime: dlopen failed: library "libxa-internal-api" not found E AndroidRuntime: E AndroidRuntime: at System.Runtime.InteropServices.NativeLibrary.LoadLibErrorTracker.Throw(String) + 0x47 E AndroidRuntime: at Internal.Runtime.CompilerHelpers.InteropHelpers.FixupModuleCell(InteropHelpers.ModuleFixupCell*) + 0xe2 E AndroidRuntime: at Internal.Runtime.CompilerHelpers.InteropHelpers.ResolvePInvokeSlow(InteropHelpers.MethodFixupCell*) + 0x35 E AndroidRuntime: at Android.Runtime.RuntimeNativeMethods.monodroid_TypeManager_get_java_class_name(IntPtr) + 0x22 E AndroidRuntime: at Java.Interop.TypeManager.GetClassName(IntPtr) + 0xe E AndroidRuntime: at Java.Interop.TypeManager.CreateInstance(IntPtr, JniHandleOwnership, Type) + 0x69 E AndroidRuntime: at Java.Lang.Object._GetObject[T](IntPtr, JniHandleOwnership) + 0x4e E AndroidRuntime: at Android.App.Application.n_OnCreate(IntPtr jnienv, IntPtr native__this) + 0x89 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:1182) E AndroidRuntime: at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6460) E AndroidRuntime: at android.app.ActivityThread.access$1300(ActivityThread.java:219) E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1859) E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:107) E AndroidRuntime: at android.os.Looper.loop(Looper.java:214) E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7356) E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) because: 1. MAUI apps provide an `Android.App.Application` sublass, and 2. [`Application` is "special"][0], and 3. When `Java.Lang.Object.GetObject<T>()` is hit for an instance which doesn't already have an instance mapping -- i.e. `Object.PeekObject()` returns `null` -- then we'd hit `TypeManager.CreateInstance()`, which is a codepath full of P/Invokes, and P/Invokes don't currently work on NativeAOT [^1]. The solution is to partially resuscitate PR #9630, but this time have `Java.Lang.Object.GetObject<T>()` call `Java.Interop.JniRuntime.JniValueManager.GetPeer()` instead of `Java.Interop.JniRuntime.JniValueManager.GetValue()`; see also dotnet/java-interop@e288589d. This allows `Application` subclasses to be used (along with other build system changes present in #9716). This also cleans up a few things if `Java.Lang.Object` introduces an internal `DynamicallyAccessedMemberTypes Constructors` field. [0]: https://learn.microsoft.com/en-us/previous-versions/xamarin/android/internals/architecture#java-activation [^1]: It's *not* that NativeAOT doesn't support P/Invokes; it does. The problem is that for a P/Invoke to work, we need to know the name of the native library we're P/Invoking into, and our NativeAOT sample environment doesn't include any `.so` files other than the one for the app, i.e. whatever we'd P/Invoke into doesn't exist.
1 parent 3856e62 commit f800c1a

File tree

16 files changed

+40
-34
lines changed

16 files changed

+40
-34
lines changed

src/Mono.Android/Android.App/Activity.cs

-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ namespace Android.App {
66

77
partial class Activity {
88

9-
internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
10-
119
public T? FindViewById<
1210
[DynamicallyAccessedMembers (Constructors)]
1311
T

src/Mono.Android/Android.App/Dialog.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ protected Dialog (Android.Content.Context context, bool cancelable, EventHandler
1010
: this (context, cancelable, new Android.Content.IDialogInterfaceOnCancelListenerImplementor () { Handler = cancelHandler }) {}
1111

1212
public T? FindViewById<
13-
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
13+
[DynamicallyAccessedMembers (Constructors)]
1414
T
1515
> (int id)
1616
where T : Android.Views.View

src/Mono.Android/Android.App/FragmentManager.cs

-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
#if ANDROID_11
66
namespace Android.App {
77
public partial class FragmentManager {
8-
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
9-
108
public T? FindFragmentById<
119
[DynamicallyAccessedMembers (Constructors)]
1210
T

src/Mono.Android/Android.OS/AsyncTask.cs

-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ public abstract class AsyncTask<
1818
TResult
1919
> : AsyncTask {
2020

21-
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
22-
2321
static IntPtr java_class_handle;
2422
internal static IntPtr class_ref {
2523
get {

src/Mono.Android/Android.Runtime/JNIEnv.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,13 @@ public static void CopyArray (IntPtr src, string[] dest)
662662
AssertIsJavaObject (type);
663663

664664
IntPtr elem = GetObjectArrayElement (source, index);
665-
return Java.Lang.Object.GetObject (elem, JniHandleOwnership.TransferLocalRef, type);
665+
return GetObject (elem, type);
666+
667+
// FIXME: Since a Dictionary<Type, Func> is used here, the trimmer will not be able to properly analyze `Type t`
668+
// error IL2111: Method 'lambda expression' with parameters or return value with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can't guarantee availability of the requirements of the method.
669+
[UnconditionalSuppressMessage ("Trimming", "IL2067", Justification = "FIXME: https://github.com/xamarin/xamarin-android/issues/8724")]
670+
static object? GetObject (IntPtr e, Type t) =>
671+
Java.Lang.Object.GetObject (e, JniHandleOwnership.TransferLocalRef, t);
666672
} },
667673
{ typeof (Array), (type, source, index) => {
668674
IntPtr elem = GetObjectArrayElement (source, index);

src/Mono.Android/Android.Runtime/JavaArray.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4-
4+
using System.Diagnostics.CodeAnalysis;
55

66
namespace Android.Runtime {
77

88
[Register ("mono/android/runtime/JavaArray", DoNotGenerateAcw=true)]
9-
public sealed class JavaArray<T> : Java.Lang.Object, IList<T> {
9+
public sealed class JavaArray<
10+
[DynamicallyAccessedMembers (Constructors)]
11+
T
12+
> : Java.Lang.Object, IList<T> {
1013

1114
public JavaArray (IntPtr handle, JniHandleOwnership transfer)
1215
: base (handle, transfer)

src/Mono.Android/Android.Runtime/JavaCollection.cs

-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ namespace Android.Runtime {
1414
// java.util.Collection allows null values
1515
public class JavaCollection : Java.Lang.Object, System.Collections.ICollection {
1616

17-
internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
18-
1917
internal static IntPtr collection_class = JNIEnv.FindClass ("java/util/Collection");
2018

2119
internal static IntPtr id_add;

src/Mono.Android/Android.Runtime/JavaDictionary.cs

-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ namespace Android.Runtime {
1212
// java.util.HashMap allows null keys and values
1313
public class JavaDictionary : Java.Lang.Object, System.Collections.IDictionary {
1414

15-
internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
16-
1715
class DictionaryEnumerator : IDictionaryEnumerator {
1816

1917
IEnumerator simple_enumerator;

src/Mono.Android/Android.Runtime/JavaList.cs

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ namespace Android.Runtime {
1010
// java.util.ArrayList allows null values
1111
public partial class JavaList : Java.Lang.Object, System.Collections.IList {
1212

13-
internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
1413
internal static readonly JniPeerMembers list_members = new XAPeerMembers ("java/util/List", typeof (JavaList), isInterface: true);
1514

1615
//

src/Mono.Android/Android.Runtime/JavaSet.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ public static IntPtr ToLocalJniHandle (ICollection? items)
269269
[Register ("java/util/HashSet", DoNotGenerateAcw=true)]
270270
// java.util.HashSet allows null
271271
public class JavaSet<
272-
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
272+
[DynamicallyAccessedMembers (Constructors)]
273273
T
274274
> : JavaSet, ICollection<T> {
275275

src/Mono.Android/Android.Util/SparseArray.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Android.Util
99
{
1010
[Register ("android/util/SparseArray", DoNotGenerateAcw=true)]
1111
public partial class SparseArray<
12-
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
12+
[DynamicallyAccessedMembers (Constructors)]
1313
E
1414
> : SparseArray
1515
{

src/Mono.Android/Android.Views/View.cs

-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ public enum SystemUiFlags {
1313
#endif
1414

1515
public partial class View {
16-
17-
internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
18-
1916
#if ANDROID_16
2017
[Obsolete ("This method uses wrong enum type. Please use PerformAccessibilityAction(Action) instead.")]
2118
public bool PerformAccessibilityAction (GlobalAction action, Bundle arguments)

src/Mono.Android/Android.Views/Window.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Android.Views {
77
partial class Window {
88

99
public T? FindViewById<
10-
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
10+
[DynamicallyAccessedMembers (Constructors)]
1111
T
1212
> (int id)
1313
where T : Android.Views.View

src/Mono.Android/Android.Widget/AdapterView.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public event EventHandler ItemSelectionCleared {
5151

5252
[Register ("android/widget/AdapterView", DoNotGenerateAcw=true)]
5353
public abstract class AdapterView<
54-
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
54+
[DynamicallyAccessedMembers (Constructors)]
5555
T
5656
> : AdapterView where T : IAdapter {
5757

src/Mono.Android/Android.Widget/ArrayAdapter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Android.Widget {
99

1010
[Register ("android/widget/ArrayAdapter", DoNotGenerateAcw=true)]
1111
public partial class ArrayAdapter<
12-
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
12+
[DynamicallyAccessedMembers (Constructors)]
1313
T
1414
> : ArrayAdapter {
1515

src/Mono.Android/Java.Lang/Object.cs

+22-11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ namespace Java.Lang {
1616
[Serializable]
1717
public partial class Object : global::Java.Interop.JavaObject, IJavaObject, IJavaObjectEx
1818
{
19+
internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
20+
1921
IntPtr IJavaObjectEx.ToLocalJniHandle ()
2022
{
2123
lock (this) {
@@ -130,39 +132,48 @@ protected void SetHandle (IntPtr value, JniHandleOwnership transfer)
130132
return (T?)PeekObject (handle, typeof (T));
131133
}
132134

133-
public static T? GetObject<T> (IntPtr jnienv, IntPtr handle, JniHandleOwnership transfer)
135+
public static T? GetObject<
136+
[DynamicallyAccessedMembers (Constructors)]
137+
T
138+
> (IntPtr jnienv, IntPtr handle, JniHandleOwnership transfer)
134139
where T : class, IJavaObject
135140
{
136141
JNIEnv.CheckHandle (jnienv);
137142
return GetObject<T> (handle, transfer);
138143
}
139144

140-
public static T? GetObject<T> (IntPtr handle, JniHandleOwnership transfer)
145+
public static T? GetObject<
146+
[DynamicallyAccessedMembers (Constructors)]
147+
T
148+
> (IntPtr handle, JniHandleOwnership transfer)
141149
where T : class, IJavaObject
142150
{
143151
return _GetObject<T>(handle, transfer);
144152
}
145153

146-
internal static T? _GetObject<T> (IntPtr handle, JniHandleOwnership transfer)
154+
internal static T? _GetObject<
155+
[DynamicallyAccessedMembers (Constructors)]
156+
T
157+
> (IntPtr handle, JniHandleOwnership transfer)
147158
{
148159
if (handle == IntPtr.Zero)
149160
return default (T);
150161

151162
return (T?) GetObject (handle, transfer, typeof (T));
152163
}
153164

154-
internal static IJavaPeerable? GetObject (IntPtr handle, JniHandleOwnership transfer, Type? type = null)
165+
internal static IJavaPeerable? GetObject (
166+
IntPtr handle,
167+
JniHandleOwnership transfer,
168+
[DynamicallyAccessedMembers (Constructors)]
169+
Type? type = null)
155170
{
156171
if (handle == IntPtr.Zero)
157172
return null;
158173

159-
var r = PeekObject (handle, type);
160-
if (r != null) {
161-
JNIEnv.DeleteRef (handle, transfer);
162-
return r;
163-
}
164-
165-
return Java.Interop.TypeManager.CreateInstance (handle, transfer, type);
174+
var r = JNIEnvInit.ValueManager!.GetPeer (new JniObjectReference (handle), type);
175+
JNIEnv.DeleteRef (handle, transfer);
176+
return r;
166177
}
167178

168179
[EditorBrowsable (EditorBrowsableState.Never)]

0 commit comments

Comments
 (0)