Skip to content

Commit

Permalink
[Mono.Android] Java.Lang.Object.GetObject<T>() implementation
Browse files Browse the repository at this point in the history
The current implementation:

    return Java.Interop.TypeManager.CreateInstance (handle, transfer, type);

Needs to become this, for NativeAOT support:

    JniObjectReference reference = new (handle);
    JniObjectReferenceOptions options = JNIEnv.ToJniObjectReferenceOptions (transfer);
    return (IJavaPeerable) JNIEnvInit.AndroidValueManager?.GetValue (ref reference, options, type);

This way we use the appropriate `AndroidValueManager` for the runtime
that is currently being used. If we just change the code in
`Java.Lang.Object` to do this for all runtimes, this can keep
everything a bit simpler.

The fallout of this change requires `type` to be decorated with:

    [DynamicallyAccessedMembers (ConstructorsInterfaces)]

This trickled to create many other trimmer warnings, that are likely a
real issue. This is a good opportunity to fix them.

I also defined the `const` values in `Java.Lang.Object`:

    internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
    internal const DynamicallyAccessedMemberTypes ConstructorsInterfaces = Constructors | DynamicallyAccessedMemberTypes.Interfaces;

Which allowed many types that extend `Java.Lang.Object` able to reuse
these `const` values and not define them again.

I also had to make a helper function to map `JniHandleOwnership` ->
`JniObjectReferenceOptions`, such as:

    internal static JniObjectReferenceOptions ToJniObjectReferenceOptions (JniHandleOwnership transfer)
  • Loading branch information
