From 7dae51c0f3d9344e27475ae0535c0829f8ffa1ac Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 29 Feb 2024 12:33:10 -0600 Subject: [PATCH] [Mono.Android] is now "trimming safe" Fixes: https://github.com/xamarin/xamarin-android/issues/5652 Each trimming problem listed below. ~~ AndroidEnvironment ~~ src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(373,19): error IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type. src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(379,22): error IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type. src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(342,20): error IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type. src\Mono.Android\Android.Runtime\AndroidEnvironment.cs(352,11): error IL2077: 'type' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicParameterlessConstructor' in call to 'System.Activator.CreateInstance(Type)'. The field 'Android.Runtime.AndroidEnvironment.httpMessageHandlerType' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. To fix these: * Use constant strings for calls like: Type.GetType ("System.Net.Http.HttpMessageHandler, System.Net.Http", throwOnError: false) Use `Type.IsAssignableFrom()` inline, and the trimmer now understands the code paths. ~~ JavaConvert ~~ src\Mono.Android\Java.Interop\JavaConvert.cs(223,12): error IL2091: 'TResult' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Java.Interop.JavaObjectExtensions._JavaCast(IJavaObject)'. The generic parameter 'T' of 'Java.Interop.JavaConvert.FromJavaObject(IJavaObject, out Boolean)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. src\Mono.Android\Java.Interop\JavaConvert.cs(254,12): error IL2067: 'resultType' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in call to 'Java.Interop.JavaObjectExtensions.JavaCast(IJavaObject, Type)'. The parameter 'targetType' of method 'Java.Interop.JavaConvert.FromJavaObject(IJavaObject, Type)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. src\Mono.Android\Java.Interop\JavaConvert.cs(67,14): error IL3050: Using member 'System.Type.MakeGenericType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime. src\Mono.Android\Java.Interop\JavaConvert.cs(73,14): error IL3050: Using member 'System.Type.MakeGenericType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime. src\Mono.Android\Java.Interop\JavaConvert.cs(79,14): error IL3050: Using member 'System.Type.MakeGenericType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime. Adding attributes fixed some of these. I suppressed warnings for: * `Type.MakeGenericType()`: `JavaDictionary<,>`, `JavaList<>`, and `JavaCollection<>` use DynamicDependency to preserve `FromJniHandle` method. * `Type.GetElementType()`: for calling `MyJavaObject[]` constructors, the `MarkJavaObjects` trimmer step should preserve these constructors. This trickled over to require more attributes for: * `AdapterView` * `ArrayAdapter` * `AsyncTask` * `JavaCollection` * `JavaDictionary` * `JavaList` * `JavaList` * `JavaObjectExtensions` * `JavaSet` * `SparseArray` * `System.Linq\Extensions` ~~ JNIEnv ~~ src\Mono.Android\Android.Runtime\JNIEnv.cs(810,38): error IL3050: Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available. src\Mono.Android\Android.Runtime\JNIEnv.cs(953,33): error IL3050: Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available. src\Mono.Android\Android.Runtime\JNIEnv.cs(1078,44): error IL3050: Using member 'System.Type.MakeArrayType()' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available. src\Mono.Android\Android.Runtime\JNIEnv.cs(1139,15): error IL2091: 'T' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Java.Interop.JavaConvert.FromJavaObject(IJavaObject)'. The generic parameter 'T' of 'Android.Runtime.JNIEnv.GetArray(Object[])' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. src\Mono.Android\Android.Runtime\JNIEnv.cs(1060,14): error IL3050: Using member 'System.Array.CreateInstance(Type, Int32)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available. src\Mono.Android\Android.Runtime\JNIEnv.cs(1065,14): error IL3050: Using member 'System.Array.CreateInstance(Type, Int32)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available. src\Mono.Android\Android.Runtime\JNIEnv.cs(1257,23): error IL2091: 'T' generic argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in 'Java.Interop.JavaConvert.FromJniHandle(nint, JniHandleOwnership)'. The generic parameter 'T' of 'Android.Runtime.JNIEnv.CopyObjectArray(nint, T[])' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. Adding some attributes fixed two of these. Suppress NativeAOT warnings, in source, for: * `Array.CreateInstance()` * `Type.MakeArrayType()` Link to an issue to fix in the future. --- .../trim-analyzers/trim-analyzers.props | 53 +++++++++++++++++++ src/Mono.Android/Android.OS/AsyncTask.cs | 11 +++- .../Android.Runtime/AndroidEnvironment.cs | 30 ++++++----- src/Mono.Android/Android.Runtime/JNIEnv.cs | 36 ++++++++++--- .../Android.Runtime/JavaCollection.cs | 16 +++++- .../Android.Runtime/JavaDictionary.cs | 11 +++- src/Mono.Android/Android.Runtime/JavaList.cs | 20 +++++-- src/Mono.Android/Android.Runtime/JavaSet.cs | 5 +- src/Mono.Android/Android.Util/SparseArray.cs | 6 ++- .../Android.Widget/AdapterView.cs | 6 ++- .../Android.Widget/ArrayAdapter.cs | 5 +- src/Mono.Android/Java.Interop/JavaConvert.cs | 49 +++++++++++++---- .../Java.Interop/JavaObjectExtensions.cs | 17 ++++-- src/Mono.Android/Mono.Android.csproj | 1 + src/Mono.Android/System.Linq/Extensions.cs | 12 ++++- 15 files changed, 234 insertions(+), 44 deletions(-) create mode 100644 build-tools/trim-analyzers/trim-analyzers.props diff --git a/build-tools/trim-analyzers/trim-analyzers.props b/build-tools/trim-analyzers/trim-analyzers.props new file mode 100644 index 00000000000..8000a12d74d --- /dev/null +++ b/build-tools/trim-analyzers/trim-analyzers.props @@ -0,0 +1,53 @@ + + + + + true + true + true + + true + + + $(WarningsAsErrors); + IL2000;IL2001;IL2002;IL2003;IL2004; + IL2005;IL2006;IL2007;IL2008;IL2009; + IL2010;IL2011;IL2012;IL2013;IL2014; + IL2015;IL2016;IL2017;IL2018;IL2019; + IL2020;IL2021;IL2022;IL2023;IL2024; + IL2025;IL2026;IL2027;IL2028;IL2029; + IL2030;IL2031;IL2032;IL2033;IL2034; + IL2035;IL2036;IL2037;IL2038;IL2039; + IL2040;IL2041;IL2042;IL2043;IL2044; + IL2045;IL2046;IL2047;IL2048;IL2049; + IL2050;IL2051;IL2052;IL2053;IL2054; + IL2055;IL2056;IL2057;IL2058;IL2059; + IL2060;IL2061;IL2062;IL2063;IL2064; + IL2065;IL2066;IL2067;IL2068;IL2069; + IL2070;IL2071;IL2072;IL2073;IL2074; + IL2075;IL2076;IL2077;IL2078;IL2079; + IL2080;IL2081;IL2082;IL2083;IL2084; + IL2085;IL2086;IL2087;IL2088;IL2089; + IL2090;IL2091;IL2092;IL2093;IL2094; + IL2095;IL2096;IL2097;IL2098;IL2099; + IL2100;IL2101;IL2102;IL2103;IL2104; + IL2105;IL2106;IL2107;IL2108;IL2109; + IL2110;IL2111;IL2112;IL2113;IL2114; + IL2115;IL2116;IL2117;IL2118;IL2119; + IL2120;IL2121;IL2122;IL2123;IL2124; + IL2125;IL2126;IL2127;IL2128;IL2129; + + + + $(WarningsAsErrors); + IL3050;IL3051;IL3052;IL3053;IL3054;IL3055;IL3056; + + + diff --git a/src/Mono.Android/Android.OS/AsyncTask.cs b/src/Mono.Android/Android.OS/AsyncTask.cs index ad5db1b113b..e66a162a145 100644 --- a/src/Mono.Android/Android.OS/AsyncTask.cs +++ b/src/Mono.Android/Android.OS/AsyncTask.cs @@ -9,7 +9,16 @@ namespace Android.OS { [global::System.Runtime.Versioning.ObsoletedOSPlatform ("android30.0")] [Register ("android/os/AsyncTask", DoNotGenerateAcw=true)] - public abstract class AsyncTask : AsyncTask { + public abstract class AsyncTask< + [DynamicallyAccessedMembers (Constructors)] + TParams, + [DynamicallyAccessedMembers (Constructors)] + TProgress, + [DynamicallyAccessedMembers (Constructors)] + TResult + > : AsyncTask { + + const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; static IntPtr java_class_handle; internal static IntPtr class_ref { diff --git a/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs b/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs index edfe42ec449..b8959b7e5d9 100644 --- a/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs +++ b/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs @@ -24,6 +24,7 @@ public static class AndroidEnvironment { static IX509TrustManager? sslTrustManager; static KeyStore? certStore; static object lock_ = new object (); + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] static Type? httpMessageHandlerType; static void SetupTrustManager () @@ -335,11 +336,16 @@ static IWebProxy GetDefaultProxy () [DynamicDependency (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, typeof (Xamarin.Android.Net.AndroidMessageHandler))] static object GetHttpMessageHandler () { + [UnconditionalSuppressMessage ("Trimming", "IL2057", Justification = "DynamicDependency should preserve AndroidMessageHandler.")] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + static Type TypeGetType (string typeName) => + Type.GetType (typeName, throwOnError: false); + if (httpMessageHandlerType is null) { var handlerTypeName = Environment.GetEnvironmentVariable ("XA_HTTP_CLIENT_HANDLER_TYPE")?.Trim (); Type? handlerType = null; if (!String.IsNullOrEmpty (handlerTypeName)) - handlerType = Type.GetType (handlerTypeName, throwOnError: false); + handlerType = TypeGetType (handlerTypeName); // We don't do any type checking or casting here to avoid dependency on System.Net.Http in Mono.Android.dll if (handlerType is null || !IsAcceptableHttpMessageHandlerType (handlerType)) { @@ -355,27 +361,27 @@ static object GetHttpMessageHandler () static bool IsAcceptableHttpMessageHandlerType (Type handlerType) { - if (Extends (handlerType, "System.Net.Http.HttpClientHandler, System.Net.Http")) { + if (Type.GetType ("System.Net.Http.HttpClientHandler, System.Net.Http", throwOnError: false) is Type httpClientHandlerType && + httpClientHandlerType.IsAssignableFrom (handlerType)) { // It's not possible to construct HttpClientHandler in this method because it would cause infinite recursion // as HttpClientHandler's constructor calls the GetHttpMessageHandler function Logger.Log (LogLevel.Warn, "MonoAndroid", $"The type {handlerType.AssemblyQualifiedName} cannot be used as the native HTTP handler because it is derived from System.Net.Htt.HttpClientHandler. Use a type that extends System.Net.Http.HttpMessageHandler instead."); return false; } - if (!Extends (handlerType, "System.Net.Http.HttpMessageHandler, System.Net.Http")) { - Logger.Log (LogLevel.Warn, "MonoAndroid", $"The type {handlerType.AssemblyQualifiedName} set as the default HTTP handler is invalid. Use a type that extends System.Net.Http.HttpMessageHandler."); - return false; + if (Type.GetType ("System.Net.Http.HttpMessageHandler, System.Net.Http", throwOnError: false) is Type httpMessageHandlerType && + httpMessageHandlerType.IsAssignableFrom (handlerType)) { + return true; } - return true; - } - - static bool Extends (Type handlerType, string baseTypeName) { - var baseType = Type.GetType (baseTypeName, throwOnError: false); - return baseType?.IsAssignableFrom (handlerType) ?? false; + // Was not an acceptable type + Logger.Log (LogLevel.Warn, "MonoAndroid", $"The type {handlerType.AssemblyQualifiedName} set as the default HTTP handler is invalid. Use a type that extends System.Net.Http.HttpMessageHandler."); + return false; } - static Type GetFallbackHttpMessageHandlerType (string typeName = "Xamarin.Android.Net.AndroidMessageHandler, Mono.Android") + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + static Type GetFallbackHttpMessageHandlerType () { + const string typeName = "Xamarin.Android.Net.AndroidMessageHandler, Mono.Android"; var handlerType = Type.GetType (typeName, throwOnError: false) ?? throw new InvalidOperationException ($"The {typeName} was not found. The type was probably linked away."); diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index 80f214a1454..d5b39708168 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -16,11 +16,27 @@ namespace Android.Runtime { public static partial class JNIEnv { + const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; + [ThreadStatic] static byte[]? mvid_bytes; public static IntPtr Handle => JniEnvironment.EnvironmentPointer; + static Array ArrayCreateInstance (Type elementType, int length) => + // FIXME: https://github.com/xamarin/xamarin-android/issues/8724 + // IL3050 disabled in source: if someone uses NativeAOT, they will get the warning. + #pragma warning disable IL3050 + Array.CreateInstance (elementType, length); + #pragma warning restore IL3050 + + static Type MakeArrayType (Type type) => + // FIXME: https://github.com/xamarin/xamarin-android/issues/8724 + // IL3050 disabled in source: if someone uses NativeAOT, they will get the warning. + #pragma warning disable IL3050 + type.MakeArrayType (); + #pragma warning restore IL3050 + internal static IntPtr IdentityHash (IntPtr v) { return JNIEnvInit.LocalRefsAreIndirect ? RuntimeNativeMethods._monodroid_get_identity_hash_code (Handle, v) : v; @@ -807,7 +823,7 @@ public static void CopyArray (IntPtr src, Array dest, Type? elementType = null) throw new ArgumentNullException ("dest"); if (elementType != null && elementType.IsValueType) - AssertCompatibleArrayTypes (src, elementType.MakeArrayType ()); + AssertCompatibleArrayTypes (src, MakeArrayType (elementType)); if (elementType != null && elementType.IsArray) { for (int i = 0; i < dest.Length; ++i) { @@ -950,7 +966,7 @@ public static void CopyArray (Array source, Type elementType, IntPtr dest) throw new ArgumentNullException ("elementType"); if (elementType.IsValueType) - AssertCompatibleArrayTypes (elementType.MakeArrayType (), dest); + AssertCompatibleArrayTypes (MakeArrayType (elementType), dest); Action converter = GetConverter (CopyManagedToNativeArray, elementType, dest); @@ -1057,12 +1073,12 @@ public static void CopyArray (T[] src, IntPtr dest) } } }, { typeof (IJavaObject), (type, source, len) => { - var r = Array.CreateInstance (type!, len); + var r = ArrayCreateInstance (type!, len); CopyArray (source, r, type); return r; } }, { typeof (Array), (type, source, len) => { - var r = Array.CreateInstance (type!, len); + var r = ArrayCreateInstance (type!, len); CopyArray (source, r, type); return r; } }, @@ -1075,7 +1091,7 @@ public static void CopyArray (T[] src, IntPtr dest) return null; if (element_type != null && element_type.IsValueType) - AssertCompatibleArrayTypes (array_ptr, element_type.MakeArrayType ()); + AssertCompatibleArrayTypes (array_ptr, MakeArrayType (element_type)); int cnt = _GetArrayLength (array_ptr); @@ -1130,7 +1146,10 @@ static int _GetArrayLength (IntPtr array_ptr) return ret; } - public static T[]? GetArray (Java.Lang.Object[] array) + public static T[]? GetArray< + [DynamicallyAccessedMembers (Constructors)] + T + > (Java.Lang.Object[] array) { if (array == null) return null; @@ -1244,7 +1263,10 @@ static IntPtr GetArrayElementClass(T[] values) return FindClass (elementType); } - public static void CopyObjectArray(IntPtr source, T[] destination) + public static void CopyObjectArray< + [DynamicallyAccessedMembers (Constructors)] + T + >(IntPtr source, T[] destination) { if (source == IntPtr.Zero) return; diff --git a/src/Mono.Android/Android.Runtime/JavaCollection.cs b/src/Mono.Android/Android.Runtime/JavaCollection.cs index 70660bc24a3..d14ef75766e 100644 --- a/src/Mono.Android/Android.Runtime/JavaCollection.cs +++ b/src/Mono.Android/Android.Runtime/JavaCollection.cs @@ -14,6 +14,8 @@ 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; @@ -148,6 +150,11 @@ internal Java.Lang.Object[] ToArray () // public void CopyTo (Array array, int array_index) { + [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "JavaCollection constructors are preserved by the MarkJavaObjects trimmer step.")] + [return: DynamicallyAccessedMembers (Constructors)] + static Type GetElementType (Array array) => + array.GetType ().GetElementType (); + if (array == null) throw new ArgumentNullException ("array"); if (array_index < 0) @@ -164,7 +171,7 @@ public void CopyTo (Array array, int array_index) JavaConvert.FromJniHandle ( JNIEnv.GetObjectArrayElement (lrefArray, i), JniHandleOwnership.TransferLocalRef, - array.GetType ().GetElementType ()), + GetElementType (array)), array_index + i); JNIEnv.DeleteLocalRef (lrefArray); } @@ -202,8 +209,13 @@ public static IntPtr ToLocalJniHandle (ICollection? items) } } + // Preserve FromJniHandle + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] [Register ("java/util/Collection", DoNotGenerateAcw=true)] - public sealed class JavaCollection : JavaCollection, ICollection { + public sealed class JavaCollection< + [DynamicallyAccessedMembers (Constructors)] + T + > : JavaCollection, ICollection { public JavaCollection (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) diff --git a/src/Mono.Android/Android.Runtime/JavaDictionary.cs b/src/Mono.Android/Android.Runtime/JavaDictionary.cs index 80656dfc58c..cf6469f7306 100644 --- a/src/Mono.Android/Android.Runtime/JavaDictionary.cs +++ b/src/Mono.Android/Android.Runtime/JavaDictionary.cs @@ -12,6 +12,8 @@ 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; @@ -395,8 +397,15 @@ public static IntPtr ToLocalJniHandle (IDictionary? dictionary) // instantiates a type we don't know at this time, so we have no information about the exceptions // it may throw. // + // Preserve FromJniHandle + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] [Register ("java/util/HashMap", DoNotGenerateAcw=true)] - public class JavaDictionary : JavaDictionary, IDictionary { + public class JavaDictionary< + [DynamicallyAccessedMembers (Constructors)] + K, + [DynamicallyAccessedMembers (Constructors)] + V + > : JavaDictionary, IDictionary { [Register (".ctor", "()V", "")] public JavaDictionary () diff --git a/src/Mono.Android/Android.Runtime/JavaList.cs b/src/Mono.Android/Android.Runtime/JavaList.cs index 980b01f386e..bea4e9b0871 100644 --- a/src/Mono.Android/Android.Runtime/JavaList.cs +++ b/src/Mono.Android/Android.Runtime/JavaList.cs @@ -10,6 +10,7 @@ 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); // @@ -23,7 +24,10 @@ public partial class JavaList : Java.Lang.Object, System.Collections.IList { // // https://developer.android.com/reference/java/util/List.html?hl=en#get(int) // - internal unsafe object? InternalGet (int location, Type? targetType = null) + internal unsafe object? InternalGet ( + int location, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType = null) { const string id = "get.(I)Ljava/lang/Object;"; JniObjectReference obj; @@ -266,6 +270,11 @@ public unsafe bool Contains (object? item) public void CopyTo (Array array, int array_index) { + [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "JavaList constructors are preserved by the MarkJavaObjects trimmer step.")] + [return: DynamicallyAccessedMembers (Constructors)] + static Type GetElementType (Array array) => + array.GetType ().GetElementType (); + if (array == null) throw new ArgumentNullException ("array"); if (array_index < 0) @@ -273,7 +282,7 @@ public void CopyTo (Array array, int array_index) if (array.Length < array_index + Count) throw new ArgumentException ("array"); - var targetType = array.GetType ().GetElementType (); + var targetType = GetElementType (array); int c = Count; for (int i = 0; i < c; i++) array.SetValue (InternalGet (i, targetType), array_index + i); @@ -672,8 +681,13 @@ public virtual Java.Lang.Object [] ToArray () #endregion } + // Preserve FromJniHandle + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] [Register ("java/util/ArrayList", DoNotGenerateAcw=true)] - public class JavaList : JavaList, IList { + public class JavaList< + [DynamicallyAccessedMembers (Constructors)] + T + > : JavaList, IList { // // Exception audit: diff --git a/src/Mono.Android/Android.Runtime/JavaSet.cs b/src/Mono.Android/Android.Runtime/JavaSet.cs index c141e1058a4..b84b040289a 100644 --- a/src/Mono.Android/Android.Runtime/JavaSet.cs +++ b/src/Mono.Android/Android.Runtime/JavaSet.cs @@ -268,7 +268,10 @@ public static IntPtr ToLocalJniHandle (ICollection? items) [Register ("java/util/HashSet", DoNotGenerateAcw=true)] // java.util.HashSet allows null - public class JavaSet : JavaSet, ICollection { + public class JavaSet< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + T + > : JavaSet, ICollection { // // Exception audit: diff --git a/src/Mono.Android/Android.Util/SparseArray.cs b/src/Mono.Android/Android.Util/SparseArray.cs index 0fcd05f31f6..2a895fb7307 100644 --- a/src/Mono.Android/Android.Util/SparseArray.cs +++ b/src/Mono.Android/Android.Util/SparseArray.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using Android.Runtime; @@ -7,7 +8,10 @@ namespace Android.Util { [Register ("android/util/SparseArray", DoNotGenerateAcw=true)] - public partial class SparseArray : SparseArray + public partial class SparseArray< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + E + > : SparseArray { public SparseArray () { diff --git a/src/Mono.Android/Android.Widget/AdapterView.cs b/src/Mono.Android/Android.Widget/AdapterView.cs index 5ad2293d8e3..7689b0c39e3 100644 --- a/src/Mono.Android/Android.Widget/AdapterView.cs +++ b/src/Mono.Android/Android.Widget/AdapterView.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Android.Views; using JLO = Java.Lang.Object; @@ -49,7 +50,10 @@ public event EventHandler ItemSelectionCleared { } [Register ("android/widget/AdapterView", DoNotGenerateAcw=true)] - public abstract class AdapterView : AdapterView where T : IAdapter { + public abstract class AdapterView< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + T + > : AdapterView where T : IAdapter { public AdapterView (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) diff --git a/src/Mono.Android/Android.Widget/ArrayAdapter.cs b/src/Mono.Android/Android.Widget/ArrayAdapter.cs index cf35e67fc07..e54ce8d7c94 100644 --- a/src/Mono.Android/Android.Widget/ArrayAdapter.cs +++ b/src/Mono.Android/Android.Widget/ArrayAdapter.cs @@ -8,7 +8,10 @@ namespace Android.Widget { [Register ("android/widget/ArrayAdapter", DoNotGenerateAcw=true)] - public partial class ArrayAdapter : ArrayAdapter { + public partial class ArrayAdapter< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + T + > : ArrayAdapter { public ArrayAdapter (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) diff --git a/src/Mono.Android/Java.Interop/JavaConvert.cs b/src/Mono.Android/Java.Interop/JavaConvert.cs index c98bc8cd2f2..f4630ac54c5 100644 --- a/src/Mono.Android/Java.Interop/JavaConvert.cs +++ b/src/Mono.Android/Java.Interop/JavaConvert.cs @@ -10,6 +10,7 @@ namespace Java.Interop { static class JavaConvert { + const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; static Dictionary> JniHandleConverters = new Dictionary>() { { typeof (bool), (handle, transfer) => { @@ -56,6 +57,17 @@ static class JavaConvert { static Func? GetJniHandleConverter (Type? target) { + const string justification = "JavaDictionary<,>, JavaList<>, and JavaCollection<> use DynamicallyAccessedMembers for PublicMethods to preserve FromJniHandle()."; + [UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = justification)] + [UnconditionalSuppressMessage ("Trimming", "IL2068", Justification = justification)] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static Type MakeGenericType (Type type, params Type [] typeArguments) => + // FIXME: https://github.com/xamarin/xamarin-android/issues/8724 + // IL3050 disabled in source: if someone uses NativeAOT, they will get the warning. + #pragma warning disable IL3050 + type.MakeGenericType (typeArguments); + #pragma warning restore IL3050 + if (target == null) return null; @@ -64,19 +76,19 @@ static class JavaConvert { if (target.IsArray) return (h, t) => JNIEnv.GetArray (h, t, target.GetElementType ()); if (target.IsGenericType && target.GetGenericTypeDefinition() == typeof (IDictionary<,>)) { - Type t = typeof (JavaDictionary<,>).MakeGenericType (target.GetGenericArguments ()); + Type t = MakeGenericType (typeof (JavaDictionary<,>), target.GetGenericArguments ()); return GetJniHandleConverterForType (t); } if (typeof (IDictionary).IsAssignableFrom (target)) return (h, t) => JavaDictionary.FromJniHandle (h, t); if (target.IsGenericType && target.GetGenericTypeDefinition() == typeof (IList<>)) { - Type t = typeof (JavaList<>).MakeGenericType (target.GetGenericArguments ()); + Type t = MakeGenericType (typeof (JavaList<>), target.GetGenericArguments ()); return GetJniHandleConverterForType (t); } if (typeof (IList).IsAssignableFrom (target)) return (h, t) => JavaList.FromJniHandle (h, t); if (target.IsGenericType && target.GetGenericTypeDefinition() == typeof (ICollection<>)) { - Type t = typeof (JavaCollection<>).MakeGenericType (target.GetGenericArguments ()); + Type t = MakeGenericType (typeof (JavaCollection<>), target.GetGenericArguments ()); return GetJniHandleConverterForType (t); } if (typeof (ICollection).IsAssignableFrom (target)) @@ -92,13 +104,19 @@ static Func GetJniHandleConverterForType ([D typeof (Func), m); } - public static T? FromJniHandle(IntPtr handle, JniHandleOwnership transfer) + public static T? FromJniHandle< + [DynamicallyAccessedMembers (Constructors)] + T + >(IntPtr handle, JniHandleOwnership transfer) { bool set; return FromJniHandle(handle, transfer, out set); } - public static T? FromJniHandle(IntPtr handle, JniHandleOwnership transfer, out bool set) + public static T? FromJniHandle< + [DynamicallyAccessedMembers (Constructors)] + T + >(IntPtr handle, JniHandleOwnership transfer, out bool set) { if (handle == IntPtr.Zero) { set = false; @@ -133,7 +151,11 @@ static Func GetJniHandleConverterForType ([D return (T?) Convert.ChangeType (v, typeof (T), CultureInfo.InvariantCulture); } - public static object? FromJniHandle (IntPtr handle, JniHandleOwnership transfer, Type? targetType = null) + public static object? FromJniHandle ( + IntPtr handle, + JniHandleOwnership transfer, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType = null) { if (handle == IntPtr.Zero) { return null; @@ -206,13 +228,19 @@ static Func GetJniHandleConverterForType ([D return null; } - public static T? FromJavaObject(IJavaObject? value) + public static T? FromJavaObject< + [DynamicallyAccessedMembers (Constructors)] + T + >(IJavaObject? value) { bool set; return FromJavaObject(value, out set); } - public static T? FromJavaObject(IJavaObject? value, out bool set) + public static T? FromJavaObject< + [DynamicallyAccessedMembers (Constructors)] + T + >(IJavaObject? value, out bool set) { if (value == null) { set = false; @@ -245,7 +273,10 @@ static Func GetJniHandleConverterForType ([D return (T) Convert.ChangeType (value, typeof (T), CultureInfo.InvariantCulture); } - public static object? FromJavaObject (IJavaObject value, Type? targetType = null) + public static object? FromJavaObject ( + IJavaObject value, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType = null) { if (value == null) return null; diff --git a/src/Mono.Android/Java.Interop/JavaObjectExtensions.cs b/src/Mono.Android/Java.Interop/JavaObjectExtensions.cs index 5b083487c5b..345c664c61d 100644 --- a/src/Mono.Android/Java.Interop/JavaObjectExtensions.cs +++ b/src/Mono.Android/Java.Interop/JavaObjectExtensions.cs @@ -17,7 +17,10 @@ public static JavaCollection ToInteroperableCollection (this ICollection instanc } [Obsolete ("Use Android.Runtime.JavaCollection.ToLocalJniHandle()")] - public static JavaCollection ToInteroperableCollection (this ICollection instance) + public static JavaCollection ToInteroperableCollection< + [DynamicallyAccessedMembers (Constructors)] + T + > (this ICollection instance) { return instance is JavaCollection ? (JavaCollection) instance : new JavaCollection (instance); } @@ -29,7 +32,10 @@ public static JavaList ToInteroperableCollection (this IList instance) } [Obsolete ("Use Android.Runtime.JavaList.ToLocalJniHandle()")] - public static JavaList ToInteroperableCollection (this IList instance) + public static JavaList ToInteroperableCollection< + [DynamicallyAccessedMembers (Constructors)] + T + > (this IList instance) { return instance is JavaList ? (JavaList) instance : new JavaList (instance); } @@ -41,7 +47,12 @@ public static JavaDictionary ToInteroperableCollection (this IDictionary instanc } [Obsolete ("Use Android.Runtime.JavaDictionary.ToLocalJniHandle()")] - public static JavaDictionary ToInteroperableCollection (this IDictionary instance) + public static JavaDictionary ToInteroperableCollection< + [DynamicallyAccessedMembers (Constructors)] + K, + [DynamicallyAccessedMembers (Constructors)] + V + > (this IDictionary instance) { return instance is JavaDictionary ? (JavaDictionary) instance : new JavaDictionary (instance); } diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index 006ebd5eb41..4ed01cacea2 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -3,6 +3,7 @@ + $(DotNetTargetFramework) diff --git a/src/Mono.Android/System.Linq/Extensions.cs b/src/Mono.Android/System.Linq/Extensions.cs index ac555e17949..c8bb0a4a740 100644 --- a/src/Mono.Android/System.Linq/Extensions.cs +++ b/src/Mono.Android/System.Linq/Extensions.cs @@ -9,6 +9,8 @@ namespace System.Linq { public static class Extensions { + const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; + static IntPtr id_next; static Extensions () @@ -40,7 +42,10 @@ internal static IEnumerator ToEnumerator_Dispose (this Java.Util.IIterator sourc } } - public static IEnumerable ToEnumerable (this Java.Lang.IIterable source) + public static IEnumerable ToEnumerable< + [DynamicallyAccessedMembers (Constructors)] + T + > (this Java.Lang.IIterable source) { if (source == null) throw new ArgumentNullException ("source"); @@ -52,7 +57,10 @@ internal static IEnumerator ToEnumerator_Dispose (this Java.Util.IIterator sourc } } - internal static IEnumerator ToEnumerator_Dispose (this Java.Util.IIterator source) + internal static IEnumerator ToEnumerator_Dispose< + [DynamicallyAccessedMembers (Constructors)] + T + > (this Java.Util.IIterator source) { using (source) while (source.HasNext) {