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

[Trimming] Fix remaining trimming warnings related to XAML parsing #20474

Merged
Merged
18 changes: 17 additions & 1 deletion src/Controls/src/Core/BindablePropertyConverter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable disable
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
Expand All @@ -23,6 +24,11 @@ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinati

object IExtendedTypeConverter.ConvertFromInvariantString(string value, IServiceProvider serviceProvider)
{
if (!RuntimeFeature.IsXamlRuntimeParsingSupported)
{
throw new InvalidOperationException(RuntimeFeature.XamlRuntimeParsingNotSupportedErrorMessage);
}
Comment on lines 25 to +30
Copy link
Member

@jonathanpeppers jonathanpeppers Feb 13, 2024

Choose a reason for hiding this comment

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

It looks like the converter has a XamlC (build-time) replacement:

[Xaml.ProvideCompiled("Microsoft.Maui.Controls.XamlC.BindablePropertyConverter")]
public sealed class BindablePropertyConverter : TypeConverter, IExtendedTypeConverter

So it seems we should be fine to throw here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, XamlC uses the other class to generate the IL directly. The code that I added to throw won't be ever called for any XAML that's compiled. It can only be reached if the developer manually calls LoadFromXaml.


if (string.IsNullOrWhiteSpace(value))
return null;
if (serviceProvider == null)
Expand Down Expand Up @@ -75,6 +81,11 @@ object IExtendedTypeConverter.ConvertFromInvariantString(string value, IServiceP

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (!RuntimeFeature.IsXamlRuntimeParsingSupported)
{
throw new InvalidOperationException(RuntimeFeature.XamlRuntimeParsingNotSupportedErrorMessage);
}

var strValue = value?.ToString();

if (string.IsNullOrWhiteSpace(strValue))
Expand All @@ -90,10 +101,15 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c
Application.Current?.FindMauiContext()?.CreateLogger<BindablePropertyConverter>()?.LogWarning($"Can't resolve {value}. Accepted syntax is Type.PropertyName.");
return null;
}
Type type = Type.GetType("Microsoft.Maui.Controls." + parts[0]);
Type type = GetControlType(parts[0]);
return ConvertFrom(type, parts[1], null);

[RequiresUnreferencedCode(TrimmerConstants.XamlRuntimeParsingNotSupportedWarning)]
static Type GetControlType(string typeName)
=> Type.GetType("Microsoft.Maui.Controls." + typeName);
}

