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 = JNIEnv.CreateJniObjectReference (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 (Constructors)]

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` value in `Java.Lang.Object`:

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

Which allowed many types that extend `Java.Lang.Object` able to reuse
this `const` value and not define it again.

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

    internal static JniObjectReferenceOptions ToJniObjectReferenceOptions (JniHandleOwnership transfer)

And `CreateJniObjectReference()`:

    internal static JniObjectReference CreateJniObjectReference (IntPtr handle, JniHandleOwnership transfer)

Other changes:

* Bump to dotnet/java-interop@2c06b3c2a

Changes: dotnet/java-interop@f800ea5...2c06b3c
  • Loading branch information
jonathanpeppers committed Dec 18, 2024
1 parent de04316 commit 6398552
Show file tree
Hide file tree
Showing 20 changed files with 69 additions and 33 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
4 changes: 2 additions & 2 deletions src/Mono.Android/Android.Graphics/Color.cs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ public static void RGBToHSV (int red, int green, int blue, float[] hsv)

public class ColorValueMarshaler : JniValueMarshaler<Color>
{
const DynamicallyAccessedMemberTypes ConstructorsAndInterfaces = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.Interfaces;
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
const string ExpressionRequiresUnreferencedCode = "System.Linq.Expression usage may trim away required code.";

public override Type MarshalType {
Expand All @@ -405,7 +405,7 @@ public override Type MarshalType {
public override Color CreateGenericValue (
ref JniObjectReference reference,
JniObjectReferenceOptions options,
[DynamicallyAccessedMembers (ConstructorsAndInterfaces)]
[DynamicallyAccessedMembers (Constructors)]
Type targetType)
{
throw new NotImplementedException ();
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
4 changes: 2 additions & 2 deletions src/Mono.Android/Android.Runtime/IJavaObjectValueMarshaler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ namespace Android.Runtime
{
sealed class IJavaObjectValueMarshaler : JniValueMarshaler<IJavaObject> {

const DynamicallyAccessedMemberTypes ConstructorsAndInterfaces = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.Interfaces;
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
const string ExpressionRequiresUnreferencedCode = "System.Linq.Expression usage may trim away required code.";

internal static IJavaObjectValueMarshaler Instance = new IJavaObjectValueMarshaler ();

public override IJavaObject CreateGenericValue (
ref JniObjectReference reference,
JniObjectReferenceOptions options,
[DynamicallyAccessedMembers (ConstructorsAndInterfaces)]
[DynamicallyAccessedMembers (Constructors)]
Type? targetType)
{
throw new NotImplementedException ();
Expand Down
31 changes: 30 additions & 1 deletion src/Mono.Android/Android.Runtime/JNIEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,29 @@ public static IntPtr FindClass (System.Type type)
}
}

internal static JniObjectReferenceOptions ToJniObjectReferenceOptions (JniHandleOwnership transfer)
{
JniObjectReferenceOptions options = default;
if (transfer.HasFlag (JniHandleOwnership.TransferLocalRef) ||
transfer.HasFlag (JniHandleOwnership.TransferGlobalRef))
options |= JniObjectReferenceOptions.CopyAndDispose;
if (transfer.HasFlag (JniHandleOwnership.DoNotRegister))
options |= JniObjectReferenceOptions.CopyAndDoNotRegister;
if (transfer == JniHandleOwnership.DoNotTransfer) // 0; can't use with .HasFlag()
options |= JniObjectReferenceOptions.Copy;

return options;
}

internal static JniObjectReference CreateJniObjectReference (IntPtr handle, JniHandleOwnership transfer)
{
if (transfer.HasFlag (JniHandleOwnership.TransferLocalRef))
return new JniObjectReference (handle, JniObjectReferenceType.Local);
if (transfer.HasFlag (JniHandleOwnership.TransferGlobalRef))
return new JniObjectReference (handle, JniObjectReferenceType.Global);
return new JniObjectReference (handle); // there be possible dragons here!
}

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

Expand Down Expand Up @@ -704,7 +727,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
27 changes: 22 additions & 5 deletions src/Mono.Android/Java.Lang/Object.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public partial class Object : IDisposable, IJavaObject, IJavaObjectEx
, IJavaPeerable
#endif // JAVA_INTEROP
{
internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;

[NonSerialized] IntPtr key_handle;
#pragma warning disable CS0649, CS0169, CS0414 // Suppress fields are never used warnings, these fields are used directly by monodroid-glue.cc
[NonSerialized] int refs_added;
Expand Down Expand Up @@ -263,28 +265,41 @@ 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;
Expand All @@ -295,7 +310,9 @@ protected void SetHandle (IntPtr value, JniHandleOwnership transfer)
return r;
}

return Java.Interop.TypeManager.CreateInstance (handle, transfer, type);
JniObjectReference reference = JNIEnv.CreateJniObjectReference (handle);
JniObjectReferenceOptions options = JNIEnv.ToJniObjectReferenceOptions (transfer);
return (IJavaPeerable) JNIEnvInit.AndroidValueManager?.GetValue (ref reference, options, type);
}

[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 6398552

Please sign in to comment.