From 310dee1871af1c119b3226a63dd506154453961d Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 30 Jan 2025 09:57:58 -0600 Subject: [PATCH] [Mono.Android] Java.Lang.Object.GetObject() implementation Context: https://github.com/dotnet/android/pull/9630 Context: https://github.com/dotnet/java-interop/pull/1295 As an alternative to #9630... For NativeAOT support, implement `Java.Lang.Object.GetObject()` using the new `JniRuntime.JniValueManager.GetPeer()` method. This also cleans up a few things if `Java.Lang.Object` introduces an internal `DynamicallyAccessedMemberTypes Constructors` field. --- src/Mono.Android/Android.App/Activity.cs | 2 -- src/Mono.Android/Android.App/Dialog.cs | 2 +- .../Android.App/FragmentManager.cs | 2 -- src/Mono.Android/Android.OS/AsyncTask.cs | 2 -- src/Mono.Android/Android.Runtime/JNIEnv.cs | 8 ++++- src/Mono.Android/Android.Runtime/JavaArray.cs | 7 ++-- .../Android.Runtime/JavaCollection.cs | 2 -- .../Android.Runtime/JavaDictionary.cs | 2 -- src/Mono.Android/Android.Runtime/JavaList.cs | 1 - src/Mono.Android/Android.Runtime/JavaSet.cs | 2 +- src/Mono.Android/Android.Util/SparseArray.cs | 2 +- src/Mono.Android/Android.Views/View.cs | 3 -- src/Mono.Android/Android.Views/Window.cs | 2 +- .../Android.Widget/AdapterView.cs | 2 +- .../Android.Widget/ArrayAdapter.cs | 2 +- src/Mono.Android/Java.Lang/Object.cs | 33 ++++++++++++------- 16 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/Mono.Android/Android.App/Activity.cs b/src/Mono.Android/Android.App/Activity.cs index dd881a6b539..4fd2a108c7f 100644 --- a/src/Mono.Android/Android.App/Activity.cs +++ b/src/Mono.Android/Android.App/Activity.cs @@ -6,8 +6,6 @@ namespace Android.App { partial class Activity { - internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - public T? FindViewById< [DynamicallyAccessedMembers (Constructors)] T diff --git a/src/Mono.Android/Android.App/Dialog.cs b/src/Mono.Android/Android.App/Dialog.cs index 2a1a1f10ce8..ba8844ece2f 100644 --- a/src/Mono.Android/Android.App/Dialog.cs +++ b/src/Mono.Android/Android.App/Dialog.cs @@ -10,7 +10,7 @@ protected Dialog (Android.Content.Context context, bool cancelable, EventHandler : this (context, cancelable, new Android.Content.IDialogInterfaceOnCancelListenerImplementor () { Handler = cancelHandler }) {} public T? FindViewById< - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + [DynamicallyAccessedMembers (Constructors)] T > (int id) where T : Android.Views.View diff --git a/src/Mono.Android/Android.App/FragmentManager.cs b/src/Mono.Android/Android.App/FragmentManager.cs index 5269111a1a0..b6f421c9b91 100644 --- a/src/Mono.Android/Android.App/FragmentManager.cs +++ b/src/Mono.Android/Android.App/FragmentManager.cs @@ -5,8 +5,6 @@ #if ANDROID_11 namespace Android.App { public partial class FragmentManager { - const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - public T? FindFragmentById< [DynamicallyAccessedMembers (Constructors)] T diff --git a/src/Mono.Android/Android.OS/AsyncTask.cs b/src/Mono.Android/Android.OS/AsyncTask.cs index 7dcb9049b6f..f1d556e985a 100644 --- a/src/Mono.Android/Android.OS/AsyncTask.cs +++ b/src/Mono.Android/Android.OS/AsyncTask.cs @@ -18,8 +18,6 @@ public abstract class AsyncTask< TResult > : AsyncTask { - const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - static IntPtr java_class_handle; internal static IntPtr class_ref { get { diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index 26a4e3fd5cb..73530406da5 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -662,7 +662,13 @@ public static void CopyArray (IntPtr src, string[] dest) AssertIsJavaObject (type); IntPtr elem = GetObjectArrayElement (source, index); - return Java.Lang.Object.GetObject (elem, JniHandleOwnership.TransferLocalRef, type); + return GetObject (elem, type); + + // FIXME: Since a Dictionary is used here, the trimmer will not be able to properly analyze `Type t` + // 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. + [UnconditionalSuppressMessage ("Trimming", "IL2067", Justification = "FIXME: https://github.com/xamarin/xamarin-android/issues/8724")] + static object? GetObject (IntPtr e, Type t) => + Java.Lang.Object.GetObject (e, JniHandleOwnership.TransferLocalRef, t); } }, { typeof (Array), (type, source, index) => { IntPtr elem = GetObjectArrayElement (source, index); diff --git a/src/Mono.Android/Android.Runtime/JavaArray.cs b/src/Mono.Android/Android.Runtime/JavaArray.cs index 4d05a7de4a3..bbae4d78a33 100644 --- a/src/Mono.Android/Android.Runtime/JavaArray.cs +++ b/src/Mono.Android/Android.Runtime/JavaArray.cs @@ -1,12 +1,15 @@ using System; using System.Collections; using System.Collections.Generic; - +using System.Diagnostics.CodeAnalysis; namespace Android.Runtime { [Register ("mono/android/runtime/JavaArray", DoNotGenerateAcw=true)] - public sealed class JavaArray : Java.Lang.Object, IList { + public sealed class JavaArray< + [DynamicallyAccessedMembers (Constructors)] + T + > : Java.Lang.Object, IList { public JavaArray (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) diff --git a/src/Mono.Android/Android.Runtime/JavaCollection.cs b/src/Mono.Android/Android.Runtime/JavaCollection.cs index 8e23660f33b..9a41485cf71 100644 --- a/src/Mono.Android/Android.Runtime/JavaCollection.cs +++ b/src/Mono.Android/Android.Runtime/JavaCollection.cs @@ -14,8 +14,6 @@ namespace Android.Runtime { // java.util.Collection allows null values public class JavaCollection : Java.Lang.Object, System.Collections.ICollection { - internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - internal static IntPtr collection_class = JNIEnv.FindClass ("java/util/Collection"); internal static IntPtr id_add; diff --git a/src/Mono.Android/Android.Runtime/JavaDictionary.cs b/src/Mono.Android/Android.Runtime/JavaDictionary.cs index 311dbf36ebe..3ca9fdfea17 100644 --- a/src/Mono.Android/Android.Runtime/JavaDictionary.cs +++ b/src/Mono.Android/Android.Runtime/JavaDictionary.cs @@ -12,8 +12,6 @@ namespace Android.Runtime { // java.util.HashMap allows null keys and values public class JavaDictionary : Java.Lang.Object, System.Collections.IDictionary { - internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - class DictionaryEnumerator : IDictionaryEnumerator { IEnumerator simple_enumerator; diff --git a/src/Mono.Android/Android.Runtime/JavaList.cs b/src/Mono.Android/Android.Runtime/JavaList.cs index e1877533577..478fbdfdb33 100644 --- a/src/Mono.Android/Android.Runtime/JavaList.cs +++ b/src/Mono.Android/Android.Runtime/JavaList.cs @@ -10,7 +10,6 @@ namespace Android.Runtime { // java.util.ArrayList allows null values public partial class JavaList : Java.Lang.Object, System.Collections.IList { - internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; internal static readonly JniPeerMembers list_members = new XAPeerMembers ("java/util/List", typeof (JavaList), isInterface: true); // diff --git a/src/Mono.Android/Android.Runtime/JavaSet.cs b/src/Mono.Android/Android.Runtime/JavaSet.cs index b84b040289a..eec208f08ce 100644 --- a/src/Mono.Android/Android.Runtime/JavaSet.cs +++ b/src/Mono.Android/Android.Runtime/JavaSet.cs @@ -269,7 +269,7 @@ public static IntPtr ToLocalJniHandle (ICollection? items) [Register ("java/util/HashSet", DoNotGenerateAcw=true)] // java.util.HashSet allows null public class JavaSet< - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + [DynamicallyAccessedMembers (Constructors)] T > : JavaSet, ICollection { diff --git a/src/Mono.Android/Android.Util/SparseArray.cs b/src/Mono.Android/Android.Util/SparseArray.cs index 2a895fb7307..70ce5f42347 100644 --- a/src/Mono.Android/Android.Util/SparseArray.cs +++ b/src/Mono.Android/Android.Util/SparseArray.cs @@ -9,7 +9,7 @@ namespace Android.Util { [Register ("android/util/SparseArray", DoNotGenerateAcw=true)] public partial class SparseArray< - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + [DynamicallyAccessedMembers (Constructors)] E > : SparseArray { diff --git a/src/Mono.Android/Android.Views/View.cs b/src/Mono.Android/Android.Views/View.cs index 5ef341f243b..b6c48d9d781 100644 --- a/src/Mono.Android/Android.Views/View.cs +++ b/src/Mono.Android/Android.Views/View.cs @@ -13,9 +13,6 @@ public enum SystemUiFlags { #endif public partial class View { - - internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - #if ANDROID_16 [Obsolete ("This method uses wrong enum type. Please use PerformAccessibilityAction(Action) instead.")] public bool PerformAccessibilityAction (GlobalAction action, Bundle arguments) diff --git a/src/Mono.Android/Android.Views/Window.cs b/src/Mono.Android/Android.Views/Window.cs index 1d82b614ec4..11920d2d988 100644 --- a/src/Mono.Android/Android.Views/Window.cs +++ b/src/Mono.Android/Android.Views/Window.cs @@ -7,7 +7,7 @@ namespace Android.Views { partial class Window { public T? FindViewById< - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + [DynamicallyAccessedMembers (Constructors)] T > (int id) where T : Android.Views.View diff --git a/src/Mono.Android/Android.Widget/AdapterView.cs b/src/Mono.Android/Android.Widget/AdapterView.cs index 7689b0c39e3..f2da20df26b 100644 --- a/src/Mono.Android/Android.Widget/AdapterView.cs +++ b/src/Mono.Android/Android.Widget/AdapterView.cs @@ -51,7 +51,7 @@ public event EventHandler ItemSelectionCleared { [Register ("android/widget/AdapterView", DoNotGenerateAcw=true)] public abstract class AdapterView< - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + [DynamicallyAccessedMembers (Constructors)] T > : AdapterView where T : IAdapter { diff --git a/src/Mono.Android/Android.Widget/ArrayAdapter.cs b/src/Mono.Android/Android.Widget/ArrayAdapter.cs index e54ce8d7c94..a7c1cbc95d7 100644 --- a/src/Mono.Android/Android.Widget/ArrayAdapter.cs +++ b/src/Mono.Android/Android.Widget/ArrayAdapter.cs @@ -9,7 +9,7 @@ namespace Android.Widget { [Register ("android/widget/ArrayAdapter", DoNotGenerateAcw=true)] public partial class ArrayAdapter< - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + [DynamicallyAccessedMembers (Constructors)] T > : ArrayAdapter { diff --git a/src/Mono.Android/Java.Lang/Object.cs b/src/Mono.Android/Java.Lang/Object.cs index fe6b49a8abf..9e58768d9ee 100644 --- a/src/Mono.Android/Java.Lang/Object.cs +++ b/src/Mono.Android/Java.Lang/Object.cs @@ -16,6 +16,8 @@ namespace Java.Lang { [Serializable] public partial class Object : global::Java.Interop.JavaObject, IJavaObject, IJavaObjectEx { + internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; + IntPtr IJavaObjectEx.ToLocalJniHandle () { lock (this) { @@ -130,20 +132,29 @@ protected void SetHandle (IntPtr value, JniHandleOwnership transfer) return (T?)PeekObject (handle, typeof (T)); } - public static T? GetObject (IntPtr jnienv, IntPtr handle, JniHandleOwnership transfer) + public static T? GetObject< + [DynamicallyAccessedMembers (Constructors)] + T + > (IntPtr jnienv, IntPtr handle, JniHandleOwnership transfer) where T : class, IJavaObject { JNIEnv.CheckHandle (jnienv); return GetObject (handle, transfer); } - public static T? GetObject (IntPtr handle, JniHandleOwnership transfer) + public static T? GetObject< + [DynamicallyAccessedMembers (Constructors)] + T + > (IntPtr handle, JniHandleOwnership transfer) where T : class, IJavaObject { return _GetObject(handle, transfer); } - internal static T? _GetObject (IntPtr handle, JniHandleOwnership transfer) + internal static T? _GetObject< + [DynamicallyAccessedMembers (Constructors)] + T + > (IntPtr handle, JniHandleOwnership transfer) { if (handle == IntPtr.Zero) return default (T); @@ -151,18 +162,18 @@ protected void SetHandle (IntPtr value, JniHandleOwnership transfer) return (T?) GetObject (handle, transfer, typeof (T)); } - internal static IJavaPeerable? GetObject (IntPtr handle, JniHandleOwnership transfer, Type? type = null) + internal static IJavaPeerable? GetObject ( + IntPtr handle, + JniHandleOwnership transfer, + [DynamicallyAccessedMembers (Constructors)] + Type? type = null) { if (handle == IntPtr.Zero) return null; - var r = PeekObject (handle, type); - if (r != null) { - JNIEnv.DeleteRef (handle, transfer); - return r; - } - - return Java.Interop.TypeManager.CreateInstance (handle, transfer, type); + var r = JNIEnvInit.ValueManager!.GetPeer (new JniObjectReference (handle), type); + JNIEnv.DeleteRef (handle, transfer); + return r; } [EditorBrowsable (EditorBrowsableState.Never)]