From 20f08bd2086f9bf6d48ce658c62298f68af9881f Mon Sep 17 00:00:00 2001 From: Matous Kozak <55735845+matouskozak@users.noreply.github.com> Date: Wed, 20 Nov 2024 05:03:23 +0100 Subject: [PATCH] [trimming] Enable `$(_DefaultValueAttributeSupport)` for `$(TrimMode)=partial` (#9525) Context: https://github.com/dotnet/runtime/issues/109724 In .NET 9, certain apps could crash with: System.ArgumentException: RuntimeInstanceNotAllowed ?, in object DefaultValueAttribute.get_Value() ?, in new XmlAttributes(ICustomAttributeProvider) ?, in XmlAttributes XmlReflectionImporter.GetAttributes(MemberInfo) ?, in bool XmlReflectionImporter.InitializeStructMembers(StructMapping, StructModel, bool, string, RecursionLimiter) ?, in StructMapping XmlReflectionImporter.ImportStructLikeMapping(StructModel, string, bool, XmlAttributes, RecursionLimiter) .NET's concept of `$(PublishTrimmed)` is used to decide which trimmer feature switches are toggled. This is normally set for both Debug & Release, but Android only sets it for Release. This means that the `$(_DefaultValueAttributeSupport)` feature switch is not set properly in some cases, which causes the crash. For now, we can set `$(_DefaultValueAttributeSupport)` for `TrimMode=partial`, the default in .NET MAUI apps. See https://github.com/dotnet/android/issues/9526 for how we might better address this in the future. In order to test this change: * Add a `XmlSerializerTest` to `Mono.Android-Tests` * Run a copy of `Mono.Android-Tests` with `-p:TestsFlavor=TrimModePartial` * Also set `$(_DefaultValueAttributeSupport)` for `TrimMode=full` in our test project, so `XmlSerializerTest` will pass in that mode as well. Co-authored-by: Jonathan Peppers --- build-tools/automation/azure-pipelines.yaml | 10 ++++++++ ...soft.Android.Sdk.DefaultProperties.targets | 2 ++ .../Mono.Android-Test.Shared.projitems | 1 + .../Mono.Android.NET-Tests.csproj | 3 ++- .../System.Xml/XmlSerializer.cs | 23 +++++++++++++++++++ 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/Mono.Android-Tests/System.Xml/XmlSerializer.cs diff --git a/build-tools/automation/azure-pipelines.yaml b/build-tools/automation/azure-pipelines.yaml index ed8514e4c9c..0796a5fb81e 100644 --- a/build-tools/automation/azure-pipelines.yaml +++ b/build-tools/automation/azure-pipelines.yaml @@ -203,6 +203,16 @@ extends: artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Mono.Android.NET_Tests-Signed.aab artifactFolder: $(DotNetTargetFramework)-NoAot + - template: /build-tools/automation/yaml-templates/apk-instrumentation.yaml@self + parameters: + configuration: $(XA.Build.Configuration) + testName: Mono.Android.NET_Tests-TrimModePartial + project: tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj + testResultsFiles: TestResult-Mono.Android.NET_Tests-$(XA.Build.Configuration)TrimModePartial.xml + extraBuildArgs: -p:TestsFlavor=TrimModePartial -p:TrimMode=partial + artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Mono.Android.NET_Tests-Signed.aab + artifactFolder: $(DotNetTargetFramework)-TrimModePartial + - template: /build-tools/automation/yaml-templates/apk-instrumentation.yaml@self parameters: configuration: $(XA.Build.Configuration) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index da2afb66828..3924b92d5ac 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -114,6 +114,8 @@ true false true + + <_DefaultValueAttributeSupport Condition="'$(_DefaultValueAttributeSupport)' == '' and '$(TrimMode)' == 'partial'">true false $(AvoidEmitForPerformance) true diff --git a/tests/Mono.Android-Tests/Mono.Android-Test.Shared.projitems b/tests/Mono.Android-Tests/Mono.Android-Test.Shared.projitems index 0b1ccf1d595..a13accab96d 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Test.Shared.projitems +++ b/tests/Mono.Android-Tests/Mono.Android-Test.Shared.projitems @@ -48,6 +48,7 @@ + diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj index d8affbe62dd..ad1e6e8e7fa 100644 --- a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj +++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj @@ -38,7 +38,8 @@ r8 full - true + true + <_DefaultValueAttributeSupport Condition="'$(TrimMode)' == 'full'">true diff --git a/tests/Mono.Android-Tests/System.Xml/XmlSerializer.cs b/tests/Mono.Android-Tests/System.Xml/XmlSerializer.cs new file mode 100644 index 00000000000..61cb3ce56a9 --- /dev/null +++ b/tests/Mono.Android-Tests/System.Xml/XmlSerializer.cs @@ -0,0 +1,23 @@ +using System.Xml.Serialization; +using System.ComponentModel; + +using NUnit.Framework; + +namespace System.XmlTests { + public class C { + [DefaultValue(typeof(C), "c")] + public Type? T { get; } + } + + [TestFixture] + public class XmlSerializerTest { + + [Test] + public void TrimmingDefaultValueAttribute () + { + // Context: https://github.com/dotnet/runtime/issues/109724 + var s = new XmlSerializer(typeof(C)); + _ = new C().T; // Prevent C.T from being removed by trimming + } + } +}