Skip to content

Commit

Permalink
[Trimming] Fix several trimming warnings (dotnet#19402)
Browse files Browse the repository at this point in the history
* Get converter type from attribute with correct annotations

* Disable hot reload helper when it's not supported

* Fix trim analysis warning in ResourceDictionary

* Fix warning in DependencyService

* Suppress warning IL2111 when creating RouteProperty

* Fix BindableProperty ReturnType warnings

* Update src/Controls/src/Core/Routing.cs

Co-authored-by: Vitek Karas <10670590+vitek-karas@users.noreply.github.com>

* Remove fixed warnings from tests

* Simplify DependencyType

---------

Co-authored-by: Vitek Karas <10670590+vitek-karas@users.noreply.github.com>
  • Loading branch information
simonrozsival and vitek-karas authored Jan 16, 2024
1 parent 5727faa commit a36ceae
Show file tree
Hide file tree
Showing 10 changed files with 65 additions and 146 deletions.
16 changes: 9 additions & 7 deletions src/Controls/src/Core/BindableProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace Microsoft.Maui.Controls
public sealed class BindableProperty
{
internal const DynamicallyAccessedMemberTypes DeclaringTypeMembers = DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicMethods;
internal const DynamicallyAccessedMemberTypes ReturnTypeMembers = DynamicallyAccessedMemberTypes.PublicParameterlessConstructor;

public delegate void BindingPropertyChangedDelegate(BindableObject bindable, object oldValue, object newValue);

Expand Down Expand Up @@ -70,7 +71,7 @@ public sealed class BindableProperty
/// <include file="../../docs/Microsoft.Maui.Controls/BindableProperty.xml" path="//Member[@MemberName='UnsetValue']/Docs/*" />
public static readonly object UnsetValue = new object();

BindableProperty(string propertyName, Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
BindableProperty(string propertyName, [DynamicallyAccessedMembers(ReturnTypeMembers)] Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
CoerceValueDelegate coerceValue = null, BindablePropertyBindingChanging bindingChanging = null, bool isReadOnly = false, CreateDefaultValueDelegate defaultValueCreator = null)
{
Expand Down Expand Up @@ -125,6 +126,7 @@ public sealed class BindableProperty
public string PropertyName { get; }

/// <include file="../../docs/Microsoft.Maui.Controls/BindableProperty.xml" path="//Member[@MemberName='ReturnType']/Docs/*" />
[DynamicallyAccessedMembers(ReturnTypeMembers)]
public Type ReturnType { get; }

internal BindablePropertyBindingChanging BindingChanging { get; private set; }
Expand All @@ -140,7 +142,7 @@ public sealed class BindableProperty
internal ValidateValueDelegate ValidateValue { get; private set; }

/// <include file="../../docs/Microsoft.Maui.Controls/BindableProperty.xml" path="//Member[@MemberName='Create']/Docs/*" />
public static BindableProperty Create(string propertyName, Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue = null, BindingMode defaultBindingMode = BindingMode.OneWay,
public static BindableProperty Create(string propertyName, [DynamicallyAccessedMembers(ReturnTypeMembers)] Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue = null, BindingMode defaultBindingMode = BindingMode.OneWay,
ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
{
Expand All @@ -149,15 +151,15 @@ public static BindableProperty Create(string propertyName, Type returnType, [Dyn
}

/// <include file="../../docs/Microsoft.Maui.Controls/BindableProperty.xml" path="//Member[@MemberName='CreateAttached']/Docs/*" />
public static BindableProperty CreateAttached(string propertyName, Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
public static BindableProperty CreateAttached(string propertyName, [DynamicallyAccessedMembers(ReturnTypeMembers)] Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
{
return CreateAttached(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, false, defaultValueCreator);
}

/// <include file="../../docs/Microsoft.Maui.Controls/BindableProperty.xml" path="//Member[@MemberName='CreateAttachedReadOnly']/Docs/*" />
public static BindablePropertyKey CreateAttachedReadOnly(string propertyName, Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource,
public static BindablePropertyKey CreateAttachedReadOnly(string propertyName, [DynamicallyAccessedMembers(ReturnTypeMembers)] Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource,
ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
{
Expand All @@ -167,7 +169,7 @@ public static BindablePropertyKey CreateAttachedReadOnly(string propertyName, Ty
}

/// <include file="../../docs/Microsoft.Maui.Controls/BindableProperty.xml" path="//Member[@MemberName='CreateReadOnly']/Docs/*" />
public static BindablePropertyKey CreateReadOnly(string propertyName, Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource,
public static BindablePropertyKey CreateReadOnly(string propertyName, [DynamicallyAccessedMembers(ReturnTypeMembers)] Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource,
ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
{
Expand All @@ -176,15 +178,15 @@ public static BindablePropertyKey CreateReadOnly(string propertyName, Type retur
isReadOnly: true, defaultValueCreator: defaultValueCreator));
}

internal static BindableProperty Create(string propertyName, Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
internal static BindableProperty Create(string propertyName, [DynamicallyAccessedMembers(ReturnTypeMembers)] Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
BindingPropertyChangedDelegate propertyChanged, BindingPropertyChangingDelegate propertyChanging, CoerceValueDelegate coerceValue, BindablePropertyBindingChanging bindingChanging,
CreateDefaultValueDelegate defaultValueCreator = null)
{
return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging,
defaultValueCreator: defaultValueCreator);
}

internal static BindableProperty CreateAttached(string propertyName, Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
internal static BindableProperty CreateAttached(string propertyName, [DynamicallyAccessedMembers(ReturnTypeMembers)] Type returnType, [DynamicallyAccessedMembers(DeclaringTypeMembers)] Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
BindingPropertyChangedDelegate propertyChanged, BindingPropertyChangingDelegate propertyChanging, CoerceValueDelegate coerceValue, BindablePropertyBindingChanging bindingChanging,
bool isReadOnly, CreateDefaultValueDelegate defaultValueCreator = null)
{
Expand Down
31 changes: 19 additions & 12 deletions src/Controls/src/Core/DependencyService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static class DependencyService
static readonly object s_dependencyLock = new object();
static readonly object s_initializeLock = new object();

static readonly List<Type> DependencyTypes = new List<Type>();
static readonly List<DependencyType> DependencyTypes = new List<DependencyType>();
static readonly Dictionary<Type, DependencyData> DependencyImplementations = new Dictionary<Type, DependencyData>();

public static T Resolve<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(DependencyFetchTarget fallbackFetchTarget = DependencyFetchTarget.GlobalInstance) where T : class
Expand Down Expand Up @@ -64,16 +64,14 @@ public static class DependencyService
public static void Register<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T>() where T : class
{
Type type = typeof(T);
if (!DependencyTypes.Contains(type))
DependencyTypes.Add(type);
AddDependencyTypeIfNeeded(type);
}

public static void Register<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TImpl>() where T : class where TImpl : class, T
{
Type targetType = typeof(T);
Type implementorType = typeof(TImpl);
if (!DependencyTypes.Contains(targetType))
DependencyTypes.Add(targetType);
AddDependencyTypeIfNeeded(targetType);

lock (s_dependencyLock)
DependencyImplementations[targetType] = new DependencyData { ImplementorType = implementorType };
Expand All @@ -83,16 +81,22 @@ public static class DependencyService
{
Type targetType = typeof(T);
Type implementorType = typeof(T);
if (!DependencyTypes.Contains(targetType))
DependencyTypes.Add(targetType);
AddDependencyTypeIfNeeded(targetType);

lock (s_dependencyLock)
DependencyImplementations[targetType] = new DependencyData { ImplementorType = implementorType, GlobalInstance = instance };
}

static void AddDependencyTypeIfNeeded(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type)
{
if (!DependencyTypes.Any(t => t.Type == type))
DependencyTypes.Add(new DependencyType { Type = type });
}

[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
static Type FindImplementor(Type target) =>
DependencyTypes.FirstOrDefault(t => target.IsAssignableFrom(t));
DependencyTypes.FirstOrDefault(t => target.IsAssignableFrom(t.Type)).Type;

// Once we get essentials/cg converted to using startup.cs
// we will delete the initialize code from here and just use
Expand Down Expand Up @@ -137,10 +141,7 @@ public static void Register(Assembly[] assemblies)
for (int i = 0; i < attributes.Length; i++)
{
DependencyAttribute attribute = (DependencyAttribute)attributes[i];
if (!DependencyTypes.Contains(attribute.Implementor))
{
DependencyTypes.Add(attribute.Implementor);
}
AddDependencyTypeIfNeeded(attribute.Implementor);
}
}
}
Expand Down Expand Up @@ -169,5 +170,11 @@ class DependencyData
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
public Type ImplementorType { get; set; }
}

struct DependencyType
{
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
public Type Type { get; set; }
}
}
}
12 changes: 7 additions & 5 deletions src/Controls/src/Core/Internals/CellExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,30 @@ namespace Microsoft.Maui.Controls.Internals
[EditorBrowsable(EditorBrowsableState.Never)]
public static class CellExtensions
{
public static bool GetIsGroupHeader<TView, [DynamicallyAccessedMembers(BindableProperty.DeclaringTypeMembers)] TItem>(this TItem cell) where TView : BindableObject, ITemplatedItemsView<TItem> where TItem : BindableObject
const DynamicallyAccessedMemberTypes ItemTypeMembers = BindableProperty.DeclaringTypeMembers | BindableProperty.ReturnTypeMembers;

public static bool GetIsGroupHeader<TView, [DynamicallyAccessedMembers(ItemTypeMembers)] TItem>(this TItem cell) where TView : BindableObject, ITemplatedItemsView<TItem> where TItem : BindableObject
{
return TemplatedItemsList<TView, TItem>.GetIsGroupHeader(cell);
}

public static void SetIsGroupHeader<TView, [DynamicallyAccessedMembers(BindableProperty.DeclaringTypeMembers)] TItem>(this TItem cell, bool value) where TView : BindableObject, ITemplatedItemsView<TItem> where TItem : BindableObject
public static void SetIsGroupHeader<TView, [DynamicallyAccessedMembers(ItemTypeMembers)] TItem>(this TItem cell, bool value) where TView : BindableObject, ITemplatedItemsView<TItem> where TItem : BindableObject
{
TemplatedItemsList<TView, TItem>.SetIsGroupHeader(cell, value);
}

public static TItem GetGroupHeaderContent<TView, [DynamicallyAccessedMembers(BindableProperty.DeclaringTypeMembers)] TItem>(this TItem cell) where TView : BindableObject, ITemplatedItemsView<TItem> where TItem : BindableObject
public static TItem GetGroupHeaderContent<TView, [DynamicallyAccessedMembers(ItemTypeMembers)] TItem>(this TItem cell) where TView : BindableObject, ITemplatedItemsView<TItem> where TItem : BindableObject
{
var group = TemplatedItemsList<TView, TItem>.GetGroup(cell);
return group.HeaderContent;
}

public static int GetIndex<TView, [DynamicallyAccessedMembers(BindableProperty.DeclaringTypeMembers)] TItem>(this TItem cell) where TView : BindableObject, ITemplatedItemsView<TItem> where TItem : BindableObject
public static int GetIndex<TView, [DynamicallyAccessedMembers(ItemTypeMembers)] TItem>(this TItem cell) where TView : BindableObject, ITemplatedItemsView<TItem> where TItem : BindableObject
{
return TemplatedItemsList<TView, TItem>.GetIndex(cell);
}

public static ITemplatedItemsList<TItem> GetGroup<TView, [DynamicallyAccessedMembers(BindableProperty.DeclaringTypeMembers)] TItem>(this TItem cell) where TView : BindableObject, ITemplatedItemsView<TItem> where TItem : BindableObject
public static ITemplatedItemsList<TItem> GetGroup<TView, [DynamicallyAccessedMembers(ItemTypeMembers)] TItem>(this TItem cell) where TView : BindableObject, ITemplatedItemsView<TItem> where TItem : BindableObject
{
return TemplatedItemsList<TView, TItem>.GetGroup(cell);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Controls/src/Core/ItemsView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace Microsoft.Maui.Controls
{
/// <include file="../../docs/Microsoft.Maui.Controls/ItemsView.xml" path="Type[@FullName='Microsoft.Maui.Controls.ItemsView']/Docs/*" />
public abstract class ItemsView<[DynamicallyAccessedMembers(BindableProperty.DeclaringTypeMembers)] TVisual> : View, ITemplatedItemsView<TVisual> where TVisual : BindableObject
public abstract class ItemsView<[DynamicallyAccessedMembers(BindableProperty.DeclaringTypeMembers | BindableProperty.ReturnTypeMembers)] TVisual> : View, ITemplatedItemsView<TVisual> where TVisual : BindableObject
{
/*
/// <summary>Bindable property for <see cref="InfiniteScrolling"/>.</summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Controls/src/Core/ResourceDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void SetAndLoadSource(Uri value, string resourcePath, Assembly assembly,
//this will return a type if the RD as an x:Class element, and codebehind
var type = XamlResourceIdAttribute.GetTypeForPath(assembly, resourcePath);
if (type != null)
_mergedInstance = s_instances.GetValue(type, (key) => (ResourceDictionary)Activator.CreateInstance(key));
_mergedInstance = s_instances.GetValue(type, _ => (ResourceDictionary)Activator.CreateInstance(type));
else
_mergedInstance = DependencyService.Get<IResourcesLoader>().CreateFromResource<ResourceDictionary>(resourcePath, assembly, lineInfo);
OnValuesChanged(_mergedInstance.ToArray());
Expand Down
11 changes: 9 additions & 2 deletions src/Controls/src/Core/Routing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,15 @@ internal static void Clear()
}

/// <summary>Bindable property for attached property <c>Route</c>.</summary>
public static readonly BindableProperty RouteProperty =
BindableProperty.CreateAttached("Route", typeof(string), typeof(Routing), null,
public static readonly BindableProperty RouteProperty = CreateRouteProperty();

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2111:ReflectionToDynamicallyAccessedMembers",
Justification = "The CreateAttached method has a DynamicallyAccessedMembers annotation for all public methods"
+ "on the declaring type. This includes the Routing.RegisterRoute(string, Type) method which also has a "
+ "DynamicallyAccessedMembers annotation and the trimmer can't guarantee the availability of the requirements"
+ "of the method. `BindableProperty` only needs methods starting with `Get`, so `RegisterRoute` is never accessed via reflection.")]
private static BindableProperty CreateRouteProperty()
=> BindableProperty.CreateAttached("Route", typeof(string), typeof(Routing), null,
defaultValueCreator: CreateDefaultRoute);

static object CreateDefaultRoute(BindableObject bindable)
Expand Down
2 changes: 1 addition & 1 deletion src/Controls/src/Core/TemplatedItemsList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace Microsoft.Maui.Controls.Internals
{

[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class TemplatedItemsList<TView, [DynamicallyAccessedMembers(BindableProperty.DeclaringTypeMembers)] TItem> : BindableObject, ITemplatedItemsList<TItem>, IList, IDisposable
public sealed class TemplatedItemsList<TView, [DynamicallyAccessedMembers(BindableProperty.DeclaringTypeMembers | BindableProperty.ReturnTypeMembers)] TItem> : BindableObject, ITemplatedItemsList<TItem>, IList, IDisposable
where TView : BindableObject, IItemsView<TItem>
where TItem : BindableObject
{
Expand Down
23 changes: 6 additions & 17 deletions src/Controls/src/Core/Xaml/TypeConversionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ internal static object ConvertTo(this object value, Type toType, Func<ParameterI
if (pinfoRetriever == null || pinfoRetriever() is not ParameterInfo pInfo)
return null;
var convertertype = pInfo.CustomAttributes.GetTypeConverterType();
var convertertype = pInfo.GetCustomAttribute<TypeConverterAttribute>()?.GetConverterType();
if (convertertype == null)
return null;
return (TypeConverter)Activator.CreateInstance(convertertype);
Expand All @@ -69,7 +69,7 @@ internal static object ConvertTo(this object value, Type toType, Func<MemberInfo
{
if (!s_converterCache.TryGetValue(memberInfo, out converter))
{
if (memberInfo.CustomAttributes.GetTypeConverterType() is Type converterType)
if (memberInfo.GetCustomAttribute<TypeConverterAttribute>()?.GetConverterType() is Type converterType)
{
converter = (TypeConverter)Activator.CreateInstance(converterType);
}
Expand All @@ -86,7 +86,7 @@ internal static object ConvertTo(this object value, Type toType, Func<MemberInfo
if (!s_converterCache.TryGetValue(toType, out converter))
{
if (toType.CustomAttributes.GetTypeConverterType() is Type converterType)
if (toType.GetCustomAttribute<TypeConverterAttribute>()?.GetConverterType() is Type converterType)
{
converter = (TypeConverter)Activator.CreateInstance(converterType);
}
Expand All @@ -101,20 +101,9 @@ internal static object ConvertTo(this object value, Type toType, Func<MemberInfo
return ConvertTo(value, toType, getConverter, serviceProvider, out exception);
}

static Type GetTypeConverterType(this IEnumerable<CustomAttributeData> attributes)
{
foreach (var converterAttribute in attributes)
{
if (converterAttribute.AttributeType != typeof(System.ComponentModel.TypeConverterAttribute))
continue;
var ctor = converterAttribute.ConstructorArguments[0];
if (ctor.ArgumentType == typeof(string))
return Type.GetType((string)ctor.Value);
if (ctor.ArgumentType == typeof(Type))
return (Type)ctor.Value;
}
return null;
}
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
static Type GetConverterType(this TypeConverterAttribute attribute)
=> Type.GetType(attribute.ConverterTypeName);

//Don't change the name or the signature of this, it's used by XamlC
public static object ConvertTo(
Expand Down
Loading

0 comments on commit a36ceae

Please sign in to comment.