Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Java.Interop] address some "easy" trimmer warnings (#1184)
Context: #1157 We want to enable `$(IsAotCompatible)`=true, so we can identify and fix trimmer warnings within `Java.Interop.dll`. (Then later work our way "up the stack", fixing trimmer warnings within `Mono.Android.dll` and `Microsoft.Maui.dll` and…) On the path to enabling `$(IsAotCompatible)`=true, we can enable some settings to get started: <IsTrimmable>true</IsTrimmable> <EnableSingleFileAnalyzer>true</EnableSingleFileAnalyzer> <EnableAotAnalyzer>true</EnableAotAnalyzer> This opts into the analyzers without declaring that the assembly is fully AOT-compatible. Starting out, I got 33 warnings: this is an attempt to address the ones that don't require too much thinking. Unfortunately, solving one warning likely will create dozens more -- as you have to update all callers. This results in 24 warnings remaining. Since `Release` builds have `$(TreatWarningsAsErrors)`, I will wait to enable the analyzers until all warnings are addressed. ~~ Example Warnings ~~ **`System.Linq.Expression` usage:** `JniRuntime.JniValueManager.CreateParameterFromManagedExpression()` /* 638 */ partial class JavaPeerableValueMarshaler { /* 667 */ public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) /* 668 */ { /* 669 */ var r = CreateIntermediaryExpressionFromManagedExpression (context, sourceValue); /* 670 */ var h = Expression.Variable (typeof (IntPtr), sourceValue.Name + "_handle"); /* 671 */ context.LocalVariables.Add (h); /* 672 */ context.CreationStatements.Add (Expression.Assign (h, Expression.Property (r, "Handle"))); /* 674 */ return h; /* 675 */ } /* 710 */ } emits an IL2026: src\Java.Interop\Java.Interop\JniRuntime.JniValueManager.cs(672,58): warning IL2026: Using member 'System.Linq.Expressions.Expression.Property(Expression, String)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed. I updated this with: partial class JniValueMarshaler { internal const string ExpressionRequiresUnreferencedCode = "System.Linq.Expression usage may trim away required code."; [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] public virtual Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) => … } partial class JavaPeerableValueMarshaler /* indirectly inherits JniValueMarshaler */ { [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)] public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize) => … } **`Type.GetNestedType()` usage:** `JniRuntime.JniTypeManager.TryLoadJniMarshalMethods()`: /* 82 */ partial class JniRuntime.JniTypeManager { /* 445 */ bool TryLoadJniMarshalMethods (JniType nativeClass, Type type, string? methods) /* 446 */ { /* 447 */ var marshalType = type?.GetNestedType ("__<$>_jni_marshal_methods", BindingFlags.NonPublic); emits an IL2070 warning: src\Java.Interop\Java.Interop\JniRuntime.JniTypeManager.cs(447,28): warning IL2070: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.NonPublicNestedTypes' in call to 'System.Type.GetNestedType(String, BindingFlags)'. The parameter 'type' of method 'Java.Interop.JniRuntime.JniTypeManager.TryLoadJniMarshalMethods(JniType, Type, String)' 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. I updated this with: partial class JniRuntime.JniTypeManager { bool TryLoadJniMarshalMethods ( JniType nativeClass, [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] Type type, string? methods) => … **`Activator.CreateInstance()` usage:** `JniRuntime.JniValueManager.GetValueMarshaler()`: /* 50 */ partial class JniRuntime.JniValueManager { /* 530 */ public JniValueMarshaler GetValueMarshaler (Type type) /* 531 */ { /* 541 */ if (marshalerAttr != null) /* 542 */ return (JniValueMarshaler) Activator.CreateInstance (marshalerAttr.MarshalerType)!; /* 591 */ } /* 612 */ } emits an IL2072 warning: src\Java.Interop\Java.Interop\JniRuntime.JniValueManager.cs(542,33): warning IL2072: 'type' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicParameterlessConstructor' in call to 'System.Activator.CreateInstance(Type)'. The return value of method 'Java.Interop.JniValueMarshalerAttribute.MarshalerType.get' 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. I updated this with: partial class JniRuntime.JniValueManager { public JniValueMarshaler GetValueMarshaler ( [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] Type type) => … } partial class JniValueMarshalerAttribute { public Type MarshalerType { [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] get; } } ~~ Code that Actually Changed ~~ As I added more attributes, these snowballed into more and more warnings! I eventually had to make `JniRuntime.JniValueManager.GetPeerType()` look like: partial class JniRuntime.JniValueManager { internal const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; [return: DynamicallyAccessedMembers (Constructors)] static Type GetPeerType ([DynamicallyAccessedMembers Constructors)] Type type) => … } The analyzer was not able to understand code like: partial class JniRuntime.JniValueManager { static readonly KeyValuePair<Type, Type>[] PeerTypeMappings = new []{ new KeyValuePair<Type, Type>(typeof (object), typeof (JavaObject)), new KeyValuePair<Type, Type>(typeof (IJavaPeerable), typeof (JavaObject)), new KeyValuePair<Type, Type>(typeof (Exception), typeof (JavaException)), }; static Type GetPeerType (Type type) { foreach (var m in PeerTypeMappings) { if (m.Key == type) return m.Value; } } } Simply removing the `PeerTypeMappings` array and using `if` statements solved the warnings. This may be the only real code change if any.
- Loading branch information