|
// 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 {
// 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 ();
}
}
|