// 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; case IntrinsicId.Type_get_Assembly: if (instanceValue.IsEmpty()) { returnValue = MultiValueLattice.Top; break; } foreach (var value in instanceValue.AsEnumerable()) { string? assemblyName; if (value is SystemTypeValue systemType && (assemblyName = GetAssemblyName(systemType.RepresentedType)) is not null) { AddReturnValue(new AssemblyValue(assemblyName)); } else { AddReturnValue(annotatedMethodReturnValue); } } 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, Type[]) // 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, GenericParameterValue genericParam => new NullableUnwrappedGenericParameterValue(genericParam), 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; // // System.Reflection.Assembly // // GetType(string name) // GetType(string name, bool throwOnError) // GetType(string name, bool throwOnError, bool ignoreCase) // case IntrinsicId.Assembly_GetType: { if (instanceValue.IsEmpty() || argumentValues[0].IsEmpty()) { returnValue = MultiValueLattice.Top; break; } bool triggersWarning = calledMethod.HasMetadataParametersCount(3) && calledMethod.HasParameterOfType((ParameterIndex)3, "System.Boolean") && argumentValues[2].AsConstInt() != 0; if (!triggersWarning) { foreach (var assemblyValue in instanceValue.AsEnumerable()) { if (assemblyValue is not AssemblyValue knownAssembly) { triggersWarning = true; continue; } foreach (var typeNameValue in argumentValues[0].AsEnumerable()) { if (typeNameValue is KnownStringValue knownStringValue) { if (!TryResolveTypeNameInAssemblyAndMark(knownAssembly.AssemblyName, knownStringValue.Contents, out TypeProxy foundType)) { // Intentionally ignore - it's not wrong for code to call Assembly.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 { // Unlike Type.GetType, we can't propagate DAM annotations from the string parameter because // Assembly.GetType resolves within a specific assembly, while DAM enforcement at the call site // uses global (Type.GetType-like) resolution. These can resolve to different types. triggersWarning = true; } } } } if (triggersWarning) ReportRequiresUnreferencedCode(calledMethod); } 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 string? GetAssemblyName(TypeProxy type); private partial bool TryResolveTypeNameInAssemblyAndMark(string assemblyName, string typeName, out TypeProxy resolvedType); private partial void ReportRequiresUnreferencedCode(MethodProxy calledMethod); 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(); } } |