From 7d1e7057cf4b0adcf65e7064186326dafce11b72 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 14 Feb 2024 13:32:07 -0600 Subject: [PATCH] [Java.Interop] ignore remaining trimming warnings (#1190) Fixes: https://github.com/xamarin/java.interop/issues/1157 The remaining trimmer warnings are around usage of System.Reflection such as: * Creating an `Invoker` type from an original type via `Type.Assembly.GetType()`, within `JniRuntime.JniValueManager.GetInvokerType()`. * Create a `JniValueMarshaler` corresponding to `ParameterInfo.ParameterType` in `JniRuntime.JniMarshalMemberBuilder.GetParameterMarshaler()`. * Create a value corresponding to `ParameterInfo.ParameterType` in `ManagedPeer.GetValues()`. * Use `typeof(JavaObjectArray<>).MakeGenericType()` in `JniRuntime.JniTypeManager.GetTypes(JniTypeSignature)`. * Use `Type.MakeArrayType()` in `JniRuntime.JniTypeManager.GetTypes(JniTypeSignature)`. The trimming warnings themselves all indicate that these types "might be trimmed", in that nothing explicitly preserves them. However, we have a plethora of custom trimmer steps that work to preserve these types involved in Java interop, such as: * https://github.com/xamarin/xamarin-android/blob/71b6fcc92f9e86246e0fe575bc1f44bbf73c3132/src/Microsoft.Android.Sdk.ILLink/PreserveApplications.cs * https://github.com/xamarin/xamarin-android/blob/71b6fcc92f9e86246e0fe575bc1f44bbf73c3132/src/Microsoft.Android.Sdk.ILLink/PreserveExportedTypes.cs * https://github.com/xamarin/xamarin-android/blob/71b6fcc92f9e86246e0fe575bc1f44bbf73c3132/src/Microsoft.Android.Sdk.ILLink/PreserveJavaExceptions.cs * https://github.com/xamarin/xamarin-android/blob/71b6fcc92f9e86246e0fe575bc1f44bbf73c3132/src/Microsoft.Android.Sdk.ILLink/PreserveJavaInterfaces.cs * https://github.com/xamarin/xamarin-android/blob/71b6fcc92f9e86246e0fe575bc1f44bbf73c3132/src/Microsoft.Android.Sdk.ILLink/PreserveRegistrations.cs One day, in a perfect world, we could remove these trimmer steps and completely rely on the trimmer's warnings & attributing mechanisms. That day doesn't have to be today -- as our goal is begin surfacing trimmer warnings to users in their own code and dependencies. With these warnings ignored, we can fully enable analyzers with: true true We can then remove: [assembly: AssemblyMetadata ("IsTrimmable", "True")] As the MSBuild property does this for us, in addition to enabling analyzers. I don't think we should enable `$(IsAotCompatible)` quite yet, as `Java.Interop.dll` likely *won't work* yet on NativeAOT, and this places metadata that says an assembly is supported. We should just use the `$(EnableAotAnalyzer)` analyzers for now, so we don't introduce new issues. --- src/Java.Interop/Java.Interop.csproj | 5 ++- .../JniRuntime.JniMarshalMemberBuilder.cs | 8 ++++- .../Java.Interop/JniRuntime.JniTypeManager.cs | 23 +++++++++--- .../JniRuntime.JniValueManager.cs | 36 ++++++++++++++++--- .../JniValueMarshalerAttribute.cs | 9 +++-- src/Java.Interop/Java.Interop/ManagedPeer.cs | 7 +++- src/Java.Interop/Properties/AssemblyInfo.cs | 1 - 7 files changed, 71 insertions(+), 18 deletions(-) diff --git a/src/Java.Interop/Java.Interop.csproj b/src/Java.Interop/Java.Interop.csproj index 51bb3a90d..80eb91aee 100644 --- a/src/Java.Interop/Java.Interop.csproj +++ b/src/Java.Interop/Java.Interop.csproj @@ -15,9 +15,8 @@ enable true true - - - + true + true NU1702 diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniMarshalMemberBuilder.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniMarshalMemberBuilder.cs index e52bae71b..e2c6047cf 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniMarshalMemberBuilder.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniMarshalMemberBuilder.cs @@ -152,6 +152,12 @@ string GetTypeSignature (ParameterInfo p) public JniValueMarshaler GetParameterMarshaler (ParameterInfo parameter) { + // Activator.CreateInstance requires DynamicallyAccessedMemberTypes.PublicParameterlessConstructor + // GetValueMarshaler requires DynamicallyAccessedMemberTypes.Interfaces + [UnconditionalSuppressMessage ("Trimming", "IL2072", Justification = "JniValueMarshalerAttribute is decorated with [DynamicallyAccessedMembers]")] + static JniValueMarshaler GetValueMarshaler (JniValueManager manager, ParameterInfo parameter) => + manager.GetValueMarshaler (parameter.ParameterType); + if (parameter.ParameterType == typeof (IntPtr)) return IntPtrValueMarshaler.Instance; @@ -164,7 +170,7 @@ public JniValueMarshaler GetParameterMarshaler (ParameterInfo parameter) if (attr != null) { return (JniValueMarshaler) Activator.CreateInstance (attr.MarshalerType)!; } - return Runtime.ValueManager.GetValueMarshaler (parameter.ParameterType); + return GetValueMarshaler (Runtime.ValueManager, parameter); } // Heuristic: if first two parameters are IntPtr, this is a "direct" wrapper. diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs index 12bbff89b..6e4867192 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs @@ -269,8 +269,18 @@ protected virtual IEnumerable GetSimpleReferences (Type type) static readonly string[] EmptyStringArray = Array.Empty (); static readonly Type[] EmptyTypeArray = Array.Empty (); + const string NotUsedInAndroid = "This code path is not used in Android projects."; + // FIXME: https://github.com/xamarin/java.interop/issues/1192 + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = NotUsedInAndroid)] + static Type MakeArrayType (Type type) => type.MakeArrayType (); + // FIXME: https://github.com/xamarin/java.interop/issues/1192 + [UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = NotUsedInAndroid)] + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = NotUsedInAndroid)] + static Type MakeGenericType (Type type, Type arrayType) => type.MakeGenericType (arrayType); + + [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Types returned here should be preserved via other means.")] [return: DynamicallyAccessedMembers (MethodsConstructorsInterfaces)] public Type? GetType (JniTypeSignature typeSignature) { @@ -309,7 +319,7 @@ IEnumerable CreateGetTypesEnumerator (JniTypeSignature typeSignature) var rank = typeSignature.ArrayRank; var arrayType = type; while (rank-- > 0) { - arrayType = typeof (JavaObjectArray<>).MakeGenericType (arrayType); + arrayType = MakeGenericType (typeof (JavaObjectArray<>), arrayType); } yield return arrayType; } @@ -318,7 +328,7 @@ IEnumerable CreateGetTypesEnumerator (JniTypeSignature typeSignature) var rank = typeSignature.ArrayRank; var arrayType = type; while (rank-- > 0) { - arrayType = arrayType.MakeArrayType (); + arrayType = MakeArrayType (arrayType); } yield return arrayType; } @@ -341,14 +351,14 @@ IEnumerable GetPrimitiveArrayTypesForSimpleReference (JniTypeSignature typ var rank = typeSignature.ArrayRank-1; var arrayType = t; while (rank-- > 0) { - arrayType = typeof (JavaObjectArray<>).MakeGenericType (arrayType); + arrayType = MakeGenericType (typeof (JavaObjectArray<>), arrayType); } yield return arrayType; rank = typeSignature.ArrayRank-1; arrayType = t; while (rank-- > 0) { - arrayType = arrayType.MakeArrayType (); + arrayType = MakeArrayType (arrayType); } yield return arrayType; } @@ -462,6 +472,11 @@ protected bool TryRegisterNativeMembers ( static Type [] registerMethodParameters = new Type [] { typeof (JniNativeMethodRegistrationArguments) }; + // https://github.com/xamarin/xamarin-android/blob/5472eec991cc075e4b0c09cd98a2331fb93aa0f3/src/Microsoft.Android.Sdk.ILLink/PreserveRegistrations.cs#L85 + const string MarshalMethods = "'jni_marshal_methods' is preserved by the PreserveRegistrations trimmer step."; + + [UnconditionalSuppressMessage ("Trimming", "IL2072", Justification = MarshalMethods)] + [UnconditionalSuppressMessage ("Trimming", "IL2075", Justification = MarshalMethods)] bool TryLoadJniMarshalMethods ( JniType nativeClass, [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index 91e862dc7..181469f6d 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -359,18 +359,36 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) static Type? GetInvokerType (Type type) { const string suffix = "Invoker"; + + // https://github.com/xamarin/xamarin-android/blob/5472eec991cc075e4b0c09cd98a2331fb93aa0f3/src/Microsoft.Android.Sdk.ILLink/MarkJavaObjects.cs#L176-L186 + const string assemblyGetTypeMessage = "'Invoker' types are preserved by the MarkJavaObjects trimmer step."; + const string makeGenericTypeMessage = "Generic 'Invoker' types are preserved by the MarkJavaObjects trimmer step."; + + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = assemblyGetTypeMessage)] + [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = assemblyGetTypeMessage)] + [return: DynamicallyAccessedMembers (Constructors)] + static Type? AssemblyGetType (Assembly assembly, string typeName) => + assembly.GetType (typeName); + + // FIXME: https://github.com/xamarin/java.interop/issues/1192 + [UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = makeGenericTypeMessage)] + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = makeGenericTypeMessage)] + [return: DynamicallyAccessedMembers (Constructors)] + static Type MakeGenericType (Type type, Type [] arguments) => + type.MakeGenericType (arguments); + Type[] arguments = type.GetGenericArguments (); if (arguments.Length == 0) - return type.Assembly.GetType (type + suffix); + return AssemblyGetType (type.Assembly, type + suffix); Type definition = type.GetGenericTypeDefinition (); int bt = definition.FullName!.IndexOf ("`", StringComparison.Ordinal); if (bt == -1) throw new NotSupportedException ("Generic type doesn't follow generic type naming convention! " + type.FullName); - Type? suffixDefinition = definition.Assembly.GetType ( + Type? suffixDefinition = AssemblyGetType (definition.Assembly, definition.FullName.Substring (0, bt) + suffix + definition.FullName.Substring (bt)); if (suffixDefinition == null) return null; - return suffixDefinition.MakeGenericType (arguments); + return MakeGenericType (suffixDefinition, arguments); } public object? CreateValue ( @@ -634,9 +652,17 @@ public JniValueMarshaler GetValueMarshaler ( static JniValueMarshaler GetObjectArrayMarshaler (Type elementType) { + const string makeGenericMethodMessage = "This code path is not used in Android projects."; + + // FIXME: https://github.com/xamarin/java.interop/issues/1192 + [UnconditionalSuppressMessage ("Trimming", "IL2060", Justification = makeGenericMethodMessage)] + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = makeGenericMethodMessage)] + static MethodInfo MakeGenericMethod (MethodInfo method, Type type) => + method.MakeGenericMethod (type); + Func indirect = GetObjectArrayMarshalerHelper; - var reifiedMethodInfo = indirect.Method.GetGenericMethodDefinition () - .MakeGenericMethod (elementType); + var reifiedMethodInfo = MakeGenericMethod ( + indirect.Method.GetGenericMethodDefinition (), elementType); Func direct = (Func) Delegate.CreateDelegate (typeof (Func), reifiedMethodInfo); return direct (); } diff --git a/src/Java.Interop/Java.Interop/JniValueMarshalerAttribute.cs b/src/Java.Interop/Java.Interop/JniValueMarshalerAttribute.cs index f4869fcf1..b674094be 100644 --- a/src/Java.Interop/Java.Interop/JniValueMarshalerAttribute.cs +++ b/src/Java.Interop/Java.Interop/JniValueMarshalerAttribute.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable using System; using System.Diagnostics.CodeAnalysis; @@ -8,13 +8,16 @@ namespace Java.Interop { [AttributeUsage (Targets, AllowMultiple=false)] public class JniValueMarshalerAttribute : Attribute { + const DynamicallyAccessedMemberTypes ParameterlessConstructorsInterfaces = DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.Interfaces; const AttributeTargets Targets = AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Parameter | AttributeTargets.ReturnValue; - public JniValueMarshalerAttribute (Type marshalerType) + public JniValueMarshalerAttribute ( + [DynamicallyAccessedMembers (ParameterlessConstructorsInterfaces)] + Type marshalerType) { if (marshalerType == null) throw new ArgumentNullException (nameof (marshalerType)); @@ -27,7 +30,7 @@ public JniValueMarshalerAttribute (Type marshalerType) } public Type MarshalerType { - [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + [return: DynamicallyAccessedMembers (ParameterlessConstructorsInterfaces)] get; } } diff --git a/src/Java.Interop/Java.Interop/ManagedPeer.cs b/src/Java.Interop/Java.Interop/ManagedPeer.cs index f3874b635..763e439f2 100644 --- a/src/Java.Interop/Java.Interop/ManagedPeer.cs +++ b/src/Java.Interop/Java.Interop/ManagedPeer.cs @@ -229,6 +229,11 @@ static List[] GetConstructorCandidateParameterTypes (string signature) static object?[]? GetValues (JniRuntime runtime, JniObjectReference values, ConstructorInfo cinfo) { + // https://github.com/xamarin/xamarin-android/blob/5472eec991cc075e4b0c09cd98a2331fb93aa0f3/src/Microsoft.Android.Sdk.ILLink/MarkJavaObjects.cs#L51-L132 + [UnconditionalSuppressMessage ("Trimming", "IL2072", Justification = "Constructors are preserved by the MarkJavaObjects trimmer step.")] + static object? ValueManagerGetValue (JniRuntime runtime, ref JniObjectReference value, ParameterInfo parameter) => + runtime.ValueManager.GetValue (ref value, JniObjectReferenceOptions.CopyAndDispose, parameter.ParameterType); + if (!values.IsValid) return null; @@ -240,7 +245,7 @@ static List[] GetConstructorCandidateParameterTypes (string signature) var pvalues = new object? [len]; for (int i = 0; i < len; ++i) { var n_value = JniEnvironment.Arrays.GetObjectArrayElement (values, i); - var value = runtime.ValueManager.GetValue (ref n_value, JniObjectReferenceOptions.CopyAndDispose, parameters [i].ParameterType); + var value = ValueManagerGetValue (runtime, ref n_value, parameters [i]); pvalues [i] = value; } diff --git a/src/Java.Interop/Properties/AssemblyInfo.cs b/src/Java.Interop/Properties/AssemblyInfo.cs index b32e4cf93..01afd8e26 100644 --- a/src/Java.Interop/Properties/AssemblyInfo.cs +++ b/src/Java.Interop/Properties/AssemblyInfo.cs @@ -6,7 +6,6 @@ [assembly: AssemblyDescription ("")] [assembly: AssemblyCulture ("")] [assembly: AssemblyTrademark ("Microsoft Corporation")] -[assembly: AssemblyMetadata ("IsTrimmable", "True")] [assembly: InternalsVisibleTo ( "Java.Interop.GenericMarshaler, PublicKey=" +