[RequiresUnreferencedCode(TrimmerConstants.XamlRuntimeParsingNotSupportedWarning)]
BindableProperty ConvertFrom(Type type, string propertyName, IXmlLineInfo lineinfo)
{
string name = propertyName + "Property";
Expand Down
2 changes: 1 addition & 1 deletion src/Controls/src/Core/TrimmerConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ class TrimmerConstants

internal const string NativeBindingService = "This method properly handles missing properties, and there is not a way to preserve them from this method.";

internal const string XamlLoadingTrimmerWarning = "Loading XAML at runtime might require types that cannot be statically analyzed. Make sure all of the required types are preserved.";
internal const string XamlRuntimeParsingNotSupportedWarning = "Loading XAML at runtime might require types and members that cannot be statically analyzed. Make sure all of the required types and members are preserved.";
}
6 changes: 3 additions & 3 deletions src/Controls/src/Xaml/ViewExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,21 @@ namespace Microsoft.Maui.Controls.Xaml
{
public static class Extensions
{
[RequiresUnreferencedCode(TrimmerConstants.XamlLoadingTrimmerWarning)]
[RequiresUnreferencedCode(TrimmerConstants.XamlRuntimeParsingNotSupportedWarning)]
public static TXaml LoadFromXaml<TXaml>(this TXaml view, Type callingType)
{
XamlLoader.Load(view, callingType);
return view;
}

[RequiresUnreferencedCode(TrimmerConstants.XamlLoadingTrimmerWarning)]
[RequiresUnreferencedCode(TrimmerConstants.XamlRuntimeParsingNotSupportedWarning)]
public static TXaml LoadFromXaml<TXaml>(this TXaml view, string xaml)
{
XamlLoader.Load(view, xaml);
return view;
}

[RequiresUnreferencedCode(TrimmerConstants.XamlLoadingTrimmerWarning)]
[RequiresUnreferencedCode(TrimmerConstants.XamlRuntimeParsingNotSupportedWarning)]
internal static TXaml LoadFromXaml<TXaml>(this TXaml view, string xaml, Assembly rootAssembly)
{
XamlLoader.Load(view, xaml, rootAssembly);
Expand Down
13 changes: 13 additions & 0 deletions src/Controls/src/Xaml/XamlParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -358,6 +359,18 @@ static void GatherXmlnsDefinitionAttributes()

public static Type GetElementType(XmlType xmlType, IXmlLineInfo xmlInfo, Assembly currentAssembly,
out XamlParseException exception)
{
if (!RuntimeFeature.IsXamlRuntimeParsingSupported)
{
throw new NotSupportedException(RuntimeFeature.XamlRuntimeParsingNotSupportedErrorMessage);
}

return GetElementTypeCore(xmlType, xmlInfo, currentAssembly, out exception);
}

[RequiresUnreferencedCode(TrimmerConstants.XamlRuntimeParsingNotSupportedWarning)]
private static Type GetElementTypeCore(XmlType xmlType, IXmlLineInfo xmlInfo, Assembly currentAssembly,
out XamlParseException exception)
{
bool hasRetriedNsSearch = false;

Expand Down
5 changes: 5 additions & 0 deletions src/Core/src/RuntimeFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ internal static bool IsXamlRuntimeParsingSupported
? isEnabled
: IsXamlRuntimeParsingSupportedByDefault;

internal const string XamlRuntimeParsingNotSupportedErrorMessage = "XAML runtime parsing is not supported. " +
"Ensure all XAML resources are compiled using XamlC. Alternatively, enable parsing XAML resources at runtime by setting " +
"the MauiXamlRuntimeParsingSupport MSBuild property to true. Note: this feature is not trimming-safe and it might not " +
"behave as expected when the application is trimmed.";

internal static bool IsIVisualAssemblyScanningEnabled =>
AppContext.TryGetSwitch("Microsoft.Maui.RuntimeFeature.IsIVisualAssemblyScanningEnabled", out bool isEnabled)
? isEnabled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,37 +123,6 @@ public static void AssertWarnings(this List<WarningsPerFile> actualWarnings, Lis
// IMPORTANT: Always store expected File information as a relative path to the repo ROOT
private static readonly List<WarningsPerFile> expectedNativeAOTWarnings = new()
{
new WarningsPerFile
{
File = "src/Controls/src/Xaml/XamlParser.cs",
WarningsPerCode = new List<WarningsPerCode>
{
new WarningsPerCode
{
Code = "IL2055",
Messages = new List<string>
{
"Microsoft.Maui.Controls.Xaml.XamlParser.GetElementType(XmlType,IXmlLineInfo,Assembly,XamlParseException&): Call to 'System.Type.MakeGenericType(Type[])' can not be statically analyzed. It's not possible to guarantee the availability of requirements of the generic type.",
}
},
new WarningsPerCode
{
Code = "IL3050",
Messages = new List<string>
{
"Microsoft.Maui.Controls.Xaml.XamlParser.GetElementType(XmlType,IXmlLineInfo,Assembly,XamlParseException&): Using member 'System.Type.MakeGenericType(Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.",
}
},
new WarningsPerCode
{
Code = "IL2057",
Messages = new List<string>
{
"Microsoft.Maui.Controls.Xaml.XamlParser.<>c__DisplayClass11_0.<GetElementType>b__0(ValueTuple`3<String,String,String>): Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String)'. It's not possible to guarantee the availability of the target type.",
}
},
}
},
new WarningsPerFile
{
File = "src/Controls/src/Core/Xaml/TypeConversionExtensions.cs",
Expand Down Expand Up @@ -187,21 +156,6 @@ public static void AssertWarnings(this List<WarningsPerFile> actualWarnings, Lis
}
},
new WarningsPerFile
{
File = "src/Controls/src/Core/BindablePropertyConverter.cs",
WarningsPerCode = new List<WarningsPerCode>
{
new WarningsPerCode
{
Code = "IL2057",
Messages = new List<string>
{
"Microsoft.Maui.Controls.BindablePropertyConverter.ConvertFrom(ITypeDescriptorContext,CultureInfo,Object): Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String)'. It's not possible to guarantee the availability of the target type.",
}
},
}
},
new WarningsPerFile
{
File = "src/Controls/src/Core/BindingExpression.cs",
WarningsPerCode = new List<WarningsPerCode>
Expand Down Expand Up @@ -263,21 +217,6 @@ public static void AssertWarnings(this List<WarningsPerFile> actualWarnings, Lis
},
}
},
new WarningsPerFile
{
File = "src/Core/src/Platform/ReflectionExtensions.cs",
WarningsPerCode = new List<WarningsPerCode>
{
new WarningsPerCode
{
Code = "IL2070",
Messages = new List<string>
{
"Microsoft.Maui.Platform.ReflectionExtensions.<>c.<GetFields>b__1_0(TypeInfo): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicFields', 'DynamicallyAccessedMemberTypes.NonPublicFields' in call to 'System.Reflection.TypeInfo.DeclaredFields.get'. The parameter 'i' of method 'Microsoft.Maui.Platform.ReflectionExtensions.<>c.<GetFields>b__1_0(TypeInfo)' 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.",
}
},
}
},
};

#region Utility methods for generating the list of expected warnings
Expand Down
Loading