|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection.Metadata;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using ILCompiler.Dataflow;
using ILCompiler.DependencyAnalysisFramework;
using ILCompiler.Logging;
using ILLink.Shared;
using static ILCompiler.Dataflow.DynamicallyAccessedMembersBinder;
namespace ILCompiler.DependencyAnalysis
{
/// <summary>
/// Computes the list of dependencies from DynamicDependencyAttribute.
/// https://learn.microsoft.com/dotnet/api/system.diagnostics.codeanalysis.dynamicdependencyattribute
/// </summary>
public class DynamicDependencyAttributesOnEntityNode : DependencyNodeCore<NodeFactory>
{
private readonly TypeSystemEntity _entity;
public DynamicDependencyAttributesOnEntityNode(TypeSystemEntity entity)
{
Debug.Assert(entity is EcmaMethod || entity is EcmaField);
_entity = entity;
}
public static void AddDependenciesDueToDynamicDependencyAttribute(ref DependencyList dependencies, NodeFactory factory, EcmaMethod method)
{
if (method.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "DynamicDependencyAttribute"))
{
dependencies ??= new DependencyList();
dependencies.Add(factory.DynamicDependencyAttributesOnEntity(method), "DynamicDependencyAttribute present");
}
}
public static void AddDependenciesDueToDynamicDependencyAttribute(ref DependencyList dependencies, NodeFactory factory, EcmaField field)
{
if (field.HasCustomAttribute("System.Diagnostics.CodeAnalysis", "DynamicDependencyAttribute"))
{
dependencies ??= new DependencyList();
dependencies.Add(factory.DynamicDependencyAttributesOnEntity(field), "DynamicDependencyAttribute present");
}
}
public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
{
DependencyList dependencies = null;
try
{
(TypeDesc owningType, IEnumerable<CustomAttributeValue<TypeDesc>> attributes) = _entity switch
{
EcmaMethod method => (method.OwningType, method.GetDecodedCustomAttributes("System.Diagnostics.CodeAnalysis", "DynamicDependencyAttribute")),
_ => (((EcmaField)_entity).OwningType, ((EcmaField)_entity).GetDecodedCustomAttributes("System.Diagnostics.CodeAnalysis", "DynamicDependencyAttribute")),
};
foreach (CustomAttributeValue<TypeDesc> attribute in attributes)
{
AddDependenciesDueToDynamicDependencyAttribute(ref dependencies, factory, _entity, owningType, attribute);
}
}
catch (TypeSystemException)
{
// Ignore entities with custom attributes that don't work.
}
return dependencies;
}
private static void AddDependenciesDueToDynamicDependencyAttribute(
ref DependencyList dependencies,
NodeFactory factory,
TypeSystemEntity entity,
TypeDesc owningType,
CustomAttributeValue<TypeDesc> attribute)
{
IEnumerable<TypeSystemEntity> members;
static MetadataType Linkerify(TypeDesc type)
{
// IL Linker compatibility: illink will call Resolve() that will strip parameter types and genericness
// and operate on the definition.
while (type.IsParameterizedType)
type = ((ParameterizedType)type).ParameterType;
return (MetadataType)type.GetTypeDefinition();
}
// First figure out the list of members that this maps to.
// These are the ways to specify the members:
//
// * A string that contains a documentation signature
// * DynamicallyAccessedMembers enum
var fixedArgs = attribute.FixedArguments;
TypeDesc targetType;
UsageBasedMetadataManager metadataManager = (UsageBasedMetadataManager)factory.MetadataManager;
if (fixedArgs.Length > 0 && fixedArgs[0].Value is string sigFromAttribute)
{
switch (fixedArgs.Length)
{
// DynamicDependencyAttribute(String)
case 1:
targetType = owningType;
break;
// DynamicDependencyAttribute(String, Type)
case 2 when fixedArgs[1].Value is TypeDesc typeFromAttribute:
targetType = typeFromAttribute;
break;
// DynamicDependencyAttribute(String, String, String)
case 3 when fixedArgs[1].Value is string typeStringFromAttribute
&& fixedArgs[2].Value is string assemblyStringFromAttribute:
ModuleDesc asm = factory.TypeSystemContext.ResolveAssembly(AssemblyNameInfo.Parse(assemblyStringFromAttribute), throwIfNotFound: false);
if (asm == null)
{
metadataManager.Logger.LogWarning(
new MessageOrigin(entity),
DiagnosticId.UnresolvedAssemblyInDynamicDependencyAttribute,
assemblyStringFromAttribute);
return;
}
targetType = DocumentationSignatureParser.GetTypeByDocumentationSignature((IAssemblyDesc)asm, typeStringFromAttribute);
if (targetType == null)
{
metadataManager.Logger.LogWarning(
new MessageOrigin(entity),
DiagnosticId.UnresolvedTypeInDynamicDependencyAttribute,
typeStringFromAttribute);
return;
}
break;
default:
Debug.Fail("Did we introduce a new overload?");
return;
}
members = DocumentationSignatureParser.GetMembersByDocumentationSignature(Linkerify(targetType), sigFromAttribute, acceptName: true);
if (!members.Any())
{
metadataManager.Logger.LogWarning(
new MessageOrigin(entity),
DiagnosticId.NoMembersResolvedForMemberSignatureOrType,
sigFromAttribute,
targetType.GetDisplayName());
return;
}
}
else if (fixedArgs.Length > 0 && fixedArgs[0].Value is int memberTypesFromAttribute)
{
if (fixedArgs.Length == 2 && fixedArgs[1].Value is TypeDesc typeFromAttribute)
{
// DynamicDependencyAttribute(DynamicallyAccessedMemberTypes, Type)
targetType = typeFromAttribute;
}
else if (fixedArgs.Length == 3 && fixedArgs[1].Value is string typeStringFromAttribute
&& fixedArgs[2].Value is string assemblyStringFromAttribute)
{
// DynamicDependencyAttribute(DynamicallyAccessedMemberTypes, String, String)
ModuleDesc asm = factory.TypeSystemContext.ResolveAssembly(AssemblyNameInfo.Parse(assemblyStringFromAttribute), throwIfNotFound: false);
if (asm == null)
{
metadataManager.Logger.LogWarning(
new MessageOrigin(entity),
DiagnosticId.UnresolvedAssemblyInDynamicDependencyAttribute,
assemblyStringFromAttribute);
return;
}
targetType = DocumentationSignatureParser.GetTypeByDocumentationSignature((IAssemblyDesc)asm, typeStringFromAttribute);
if (targetType == null)
{
metadataManager.Logger.LogWarning(
new MessageOrigin(entity),
DiagnosticId.UnresolvedTypeInDynamicDependencyAttribute,
typeStringFromAttribute);
return;
}
}
else
{
Debug.Fail("Did we introduce a new overload?");
return;
}
members = Linkerify(targetType).GetDynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)memberTypesFromAttribute);
if (!members.Any())
{
metadataManager.Logger.LogWarning(
new MessageOrigin(entity),
DiagnosticId.NoMembersResolvedForMemberSignatureOrType,
((DynamicallyAccessedMemberTypes)memberTypesFromAttribute).ToString(),
targetType.GetDisplayName());
return;
}
}
else
{
Debug.Fail("Did we introduce a new overload?");
return;
}
Debug.Assert(members.Any());
const string reason = "DynamicDependencyAttribute";
// Now root the discovered members
ReflectionMarker reflectionMarker = new ReflectionMarker(
metadataManager.Logger,
factory,
metadataManager.FlowAnnotations,
typeHierarchyDataFlowOrigin: null,
enabled: true);
foreach (var member in members)
{
reflectionMarker.MarkTypeSystemEntity(new MessageOrigin(entity), member, reason);
}
dependencies ??= new DependencyList();
dependencies.AddRange(reflectionMarker.Dependencies);
}
protected override string GetName(NodeFactory factory)
{
return "DynamicDependencyAttribute analysis for " + _entity.GetDisplayName();
}
public override bool InterestingForDynamicDependencyAnalysis => false;
public override bool HasDynamicDependencies => false;
public override bool HasConditionalStaticDependencies => false;
public override bool StaticDependenciesAreComputed => true;
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory context) => null;
public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null;
}
}
|