|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using ILLink.Shared;
using Mono.Cecil;
namespace Mono.Linker
{
readonly struct LinkerAttributesInformation
{
readonly List<(Type Type, List<Attribute> Attributes)>? _linkerAttributes;
private LinkerAttributesInformation(List<(Type Type, List<Attribute> Attributes)>? cache)
{
this._linkerAttributes = cache;
}
private static bool TryFindAttributeList(List<(Type Type, List<Attribute> Attributes)> list, Type type, [NotNullWhen(returnValue: true)] out List<Attribute>? foundAttributes)
{
foreach (var item in list)
{
if (item.Type == type)
{
foundAttributes = item.Attributes;
return true;
}
}
foundAttributes = null;
return false;
}
public static LinkerAttributesInformation Create(LinkContext context, IMemberDefinition provider)
{
Debug.Assert(context.CustomAttributes.HasAny(provider));
List<(Type Type, List<Attribute> Attributes)>? cache = null;
foreach (var customAttribute in context.CustomAttributes.GetCustomAttributes(provider))
{
var attributeType = customAttribute.AttributeType;
Attribute? attributeValue;
bool allowMultiple = false;
switch (attributeType.Name)
{
case "RequiresUnreferencedCodeAttribute" when attributeType.Namespace == "System.Diagnostics.CodeAnalysis":
attributeValue = ProcessRequiresUnreferencedCodeAttribute(context, provider, customAttribute);
break;
case "DynamicDependencyAttribute" when attributeType.Namespace == "System.Diagnostics.CodeAnalysis":
attributeValue = DynamicDependency.ProcessAttribute(context, provider, customAttribute);
allowMultiple = true;
break;
case "RemoveAttributeInstancesAttribute":
if (provider is not TypeDefinition td)
continue;
// The attribute is never removed if it's explicitly preserved (e.g. via xml descriptor)
if (context.Annotations.TryGetPreserve(td, out TypePreserve preserve) && preserve != TypePreserve.Nothing)
continue;
attributeValue = new RemoveAttributeInstancesAttribute(customAttribute.ConstructorArguments);
allowMultiple = true;
break;
default:
continue;
}
if (attributeValue == null)
continue;
cache ??= new List<(Type Type, List<Attribute> Attributes)>();
Type attributeValueType = attributeValue.GetType();
if (!TryFindAttributeList(cache, attributeValueType, out var attributeList))
{
attributeList = new List<Attribute>();
cache.Add((attributeValueType, attributeList));
}
else if (!allowMultiple)
{
context.LogWarning(provider, DiagnosticId.AttributeShouldOnlyBeUsedOnceOnMember, attributeValueType.FullName ?? "", (provider is MemberReference memberRef) ? memberRef.GetDisplayName() : provider.FullName);
continue;
}
attributeList.Add(attributeValue);
}
return new LinkerAttributesInformation(cache);
}
public bool HasAttribute<T>() where T : Attribute
{
return _linkerAttributes != null && TryFindAttributeList(_linkerAttributes, typeof(T), out _);
}
public IEnumerable<T> GetAttributes<T>() where T : Attribute
{
if (_linkerAttributes == null)
return Enumerable.Empty<T>();
if (!TryFindAttributeList(_linkerAttributes, typeof(T), out var attributeList))
return Enumerable.Empty<T>();
if (attributeList.Count == 0)
throw new InvalidOperationException("Unexpected list of attributes.");
return attributeList.Cast<T>();
}
static RequiresUnreferencedCodeAttribute? ProcessRequiresUnreferencedCodeAttribute(LinkContext context, ICustomAttributeProvider provider, CustomAttribute customAttribute)
{
if (!(provider is MethodDefinition || provider is TypeDefinition))
return null;
if (customAttribute.HasConstructorArguments && customAttribute.ConstructorArguments[0].Value is string message)
{
var ruca = new RequiresUnreferencedCodeAttribute(message);
if (customAttribute.HasProperties)
{
foreach (var prop in customAttribute.Properties)
{
if (prop.Name == "Url")
{
ruca.Url = prop.Argument.Value as string;
break;
}
}
}
return ruca;
}
context.LogWarning((IMemberDefinition)provider, DiagnosticId.AttributeDoesntHaveTheRequiredNumberOfParameters, typeof(RequiresUnreferencedCodeAttribute).FullName ?? "");
return null;
}
}
}
|