Skip to content

Commit

Permalink
[Mono.Android] Java.Lang.Object.GetObject<T>() implementation
Browse files Browse the repository at this point in the history
Context: #9630
Context: dotnet/java-interop#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.
  • Loading branch information
jonathanpeppers committed Jan 30, 2025
1 parent 3856e62 commit 5a208fb
Show file tree
Hide file tree
Showing 17 changed files with 41 additions and 34 deletions.
2 changes: 0 additions & 2 deletions src/Mono.Android/Android.App/Activity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ namespace Android.App {

partial class Activity {

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

public T? FindViewById<
[DynamicallyAccessedMembers (Constructors)]
T
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 (Constructors)]
T
> (int id)
where T : Android.Views.View
Expand Down
2 changes: 0 additions & 2 deletions src/Mono.Android/Android.App/FragmentManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions src/Mono.Android/Android.OS/AsyncTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
8 changes: 7 additions & 1 deletion src/Mono.Android/Android.Runtime/JNIEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<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 = "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);
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 (Constructors)]
T
> : Java.Lang.Object, IList<T> {

public JavaArray (IntPtr handle, JniHandleOwnership transfer)
: base (handle, transfer)
Expand Down
2 changes: 0 additions & 2 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
2 changes: 0 additions & 2 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
1 change: 0 additions & 1 deletion 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 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 (Constructors)]
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 (Constructors)]
E
> : SparseArray
{
Expand Down
3 changes: 0 additions & 3 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 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 (Constructors)]
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 (Constructors)]
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 (Constructors)]
T
> : ArrayAdapter {

Expand Down
33 changes: 22 additions & 11 deletions src/Mono.Android/Java.Lang/Object.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -130,39 +132,48 @@ protected void SetHandle (IntPtr value, JniHandleOwnership transfer)
return (T?)PeekObject (handle, typeof (T));
}

public static T? GetObject<T> (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<T> (handle, transfer);
}

public static T? GetObject<T> (IntPtr handle, JniHandleOwnership transfer)
public static T? GetObject<
[DynamicallyAccessedMembers (Constructors)]
T
> (IntPtr handle, JniHandleOwnership transfer)
where T : class, IJavaObject
{
return _GetObject<T>(handle, transfer);
}

internal static T? _GetObject<T> (IntPtr handle, JniHandleOwnership transfer)
internal static T? _GetObject<
[DynamicallyAccessedMembers (Constructors)]
T
> (IntPtr handle, JniHandleOwnership transfer)
{
if (handle == IntPtr.Zero)
return default (T);

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)]
Expand Down
1 change: 1 addition & 0 deletions tests/api-compatibility/api-compat-exclude-attributes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ T:System.Runtime.CompilerServices.IteratorStateMachineAttribute
T:System.Runtime.CompilerServices.NullableAttribute
T:System.Runtime.CompilerServices.NullableContextAttribute
T:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute
T:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute

0 comments on commit 5a208fb

Please sign in to comment.