File: Linker.Dataflow\GenericArgumentDataFlow.cs
Web Access
Project: src\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.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using ILLink.Shared.TrimAnalysis;
using Mono.Cecil;
using Mono.Linker.Steps;
using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>;
 
namespace Mono.Linker.Dataflow
{
    internal static class GenericArgumentDataFlow
    {
        public static void ProcessGenericArgumentDataFlow(in MessageOrigin origin, MarkStep markStep, LinkContext context, TypeReference type)
        {
            var diagnosticContext = new DiagnosticContext(origin, !context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode(origin.Provider, out _), context);
            var reflectionMarker = new ReflectionMarker(context, markStep, enabled: true);
            ProcessGenericArgumentDataFlow(in diagnosticContext, reflectionMarker, context, type);
        }
 
        public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, LinkContext context, TypeReference type)
        {
            if (type is GenericInstanceType genericInstanceType && context.TryResolve(type) is TypeDefinition typeDefinition)
            {
                ProcessGenericInstantiation(diagnosticContext, reflectionMarker, context, genericInstanceType, typeDefinition);
            }
        }
 
        public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, LinkContext context, MethodReference method)
        {
            if (method is GenericInstanceMethod genericInstanceMethod && context.TryResolve(method) is MethodDefinition methodDefinition)
            {
                ProcessGenericInstantiation(diagnosticContext, reflectionMarker, context, genericInstanceMethod, methodDefinition);
            }
 
            ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, context, method.DeclaringType);
        }
 
        public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, LinkContext context, FieldReference field)
        {
            ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, context, field.DeclaringType);
        }
 
        private static void ProcessGenericInstantiation(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, LinkContext context, IGenericInstance genericInstance, IGenericParameterProvider genericParameterProvider)
        {
            var arguments = genericInstance.GenericArguments;
            var parameters = genericParameterProvider.GenericParameters;
 
            for (int i = 0; i < arguments.Count; i++)
            {
                var genericArgument = arguments[i];
                var genericParameter = parameters[i];
 
                var genericParameterValue = context.Annotations.FlowAnnotations.GetGenericParameterValue(genericParameter);
                if (genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None)
                {
                    MultiValue genericArgumentValue = context.Annotations.FlowAnnotations.GetTypeValueFromGenericArgument(genericArgument);
 
                    var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(context, reflectionMarker, diagnosticContext);
                    requireDynamicallyAccessedMembersAction.Invoke(genericArgumentValue, genericParameterValue);
                }
 
                // Recursively process generic argument data flow on the generic argument if it itself is generic
                if (genericArgument.IsGenericInstance)
                {
                    ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, context, genericArgument);
                }
            }
        }
 
        internal static bool RequiresGenericArgumentDataFlow(FlowAnnotations flowAnnotations, MethodReference method)
        {
            // Method callsites can contain generic instantiations which may contain annotations inside nested generics
            // so we have to check all of the instantiations for that case.
            // For example:
            //   OuterGeneric<InnerGeneric<Annotated>>.Method<InnerGeneric<AnotherAnnotated>>();
 
            if (method is GenericInstanceMethod genericInstanceMethod)
            {
                if (flowAnnotations.HasGenericParameterAnnotation(method))
                    return true;
 
                foreach (var genericArgument in genericInstanceMethod.GenericArguments)
                {
                    if (RequiresGenericArgumentDataFlow(flowAnnotations, genericArgument))
                        return true;
                }
            }
 
            return RequiresGenericArgumentDataFlow(flowAnnotations, method.DeclaringType);
        }
 
        internal static bool RequiresGenericArgumentDataFlow(FlowAnnotations flowAnnotations, FieldReference field)
        {
            return RequiresGenericArgumentDataFlow(flowAnnotations, field.DeclaringType);
        }
 
        internal static bool RequiresGenericArgumentDataFlow(FlowAnnotations flowAnnotations, TypeReference type)
        {
            if (flowAnnotations.HasGenericParameterAnnotation(type))
            {
                return true;
            }
 
            if (type is GenericInstanceType genericInstanceType)
            {
                foreach (var genericArgument in genericInstanceType.GenericArguments)
                {
                    if (RequiresGenericArgumentDataFlow(flowAnnotations, genericArgument))
                    {
                        return true;
                    }
                }
            }
 
            return false;
        }
    }
}