Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mono.Android] is now "trimming safe" #8778

Merged
merged 6 commits into from
Mar 13, 2024

Conversation

jonathanpeppers
Copy link
Member

@jonathanpeppers jonathanpeppers commented Feb 29, 2024

Fixes: #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.

There is a problem here if either of these are used with a custom type:

  • $(AndroidHttpClientHandlerType)
  • $XA_HTTP_CLIENT_HANDLER_TYPE

This is a rarely used feature, I found one usage in GitHub:

https://github.com/search?q=%3CAndroidHttpClientHandlerType%3E+NOT+Xamarin.Android.Net+NOT+System.Net.Http+NOT+Default&type=code

Filed an issue to fix this in the future:

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<TResult>(IJavaObject)'. The generic parameter 'T' of 'Java.Interop.JavaConvert.FromJavaObject<T>(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<T>(IJavaObject)'. The generic parameter 'T' of 'Android.Runtime.JNIEnv.GetArray<T>(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<T>(nint, JniHandleOwnership)'. The generic parameter 'T' of 'Android.Runtime.JNIEnv.CopyObjectArray<T>(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.

@jonathanpeppers jonathanpeppers force-pushed the Mono.Android-TrimSafe branch 4 times, most recently from f2716fa to 7dae51c Compare March 6, 2024 21:07
Fixes: dotnet#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.

There is a problem here if either of these are used with a custom type:

* `$(AndroidHttpClientHandlerType)`
* `$XA_HTTP_CLIENT_HANDLER_TYPE`

This is a rarely used feature, I found one usage in GitHub:

https://github.com/search?q=%3CAndroidHttpClientHandlerType%3E+NOT+Xamarin.Android.Net+NOT+System.Net.Http+NOT+Default&type=code

Filed an issue to fix this in the future:

* dotnet#8797

~~ 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<TResult>(IJavaObject)'. The generic parameter 'T' of 'Java.Interop.JavaConvert.FromJavaObject<T>(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<T>(IJavaObject)'. The generic parameter 'T' of 'Android.Runtime.JNIEnv.GetArray<T>(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<T>(nint, JniHandleOwnership)'. The generic parameter 'T' of 'Android.Runtime.JNIEnv.CopyObjectArray<T>(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.
@jonathanpeppers jonathanpeppers marked this pull request as ready for review March 7, 2024 15:09
@jonathanpeppers
Copy link
Member Author

/cc @simonrozsival @vitek-karas

Comment on lines +15 to +17
<WarningsAsErrors>
$(WarningsAsErrors);
IL2000;IL2001;IL2002;IL2003;IL2004;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this to get the analyzer warnings to be treated as errors?
For trimmer the ILLinkTreatWarningsAsErrors should be enough to do that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found it didn't work in a class library, unless I added these.

Let me look at an MSBuild log to see why.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find anything in the ILLink analyzer that would look for $(ILLinkTreatWarningsAsErrors) and promote the warnings to errors.

I was looking here:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Analyzer doesn't do this, if you need this for the analyzer you do need to use the Roslyn's properties - so WarnAsError or TreatWarningsAsErrors (if you can enable it for all warnings). The ILLinkTreatWarningsAsErrors is only for ILLink.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what we have here is OK for now. We could consider $(TreatWarningsAsErrors) as some point, but we have ~31 long-standing warnings from binding the Android API surface. There are a lot fewer now than there used to be, so it might be reasonable to fix these in .NET 9.

src/Mono.Android/Android.Runtime/AndroidEnvironment.cs Outdated Show resolved Hide resolved
Comment on lines 61 to 62
[UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = justification)]
[UnconditionalSuppressMessage ("Trimming", "IL2068", Justification = justification)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of these is about the fact that the type is unknown and it might have annotations on generic parameters. And in fact that happens here. For example JavaList<[DAM(Ctors)] T>. Suppressing the warning here means that we can create JavaList<SomeType> but not guarantee that SomeType.ctor is preserved -> break.

For the warning about the return type, it should be enough to annotate the Type type with the same DAM - the MakeGenericType "transfers" the annotation from the open generic to the instantiated generic. And since this helper is used on statically known types, that should be 100% fixable (no need for suppression).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the warnings were about public methods:

src\Mono.Android\Java.Interop\JavaConvert.cs(68,5): error IL2068: 'MakeGenericType(Type, params Type[])' method return value does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' requirements. The parameter 'type' of method 'MakeGenericType(Type, params 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(68,5): error IL2055: Call to 'System.Type.MakeGenericType(params Type[])' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic type.

There is some code below that would call:

typeof(JavaList<T>).GetMethod ("FromJniHandle", BindingFlags.Static | BindingFlags.Public)

I added [DAM(PublicMethods)] on JavaList<T>, does that seem like it would work for now?

I think we are good on the generic parameters on the three types, I have [DAM(Ctors)] for generics on JavaList<T>, JavaCollection<T>, and JavaDictionary<K,V>:

Warnings for these appeared from other places.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry - that's not how it works.
Putting DAM on a type only works if you have a variable statically declared as that type and then call .GetType() on the variable. Otherwise it will NOT apply the DAM to the type. The goal of DAM is to make things verifiable... so if you still need to use suppressions, it means you probably got it wrong.

The first warning should go away if you change the local method to:

			static Type MakeGenericType ([DynamicallyAccessedMembers(PublicMethods)] Type type, params Type [] typeArguments) 

You should also remove the DAM annotations on the specific types (JavaList and so on).

The second one doesn't seem to be fixable though:

class TestType
{
    public TestType() {}
}

// Internally this will create:
//     JavaList<TestType> instance, but that requires TestType to keep the public constructor
//  But the trimmer will remove the constructor, since it doesn't see TestType used anywhere
//  where it would need a constructor.
GetJniHandleConverter(typeof(IList<TestType>));

There's no good solution as-is... DI had the same problem and didn't really solve it either. It workaround this by checking the DAM annotations on the generic parameters of the interface at runtime, but that would not help here either, since we can't put the annotation on the interface. I don't know what to do with this second one. But I don't understand the higher level of this, so maybe there's something we can do there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I was able to remove a [UnconditionalSuppressMessage], but the IL2055 is still suppressed for now.

After this is merged, next step I'm going to enable more of our tests to verify TrimMode=full works on-device at runtime. That might find some cases that don't work in practice.

const string justification = "JavaDictionary<,>, JavaList<>, and JavaCollection<> use DynamicallyAccessedMembers for PublicMethods to preserve FromJniHandle().";
[UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = justification)]
[UnconditionalSuppressMessage ("Trimming", "IL2068", Justification = justification)]
[UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = "JavaDictionary<,>, JavaList<>, and JavaCollection<> are preserved by the MarkJavaObjects linker step.")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice :-)

Nit: The justification doesn't apply to the warning - see my other comment about the reason for the warning.

jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this pull request Mar 12, 2024
This is WIP depends on dotnet#8778.

As we have solved all trimming warnings in the Android workload, we
can now go "all in" on trimming.

Early in .NET 6 (maybe even 5?) we "hid" many trimming warnings as we
did not yet plan to solve them:

    <SuppressTrimAnalysisWarnings Condition=" '$(SuppressTrimAnalysisWarnings)' == '' ">true</SuppressTrimAnalysisWarnings>

These warnings were not *actionable* at the time for customers, as
many warnings were in `Mono.Android.dll`, `Java.Interop.dll`, etc.

Going forward, let's stop suppressing these warnings if `TrimMode=full`.

We can also enable trimming for new projects:

* `dotnet new android`
* `dotnet new android-wear`

These will have the `TrimMode` property set to `Full` by default:

    <!--
      Enable full trimming in Release mode.
      To learn more, see: https://learn.microsoft.com/dotnet/core/deploying/trimming/trimming-options#trimming-granularity
    -->
    <PropertyGroup Condition="'$(Configuration)' == 'Release'">
      <TrimMode>full</TrimMode>
    </PropertyGroup>

We wouldn't want to do this for existing projects *yet*, as they might
have existing code, NuGet packages, etc. where trimming warnings might
be present.

We can also improve the templates for Android class libraries:

* `dotnet new androidlib`
* `dotnet new android-bindinglib`

    <!--
      Enable trim analyzers for Android class libraries.
      To learn more, see: https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming
    -->
    <IsTrimmable>true</IsTrimmable>

This way, new class libraries will be "trimmable" by default and be
able to react to trimming warnings.

We can also use `TrimMode=full` in many of our existing tests:

* MSBuild tests that assert 0 warnings can use `TrimMode=full`.

* On-device tests can import `trim-analyzers.props` and use `TrimMode=full`.
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this pull request Mar 12, 2024
This is WIP depends on dotnet#8778.

As we have solved all trimming warnings in the Android workload, we
can now go "all in" on trimming.

Early in .NET 6 (maybe even 5?) we "hid" many trimming warnings as we
did not yet plan to solve them:

    <SuppressTrimAnalysisWarnings Condition=" '$(SuppressTrimAnalysisWarnings)' == '' ">true</SuppressTrimAnalysisWarnings>

These warnings were not *actionable* at the time for customers, as
many warnings were in `Mono.Android.dll`, `Java.Interop.dll`, etc.

Going forward, let's stop suppressing these warnings if `TrimMode=full`.

We can also enable trimming for new projects:

* `dotnet new android`
* `dotnet new android-wear`

These will have the `TrimMode` property set to `Full` by default:

    <!--
      Enable full trimming in Release mode.
      To learn more, see: https://learn.microsoft.com/dotnet/core/deploying/trimming/trimming-options#trimming-granularity
    -->
    <PropertyGroup Condition="'$(Configuration)' == 'Release'">
      <TrimMode>full</TrimMode>
    </PropertyGroup>

We wouldn't want to do this for existing projects *yet*, as they might
have existing code, NuGet packages, etc. where trimming warnings might
be present.

We can also improve the templates for Android class libraries:

* `dotnet new androidlib`
* `dotnet new android-bindinglib`

    <!--
      Enable trim analyzers for Android class libraries.
      To learn more, see: https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming
    -->
    <IsTrimmable>true</IsTrimmable>

This way, new class libraries will be "trimmable" by default and be
able to react to trimming warnings.

We can also use `TrimMode=full` in many of our existing tests:

* MSBuild tests that assert 0 warnings can use `TrimMode=full`.

* On-device tests can import `trim-analyzers.props` and use `TrimMode=full`.
@jonpryor
Copy link
Member

Fixes: https://github.com/xamarin/xamarin-android/issues/5652

Add `build-tools/trim-analyzers/trim-analyzers.props`, which when
`<Import/>`ed into a project will enable trimmer analyzers and enable
`$(WarningsAsErrors)` for the warnings listed below so that that these
trimmer warnings need to be fixed as part of the build:

  * IL2000 through IL2129
  * IL3050 through IL3056

Update `Mono.Android.csproj` to import `trim-analyzers.props`.

Address the trimmer warnings that are now errors


~~ `$(AndroidHttpClientHandlerType)` and AndroidEnvironment ~~

The default `HttpClientHandler` type used with `new HttpClient()` can
be controlled via the [`$(AndroidHttpClientHandlerType)`][0] MSBuild
property, which sets the `$XA_HTTP_CLIENT_HANDLER_TYPE` environment
variable, which is used with `Type.GetType()`.  (Setting
`$(AndroidHttpClientHandlerType)` [is rare][1], has not been
deprecated, and thus is supported.)

The trimmer only supports `Type.GetType()` with string constants, not
with runtime values such as environment variables.

This results in numerous trimmer warnings:

	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.

Partially address these issues by:

  * Using string constants instead of default parameter values.

        // This works
        static Type GetFallbackHttpMessageHandlerType() {
            const string typeName = "Xamarin.Android.Net.AndroidMessageHandler, Mono.Android";
            Type.GetType(typeName, …);
        }

        // This does not, as the *caller* gets the value
        static Type GetFallbackHttpMessageHandlerType(string typeName = "Xamarin.Android.Net.AndroidMessageHandler, Mono.Android") {
            Type.GetType(typeName, …);
        }

  * Spread `[DynamicallyAccessedMembers]` around to fix IL2077.

  * Use `[UnconditionalSuppressMessage]` to explicitly suppress IL2057

TODO: we need to *actually preserve* the type referenced by
`$(AndroidHttpClientHandlerType)`.

  * https://github.com/xamarin/xamarin-android/issues/8797


~~ JavaConvert ~~

`JavaConvert` is used internally to marshal between Java and managed
types, and elicits numerous trimmer warnings:

	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<TResult>(IJavaObject)'.
	  The generic parameter 'T' of 'Java.Interop.JavaConvert.FromJavaObject<T>(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.

Address these by adding `[DynamicallyAccessedMembers]` to assist the
trimmer, `#pragma warning disable` to suppress the IL3050 warning,
and `[UnconditionalSuppressMessage]` to ignore the IL2055 warnings.

This trickled over to require more attributes for:

  * `AdapterView`
  * `ArrayAdapter`
  * `AsyncTask`
  * `JavaCollection`
  * `JavaDictionary`
  * `JavaList`
  * `JavaList`
  * `JavaObjectExtensions`
  * `JavaSet`
  * `SparseArray`
  * `System.Linq\Extensions`


~~ JNIEnv ~~

`JNIENv` is also used internally to marshal between Java and managed
types, and elicits numerous trimmer warnings:

	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<T>(IJavaObject)'.
	  The generic parameter 'T' of 'Android.Runtime.JNIEnv.GetArray<T>(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<T>(nint, JniHandleOwnership)'.
	  The generic parameter 'T' of 'Android.Runtime.JNIEnv.CopyObjectArray<T>(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 `[DynamicallyAccessedMembers]` fixes the IL2091 warnings.

Use `#pragma warning disable` to ignore the IL3050 warnings around
`Array.CreateInstance()` and `Type.MakeArrayType()`.

TODO: try to fix these issues instead of suppressing them:

  * https://github.com/xamarin/xamarin-android/issues/8724

[0]: https://github.com/xamarin/xamarin-android/blob/c20d51fcf8e910b8fb46c5351c26e55ed1fab90c/Documentation/guides/building-apps/build-properties.md#androidhttpclienthandlertype
[1]: https://github.com/search?q=%3CAndroidHttpClientHandlerType%3E+NOT+Xamarin.Android.Net+NOT+System.Net.Http+NOT+Default&type=code

@jonpryor jonpryor merged commit 8a5b2a0 into dotnet:main Mar 13, 2024
47 checks passed
@jonathanpeppers jonathanpeppers deleted the Mono.Android-TrimSafe branch March 13, 2024 15:49
grendello added a commit that referenced this pull request Mar 13, 2024
* main:
  [Mono.Android] is now "trimming safe" (#8778)
grendello added a commit that referenced this pull request Mar 15, 2024
* main:
  [runtime] Remove the last vestiges of desktop builds (#8810)
  [ci] Don't auto-retry APK test suites. (#8811)
  [Microsoft.Android.Templates] Update EN l10n template strings (#8808)
  Bump to xamarin/Java.Interop/main@651de42 (#8809)
  [Mono.Android] is now "trimming safe" (#8778)
grendello added a commit that referenced this pull request Mar 15, 2024
* main:
  [runtime] Remove the last vestiges of desktop builds (#8810)
  [ci] Don't auto-retry APK test suites. (#8811)
  [Microsoft.Android.Templates] Update EN l10n template strings (#8808)
  Bump to xamarin/Java.Interop/main@651de42 (#8809)
  [Mono.Android] is now "trimming safe" (#8778)
  [Mono.Android] Fix missing enum issues that cause BG8800 warnings. (#8707)
  Bump external/Java.Interop from `3436a30` to `5bca8ad` (#8803)
  Bump to xamarin/monodroid@77124dc1 (#8804)
grendello added a commit that referenced this pull request Mar 15, 2024
* main:
  [runtime] Remove the last vestiges of desktop builds (#8810)
  [ci] Don't auto-retry APK test suites. (#8811)
  [Microsoft.Android.Templates] Update EN l10n template strings (#8808)
  Bump to xamarin/Java.Interop/main@651de42 (#8809)
  [Mono.Android] is now "trimming safe" (#8778)
  [Mono.Android] Fix missing enum issues that cause BG8800 warnings. (#8707)
  Bump external/Java.Interop from `3436a30` to `5bca8ad` (#8803)
  Bump to xamarin/monodroid@77124dc1 (#8804)
  Bump to dotnet/installer@e911f5c82c 9.0.100-preview.3.24161.2 (#8802)
  Bump to xamarin/Java.Interop/main@3436a30 (#8799)
grendello added a commit that referenced this pull request Mar 20, 2024
* main:
  LEGO: Merge pull request 8818
  Bump to dotnet/installer@b40c44502d 9.0.100-preview.3.24165.20 (#8817)
  Bump com.android.tools:r8 from 8.2.47 to 8.3.37 (#8816)
  [Mono.Android] Prevent NullPointerException in TranslateStackTrace (#8795)
  Localized file check-in by OneLocBuild Task (#8815)
  [Xamarin.Android.Build.Tasks] Make all assemblies RID-specific (#8478)
  Localized file check-in by OneLocBuild Task (#8813)
  [Xamarin.Android.Build.Tasks] %(AndroidAsset.AssetPack) Support (#8631)
  [runtime] Remove the last vestiges of desktop builds (#8810)
  [ci] Don't auto-retry APK test suites. (#8811)
  [Microsoft.Android.Templates] Update EN l10n template strings (#8808)
  Bump to xamarin/Java.Interop/main@651de42 (#8809)
  [Mono.Android] is now "trimming safe" (#8778)
  [Mono.Android] Fix missing enum issues that cause BG8800 warnings. (#8707)
  Bump external/Java.Interop from `3436a30` to `5bca8ad` (#8803)
  Bump to xamarin/monodroid@77124dc1 (#8804)
  Bump to dotnet/installer@e911f5c82c 9.0.100-preview.3.24161.2 (#8802)
  Bump to xamarin/Java.Interop/main@3436a30 (#8799)
  [templates] Remove redundant "template" from display name. (#8773)
grendello added a commit that referenced this pull request Mar 20, 2024
* main:
  LEGO: Merge pull request 8818
  Bump to dotnet/installer@b40c44502d 9.0.100-preview.3.24165.20 (#8817)
  Bump com.android.tools:r8 from 8.2.47 to 8.3.37 (#8816)
  [Mono.Android] Prevent NullPointerException in TranslateStackTrace (#8795)
  Localized file check-in by OneLocBuild Task (#8815)
  [Xamarin.Android.Build.Tasks] Make all assemblies RID-specific (#8478)
  Localized file check-in by OneLocBuild Task (#8813)
  [Xamarin.Android.Build.Tasks] %(AndroidAsset.AssetPack) Support (#8631)
  [runtime] Remove the last vestiges of desktop builds (#8810)
  [ci] Don't auto-retry APK test suites. (#8811)
  [Microsoft.Android.Templates] Update EN l10n template strings (#8808)
  Bump to xamarin/Java.Interop/main@651de42 (#8809)
  [Mono.Android] is now "trimming safe" (#8778)
  [Mono.Android] Fix missing enum issues that cause BG8800 warnings. (#8707)
  Bump external/Java.Interop from `3436a30` to `5bca8ad` (#8803)
  Bump to xamarin/monodroid@77124dc1 (#8804)
  Bump to dotnet/installer@e911f5c82c 9.0.100-preview.3.24161.2 (#8802)
  Bump to xamarin/Java.Interop/main@3436a30 (#8799)
grendello added a commit that referenced this pull request Mar 20, 2024
* main: (99 commits)
  LEGO: Merge pull request 8818
  Bump to dotnet/installer@b40c44502d 9.0.100-preview.3.24165.20 (#8817)
  Bump com.android.tools:r8 from 8.2.47 to 8.3.37 (#8816)
  [Mono.Android] Prevent NullPointerException in TranslateStackTrace (#8795)
  Localized file check-in by OneLocBuild Task (#8815)
  [Xamarin.Android.Build.Tasks] Make all assemblies RID-specific (#8478)
  Localized file check-in by OneLocBuild Task (#8813)
  [Xamarin.Android.Build.Tasks] %(AndroidAsset.AssetPack) Support (#8631)
  [runtime] Remove the last vestiges of desktop builds (#8810)
  [ci] Don't auto-retry APK test suites. (#8811)
  [Microsoft.Android.Templates] Update EN l10n template strings (#8808)
  Bump to xamarin/Java.Interop/main@651de42 (#8809)
  [Mono.Android] is now "trimming safe" (#8778)
  [Mono.Android] Fix missing enum issues that cause BG8800 warnings. (#8707)
  Bump external/Java.Interop from `3436a30` to `5bca8ad` (#8803)
  Bump to xamarin/monodroid@77124dc1 (#8804)
  Bump to dotnet/installer@e911f5c82c 9.0.100-preview.3.24161.2 (#8802)
  Bump to xamarin/Java.Interop/main@3436a30 (#8799)
  [templates] Remove redundant "template" from display name. (#8773)
  Bump to xamarin/Java.Interop/main@a7e09b7 (#8793)
  ...
@github-actions github-actions bot locked and limited conversation to collaborators Apr 13, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Make Android SDK trimming safe
4 participants