sdk/Sdk/Extensions/CustomAttributeExtensions.cs (95 lines of code) (raw):
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
namespace Microsoft.Azure.Functions.Worker.Sdk
{
public static class CustomAttributeExtensions
{
public static IDictionary<string, object> GetAllDefinedProperties(this CustomAttribute attribute)
{
var properties = new Dictionary<string, object>();
// To avoid needing to instantiate any types, assume that the constructor
// argument names are equal to property names.
LoadDefaultProperties(properties, attribute);
LoadConstructorArguments(properties, attribute);
LoadDefinedProperties(properties, attribute);
return properties;
}
private static IEnumerable<(string, CustomAttributeArgument?)> GetDefaultValues(this CustomAttribute attribute)
{
return attribute.AttributeType.Resolve().Properties
.Select(p => (p.Name, p.CustomAttributes
.SingleOrDefault(attr => string.Equals(attr.AttributeType.FullName, Constants.DefaultValueAttributeType, StringComparison.Ordinal))
?.ConstructorArguments.SingleOrDefault()))
.Where(t => t.Item2 is not null);
}
private static void LoadDefaultProperties(IDictionary<string, object> properties, CustomAttribute attribute)
{
var propertyDefaults = attribute.GetDefaultValues();
foreach (var propertyDefault in propertyDefaults)
{
if (propertyDefault.Item2 is not null)
{
properties[propertyDefault.Item1] = propertyDefault.Item2.Value.Value;
}
}
}
private static void LoadConstructorArguments(IDictionary<string, object> properties, CustomAttribute attribute)
{
var constructorParams = attribute.Constructor.Resolve().Parameters;
for (int i = 0; i < attribute.ConstructorArguments.Count; i++)
{
var arg = attribute.ConstructorArguments[i];
var param = constructorParams[i];
string? paramName = param?.Name;
object? paramValue = arg.Value;
if (paramName is null || paramValue is null)
{
continue;
}
paramValue = GetEnrichedValue(param!.ParameterType, paramValue);
properties[paramName] = paramValue!;
}
}
private static void LoadDefinedProperties(IDictionary<string, object> properties, CustomAttribute attribute)
{
foreach (CustomAttributeNamedArgument property in attribute.Properties)
{
object? propVal = property.Argument.Value;
string? propName = property.Name;
if (propVal is null || propName is null)
{
continue;
}
propVal = GetEnrichedValue(property.Argument.Type, propVal);
properties[propName] = propVal!;
}
}
private static object? GetEnrichedValue(TypeReference type, object value)
{
if (TryGetEnumName(type.Resolve(), value, out string? enumName))
{
return enumName;
}
else if (type.IsArray)
{
var arrayValue = value as IEnumerable<CustomAttributeArgument>;
return arrayValue.Select(p => p.Value).ToArray();
}
else
{
return value;
}
}
private static bool TryGetEnumName(TypeDefinition typeDef, object enumValue, out string? enumName)
{
if (typeDef.IsEnum)
{
enumName = typeDef.Fields.Single(f => Equals(f.Constant, enumValue)).Name;
return true;
}
enumName = null;
return false;
}
}
}