| File: ReflectableTaskPropertyInfo.cs | Web Access |
| Project: Microsoft.Build.Framework.csproj (Microsoft.Build.Framework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Linq; using System.Reflection; using System.Diagnostics.CodeAnalysis; using Microsoft.Build.Framework; #nullable disable namespace Microsoft.Build.Execution { /// <summary> /// A reflection-generated TaskPropertyInfo instance. /// </summary> internal class ReflectableTaskPropertyInfo : TaskPropertyInfo { /// <summary> /// The reflection-produced PropertyInfo. /// </summary> private PropertyInfo _propertyInfo; /// <summary> /// The type of the generated tasks. /// </summary> [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] private Type _taskType; /// <summary> /// Initializes a new instance of the <see cref="ReflectableTaskPropertyInfo"/> class. /// </summary> /// <param name="taskPropertyInfo">The original property info that generated this instance.</param> /// <param name="taskType">The type to reflect over to get the reflection propertyinfo later.</param> internal ReflectableTaskPropertyInfo( TaskPropertyInfo taskPropertyInfo, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type taskType) : base(taskPropertyInfo.Name, taskPropertyInfo.PropertyType, taskPropertyInfo.Output, taskPropertyInfo.Required) { ArgumentNullException.ThrowIfNull(taskType); _taskType = taskType; IsAssignableToITask = taskPropertyInfo.IsAssignableToITask; IsValueTypeOutputParameter = taskPropertyInfo.IsValueTypeOutputParameter; if (taskPropertyInfo is ReflectableTaskPropertyInfo reflectableProperty) { IsTypeUnresolved = reflectableProperty.IsTypeUnresolved; ParameterTypeForExpansion = reflectableProperty.ParameterTypeForExpansion; DeclaredTypeName = reflectableProperty.DeclaredTypeName; } } /// <summary> /// Initializes a new instance of the <see cref="ReflectableTaskPropertyInfo"/> class. /// </summary> /// <param name="propertyInfo">The PropertyInfo used to discover this task property.</param> internal ReflectableTaskPropertyInfo(PropertyInfo propertyInfo) : base( propertyInfo.Name, propertyInfo.PropertyType, propertyInfo.GetCustomAttributes(typeof(OutputAttribute), true).Any(), propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), true).Any()) { _propertyInfo = propertyInfo; } /// <summary> /// Initializes a new <see cref="ReflectableTaskPropertyInfo"/> with precomputed parameters. This is specifically /// used with MetadataLoadContext, as these parameters cannot be computed for the property type passed in directly but /// rather the relevant base type. /// </summary> internal ReflectableTaskPropertyInfo( PropertyInfo propertyInfo, Type propertyType, bool output, bool required, bool isAssignableToITaskItemType, Type parameterTypeForExpansion, bool isTypeUnresolved) : base( propertyInfo.Name, propertyType, output, required) { _propertyInfo = propertyInfo; IsAssignableToITask = isAssignableToITaskItemType; IsValueTypeOutputParameter |= IsPathType(parameterTypeForExpansion); ParameterTypeForExpansion = parameterTypeForExpansion; IsTypeUnresolved = isTypeUnresolved; if (isTypeUnresolved) { try { DeclaredTypeName = propertyType.FullName; } catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { } } } internal ReflectableTaskPropertyInfo(PropertyInfo propertyInfo, bool output, bool required, Type parameterTypeForExpansion) : this(propertyInfo, output, required, parameterTypeForExpansion, declaredTypeName: null) { } internal ReflectableTaskPropertyInfo(PropertyInfo propertyInfo, bool output, bool required, Type parameterTypeForExpansion, string declaredTypeName) : base(propertyInfo.Name, parameterTypeForExpansion ?? typeof(object), output, required) { _propertyInfo = propertyInfo; IsTypeUnresolved = true; IsValueTypeOutputParameter |= IsPathType(parameterTypeForExpansion); ParameterTypeForExpansion = parameterTypeForExpansion; DeclaredTypeName = declaredTypeName; } private static bool IsPathType(Type type) { Type elementType = type?.IsArray == true ? type.GetElementType() : type; return elementType == typeof(System.IO.FileInfo) || elementType == typeof(System.IO.DirectoryInfo); } internal bool IsTypeUnresolved { get; } internal Type ParameterTypeForExpansion { get; } internal string DeclaredTypeName { get; } /// <summary> /// Gets or sets the reflection-produced PropertyInfo. /// </summary> internal PropertyInfo Reflection { get { if (_propertyInfo == null) { PropertyInfo foundProperty = null; foreach (PropertyInfo propertyInfo in _taskType.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { if (string.Equals(propertyInfo.Name, Name, StringComparison.OrdinalIgnoreCase)) { if (foundProperty != null) { // Multiple case-insensitive matches indicate shadowed properties or a malformed task type. throw new AmbiguousMatchException($""" Multiple properties matching '{Name}' (case-insensitive) found on type '{_taskType.FullName}'. Shadowed or duplicate property definitions are not supported. """); } foundProperty = propertyInfo; } } Assumed.NotNull(foundProperty, $"Could not find property {Name} on type {_taskType.FullName} that the task factory indicated should exist."); _propertyInfo = foundProperty; } return _propertyInfo; } } } }