|
// 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.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using ILLink.Shared.DataFlow;
using ILLink.Shared.TypeSystemProxy;
using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>;
// This is needed due to NativeAOT which doesn't enable nullable globally yet
#nullable enable
namespace ILLink.Shared.TrimAnalysis
{
[StructLayout(LayoutKind.Auto)] // A good way to avoid CS0282, we don't really care about field order
internal partial struct HandleCallAction
{
private static ValueSetLattice<SingleValue> MultiValueLattice => default;
private readonly DiagnosticContext _diagnosticContext;
private readonly FlowAnnotations _annotations;
private readonly RequireDynamicallyAccessedMembersAction _requireDynamicallyAccessedMembersAction;
private readonly bool _isNewObj;
public bool Invoke(MethodProxy calledMethod, MultiValue instanceValue, IReadOnlyList<MultiValue> argumentValues, IntrinsicId intrinsicId, out MultiValue methodReturnValue)
{
MultiValue? maybeMethodReturnValue;
var handledIntrinsic =
TryHandleIntrinsic(calledMethod, instanceValue, argumentValues, intrinsicId, out maybeMethodReturnValue) ||
TryHandleSharedIntrinsic(calledMethod, instanceValue, argumentValues, intrinsicId, out maybeMethodReturnValue);
// As a convenience, if the code above didn't set the return value (and the method has a return value),
// we will set it to be an unknown value with the return type of the method.
var annotatedMethodReturnValue = _annotations.GetMethodReturnValue(calledMethod, _isNewObj);
bool returnsVoid = calledMethod.ReturnsVoid();
methodReturnValue = maybeMethodReturnValue ??
((returnsVoid && !_isNewObj)
? MultiValueLattice.Top
: annotatedMethodReturnValue);
// Validate that the return value has the correct annotations as per the method return value annotations
if (annotatedMethodReturnValue.DynamicallyAccessedMemberTypes != 0)
{
foreach (var uniqueValue in methodReturnValue.AsEnumerable())
{
if (uniqueValue is ValueWithDynamicallyAccessedMembers methodReturnValueWithMemberTypes)
{
if (!methodReturnValueWithMemberTypes.DynamicallyAccessedMemberTypes.HasFlag(annotatedMethodReturnValue.DynamicallyAccessedMemberTypes))
throw new InvalidOperationException($"Internal trimming error: processing of call from {GetContainingSymbolDisplayName()} to {calledMethod.GetDisplayName()} returned value which is not correctly annotated with the expected dynamic member access kinds.");
}
else if (uniqueValue is SystemTypeValue)
{
// SystemTypeValue can fulfill any requirement, so it's always valid
// The requirements will be applied at the point where it's consumed (passed as a method parameter, set as field value, returned from the method)
}
else if (uniqueValue == NullValue.Instance)
{
// NullValue can fulfill any requirements because reflection access to it will typically throw.
}
else
{
throw new InvalidOperationException($"Internal trimming error: processing of call from {GetContainingSymbolDisplayName()} to {calledMethod.GetDisplayName()} returned value which is not correctly annotated with the expected dynamic member access kinds.");
}
}
}
return handledIntrinsic;
}
private partial bool TryHandleIntrinsic(
MethodProxy calledMethod,
MultiValue instanceValue,
IReadOnlyList<MultiValue> argumentValues,
IntrinsicId intrinsicId,
out MultiValue? methodReturnValue);
bool TryHandleSharedIntrinsic(
MethodProxy calledMethod,
MultiValue instanceValue,
IReadOnlyList<MultiValue> argumentValues,
IntrinsicId intrinsicId,
out MultiValue? methodReturnValue)
{
MultiValue? returnValue = methodReturnValue = null;
bool requiresDataFlowAnalysis = _annotations.MethodRequiresDataFlowAnalysis(calledMethod);
var annotatedMethodReturnValue = _annotations.GetMethodReturnValue(calledMethod, _isNewObj);
Debug.Assert(requiresDataFlowAnalysis || annotatedMethodReturnValue.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.None);
switch (intrinsicId)
{
case IntrinsicId.IntrospectionExtensions_GetTypeInfo:
Debug.Assert(instanceValue.IsEmpty());
Debug.Assert(argumentValues.Count == 1);
// typeof(Foo).GetTypeInfo()... will be commonly present in code targeting
// the dead-end reflection refactoring. The call doesn't do anything and we
// don't want to lose the annotation.
returnValue = argumentValues[0];
break;
case IntrinsicId.TypeInfo_AsType:
// someType.AsType()... will be commonly present in code targeting
// the dead-end reflection refactoring. The call doesn't do anything and we
// don't want to lose the annotation.
returnValue = instanceValue;
break;
//
// UnderlyingSystemType
//
case IntrinsicId.Type_get_UnderlyingSystemType:
// This is identity for the purposes of the analysis.
returnValue = instanceValue;
break;
case IntrinsicId.Type_GetTypeFromHandle:
// Infrastructure piece to support "typeof(Foo)" in IL and direct calls everywhere
if (argumentValues[0].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
foreach (var value in argumentValues[0].AsEnumerable())
{
AddReturnValue(value switch
{
RuntimeTypeHandleForNullableSystemTypeValue nullableSystemType
=> new NullableSystemTypeValue(nullableSystemType.NullableType, nullableSystemType.UnderlyingTypeValue),
// When generating type handles from IL, the GenericParameterValue with DAM annotations is not available.
// Once we convert it to a Value with annotations here, there is no need to convert it back in get_TypeHandle
RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers nullableDamType when nullableDamType.UnderlyingTypeValue is RuntimeTypeHandleForGenericParameterValue underlyingGenericParameter
=> new NullableValueWithDynamicallyAccessedMembers(nullableDamType.NullableType, _annotations.GetGenericParameterValue(underlyingGenericParameter.GenericParameter)),
// This should only happen if the code does something like typeof(Nullable<>).MakeGenericType(methodParameter).TypeHandle
RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers nullableDamType when nullableDamType.UnderlyingTypeValue is ValueWithDynamicallyAccessedMembers underlyingTypeValue
=> new NullableValueWithDynamicallyAccessedMembers(nullableDamType.NullableType, underlyingTypeValue),
RuntimeTypeHandleValue typeHandle
=> new SystemTypeValue(typeHandle.RepresentedType),
RuntimeTypeHandleForGenericParameterValue genericParam
=> _annotations.GetGenericParameterValue(genericParam.GenericParameter),
RuntimeTypeHandleForValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers
=> valueWithDynamicallyAccessedMembers.UnderlyingTypeValue,
_ => annotatedMethodReturnValue
});
}
break;
case IntrinsicId.Type_get_TypeHandle:
if (instanceValue.IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
foreach (var value in instanceValue.AsEnumerable())
{
if (value != NullValue.Instance)
AddReturnValue(value switch
{
NullableSystemTypeValue nullableSystemType
=> new RuntimeTypeHandleForNullableSystemTypeValue(nullableSystemType.NullableType, nullableSystemType.UnderlyingTypeValue),
NullableValueWithDynamicallyAccessedMembers nullableDamType when nullableDamType.UnderlyingTypeValue is GenericParameterValue genericParam
=> new RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers(nullableDamType.NullableType, new RuntimeTypeHandleForGenericParameterValue(genericParam.GenericParameter)),
NullableValueWithDynamicallyAccessedMembers nullableDamType
=> new RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers(nullableDamType.NullableType, nullableDamType.UnderlyingTypeValue),
SystemTypeValue typeHandle
=> new RuntimeTypeHandleValue(typeHandle.RepresentedType),
GenericParameterValue genericParam
=> new RuntimeTypeHandleForGenericParameterValue(genericParam.GenericParameter),
ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers
=> new RuntimeTypeHandleForValueWithDynamicallyAccessedMembers(valueWithDynamicallyAccessedMembers),
_ => annotatedMethodReturnValue
});
else
AddReturnValue(MultiValueLattice.Top);
}
break;
// System.Reflection.MethodBase.GetMethodFromHandle(RuntimeMethodHandle handle)
// System.Reflection.MethodBase.GetMethodFromHandle(RuntimeMethodHandle handle, RuntimeTypeHandle declaringType)
case IntrinsicId.MethodBase_GetMethodFromHandle:
{
if (argumentValues[0].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
// Infrastructure piece to support "ldtoken method -> GetMethodFromHandle"
foreach (var value in argumentValues[0].AsEnumerable())
{
if (value is RuntimeMethodHandleValue methodHandle)
AddReturnValue(new SystemReflectionMethodBaseValue(methodHandle.RepresentedMethod));
else
AddReturnValue(annotatedMethodReturnValue);
}
}
break;
case IntrinsicId.MethodBase_get_MethodHandle:
{
if (instanceValue.IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
foreach (var value in instanceValue.AsEnumerable())
{
if (value is SystemReflectionMethodBaseValue methodBaseValue)
AddReturnValue(new RuntimeMethodHandleValue(methodBaseValue.RepresentedMethod));
else
AddReturnValue(annotatedMethodReturnValue);
}
}
break;
case IntrinsicId.TypeDelegator_Ctor:
// This needs additional validation that the .ctor is called from a "newobj" instruction/operation
// so it can't be done easily in shared code yet.
case IntrinsicId.Array_Empty:
// Array.Empty<T> must for now be handled by the specific implementation since it requires instantiated generic method handling
case IntrinsicId.Object_GetType:
// Object.GetType requires additional handling by the caller to implement type hierarchy marking and related diagnostics
throw new NotImplementedException("These intrinsics should be handled by the specific implementation: " + intrinsicId);
//
// GetInterface(String)
// GetInterface(String, bool)
//
case IntrinsicId.Type_GetInterface:
{
if (instanceValue.IsEmpty() || argumentValues[0].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
var targetValue = _annotations.GetMethodThisParameterValue(calledMethod, DynamicallyAccessedMemberTypes.Interfaces);
foreach (var value in instanceValue.AsEnumerable())
{
foreach (var interfaceName in argumentValues[0].AsEnumerable())
{
if (interfaceName == NullValue.Instance)
{
// Throws on null string, so no return value.
AddReturnValue(MultiValueLattice.Top);
}
else if (interfaceName is KnownStringValue stringValue && stringValue.Contents.Length == 0)
{
AddReturnValue(NullValue.Instance);
}
else
{
// For now no support for marking a single interface by name. We would have to correctly support
// mangled names for generics to do that correctly. Simply mark all interfaces on the type for now.
// Require Interfaces annotation
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
// Interfaces is transitive, so the return values will always have at least Interfaces annotation
DynamicallyAccessedMemberTypes returnMemberTypes = DynamicallyAccessedMemberTypes.Interfaces;
// Propagate All annotation across the call - All is a superset of Interfaces
if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers
&& valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All)
returnMemberTypes = DynamicallyAccessedMemberTypes.All;
AddReturnValue(_annotations.GetMethodReturnValue(calledMethod, _isNewObj, returnMemberTypes));
}
}
}
}
break;
//
// GetInterfaces
//
case IntrinsicId.Type_GetInterfaces:
{
if (instanceValue.IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
var targetValue = _annotations.GetMethodThisParameterValue(calledMethod, DynamicallyAccessedMemberTypes.Interfaces);
foreach (var value in instanceValue.AsEnumerable())
{
// Require Interfaces annotation
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
// Interfaces is transitive, so the return values will always have at least Interfaces annotation
DynamicallyAccessedMemberTypes returnMemberTypes = DynamicallyAccessedMemberTypes.Interfaces;
// Propagate All annotation across the call - All is a superset of Interfaces
if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers
&& valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All)
returnMemberTypes = DynamicallyAccessedMemberTypes.All;
// We model this as if each individual element of the returned array was returned from the GetInterfaces method call.
// It makes diagnostics fall out nicer because we now know where the Type comes from and where to assign blame, should the requirements not match
AddReturnValue(new ArrayOfAnnotatedSystemTypeValue(_annotations.GetMethodReturnValue(calledMethod, isNewObj: false, returnMemberTypes)));
}
}
break;
//
// AssemblyQualifiedName
//
case IntrinsicId.Type_get_AssemblyQualifiedName:
{
if (instanceValue.IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
foreach (var value in instanceValue.AsEnumerable())
{
if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers)
{
// Currently we don't need to track the difference between Type and String annotated values
// that only matters when we use them, so Type.GetType is the difference really.
// For diagnostics we actually don't want to track the Type.AssemblyQualifiedName
// as the annotation does not come from that call, but from its input.
AddReturnValue(valueWithDynamicallyAccessedMembers);
}
else if (value == NullValue.Instance)
{
// NullReferenceException, no return value.
AddReturnValue(MultiValueLattice.Top);
}
else
{
AddReturnValue(UnknownValue.Instance);
}
}
}
break;
//
// System.Runtime.CompilerServices.RuntimeHelpers
//
// RunClassConstructor(RuntimeTypeHandle type)
//
case IntrinsicId.RuntimeHelpers_RunClassConstructor:
if (argumentValues[0].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
foreach (var typeHandleValue in argumentValues[0].AsEnumerable())
{
if (typeHandleValue is RuntimeTypeHandleValue runtimeTypeHandleValue)
{
MarkStaticConstructor(runtimeTypeHandleValue.RepresentedType);
}
else if (typeHandleValue is RuntimeTypeHandleForValueWithDynamicallyAccessedMembers damAnnotatedHandle
&& (damAnnotatedHandle.UnderlyingTypeValue.DynamicallyAccessedMemberTypes & DynamicallyAccessedMemberTypes.NonPublicConstructors) != 0)
{
// No action needed, NonPublicConstructors keeps the static constructor on the type
}
else if (typeHandleValue is RuntimeTypeHandleForNullableSystemTypeValue)
{
// No action needed, RunClassConstructor does not run the class constructor of the underlying type of Nullable<T>
}
else
{
_diagnosticContext.AddDiagnostic(DiagnosticId.UnrecognizedTypeInRuntimeHelpersRunClassConstructor, calledMethod.GetDisplayName());
}
}
break;
//
// GetConstructors(BindingFlags)
// GetMethods(BindingFlags)
// GetFields(BindingFlags)
// GetEvents(BindingFlags)
// GetProperties(BindingFlags)
// GetNestedTypes(BindingFlags)
// GetMembers(BindingFlags)
//
case IntrinsicId.Type_GetConstructors__BindingFlags:
case IntrinsicId.Type_GetMethods__BindingFlags:
case IntrinsicId.Type_GetFields__BindingFlags:
case IntrinsicId.Type_GetProperties__BindingFlags:
case IntrinsicId.Type_GetEvents__BindingFlags:
case IntrinsicId.Type_GetNestedTypes__BindingFlags:
case IntrinsicId.Type_GetMembers__BindingFlags:
{
BindingFlags? bindingFlags;
bindingFlags = GetBindingFlagsFromValue(argumentValues[0]);
DynamicallyAccessedMemberTypes memberTypes;
if (BindingFlagsAreUnsupported(bindingFlags))
{
memberTypes = intrinsicId switch
{
IntrinsicId.Type_GetConstructors__BindingFlags => DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors,
IntrinsicId.Type_GetMethods__BindingFlags => DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods,
IntrinsicId.Type_GetEvents__BindingFlags => DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents,
IntrinsicId.Type_GetFields__BindingFlags => DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields,
IntrinsicId.Type_GetProperties__BindingFlags => DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties,
IntrinsicId.Type_GetNestedTypes__BindingFlags => DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes,
IntrinsicId.Type_GetMembers__BindingFlags => DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors |
DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents |
DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields |
DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods |
DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties |
DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes,
_ => throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{GetContainingSymbolDisplayName()}' is of unexpected member type."),
};
}
else
{
memberTypes = intrinsicId switch
{
IntrinsicId.Type_GetConstructors__BindingFlags => GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(bindingFlags),
IntrinsicId.Type_GetMethods__BindingFlags => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags),
IntrinsicId.Type_GetEvents__BindingFlags => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents(bindingFlags),
IntrinsicId.Type_GetFields__BindingFlags => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields(bindingFlags),
IntrinsicId.Type_GetProperties__BindingFlags => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties(bindingFlags),
IntrinsicId.Type_GetNestedTypes__BindingFlags => GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes(bindingFlags),
IntrinsicId.Type_GetMembers__BindingFlags => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers(bindingFlags),
_ => throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{GetContainingSymbolDisplayName()}' is of unexpected member type."),
};
}
var targetValue = _annotations.GetMethodThisParameterValue(calledMethod, memberTypes);
_requireDynamicallyAccessedMembersAction.Invoke(instanceValue, targetValue);
}
break;
//
// GetField(string)
// GetField(string, BindingFlags)
// GetEvent(string)
// GetEvent(string, BindingFlags)
// GetProperty(string)
// GetProperty(string, BindingFlags)
// GetProperty(string, Type)
// GetProperty(string, Type[])
// GetProperty(string, Type, Type[])
// GetProperty(string, Type, Type[], ParameterModifier[])
// GetProperty(string, BindingFlags, Binder, Type, Type[], ParameterModifier[])
//
case IntrinsicId.Type_GetField:
case IntrinsicId.Type_GetProperty:
case IntrinsicId.Type_GetEvent:
{
if (instanceValue.IsEmpty() || argumentValues[0].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
BindingFlags? bindingFlags;
if (calledMethod.HasParameterOfType((ParameterIndex)2, "System.Reflection.BindingFlags"))
bindingFlags = GetBindingFlagsFromValue(argumentValues[1]);
else
// Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter
bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
DynamicallyAccessedMemberTypes memberTypes = intrinsicId switch
{
IntrinsicId.Type_GetEvent => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents(bindingFlags),
IntrinsicId.Type_GetField => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields(bindingFlags),
IntrinsicId.Type_GetProperty => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties(bindingFlags),
_ => throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{GetContainingSymbolDisplayName()}' is of unexpected member type."),
};
var targetValue = _annotations.GetMethodThisParameterValue(calledMethod, memberTypes);
foreach (var value in instanceValue.AsEnumerable())
{
if (value is SystemTypeValue systemTypeValue)
{
foreach (var stringParam in argumentValues[0].AsEnumerable())
{
if (stringParam is KnownStringValue stringValue && !BindingFlagsAreUnsupported(bindingFlags))
{
switch (intrinsicId)
{
case IntrinsicId.Type_GetEvent:
MarkEventsOnTypeHierarchy(systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags);
break;
case IntrinsicId.Type_GetField:
MarkFieldsOnTypeHierarchy(systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags);
break;
case IntrinsicId.Type_GetProperty:
MarkPropertiesOnTypeHierarchy(systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags);
break;
default:
Debug.Fail("Unreachable.");
break;
}
}
else
{
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
}
}
}
else
{
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
}
}
}
break;
//
// GetMember(String)
// GetMember(String, BindingFlags)
// GetMember(String, MemberTypes, BindingFlags)
//
case IntrinsicId.Type_GetMember:
{
if (instanceValue.IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
BindingFlags? bindingFlags;
if (calledMethod.HasMetadataParametersCount(1))
{
// Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter
bindingFlags = BindingFlags.Public | BindingFlags.Instance;
}
else if (calledMethod.HasMetadataParametersCount(2) && calledMethod.HasParameterOfType((ParameterIndex)2, "System.Reflection.BindingFlags"))
bindingFlags = GetBindingFlagsFromValue(argumentValues[1]);
else if (calledMethod.HasMetadataParametersCount(3) && calledMethod.HasParameterOfType((ParameterIndex)3, "System.Reflection.BindingFlags"))
{
bindingFlags = GetBindingFlagsFromValue(argumentValues[2]);
}
else // Non recognized intrinsic
throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{GetContainingSymbolDisplayName()}' is an unexpected intrinsic.");
DynamicallyAccessedMemberTypes requiredMemberTypes;
if (BindingFlagsAreUnsupported(bindingFlags))
{
requiredMemberTypes = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors |
DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents |
DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields |
DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods |
DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties |
DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes;
}
else
{
requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers(bindingFlags);
}
var targetValue = _annotations.GetMethodThisParameterValue(calledMethod, requiredMemberTypes);
// Go over all types we've seen
foreach (var value in instanceValue.AsEnumerable())
{
// Mark based on bitfield requirements
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
}
}
break;
//
// GetMethod(string)
// GetMethod(string, BindingFlags)
// GetMethod(string, Type[])
// GetMethod(string, Type[], ParameterModifier[])
// GetMethod(string, BindingFlags, Type[])
// GetMethod(string, BindingFlags, Binder, Type[], ParameterModifier[])
// GetMethod(string, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[])
// GetMethod(string, int, Type[])
// GetMethod(string, int, Type[], ParameterModifier[]?)
// GetMethod(string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?)
// GetMethod(string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?)
//
case IntrinsicId.Type_GetMethod:
{
if (instanceValue.IsEmpty() || argumentValues[0].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
BindingFlags? bindingFlags;
if (calledMethod.HasParameterOfType((ParameterIndex)2, "System.Reflection.BindingFlags"))
bindingFlags = GetBindingFlagsFromValue(argumentValues[1]);
else if (calledMethod.HasParameterOfType((ParameterIndex)3, "System.Reflection.BindingFlags"))
bindingFlags = GetBindingFlagsFromValue(argumentValues[2]);
else
// Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter
bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
var targetValue = _annotations.GetMethodThisParameterValue(calledMethod, GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags));
foreach (var value in instanceValue.AsEnumerable())
{
if (value is SystemTypeValue systemTypeValue)
{
foreach (var stringParam in argumentValues[0].AsEnumerable())
{
if (stringParam is KnownStringValue stringValue && !BindingFlagsAreUnsupported(bindingFlags))
{
AddReturnValue(MultiValueLattice.Top); ; // Initialize return value (so that it's not autofilled if there are no matching methods)
foreach (var methodValue in ProcessGetMethodByName(systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags))
AddReturnValue(methodValue);
}
else if (stringParam is NullValue)
{
// GetMethod(null) throws - so track empty value set as its result
AddReturnValue(MultiValueLattice.Top);
}
else
{
// Otherwise fall back to the bitfield requirements
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
AddReturnValue(annotatedMethodReturnValue);
}
}
}
else if (value is NullValue)
{
// null.GetMethod(...) throws - so track empty value set as its result
AddReturnValue(MultiValueLattice.Top);
}
else if (value is MethodReturnValue returnValueType &&
returnValueType.Method.IsDeclaredOnType("System.Reflection.EventInfo") &&
returnValueType.Method.Name == "get_EventHandlerType")
{
foreach (var stringParam in argumentValues[0].AsEnumerable())
{
if (stringParam is KnownStringValue stringValue && stringValue.Contents == "Invoke")
{
// EventInfo.EventHandlerType.GetMethod("Invoke") is trim-safe because we keep the Invoke method on delegates
AddReturnValue(MultiValueLattice.Top);
}
else
{
// Otherwise fall back to the bitfield requirements
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
AddReturnValue(annotatedMethodReturnValue);
}
}
}
else
{
// Otherwise fall back to the bitfield requirements
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
AddReturnValue(annotatedMethodReturnValue);
}
}
}
break;
//
// GetNestedType(string)
// GetNestedType(string, BindingFlags)
//
case IntrinsicId.Type_GetNestedType:
{
if (instanceValue.IsEmpty() || argumentValues[0].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
const DynamicallyAccessedMemberTypes ImplicitNestedTypeAccessLevel =
DynamicallyAccessedMemberTypesEx.PublicConstructorsWithInherited | DynamicallyAccessedMemberTypesEx.NonPublicConstructorsWithInherited |
DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypesEx.NonPublicMethodsWithInherited |
DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypesEx.NonPublicFieldsWithInherited |
DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypesEx.NonPublicPropertiesWithInherited |
DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypesEx.NonPublicEventsWithInherited |
DynamicallyAccessedMemberTypesEx.PublicNestedTypesWithInherited | DynamicallyAccessedMemberTypesEx.NonPublicNestedTypesWithInherited |
DynamicallyAccessedMemberTypes.Interfaces;
BindingFlags? bindingFlags;
if (calledMethod.HasParameterOfType((ParameterIndex)2, "System.Reflection.BindingFlags"))
bindingFlags = GetBindingFlagsFromValue(argumentValues[1]);
else
// Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter
bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
var targetValue = _annotations.GetMethodThisParameterValue(calledMethod, GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes(bindingFlags));
foreach (var value in instanceValue.AsEnumerable())
{
if (value is SystemTypeValue systemTypeValue)
{
foreach (var stringParam in argumentValues[0].AsEnumerable())
{
if (stringParam is KnownStringValue stringValue && !BindingFlagsAreUnsupported(bindingFlags))
{
AddReturnValue(MultiValueLattice.Top);
foreach (var nestedTypeValue in GetNestedTypesOnType(systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags))
{
MarkType(nestedTypeValue.RepresentedType);
AddReturnValue(nestedTypeValue);
}
}
else if (stringParam is NullValue)
{
AddReturnValue(MultiValueLattice.Top);
}
else
{
// Otherwise fall back to the bitfield requirements
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
// We only applied the annotation based on binding flags, so we will keep the necessary types
// and we keep the set of implicitly available members on them.
AddReturnValue(_annotations.GetMethodReturnValue(calledMethod, _isNewObj, ImplicitNestedTypeAccessLevel));
}
}
}
else if (value is NullValue)
{
// null.GetNestedType(..) throws - so track empty value set
AddReturnValue(MultiValueLattice.Top);
}
else
{
// Otherwise fall back to the bitfield requirements
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
// If the input is an annotated value which has All - we can propagate that to the return value
// since All applies recursively to all nested type (see MarkStep.MarkEntireType).
// Otherwise we mark the nested type with implicitly available members on it.
if (value is ValueWithDynamicallyAccessedMembers { DynamicallyAccessedMemberTypes: DynamicallyAccessedMemberTypes.All })
AddReturnValue(_annotations.GetMethodReturnValue(calledMethod, _isNewObj, DynamicallyAccessedMemberTypes.All));
else
AddReturnValue(_annotations.GetMethodReturnValue(calledMethod, _isNewObj, ImplicitNestedTypeAccessLevel));
}
}
}
break;
//
// System.Reflection.RuntimeReflectionExtensions
//
// static GetRuntimeEvent(this Type type, string name)
// static GetRuntimeField(this Type type, string name)
// static GetRuntimeMethod(this Type type, string name, Type[] parameters)
// static GetRuntimeProperty(this Type type, string name)
//
case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent:
case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField:
case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod:
case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty:
{
if (argumentValues[0].IsEmpty() || argumentValues[1].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
DynamicallyAccessedMemberTypes requiredMemberTypes = intrinsicId switch
{
IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent => DynamicallyAccessedMemberTypes.PublicEvents,
IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField => DynamicallyAccessedMemberTypes.PublicFields,
IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod => DynamicallyAccessedMemberTypes.PublicMethods,
IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty => DynamicallyAccessedMemberTypes.PublicProperties,
_ => throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{GetContainingSymbolDisplayName()}' is of unexpected member type."),
};
var targetValue = _annotations.GetMethodParameterValue(new(calledMethod, (ParameterIndex)1), requiredMemberTypes);
foreach (var value in argumentValues[0].AsEnumerable())
{
if (value is SystemTypeValue systemTypeValue)
{
foreach (var stringParam in argumentValues[1].AsEnumerable())
{
if (stringParam is KnownStringValue stringValue)
{
switch (intrinsicId)
{
case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent:
MarkEventsOnTypeHierarchy(systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags);
break;
case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField:
MarkFieldsOnTypeHierarchy(systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags);
break;
case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod:
AddReturnValue(MultiValueLattice.Top); // Initialize return value (so that it's not autofilled if there are no matching methods)
foreach (var methodValue in ProcessGetMethodByName(systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags))
AddReturnValue(methodValue);
break;
case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty:
MarkPropertiesOnTypeHierarchy(systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags);
break;
default:
throw new ArgumentException($"Error processing reflection call '{calledMethod.GetDisplayName()}' inside {GetContainingSymbolDisplayName()}. Unexpected member kind.");
}
}
else if (stringParam is NullValue)
{
// GetRuntimeMethod(type, null) throws - so track empty value set as its result
AddReturnValue(MultiValueLattice.Top);
}
else
{
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
AddReturnValue(annotatedMethodReturnValue);
}
}
}
else if (value is NullValue)
{
// GetRuntimeMethod(null, ...) throws - so track empty value set as its result
AddReturnValue(MultiValueLattice.Top);
}
else
{
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
AddReturnValue(annotatedMethodReturnValue);
}
}
}
break;
//
// System.Linq.Expressions.Expression
//
// static New(Type)
//
case IntrinsicId.Expression_New:
{
var targetValue = _annotations.GetMethodParameterValue(new(calledMethod, (ParameterIndex)0), DynamicallyAccessedMemberTypes.PublicParameterlessConstructor);
foreach (var value in argumentValues[0].AsEnumerable())
{
if (value is SystemTypeValue systemTypeValue)
{
MarkConstructorsOnType(systemTypeValue.RepresentedType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, parameterCount: null);
}
else
{
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
}
}
}
break;
//
// System.Linq.Expressions.Expression
//
// static Property(Expression, MethodInfo)
//
case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType((ParameterIndex)1, "System.Reflection.MethodInfo"):
{
if (argumentValues[1].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
foreach (var value in argumentValues[1].AsEnumerable())
{
if (value is SystemReflectionMethodBaseValue methodBaseValue)
{
// We have one of the accessors for the property. The Expression.Property will in this case search
// for the matching PropertyInfo and store that. So to be perfectly correct we need to mark the
// respective PropertyInfo as "accessed via reflection".
if (MarkAssociatedProperty(methodBaseValue.RepresentedMethod))
continue;
}
else if (value == NullValue.Instance)
{
continue;
}
// In all other cases we may not even know which type this is about, so there's nothing we can do
// report it as a warning.
_diagnosticContext.AddDiagnostic(DiagnosticId.PropertyAccessorParameterInLinqExpressionsCannotBeStaticallyDetermined,
_annotations.GetMethodParameterValue(new(calledMethod, (ParameterIndex)1), DynamicallyAccessedMemberTypes.None).GetDiagnosticArgumentsForAnnotationMismatch().ToArray());
}
}
break;
//
// System.Linq.Expressions.Expression
//
// static Field(Expression, Type, String)
// static Property(Expression, Type, String)
//
case IntrinsicId.Expression_Field:
case IntrinsicId.Expression_Property:
{
DynamicallyAccessedMemberTypes memberTypes = intrinsicId == IntrinsicId.Expression_Property
? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties
: DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields;
if (argumentValues[1].IsEmpty() || argumentValues[2].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
var targetValue = _annotations.GetMethodParameterValue(new(calledMethod, (ParameterIndex)1), memberTypes);
foreach (var value in argumentValues[1].AsEnumerable())
{
if (value is SystemTypeValue systemTypeValue)
{
foreach (var stringParam in argumentValues[2].AsEnumerable())
{
if (stringParam is KnownStringValue stringValue)
{
BindingFlags bindingFlags = argumentValues[0].AsSingleValue() is NullValue ? BindingFlags.Static : BindingFlags.Default;
if (intrinsicId == IntrinsicId.Expression_Property)
{
MarkPropertiesOnTypeHierarchy(systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags);
}
else
{
MarkFieldsOnTypeHierarchy(systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags);
}
}
else if (stringParam is NullValue)
{
// Null name will always throw, so there's nothing to do
}
else
{
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
}
}
}
else
{
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
}
}
}
break;
//
// System.Linq.Expressions.Expression
//
// static Call(Type, String, Type[], Expression[])
//
case IntrinsicId.Expression_Call:
{
BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
var targetValue = _annotations.GetMethodParameterValue(
new ParameterProxy(calledMethod, (ParameterIndex)0),
GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags));
// This is true even if we "don't know" - so it's only false if we're sure that there are no type arguments
bool hasTypeArguments = (argumentValues[2].AsSingleValue() as ArrayValue)?.Size.AsConstInt() != 0;
foreach (var value in argumentValues[0].AsEnumerable())
{
if (value is SystemTypeValue systemTypeValue)
{
foreach (var stringParam in argumentValues[1].AsEnumerable())
{
if (stringParam is KnownStringValue stringValue)
{
foreach (var method in GetMethodsOnTypeHierarchy(systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags))
{
ValidateGenericMethodInstantiation(method.RepresentedMethod, argumentValues[2], calledMethod);
MarkMethod(method.RepresentedMethod);
}
}
else
{
if (hasTypeArguments)
{
// We don't know what method the `MakeGenericMethod` was called on, so we have to assume
// that the method may have requirements which we can't fullfil -> warn.
_diagnosticContext.AddDiagnostic(DiagnosticId.MakeGenericMethod, calledMethod.GetDisplayName());
}
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
}
}
}
else
{
if (hasTypeArguments)
{
// We don't know what method the `MakeGenericMethod` was called on, so we have to assume
// that the method may have requirements which we can't fullfil -> warn.
_diagnosticContext.AddDiagnostic(DiagnosticId.MakeGenericMethod, calledMethod.GetDisplayName());
}
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
}
}
}
break;
//
// Nullable.GetUnderlyingType(Type)
//
case IntrinsicId.Nullable_GetUnderlyingType:
if (argumentValues[0].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
foreach (var singlevalue in argumentValues[0].AsEnumerable())
{
AddReturnValue(singlevalue switch
{
SystemTypeValue systemType =>
systemType.RepresentedType.IsTypeOf("System", "Nullable`1")
// This will happen if there's typeof(Nullable<>).MakeGenericType(unknown) - we know the return value is Nullable<>
// but we don't know of what. So we represent it as known type, but not as known nullable type.
// Has to be special cased here, since we need to return "unknown" type.
? annotatedMethodReturnValue
: MultiValueLattice.Top, // This returns null at runtime, so return empty value
NullableSystemTypeValue nullableSystemType => nullableSystemType.UnderlyingTypeValue,
NullableValueWithDynamicallyAccessedMembers nullableDamValue => nullableDamValue.UnderlyingTypeValue,
ValueWithDynamicallyAccessedMembers damValue => damValue,
_ => annotatedMethodReturnValue
});
}
break;
//
// System.Type
//
// GetType(string)
// GetType(string, Boolean)
// GetType(string, Boolean, Boolean)
// GetType(string, Func<AssemblyName, Assembly>, Func<Assembly, String, Boolean, Type>)
// GetType(string, Func<AssemblyName, Assembly>, Func<Assembly, String, Boolean, Type>, Boolean)
// GetType(string, Func<AssemblyName, Assembly>, Func<Assembly, String, Boolean, Type>, Boolean, Boolean)
//
case IntrinsicId.Type_GetType:
{
if (argumentValues[0].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
if ((calledMethod.HasMetadataParametersCount(3) && calledMethod.HasParameterOfType((ParameterIndex)2, "System.Boolean") && argumentValues[2].AsConstInt() != 0) ||
(calledMethod.HasMetadataParametersCount(5) && argumentValues[4].AsConstInt() != 0))
{
_diagnosticContext.AddDiagnostic(DiagnosticId.CaseInsensitiveTypeGetTypeCallIsNotSupported, calledMethod.GetDisplayName());
returnValue = MultiValueLattice.Top; // This effectively disables analysis of anything which uses the return value
break;
}
foreach (var typeNameValue in argumentValues[0].AsEnumerable())
{
if (typeNameValue is KnownStringValue knownStringValue)
{
if (!_requireDynamicallyAccessedMembersAction.TryResolveTypeNameAndMark(knownStringValue.Contents, false, out TypeProxy foundType))
{
// Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back.
AddReturnValue(MultiValueLattice.Top);
}
else
{
AddReturnValue(new SystemTypeValue(foundType));
}
}
else if (typeNameValue == NullValue.Instance)
{
// Nothing to do - this throws at runtime
AddReturnValue(MultiValueLattice.Top);
}
else if (typeNameValue is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers && valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes != 0)
{
// Propagate the annotation from the type name to the return value. Annotation on a string value will be fulfilled whenever a value is assigned to the string with annotation.
// So while we don't know which type it is, we can guarantee that it will fulfill the annotation.
AddReturnValue(_annotations.GetMethodReturnValue(calledMethod, _isNewObj, valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes));
}
else
{
_diagnosticContext.AddDiagnostic(DiagnosticId.UnrecognizedTypeNameInTypeGetType, calledMethod.GetDisplayName());
AddReturnValue(MultiValueLattice.Top);
}
}
}
break;
//
// System.Type
//
// Type MakeGenericType(params Type[] typeArguments)
//
case IntrinsicId.Type_MakeGenericType:
if (instanceValue.IsEmpty() || argumentValues[0].IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
foreach (var value in instanceValue.AsEnumerable())
{
if (value is SystemTypeValue typeValue)
{
// Special case Nullable<T>
// Nullables without a type argument are considered SystemTypeValues
if (typeValue.RepresentedType.IsTypeOf("System", "Nullable`1"))
{
// Note that we're not performing any generic parameter validation
// Special case: Nullable<T> where T : struct
// The struct constraint in C# implies new() constraint, but Nullable doesn't make a use of that part.
// There are several places even in the framework where typeof(Nullable<>).MakeGenericType would warn
// without any good reason to do so.
foreach (var argumentValue in argumentValues[0].AsEnumerable())
{
if ((argumentValue as ArrayValue)?.TryGetValueByIndex(0, out var underlyingMultiValue) == true)
{
foreach (var underlyingValue in underlyingMultiValue.AsEnumerable())
{
switch (underlyingValue)
{
// Don't warn on these types - it will throw instead
case NullableValueWithDynamicallyAccessedMembers:
case NullableSystemTypeValue:
case SystemTypeValue maybeArrayValue when maybeArrayValue.RepresentedType.IsTypeOf("System", "Array"):
AddReturnValue(MultiValueLattice.Top);
break;
case SystemTypeValue systemTypeValue:
AddReturnValue(new NullableSystemTypeValue(typeValue.RepresentedType, new SystemTypeValue(systemTypeValue.RepresentedType)));
break;
// Generic Parameters and method parameters with annotations
case ValueWithDynamicallyAccessedMembers damValue:
AddReturnValue(new NullableValueWithDynamicallyAccessedMembers(typeValue.RepresentedType, damValue));
break;
// Everything else assume it has no annotations
default:
// This returns just Nullable<> SystemTypeValue - so some things will work, but GetUnderlyingType won't propagate anything
// It's special cased to do that.
AddReturnValue(value);
break;
}
}
}
else
{
// This returns just Nullable<> SystemTypeValue - so some things will work, but GetUnderlyingType won't propagate anything
// It's special cased to do that.
AddReturnValue(value);
}
}
// We want to skip adding the `value` to the return Value because we have already added Nullable<value>
continue;
}
else
{
// Any other type - perform generic parameter validation
var genericParameterValues = GetGenericParameterValues(typeValue.RepresentedType.GetGenericParameters());
if (!AnalyzeGenericInstantiationTypeArray(argumentValues[0], genericParameterValues))
{
_diagnosticContext.AddDiagnostic(DiagnosticId.MakeGenericType, calledMethod.GetDisplayName());
}
}
}
else if (value == NullValue.Instance)
{
// At runtime this would throw - so it has no effect on analysis
AddReturnValue(MultiValueLattice.Top);
}
else
{
// We have no way to "include more" to fix this if we don't know, so we have to warn
_diagnosticContext.AddDiagnostic(DiagnosticId.MakeGenericType, calledMethod.GetDisplayName());
}
// We don't want to lose track of the type
// in case this is e.g. Activator.CreateInstance(typeof(Foo<>).MakeGenericType(...));
// Note this is not called in the Nullable case - we skipt this via the 'continue'.
AddReturnValue(value);
}
break;
//
// Type.BaseType
//
case IntrinsicId.Type_get_BaseType:
{
if (instanceValue.IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
foreach (var value in instanceValue.AsEnumerable())
{
if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers)
{
DynamicallyAccessedMemberTypes propagatedMemberTypes = DynamicallyAccessedMemberTypes.None;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All)
propagatedMemberTypes = DynamicallyAccessedMemberTypes.All;
else
{
// PublicConstructors are not propagated to base type
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypesEx.PublicConstructorsWithInherited))
propagatedMemberTypes |= DynamicallyAccessedMemberTypesEx.PublicConstructorsWithInherited;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypesEx.NonPublicConstructorsWithInherited))
propagatedMemberTypes |= DynamicallyAccessedMemberTypesEx.NonPublicConstructorsWithInherited;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicEvents))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicEvents;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypesEx.NonPublicEventsWithInherited))
propagatedMemberTypes |= DynamicallyAccessedMemberTypesEx.NonPublicEventsWithInherited;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicFields))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicFields;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypesEx.NonPublicFieldsWithInherited))
propagatedMemberTypes |= DynamicallyAccessedMemberTypesEx.NonPublicFieldsWithInherited;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicMethods))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicMethods;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypesEx.NonPublicMethodsWithInherited))
propagatedMemberTypes |= DynamicallyAccessedMemberTypesEx.NonPublicMethodsWithInherited;
// PublicNestedTypes are not propagated to base type
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypesEx.PublicNestedTypesWithInherited))
propagatedMemberTypes |= DynamicallyAccessedMemberTypesEx.PublicNestedTypesWithInherited;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypesEx.NonPublicNestedTypesWithInherited))
propagatedMemberTypes |= DynamicallyAccessedMemberTypesEx.NonPublicNestedTypesWithInherited;
// PublicParameterlessConstructor is not propagated to base type
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicProperties))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicProperties;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypesEx.NonPublicPropertiesWithInherited))
propagatedMemberTypes |= DynamicallyAccessedMemberTypesEx.NonPublicPropertiesWithInherited;
if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.Interfaces))
propagatedMemberTypes |= DynamicallyAccessedMemberTypes.Interfaces;
}
AddReturnValue(_annotations.GetMethodReturnValue(calledMethod, _isNewObj, propagatedMemberTypes));
}
else if (value is SystemTypeValue systemTypeValue)
{
if (TryGetBaseType(systemTypeValue.RepresentedType, out var baseType))
AddReturnValue(new SystemTypeValue(baseType.Value));
else
AddReturnValue(annotatedMethodReturnValue);
}
else if (value == NullValue.Instance)
{
// Ignore nulls - null.BaseType will fail at runtime, but it has no effect on static analysis
AddReturnValue(MultiValueLattice.Top);
continue;
}
else
{
// Unknown input - propagate a return value without any annotation - we know it's a Type but we know nothing about it
AddReturnValue(annotatedMethodReturnValue);
}
}
}
break;
//
// GetConstructor(Type[])
// GetConstructor(BindingFlags, Type[])
// GetConstructor(BindingFlags, Binder, Type[], ParameterModifier [])
// GetConstructor(BindingFlags, Binder, CallingConventions, Type[], ParameterModifier [])
//
case IntrinsicId.Type_GetConstructor:
{
if (instanceValue.IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
BindingFlags? bindingFlags;
if (calledMethod.HasParameterOfType((ParameterIndex)1, "System.Reflection.BindingFlags"))
bindingFlags = GetBindingFlagsFromValue(argumentValues[0]);
else
// Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter
bindingFlags = BindingFlags.Public | BindingFlags.Instance;
int? ctorParameterCount = calledMethod.GetMetadataParametersCount() switch
{
1 => (argumentValues[0].AsSingleValue() as ArrayValue)?.Size.AsConstInt(),
2 => (argumentValues[1].AsSingleValue() as ArrayValue)?.Size.AsConstInt(),
4 => (argumentValues[2].AsSingleValue() as ArrayValue)?.Size.AsConstInt(),
5 => (argumentValues[3].AsSingleValue() as ArrayValue)?.Size.AsConstInt(),
_ => null,
};
// Go over all types we've seen
foreach (var value in instanceValue.AsEnumerable())
{
if (value is SystemTypeValue systemTypeValue && !BindingFlagsAreUnsupported(bindingFlags))
{
if (HasBindingFlag(bindingFlags, BindingFlags.Public) && !HasBindingFlag(bindingFlags, BindingFlags.NonPublic)
&& ctorParameterCount == 0)
{
MarkPublicParameterlessConstructorOnType(systemTypeValue.RepresentedType);
}
else
{
MarkConstructorsOnType(systemTypeValue.RepresentedType, bindingFlags, parameterCount: null);
}
}
else
{
// Otherwise fall back to the bitfield requirements
var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(bindingFlags);
// We can scope down the public constructors requirement if we know the number of parameters is 0
if (requiredMemberTypes == DynamicallyAccessedMemberTypes.PublicConstructors && ctorParameterCount == 0)
requiredMemberTypes = DynamicallyAccessedMemberTypes.PublicParameterlessConstructor;
var targetValue = _annotations.GetMethodThisParameterValue(calledMethod, requiredMemberTypes);
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
}
}
}
break;
//
// System.Reflection.MethodInfo
//
// MakeGenericMethod(Type[] typeArguments)
//
case IntrinsicId.MethodInfo_MakeGenericMethod:
{
if (instanceValue.IsEmpty())
{
returnValue = MultiValueLattice.Top;
break;
}
foreach (var methodValue in instanceValue.AsEnumerable())
{
if (methodValue is SystemReflectionMethodBaseValue methodBaseValue)
{
ValidateGenericMethodInstantiation(methodBaseValue.RepresentedMethod, argumentValues[0], calledMethod);
}
else if (methodValue == NullValue.Instance)
{
// Nothing to do
}
else
{
// We don't know what method the `MakeGenericMethod` was called on, so we have to assume
// that the method may have requirements which we can't fullfil -> warn.
_diagnosticContext.AddDiagnostic(DiagnosticId.MakeGenericMethod, calledMethod.GetDisplayName());
}
}
// MakeGenericMethod doesn't change the identity of the MethodBase we're tracking so propagate to the return value
AddReturnValue(instanceValue);
}
break;
//
// System.Activator
//
// static CreateInstance(System.Type type)
// static CreateInstance(System.Type type, bool nonPublic)
// static CreateInstance(System.Type type, params object?[]? args)
// static CreateInstance(System.Type type, object?[]? args, object?[]? activationAttributes)
// static CreateInstance(System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture)
// static CreateInstance(System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; }
//
case IntrinsicId.Activator_CreateInstance__Type:
{
int? ctorParameterCount = null;
BindingFlags bindingFlags = BindingFlags.Instance;
if (calledMethod.GetMetadataParametersCount() > 1)
{
if (calledMethod.HasParameterOfType((ParameterIndex)1, "System.Boolean"))
{
// The overload that takes a "nonPublic" bool
bool nonPublic = argumentValues[1].AsConstInt() != 0;
if (nonPublic)
bindingFlags |= BindingFlags.NonPublic | BindingFlags.Public;
else
bindingFlags |= BindingFlags.Public;
ctorParameterCount = 0;
}
else
{
// Overload that has the parameters as the second or fourth argument
int argsParam = calledMethod.HasMetadataParametersCount(2) || calledMethod.HasMetadataParametersCount(3) ? 1 : 3;
if (argumentValues.Count > argsParam)
{
if (argumentValues[argsParam].AsSingleValue() is ArrayValue arrayValue &&
arrayValue.Size.AsConstInt() != null)
ctorParameterCount = arrayValue.Size.AsConstInt();
else if (argumentValues[argsParam].AsSingleValue() is NullValue)
ctorParameterCount = 0;
}
if (calledMethod.GetMetadataParametersCount() > 3)
{
if (argumentValues[1].AsConstInt() is int constInt)
bindingFlags |= (BindingFlags)constInt;
else
bindingFlags |= BindingFlags.NonPublic | BindingFlags.Public;
}
else
{
bindingFlags |= BindingFlags.Public;
}
}
}
else
{
// The overload with a single System.Type argument
ctorParameterCount = 0;
bindingFlags |= BindingFlags.Public;
}
// Go over all types we've seen
foreach (var value in argumentValues[0].AsEnumerable())
{
if (value is SystemTypeValue systemTypeValue)
{
// Special case known type values as we can do better by applying exact binding flags and parameter count.
MarkConstructorsOnType(systemTypeValue.RepresentedType, bindingFlags, ctorParameterCount);
}
else
{
// Otherwise fall back to the bitfield requirements
var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(bindingFlags);
// Special case the public parameterless constructor if we know that there are 0 args passed in
if (ctorParameterCount == 0 && requiredMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors))
{
requiredMemberTypes &= ~DynamicallyAccessedMemberTypes.PublicConstructors;
requiredMemberTypes |= DynamicallyAccessedMemberTypes.PublicParameterlessConstructor;
}
var targetValue = _annotations.GetMethodParameterValue(new(calledMethod, (ParameterIndex)0), requiredMemberTypes);
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
}
}
}
break;
//
// System.Activator
//
// static CreateInstance(string assemblyName, string typeName)
// static CreateInstance(string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes)
// static CreateInstance(string assemblyName, string typeName, object?[]? activationAttributes)
//
case IntrinsicId.Activator_CreateInstance__AssemblyName_TypeName:
ProcessCreateInstanceByName(calledMethod, argumentValues);
break;
//
// System.Activator
//
// static CreateInstanceFrom(string assemblyFile, string typeName)
// static CreateInstanceFrom(string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes)
// static CreateInstanceFrom(string assemblyFile, string typeName, object? []? activationAttributes)
//
case IntrinsicId.Activator_CreateInstanceFrom:
ProcessCreateInstanceByName(calledMethod, argumentValues);
break;
//
// System.AppDomain
//
// CreateInstance(string assemblyName, string typeName)
// CreateInstance(string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes)
// CreateInstance(string assemblyName, string typeName, object? []? activationAttributes)
//
// CreateInstanceAndUnwrap(string assemblyName, string typeName)
// CreateInstanceAndUnwrap(string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes)
// CreateInstanceAndUnwrap(string assemblyName, string typeName, object? []? activationAttributes)
//
// CreateInstanceFrom(string assemblyFile, string typeName)
// CreateInstanceFrom(string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes)
// CreateInstanceFrom(string assemblyFile, string typeName, object? []? activationAttributes)
//
// CreateInstanceFromAndUnwrap(string assemblyFile, string typeName)
// CreateInstanceFromAndUnwrap(string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes)
// CreateInstanceFromAndUnwrap(string assemblyFile, string typeName, object? []? activationAttributes)
//
case IntrinsicId.AppDomain_CreateInstance:
case IntrinsicId.AppDomain_CreateInstanceAndUnwrap:
case IntrinsicId.AppDomain_CreateInstanceFrom:
case IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap:
ProcessCreateInstanceByName(calledMethod, argumentValues);
break;
//
// System.Reflection.Assembly
//
// CreateInstance(string typeName)
// CreateInstance(string typeName, bool ignoreCase)
// CreateInstance(string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder? binder, object []? args, CultureInfo? culture, object []? activationAttributes)
//
case IntrinsicId.Assembly_CreateInstance:
// For now always fail since we don't track assemblies (dotnet/linker/issues/1947)
_diagnosticContext.AddDiagnostic(DiagnosticId.ParametersOfAssemblyCreateInstanceCannotBeAnalyzed, calledMethod.GetDisplayName());
break;
case IntrinsicId.None:
// Verify the argument values match the annotations on the parameter definition
if (requiresDataFlowAnalysis)
{
foreach (var parameter in calledMethod.GetParameters())
{
if (parameter.GetReferenceKind() is ReferenceKind.Out)
continue;
if (parameter.IsImplicitThis)
{
_requireDynamicallyAccessedMembersAction.Invoke(instanceValue, _annotations.GetMethodThisParameterValue(calledMethod));
continue;
}
_requireDynamicallyAccessedMembersAction.Invoke(argumentValues[parameter.MetadataIndex], _annotations.GetMethodParameterValue(parameter));
}
}
break;
default:
return false;
}
if (MethodIsTypeConstructor(calledMethod))
returnValue = UnknownValue.Instance;
methodReturnValue = returnValue;
return true;
void AddReturnValue(MultiValue value)
{
returnValue = (returnValue == null) ? value : MultiValueLattice.Meet(returnValue.Value, value);
}
}
private IEnumerable<MultiValue> ProcessGetMethodByName(TypeProxy type, string methodName, BindingFlags? bindingFlags)
{
bool foundAny = false;
foreach (var method in GetMethodsOnTypeHierarchy(type, methodName, bindingFlags))
{
MarkMethod(method.RepresentedMethod);
yield return method;
foundAny = true;
}
// If there were no methods found the API will return null at runtime, so we should
// track the null as a return value as well.
// This also prevents warnings in such case, since if we don't set the return value it will be
// "unknown" and consumers may warn.
if (!foundAny)
yield return NullValue.Instance;
}
private bool AnalyzeGenericInstantiationTypeArray(in MultiValue arrayParam, ImmutableArray<GenericParameterValue> genericParameters)
{
bool hasRequirements = false;
foreach (var genericParameter in genericParameters)
{
if (GetGenericParameterEffectiveMemberTypes(genericParameter) != DynamicallyAccessedMemberTypes.None)
{
hasRequirements = true;
break;
}
}
// If there are no requirements, then there's no point in warning
if (!hasRequirements)
return true;
foreach (var typesValue in arrayParam.AsEnumerable())
{
if (typesValue is not ArrayValue array)
{
return false;
}
int? size = array.Size.AsConstInt();
if (size == null || size != genericParameters.Length)
{
return false;
}
bool allIndicesKnown = true;
for (int i = 0; i < size.Value; i++)
{
if (!array.TryGetValueByIndex(i, out MultiValue value) || value.AsSingleValue() is UnknownValue)
{
allIndicesKnown = false;
break;
}
}
if (!allIndicesKnown)
{
return false;
}
for (int i = 0; i < size.Value; i++)
{
if (array.TryGetValueByIndex(i, out MultiValue value))
{
var targetValue = _annotations.GetGenericParameterValue(genericParameters[i].GenericParameter, GetGenericParameterEffectiveMemberTypes(genericParameters[i]));
_requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
}
}
}
return true;
// Returns effective annotation of a generic parameter where it incorporates the constraint into the annotation.
// There are basically three cases where the constraint matters:
// - NeedsNew<SpecificType> - MarkStep will simply mark the default .ctor of SpecificType in this case, it has nothing to do with reflection
// - NeedsNew<TOuter> - this should be validated by the compiler/IL - TOuter must have matching constraints by definition, so nothing to validate
// - typeof(NeedsNew<>).MakeGenericType(typeOuter) - for this case we have to do it by hand as it's reflection. This is where this method helps.
static DynamicallyAccessedMemberTypes GetGenericParameterEffectiveMemberTypes(GenericParameterValue genericParameter)
{
DynamicallyAccessedMemberTypes result = genericParameter.DynamicallyAccessedMemberTypes;
if (genericParameter.GenericParameter.HasDefaultConstructorConstraint())
result |= DynamicallyAccessedMemberTypes.PublicParameterlessConstructor;
return result;
}
}
private void ValidateGenericMethodInstantiation(
MethodProxy genericMethod,
in MultiValue genericParametersArray,
MethodProxy reflectionMethod)
{
if (!genericMethod.HasGenericParameters())
{
return;
}
var genericParameterValues = GetGenericParameterValues(genericMethod.GetGenericParameters());
if (!AnalyzeGenericInstantiationTypeArray(genericParametersArray, genericParameterValues))
{
_diagnosticContext.AddDiagnostic(DiagnosticId.MakeGenericMethod, reflectionMethod.GetDisplayName());
}
}
private ImmutableArray<GenericParameterValue> GetGenericParameterValues(ImmutableArray<GenericParameterProxy> genericParameters)
{
if (genericParameters.IsEmpty)
return ImmutableArray<GenericParameterValue>.Empty;
var builder = ImmutableArray.CreateBuilder<GenericParameterValue>(genericParameters.Length);
foreach (var genericParameter in genericParameters)
{
builder.Add(_annotations.GetGenericParameterValue(genericParameter));
}
return builder.ToImmutableArray();
}
private void ProcessCreateInstanceByName(MethodProxy calledMethod, IReadOnlyList<MultiValue> argumentValues)
{
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
bool parameterlessConstructor = true;
int offset = calledMethod.HasImplicitThis() ? 1 : 0;
if (calledMethod.HasMetadataParametersCount(8) && calledMethod.HasParameterOfType((ParameterIndex)2 + offset, "System.Boolean"))
{
parameterlessConstructor = false;
bindingFlags = BindingFlags.Instance;
if (argumentValues[3].AsConstInt() is int bindingFlagsInt)
bindingFlags |= (BindingFlags)bindingFlagsInt;
else
bindingFlags |= BindingFlags.Public | BindingFlags.NonPublic;
}
foreach (var assemblyNameValue in argumentValues[0].AsEnumerable())
{
if (assemblyNameValue is KnownStringValue assemblyNameStringValue)
{
if (assemblyNameStringValue.Contents is string assemblyName && assemblyName.Length == 0)
{
// Throws exception for zero-length assembly name.
continue;
}
foreach (var typeNameValue in argumentValues[1].AsEnumerable())
{
if (typeNameValue is NullValue)
{
// Throws exception for null type name.
continue;
}
if (typeNameValue is KnownStringValue typeNameStringValue)
{
if (!TryResolveTypeNameForCreateInstanceAndMark(calledMethod, assemblyNameStringValue.Contents, typeNameStringValue.Contents, out TypeProxy resolvedType))
{
// It's not wrong to have a reference to non-existing type - the code may well expect to get an exception in this case
// Note that we did find the assembly, so it's not a ILLink config problem, it's either intentional, or wrong versions of assemblies
// but ILLink can't know that. In case a user tries to create an array using System.Activator we should simply ignore it, the user
// might expect an exception to be thrown.
continue;
}
MarkConstructorsOnType(resolvedType, bindingFlags, parameterlessConstructor ? 0 : null);
}
else
{
_diagnosticContext.AddDiagnostic(DiagnosticId.UnrecognizedParameterInMethodCreateInstance, new ParameterProxy(calledMethod, (ParameterIndex)1 + offset).GetDisplayName(), calledMethod.GetDisplayName());
}
}
}
else
{
_diagnosticContext.AddDiagnostic(DiagnosticId.UnrecognizedParameterInMethodCreateInstance, new ParameterProxy(calledMethod, (ParameterIndex)0 + offset).GetDisplayName(), calledMethod.GetDisplayName());
}
}
}
internal static BindingFlags? GetBindingFlagsFromValue(in MultiValue parameter) => (BindingFlags?)parameter.AsConstInt();
internal static bool BindingFlagsAreUnsupported(BindingFlags? bindingFlags)
{
if (bindingFlags == null)
return true;
// Binding flags we understand
const BindingFlags UnderstoodBindingFlags =
BindingFlags.DeclaredOnly |
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.FlattenHierarchy |
BindingFlags.ExactBinding;
// Binding flags that don't affect binding outside InvokeMember (that we don't analyze).
const BindingFlags IgnorableBindingFlags =
BindingFlags.InvokeMethod |
BindingFlags.CreateInstance |
BindingFlags.GetField |
BindingFlags.SetField |
BindingFlags.GetProperty |
BindingFlags.SetProperty;
BindingFlags flags = bindingFlags.Value;
return (flags & ~(UnderstoodBindingFlags | IgnorableBindingFlags)) != 0;
}
internal static bool HasBindingFlag(BindingFlags? bindingFlags, BindingFlags? search) => bindingFlags != null && (bindingFlags & search) == search;
internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes(BindingFlags? bindingFlags) =>
(HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicNestedTypes : DynamicallyAccessedMemberTypes.None) |
(HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None) |
(BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None);
internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(BindingFlags? bindingFlags) =>
(HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicConstructors : DynamicallyAccessedMemberTypes.None) |
(HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None) |
(BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None);
internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(BindingFlags? bindingFlags) =>
(HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicMethods : DynamicallyAccessedMemberTypes.None) |
(HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None) |
(BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None);
internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields(BindingFlags? bindingFlags) =>
(HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicFields : DynamicallyAccessedMemberTypes.None) |
(HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None) |
(BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None);
internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties(BindingFlags? bindingFlags) =>
(HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicProperties : DynamicallyAccessedMemberTypes.None) |
(HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None) |
(BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None);
internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents(BindingFlags? bindingFlags) =>
(HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicEvents : DynamicallyAccessedMemberTypes.None) |
(HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None) |
(BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None);
internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers(BindingFlags? bindingFlags) =>
GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(bindingFlags) |
GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents(bindingFlags) |
GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields(bindingFlags) |
GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags) |
GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties(bindingFlags) |
GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes(bindingFlags);
/// <Summary>
/// Returns true if the method is a .ctor for System.Type or a type that derives from System.Type (i.e. fields and params of this type can have DynamicallyAccessedMembers annotations)
/// </Summary>
private partial bool MethodIsTypeConstructor(MethodProxy method);
private partial IEnumerable<SystemReflectionMethodBaseValue> GetMethodsOnTypeHierarchy(TypeProxy type, string name, BindingFlags? bindingFlags);
private partial IEnumerable<SystemTypeValue> GetNestedTypesOnType(TypeProxy type, string name, BindingFlags? bindingFlags);
private partial bool TryGetBaseType(TypeProxy type, [NotNullWhen(true)] out TypeProxy? baseType);
private partial bool TryResolveTypeNameForCreateInstanceAndMark(in MethodProxy calledMethod, string assemblyName, string typeName, out TypeProxy resolvedType);
private partial void MarkStaticConstructor(TypeProxy type);
private partial void MarkEventsOnTypeHierarchy(TypeProxy type, string name, BindingFlags? bindingFlags);
private partial void MarkFieldsOnTypeHierarchy(TypeProxy type, string name, BindingFlags? bindingFlags);
private partial void MarkPropertiesOnTypeHierarchy(TypeProxy type, string name, BindingFlags? bindingFlags);
private partial void MarkPublicParameterlessConstructorOnType(TypeProxy type);
private partial void MarkConstructorsOnType(TypeProxy type, BindingFlags? bindingFlags, int? parameterCount);
private partial void MarkMethod(MethodProxy method);
private partial void MarkType(TypeProxy type);
private partial bool MarkAssociatedProperty(MethodProxy method);
// Only used for internal diagnostic purposes (not even for warning messages)
private partial string GetContainingSymbolDisplayName();
}
}
|