| File: Linker\LinkerAttributesInformation.cs | Web Access |
| Project: src\runtime\src\tools\illink\src\linker\Mono.Linker.csproj (illink) |
// 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) { string? url = null; bool excludeStatics = false; if (customAttribute.HasProperties) { foreach (var prop in customAttribute.Properties) { if (prop.Name == "Url") { url = prop.Argument.Value as string; } else if (prop.Name == "ExcludeStatics" && prop.Argument.Value is true) { excludeStatics = true; } } } return new RequiresUnreferencedCodeAttribute(message) { Url = url, ExcludeStatics = excludeStatics }; } context.LogWarning((IMemberDefinition)provider, DiagnosticId.AttributeDoesntHaveTheRequiredNumberOfParameters, typeof(RequiresUnreferencedCodeAttribute).FullName ?? ""); return null; } } }