diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index 20b635cfb70..5464d377c39 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -624,8 +624,14 @@ public static void CopyArray (IntPtr src, string[] dest) dest [i] = GetString (GetObjectArrayElement (src, i), JniHandleOwnership.TransferLocalRef)!; } - static Dictionary>? nativeArrayElementToManaged; - static Dictionary> NativeArrayElementToManaged { + delegate object? NativeArrayElementToManagedConverter ( + [DynamicallyAccessedMembers (Constructors)] + Type? type, + IntPtr source, + int index); + + static Dictionary? nativeArrayElementToManaged; + static Dictionary NativeArrayElementToManaged { get { if (nativeArrayElementToManaged != null) return nativeArrayElementToManaged; @@ -636,9 +642,9 @@ public static void CopyArray (IntPtr src, string[] dest) } } - static Dictionary> CreateNativeArrayElementToManaged () + static Dictionary CreateNativeArrayElementToManaged () { - return new Dictionary> () { + return new Dictionary () { { typeof (bool), (type, source, index) => { var r = new bool [1]; _GetBooleanArrayRegion (source, index, 1, r); @@ -691,15 +697,19 @@ public static void CopyArray (IntPtr src, string[] dest) IntPtr elem = GetObjectArrayElement (source, index); 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) => + // DAM annotations cannot flow through delegate-based dispatch; suppress trimmer warning for this known-safe call. + [UnconditionalSuppressMessage ("Trimming", "IL2067", Justification = "Type is propagated through dictionary-based dispatch; callers ensure constructors are preserved.")] + 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); - return GetArray (elem, JniHandleOwnership.TransferLocalRef, type); + return GetArrayForType (elem, type); + + // DAM annotations cannot flow through delegate-based dispatch; suppress trimmer warning for this known-safe call. + [UnconditionalSuppressMessage ("Trimming", "IL2067", Justification = "Type is propagated through dictionary-based dispatch; callers ensure constructors are preserved.")] + static Array? GetArrayForType (IntPtr e, Type? t) => + GetArray (e, JniHandleOwnership.TransferLocalRef, t); } }, }; } @@ -795,7 +805,11 @@ static unsafe void _GetDoubleArrayRegion (IntPtr array, int start, int length, d } #pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads - public static void CopyArray (IntPtr src, Array dest, Type? elementType = null) + public static void CopyArray ( + IntPtr src, + Array dest, + [DynamicallyAccessedMembers (Constructors)] + Type? elementType = null) #pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads { if (dest == null) @@ -809,10 +823,14 @@ public static void CopyArray (IntPtr src, Array dest, Type? elementType = null) IntPtr a = GetObjectArrayElement (src, i); try { var d = (Array?) dest.GetValue (i); + // Type.GetElementType() does not preserve DAM annotations; suppress since elementType + // already has [DAM(Constructors)] and its element type will have the same preservation. +#pragma warning disable IL2072 if (d == null) dest.SetValue (GetArray (a, JniHandleOwnership.DoNotTransfer, elementType.GetElementType ()), i); else CopyArray (a, d, elementType.GetElementType ()); +#pragma warning restore IL2072 } finally { DeleteLocalRef (a); } @@ -832,7 +850,10 @@ static void AssertIsJavaObject (Type? targetType) throw new NotSupportedException ("Don't know how to convert type '" + targetType.FullName + "' to an Android.Runtime.IJavaObject."); } - public static void CopyArray (IntPtr src, T[] dest) + public static void CopyArray< + [DynamicallyAccessedMembers (Constructors)] + T + > (IntPtr src, T[] dest) { if (dest == null) throw new ArgumentNullException ("dest"); @@ -960,7 +981,11 @@ public static void CopyArray (T[] src, IntPtr dest) CopyArray (src, typeof (T), dest); } - public static Array? GetArray (IntPtr array_ptr, JniHandleOwnership transfer, Type? element_type = null) + public static Array? GetArray ( + IntPtr array_ptr, + JniHandleOwnership transfer, + [DynamicallyAccessedMembers (Constructors)] + Type? element_type = null) { try { return _GetArray (array_ptr, element_type); @@ -970,8 +995,14 @@ public static void CopyArray (T[] src, IntPtr dest) } } - static Dictionary>? nativeArrayToManaged; - static Dictionary> NativeArrayToManaged { + delegate Array NativeArrayToManagedConverter ( + [DynamicallyAccessedMembers (Constructors)] + Type? type, + IntPtr source, + int length); + + static Dictionary? nativeArrayToManaged; + static Dictionary NativeArrayToManaged { get { if (nativeArrayToManaged != null) return nativeArrayToManaged; @@ -982,9 +1013,9 @@ public static void CopyArray (T[] src, IntPtr dest) } } - static Dictionary> CreateNativeArrayToManaged () + static Dictionary CreateNativeArrayToManaged () { - return new Dictionary> () { + return new Dictionary () { { typeof (bool), (type, source, len) => { var r = new bool [len]; CopyArray (source, r); @@ -1053,18 +1084,31 @@ public static void CopyArray (T[] src, IntPtr dest) } }, { typeof (IJavaObject), (type, source, len) => { var r = ArrayCreateInstance (type!, len); - CopyArray (source, r, type); + CopyArrayForType (source, r, type); return r; + + // DAM annotations cannot flow through delegate-based dispatch; suppress trimmer warning for this known-safe call. + [UnconditionalSuppressMessage ("Trimming", "IL2067", Justification = "Type is propagated through dictionary-based dispatch; callers ensure constructors are preserved.")] + static void CopyArrayForType (IntPtr s, Array d, Type? t) => + CopyArray (s, d, t); } }, { typeof (Array), (type, source, len) => { var r = ArrayCreateInstance (type!, len); - CopyArray (source, r, type); + CopyArrayForType (source, r, type); return r; + + // DAM annotations cannot flow through delegate-based dispatch; suppress trimmer warning for this known-safe call. + [UnconditionalSuppressMessage ("Trimming", "IL2067", Justification = "Type is propagated through dictionary-based dispatch; callers ensure constructors are preserved.")] + static void CopyArrayForType (IntPtr s, Array d, Type? t) => + CopyArray (s, d, t); } }, }; } - static Array? _GetArray (IntPtr array_ptr, Type? element_type) + static Array? _GetArray ( + IntPtr array_ptr, + [DynamicallyAccessedMembers (Constructors)] + Type? element_type) { if (array_ptr == IntPtr.Zero) return null; @@ -1084,7 +1128,9 @@ static int _GetArrayLength (IntPtr array_ptr) return JniEnvironment.Arrays.GetArrayLength (new JniObjectReference (array_ptr)); } - public static object?[]? GetObjectArray (IntPtr array_ptr, Type[] element_types) + public static object?[]? GetObjectArray ( + IntPtr array_ptr, + Type[] element_types) { if (array_ptr == IntPtr.Zero) return null; @@ -1099,8 +1145,12 @@ static int _GetArrayLength (IntPtr array_ptr) for (int i = 0; i < cnt; i++) { Type? targetType = (element_types != null && i < element_types.Length) ? element_types [i] : null; + // The converter is retrieved from a dictionary; the trimmer cannot statically determine + // that the delegate's [DAM] parameter requirement is satisfied. +#pragma warning disable IL2062 object? value = converter ((targetType == null || targetType.IsValueType) ? null : targetType, array_ptr, i); +#pragma warning restore IL2062 ret [i] = value; ret [i] = targetType == null || targetType.IsInstanceOfType (value) @@ -1111,7 +1161,10 @@ static int _GetArrayLength (IntPtr array_ptr) return ret; } - public static T[]? GetArray (IntPtr array_ptr) + public static T[]? GetArray< + [DynamicallyAccessedMembers (Constructors)] + T + > (IntPtr array_ptr) { if (array_ptr == IntPtr.Zero) return null; @@ -1138,7 +1191,10 @@ public static T[]? GetArray< return ret; } - public static T GetArrayItem (IntPtr array_ptr, int index) + public static T GetArrayItem< + [DynamicallyAccessedMembers (Constructors)] + T + > (IntPtr array_ptr, int index) { if (array_ptr == IntPtr.Zero) throw new ArgumentException ("array_ptr"); diff --git a/src/Mono.Android/Java.Interop/JavaConvert.cs b/src/Mono.Android/Java.Interop/JavaConvert.cs index ebff6c1f765..3ff48ab6ec0 100644 --- a/src/Mono.Android/Java.Interop/JavaConvert.cs +++ b/src/Mono.Android/Java.Interop/JavaConvert.cs @@ -79,7 +79,11 @@ params Type [] typeArguments if (JniHandleConverters.TryGetValue (target, out var converter)) return converter; if (target.IsArray) + // Type.GetElementType() does not preserve DAM annotations; the actual element type's + // constructors are preserved through the type hierarchy at the call site. +#pragma warning disable IL2072 return (h, t) => JNIEnv.GetArray (h, t, target.GetElementType ()); +#pragma warning restore IL2072 if (RuntimeFeature.TrimmableTypeMap) { var factoryConverter = TryGetFactoryBasedConverter (target); diff --git a/src/Mono.Android/Java.Lang/Object.cs b/src/Mono.Android/Java.Lang/Object.cs index 0942d917e57..3bc58f8c13a 100644 --- a/src/Mono.Android/Java.Lang/Object.cs +++ b/src/Mono.Android/Java.Lang/Object.cs @@ -186,7 +186,10 @@ internal static T? _GetObject< } [EditorBrowsable (EditorBrowsableState.Never)] - public T[]? ToArray() + public T[]? ToArray< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + T + >() { return JNIEnv.GetArray(Handle); }