jonathanpeppers committed Dec 18, 2024
1 parent de04316 commit eff56dd
Show file tree
Hide file tree
Showing 21 changed files with 97 additions and 63 deletions.
6 changes: 2 additions & 4 deletions src/Mono.Android/Android.App/Activity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ namespace Android.App {

partial class Activity {

internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;

public T? FindViewById<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> (int id)
where T : Android.Views.View
Expand All @@ -19,7 +17,7 @@ public T? FindViewById<

// See: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/Activity.java;l=3430
public T RequireViewById<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> (int id)
where T : Android.Views.View
Expand Down
2 changes: 1 addition & 1 deletion src/Mono.Android/Android.App/Dialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 (ConstructorsInterfaces)]
T
> (int id)
where T : Android.Views.View
Expand Down
8 changes: 3 additions & 5 deletions src/Mono.Android/Android.App/FragmentManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
#if ANDROID_11
namespace Android.App {
public partial class FragmentManager {
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;

public T? FindFragmentById<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> (int id)
where T : Fragment
Expand All @@ -17,7 +15,7 @@ public T? FindFragmentById<
}

public T? FindFragmentByTag<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> (string tag)
where T : Fragment
Expand All @@ -26,7 +24,7 @@ public T? FindFragmentByTag<
}

public T? GetFragment<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> (Bundle bundle, string key)
where T : Fragment
Expand Down
8 changes: 3 additions & 5 deletions src/Mono.Android/Android.OS/AsyncTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,14 @@ namespace Android.OS {
[global::System.Runtime.Versioning.ObsoletedOSPlatform ("android30.0")]
[Register ("android/os/AsyncTask", DoNotGenerateAcw=true)]
public abstract class AsyncTask<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
TParams,
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
TProgress,
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
TResult
> : AsyncTask {

const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;

static IntPtr java_class_handle;
internal static IntPtr class_ref {
get {
Expand Down
2 changes: 1 addition & 1 deletion src/Mono.Android/Android.Runtime/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static class Extensions {

[return: NotNullIfNotNull ("instance")]
public static TResult? JavaCast<
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.Interfaces)]
TResult
> (this IJavaObject? instance)
where TResult : class, IJavaObject
Expand Down
29 changes: 26 additions & 3 deletions src/Mono.Android/Android.Runtime/JNIEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
namespace Android.Runtime {
public static partial class JNIEnv {
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
const DynamicallyAccessedMemberTypes ConstructorsInterfaces = Constructors | DynamicallyAccessedMemberTypes.Interfaces;

[ThreadStatic]
static byte[]? mvid_bytes;
Expand Down Expand Up @@ -322,6 +323,21 @@ public static IntPtr FindClass (System.Type type)
}
}

internal static JniObjectReferenceOptions ToJniObjectReferenceOptions (JniHandleOwnership transfer)
{
switch (transfer) {
case JniHandleOwnership.DoNotTransfer:
return JniObjectReferenceOptions.Copy;
case JniHandleOwnership.TransferGlobalRef:
case JniHandleOwnership.TransferLocalRef:
return JniObjectReferenceOptions.CopyAndDispose;
case JniHandleOwnership.DoNotRegister:
return JniObjectReferenceOptions.CopyAndDoNotRegister;
default:
throw new ArgumentOutOfRangeException (nameof (transfer), transfer, "Invalid JniHandleOwnership value.");
}
}

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

Expand Down Expand Up @@ -704,7 +720,14 @@ 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: https://github.com/xamarin/xamarin-android/issues/8724
// Since a Dictionary<Type, Func> 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 = "[DynamicallyAccessedMembers] manually added to all callers")]
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);
Expand Down Expand Up @@ -1135,7 +1158,7 @@ static int _GetArrayLength (IntPtr array_ptr)
}

public static T[]? GetArray<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> (Java.Lang.Object[] array)
{
Expand Down Expand Up @@ -1252,7 +1275,7 @@ static IntPtr GetArrayElementClass<T>(T[] values)
}

public static void CopyObjectArray<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
>(IntPtr source, T[] destination)
{
Expand Down
7 changes: 5 additions & 2 deletions src/Mono.Android/Android.Runtime/JavaArray.cs
Original file line number Diff line number Diff line change
@@ -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<T> : Java.Lang.Object, IList<T> {
public sealed class JavaArray<
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> : Java.Lang.Object, IList<T> {

public JavaArray (IntPtr handle, JniHandleOwnership transfer)
: base (handle, transfer)
Expand Down
6 changes: 2 additions & 4 deletions src/Mono.Android/Android.Runtime/JavaCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -151,7 +149,7 @@ internal Java.Lang.Object[] ToArray ()
public void CopyTo (Array array, int array_index)
{
[UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "JavaCollection<T> constructors are preserved by the MarkJavaObjects trimmer step.")]
[return: DynamicallyAccessedMembers (Constructors)]
[return: DynamicallyAccessedMembers (ConstructorsInterfaces)]
static Type GetElementType (Array array) =>
array.GetType ().GetElementType ();

Expand Down Expand Up @@ -211,7 +209,7 @@ public static IntPtr ToLocalJniHandle (ICollection? items)

[Register ("java/util/Collection", DoNotGenerateAcw=true)]
public sealed class JavaCollection<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> : JavaCollection, ICollection<T> {

Expand Down
6 changes: 2 additions & 4 deletions src/Mono.Android/Android.Runtime/JavaDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -399,9 +397,9 @@ public static IntPtr ToLocalJniHandle (IDictionary? dictionary)
//
[Register ("java/util/HashMap", DoNotGenerateAcw=true)]
public class JavaDictionary<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
K,
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
V
> : JavaDictionary, IDictionary<K, V> {

Expand Down
7 changes: 3 additions & 4 deletions src/Mono.Android/Android.Runtime/JavaList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

//
Expand All @@ -26,7 +25,7 @@ public partial class JavaList : Java.Lang.Object, System.Collections.IList {
//
internal unsafe object? InternalGet (
int location,
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
Type? targetType = null)
{
const string id = "get.(I)Ljava/lang/Object;";
Expand Down Expand Up @@ -271,7 +270,7 @@ public unsafe bool Contains (object? item)
public void CopyTo (Array array, int array_index)
{
[UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "JavaList<T> constructors are preserved by the MarkJavaObjects trimmer step.")]
[return: DynamicallyAccessedMembers (Constructors)]
[return: DynamicallyAccessedMembers (ConstructorsInterfaces)]
static Type GetElementType (Array array) =>
array.GetType ().GetElementType ();

Expand Down Expand Up @@ -683,7 +682,7 @@ public virtual Java.Lang.Object [] ToArray ()

[Register ("java/util/ArrayList", DoNotGenerateAcw=true)]
public class JavaList<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> : JavaList, IList<T> {

Expand Down
2 changes: 1 addition & 1 deletion src/Mono.Android/Android.Runtime/JavaSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 (ConstructorsInterfaces)]
T
> : JavaSet, ICollection<T> {

Expand Down
2 changes: 1 addition & 1 deletion src/Mono.Android/Android.Util/SparseArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Android.Util
{
[Register ("android/util/SparseArray", DoNotGenerateAcw=true)]
public partial class SparseArray<
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
E
> : SparseArray
{
Expand Down
7 changes: 2 additions & 5 deletions src/Mono.Android/Android.Views/View.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -25,7 +22,7 @@ public bool PerformAccessibilityAction (GlobalAction action, Bundle arguments)
#endif

public T? FindViewById<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> (int id)
where T : Android.Views.View
Expand All @@ -35,7 +32,7 @@ public T? FindViewById<

// See: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/View.java;l=25322
public T RequireViewById<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> (int id)
where T : Android.Views.View
Expand Down
2 changes: 1 addition & 1 deletion src/Mono.Android/Android.Views/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Android.Views {
partial class Window {

public T? FindViewById<
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> (int id)
where T : Android.Views.View
Expand Down
2 changes: 1 addition & 1 deletion src/Mono.Android/Android.Widget/AdapterView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public event EventHandler ItemSelectionCleared {

[Register ("android/widget/AdapterView", DoNotGenerateAcw=true)]
public abstract class AdapterView<
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> : AdapterView where T : IAdapter {

Expand Down
2 changes: 1 addition & 1 deletion src/Mono.Android/Android.Widget/ArrayAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Android.Widget {

[Register ("android/widget/ArrayAdapter", DoNotGenerateAcw=true)]
public partial class ArrayAdapter<
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
> : ArrayAdapter {

Expand Down
13 changes: 7 additions & 6 deletions src/Mono.Android/Java.Interop/JavaConvert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace Java.Interop {

static class JavaConvert {
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
const DynamicallyAccessedMemberTypes ConstructorsInterfaces = Constructors | DynamicallyAccessedMemberTypes.Interfaces;

static Dictionary<Type, Func<IntPtr, JniHandleOwnership, object>> JniHandleConverters = new Dictionary<Type, Func<IntPtr, JniHandleOwnership, object>>() {
{ typeof (bool), (handle, transfer) => {
Expand Down Expand Up @@ -109,7 +110,7 @@ static Func<IntPtr, JniHandleOwnership, object> GetJniHandleConverterForType ([D
}

public static T? FromJniHandle<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
>(IntPtr handle, JniHandleOwnership transfer)
{
Expand All @@ -118,7 +119,7 @@ public static T? FromJniHandle<
}

public static T? FromJniHandle<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
>(IntPtr handle, JniHandleOwnership transfer, out bool set)
{
Expand Down Expand Up @@ -158,7 +159,7 @@ public static T? FromJniHandle<
public static object? FromJniHandle (
IntPtr handle,
JniHandleOwnership transfer,
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
Type? targetType = null)
{
if (handle == IntPtr.Zero) {
Expand Down Expand Up @@ -233,7 +234,7 @@ public static T? FromJniHandle<
}

public static T? FromJavaObject<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
>(IJavaObject? value)
{
Expand All @@ -242,7 +243,7 @@ public static T? FromJavaObject<
}

public static T? FromJavaObject<
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
T
>(IJavaObject? value, out bool set)
{
Expand Down Expand Up @@ -279,7 +280,7 @@ public static T? FromJavaObject<

public static object? FromJavaObject (
IJavaObject value,
[DynamicallyAccessedMembers (Constructors)]
[DynamicallyAccessedMembers (ConstructorsInterfaces)]
Type? targetType = null)
{
if (value == null)
Expand Down
Loading

0 comments on commit eff56dd

Please sign in to comment.