diff options
18 files changed, 795 insertions, 570 deletions
diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs index d0a7af8c7..ebb14a434 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs @@ -1,8 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Reflection; using ILLink.RoslynAnalyzer; +using ILLink.RoslynAnalyzer.DataFlow; using ILLink.RoslynAnalyzer.TrimAnalysis; using ILLink.Shared.TypeSystemProxy; using Microsoft.CodeAnalysis; @@ -16,13 +19,15 @@ namespace ILLink.Shared.TrimAnalysis readonly ISymbol _owningSymbol; readonly IOperation _operation; + readonly ReflectionAccessAnalyzer _reflectionAccessAnalyzer; public HandleCallAction (in DiagnosticContext diagnosticContext, ISymbol owningSymbol, IOperation operation) { _owningSymbol = owningSymbol; _operation = operation; _diagnosticContext = diagnosticContext; - _requireDynamicallyAccessedMembersAction = new (diagnosticContext, new ReflectionAccessAnalyzer ()); + _reflectionAccessAnalyzer = new ReflectionAccessAnalyzer (); + _requireDynamicallyAccessedMembersAction = new (diagnosticContext, _reflectionAccessAnalyzer); } // TODO: This is relatively expensive on the analyzer since it doesn't cache the annotation information @@ -41,9 +46,39 @@ namespace ILLink.Shared.TrimAnalysis private partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) => new (method.Method, dynamicallyAccessedMemberTypes); + private partial MethodParameterValue GetMethodParameterValue (MethodProxy method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new (method.Method.Parameters[parameterIndex], dynamicallyAccessedMemberTypes); + + private partial IEnumerable<SystemReflectionMethodBaseValue> GetMethodsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) + { + foreach (var method in type.Type.GetMethodsOnTypeHierarchy (m => m.Name == name, bindingFlags)) + yield return new SystemReflectionMethodBaseValue (new MethodProxy (method)); + } + + private partial IEnumerable<SystemTypeValue> GetNestedTypesOnType (TypeProxy type, string name, BindingFlags? bindingFlags) + { + foreach (var nestedType in type.Type.GetNestedTypesOnType (t => t.Name == name, bindingFlags)) + yield return new SystemTypeValue (new TypeProxy (nestedType)); + } + // TODO: Does the analyzer need to do something here? private partial void MarkStaticConstructor (TypeProxy type) { } + private partial void MarkEventsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) + => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForEventsOnTypeHierarchy (_diagnosticContext, type.Type, name, bindingFlags); + + private partial void MarkFieldsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) + => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForFieldsOnTypeHierarchy (_diagnosticContext, type.Type, name, bindingFlags); + + private partial void MarkPropertiesOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) + => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchy (_diagnosticContext, type.Type, name, bindingFlags); + + private partial void MarkMethod (MethodProxy method) + => ReflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForMethod (_diagnosticContext, method.Method); + + // TODO: Does the analyzer need to do something here? + private partial void MarkType (TypeProxy type) { } + private partial string GetContainingSymbolDisplayName () => _operation.FindContainingSymbol (_owningSymbol).GetDisplayName (); } } diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodParameterValue.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodParameterValue.cs index e7b44d8a2..0aa1abbcf 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodParameterValue.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodParameterValue.cs @@ -11,11 +11,15 @@ namespace ILLink.Shared.TrimAnalysis { partial record MethodParameterValue { - public MethodParameterValue (IParameterSymbol parameterSymbol) => ParameterSymbol = parameterSymbol; + public MethodParameterValue (IParameterSymbol parameterSymbol) + : this (parameterSymbol, FlowAnnotations.GetMethodParameterAnnotation (parameterSymbol)) { } + + public MethodParameterValue (IParameterSymbol parameterSymbol, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => (ParameterSymbol, DynamicallyAccessedMemberTypes) = (parameterSymbol, dynamicallyAccessedMemberTypes); public readonly IParameterSymbol ParameterSymbol; - public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes => FlowAnnotations.GetMethodParameterAnnotation (ParameterSymbol); + public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } public override IEnumerable<string> GetDiagnosticArgumentsForAnnotationMismatch () => new string[] { ParameterSymbol.GetDisplayName (), ParameterSymbol.ContainingSymbol.GetDisplayName () }; diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs index b22fb9fde..b5663698a 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Reflection; using ILLink.RoslynAnalyzer.DataFlow; using ILLink.Shared; using ILLink.Shared.TrimAnalysis; @@ -17,7 +18,7 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis foreach (var member in typeSymbol.GetDynamicallyAccessedMembers (requiredMemberTypes, declaredOnly)) { switch (member) { case IMethodSymbol method: - GetDiagnosticsForMethod (diagnosticContext, method); + GetReflectionAccessDiagnosticsForMethod (diagnosticContext, method); break; case IFieldSymbol field: GetDiagnosticsForField (diagnosticContext, field); @@ -40,6 +41,24 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis } } + internal void GetReflectionAccessDiagnosticsForEventsOnTypeHierarchy (in DiagnosticContext diagnosticContext, ITypeSymbol typeSymbol, string name, BindingFlags? bindingFlags) + { + foreach (var @event in typeSymbol.GetEventsOnTypeHierarchy (e => e.Name == name, bindingFlags)) + GetDiagnosticsForEvent (diagnosticContext, @event); + } + + internal void GetReflectionAccessDiagnosticsForFieldsOnTypeHierarchy (in DiagnosticContext diagnosticContext, ITypeSymbol typeSymbol, string name, BindingFlags? bindingFlags) + { + foreach (var field in typeSymbol.GetFieldsOnTypeHierarchy (f => f.Name == name, bindingFlags)) + GetDiagnosticsForField (diagnosticContext, field); + } + + internal void GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchy (in DiagnosticContext diagnosticContext, ITypeSymbol typeSymbol, string name, BindingFlags? bindingFlags) + { + foreach (var prop in typeSymbol.GetPropertiesOnTypeHierarchy (p => p.Name == name, bindingFlags)) + GetDiagnosticsForProperty (diagnosticContext, prop); + } + static void ReportRequiresUnreferencedCodeDiagnostic (in DiagnosticContext diagnosticContext, AttributeData requiresAttributeData, ISymbol member) { var message = RequiresUnreferencedCodeUtils.GetMessageFromAttribute (requiresAttributeData); @@ -47,7 +66,7 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis diagnosticContext.AddDiagnostic (DiagnosticId.RequiresUnreferencedCode, member.GetDisplayName (), message, url); } - static void GetDiagnosticsForMethod (in DiagnosticContext diagnosticContext, IMethodSymbol methodSymbol) + internal static void GetReflectionAccessDiagnosticsForMethod (in DiagnosticContext diagnosticContext, IMethodSymbol methodSymbol) { if (methodSymbol.TryGetRequiresUnreferencedCodeAttribute (out var requiresAttributeData)) ReportRequiresUnreferencedCodeDiagnostic (diagnosticContext, requiresAttributeData, methodSymbol); @@ -69,19 +88,19 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis static void GetDiagnosticsForProperty (in DiagnosticContext diagnosticContext, IPropertySymbol propertySymbol) { if (propertySymbol.SetMethod is not null) - GetDiagnosticsForMethod (diagnosticContext, propertySymbol.SetMethod); + GetReflectionAccessDiagnosticsForMethod (diagnosticContext, propertySymbol.SetMethod); if (propertySymbol.GetMethod is not null) - GetDiagnosticsForMethod (diagnosticContext, propertySymbol.GetMethod); + GetReflectionAccessDiagnosticsForMethod (diagnosticContext, propertySymbol.GetMethod); } static void GetDiagnosticsForEvent (in DiagnosticContext diagnosticContext, IEventSymbol eventSymbol) { if (eventSymbol.AddMethod is not null) - GetDiagnosticsForMethod (diagnosticContext, eventSymbol.AddMethod); + GetReflectionAccessDiagnosticsForMethod (diagnosticContext, eventSymbol.AddMethod); if (eventSymbol.RemoveMethod is not null) - GetDiagnosticsForMethod (diagnosticContext, eventSymbol.RemoveMethod); + GetReflectionAccessDiagnosticsForMethod (diagnosticContext, eventSymbol.RemoveMethod); if (eventSymbol.RaiseMethod is not null) - GetDiagnosticsForMethod (diagnosticContext, eventSymbol.RaiseMethod); + GetReflectionAccessDiagnosticsForMethod (diagnosticContext, eventSymbol.RaiseMethod); } static void GetDiagnosticsForField (in DiagnosticContext diagnosticContext, IFieldSymbol fieldSymbol) diff --git a/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs b/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs index 7a61a03a8..04b3367e0 100644 --- a/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs +++ b/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs @@ -22,12 +22,15 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis { public readonly TrimAnalysisPatternStore TrimAnalysisPatterns; + readonly ValueSetLattice<SingleValue> _multiValueLattice; + public TrimAnalysisVisitor ( LocalStateLattice<MultiValue, ValueSetLattice<SingleValue>> lattice, OperationBlockAnalysisContext context ) : base (lattice, context) { - TrimAnalysisPatterns = new TrimAnalysisPatternStore (lattice.Lattice.ValueLattice); + _multiValueLattice = lattice.Lattice.ValueLattice; + TrimAnalysisPatterns = new TrimAnalysisPatternStore (_multiValueLattice); } // Override visitor methods to create tracked values when visiting operations @@ -36,6 +39,26 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis // - 'this' parameter (for annotated methods) // - field reference + public override MultiValue Visit (IOperation? operation, StateValue argument) + { + var returnValue = base.Visit (operation, argument); + + // If the return value is empty (TopValue basically) and the Operation tree + // reports it as having a constant value, use that as it will automatically cover + // cases we don't need/want to handle. + if (operation != null && returnValue.IsEmpty () && operation.ConstantValue.HasValue) { + object? constantValue = operation.ConstantValue.Value; + if (constantValue == null) + return NullValue.Instance; + else if (operation.Type?.SpecialType == SpecialType.System_String && constantValue is string stringConstantValue) + return new KnownStringValue (stringConstantValue); + else if (operation.Type?.TypeKind == TypeKind.Enum && constantValue is int intConstantValue) + return new ConstIntValue (intConstantValue); + } + + return returnValue; + } + public override MultiValue VisitConversion (IConversionOperation operation, StateValue state) { var value = base.VisitConversion (operation, state); @@ -70,14 +93,15 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis public override MultiValue VisitFieldReference (IFieldReferenceOperation fieldRef, StateValue state) { - if (!fieldRef.Field.Type.IsTypeInterestingForDataflow ()) - return TopValue; + if (fieldRef.Field.Type.IsTypeInterestingForDataflow ()) { + var field = fieldRef.Field; + if (field.Name is "Empty" && field.ContainingType.HasName ("System.String")) + return new KnownStringValue (string.Empty); - var field = fieldRef.Field; - if (field.Name is "Empty" && field.ContainingType.HasName ("System.String")) - return new KnownStringValue (string.Empty); + return new FieldValue (fieldRef.Field); + } - return new FieldValue (fieldRef.Field); + return TopValue; } public override MultiValue VisitTypeOf (ITypeOfOperation typeOfOperation, StateValue state) @@ -90,9 +114,34 @@ namespace ILLink.RoslynAnalyzer.TrimAnalysis return TopValue; } - public override MultiValue VisitLiteral (ILiteralOperation literalOperation, StateValue state) + public override MultiValue VisitBinaryOperator (IBinaryOperation operation, StateValue argument) { - return literalOperation.ConstantValue.Value == null ? NullValue.Instance : TopValue; + if (!operation.ConstantValue.HasValue && // Optimization - if there is already a constant value available, rely on the Visit(IOperation) instead + operation.OperatorKind == BinaryOperatorKind.Or && + operation.OperatorMethod is null && + (operation.Type?.TypeKind == TypeKind.Enum || operation.Type?.SpecialType == SpecialType.System_Int32)) { + MultiValue leftValue = Visit (operation.LeftOperand, argument); + MultiValue rightValue = Visit (operation.RightOperand, argument); + + MultiValue result = TopValue; + foreach (var left in leftValue) { + if (left is UnknownValue) + result = _multiValueLattice.Meet (result, left); + else if (left is ConstIntValue leftConstInt) { + foreach (var right in rightValue) { + if (right is UnknownValue) + result = _multiValueLattice.Meet (result, right); + else if (right is ConstIntValue rightConstInt) { + result = _multiValueLattice.Meet (result, new ConstIntValue (leftConstInt.Value | rightConstInt.Value)); + } + } + } + } + + return result; + } + + return base.VisitBinaryOperator (operation, argument); } // Override handlers for situations where annotated locations may be involved in reflection access: diff --git a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index ee8c257e0..57a48187b 100644 --- a/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Reflection; using System.Runtime.InteropServices; using ILLink.Shared.DataFlow; using ILLink.Shared.TypeSystemProxy; @@ -147,6 +148,313 @@ namespace ILLink.Shared.TrimAnalysis } break; + // + // GetConstructors (BindingFlags) + // GetMethods (BindingFlags) + // GetFields (BindingFlags) + // GetEvents (BindingFlags) + // GetProperties (BindingFlags) + // GetNestedTypes (BindingFlags) + // GetMembers (BindingFlags) + // + case var callType when (callType == IntrinsicId.Type_GetConstructors || callType == IntrinsicId.Type_GetMethods || callType == IntrinsicId.Type_GetFields || + callType == IntrinsicId.Type_GetProperties || callType == IntrinsicId.Type_GetEvents || callType == IntrinsicId.Type_GetNestedTypes || callType == IntrinsicId.Type_GetMembers) + && calledMethod.IsDeclaredOnType ("System.Type") + && calledMethod.HasParameterOfType (0, "System.Reflection.BindingFlags") + && !calledMethod.IsStatic (): { + + BindingFlags? bindingFlags; + bindingFlags = GetBindingFlagsFromValue (argumentValues[0]); + DynamicallyAccessedMemberTypes memberTypes; + if (BindingFlagsAreUnsupported (bindingFlags)) { + memberTypes = callType switch { + IntrinsicId.Type_GetConstructors => DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors, + IntrinsicId.Type_GetMethods => DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods, + IntrinsicId.Type_GetEvents => DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents, + IntrinsicId.Type_GetFields => DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields, + IntrinsicId.Type_GetProperties => DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties, + IntrinsicId.Type_GetNestedTypes => DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, + IntrinsicId.Type_GetMembers => 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 = callType switch { + IntrinsicId.Type_GetConstructors => GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags), + IntrinsicId.Type_GetMethods => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags), + IntrinsicId.Type_GetEvents => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents (bindingFlags), + IntrinsicId.Type_GetFields => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields (bindingFlags), + IntrinsicId.Type_GetProperties => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (bindingFlags), + IntrinsicId.Type_GetNestedTypes => GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (bindingFlags), + IntrinsicId.Type_GetMembers => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers (bindingFlags), + _ => throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{GetContainingSymbolDisplayName ()}' is of unexpected member type."), + }; + } + + var targetValue = 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 var fieldPropertyOrEvent when (fieldPropertyOrEvent == IntrinsicId.Type_GetField || fieldPropertyOrEvent == IntrinsicId.Type_GetProperty || fieldPropertyOrEvent == IntrinsicId.Type_GetEvent) + && calledMethod.IsDeclaredOnType ("System.Type") + && calledMethod.HasParameterOfType (0, "System.String") + && !calledMethod.IsStatic (): { + + BindingFlags? bindingFlags; + if (calledMethod.HasParameterOfType (1, "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 = fieldPropertyOrEvent 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 = GetMethodThisParameterValue (calledMethod, memberTypes); + foreach (var value in instanceValue) { + if (value is SystemTypeValue systemTypeValue) { + foreach (var stringParam in argumentValues[0]) { + if (stringParam is KnownStringValue stringValue && !BindingFlagsAreUnsupported (bindingFlags)) { + switch (fieldPropertyOrEvent) { + 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: { + BindingFlags? bindingFlags; + if (calledMethod.HasParametersCount (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.HasParametersCount (2) && calledMethod.HasParameterOfType (1, "System.Reflection.BindingFlags")) + bindingFlags = GetBindingFlagsFromValue (argumentValues[1]); + else if (calledMethod.HasParametersCount (3) && calledMethod.HasParameterOfType (2, "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 = GetMethodThisParameterValue (calledMethod, requiredMemberTypes); + + // Go over all types we've seen + foreach (var value in instanceValue) { + // 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: { + BindingFlags? bindingFlags; + if (calledMethod.HasParameterOfType (1, "System.Reflection.BindingFlags")) + bindingFlags = GetBindingFlagsFromValue (argumentValues[1]); + else if (calledMethod.HasParameterOfType (2, "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 = GetMethodThisParameterValue (calledMethod, GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags)); + foreach (var value in instanceValue) { + if (value is SystemTypeValue systemTypeValue) { + foreach (var stringParam in argumentValues[0]) { + if (stringParam is KnownStringValue stringValue && !BindingFlagsAreUnsupported (bindingFlags)) { + foreach (var methodValue in ProcessGetMethodByName (systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags)) + AddReturnValue (methodValue); + } else { + // Otherwise fall back to the bitfield requirements + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + } + } + } else { + // Otherwise fall back to the bitfield requirements + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + } + } + } + break; + + // + // GetNestedType (string) + // GetNestedType (string, BindingFlags) + // + case IntrinsicId.Type_GetNestedType: { + BindingFlags? bindingFlags; + if (calledMethod.HasParameterOfType (1, "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 = GetMethodThisParameterValue (calledMethod, GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (bindingFlags)); + bool everyParentTypeHasAll = true; + foreach (var value in instanceValue) { + if (value is SystemTypeValue systemTypeValue) { + foreach (var stringParam in argumentValues[0]) { + if (stringParam is KnownStringValue stringValue && !BindingFlagsAreUnsupported (bindingFlags)) { + foreach (var nestedTypeValue in GetNestedTypesOnType (systemTypeValue.RepresentedType, stringValue.Contents, bindingFlags)) { + MarkType (nestedTypeValue.RepresentedType); + AddReturnValue (nestedTypeValue); + } + } else { + // Otherwise fall back to the bitfield requirements + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + } + } + } else { + // Otherwise fall back to the bitfield requirements + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + } + + if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers) { + if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.All) + everyParentTypeHasAll = false; + } else if (!(value is NullValue || value is SystemTypeValue)) { + // Known Type values are always OK - either they're fully resolved above and thus the return value + // is set to the known resolved type, or if they're not resolved, they won't exist at runtime + // and will cause exceptions - and thus don't introduce new requirements on marking. + // nulls are intentionally ignored as they will lead to exceptions at runtime + // and thus don't introduce new requirements on marking. + everyParentTypeHasAll = false; + } + } + + // If the parent type (all the possible values) has DynamicallyAccessedMemberTypes.All it means its nested types are also fully marked + // (see MarkStep.MarkEntireType - it will recursively mark entire type on nested types). In that case we can annotate + // the returned type (the nested type) with DynamicallyAccessedMemberTypes.All as well. + // Note it's OK to blindly overwrite any potential annotation on the return value from the method definition + // since DynamicallyAccessedMemberTypes.All is a superset of any other annotation. + if (everyParentTypeHasAll && returnValue == null) + returnValue = GetMethodReturnValue (calledMethod, DynamicallyAccessedMemberTypes.All); + } + 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 var getRuntimeMember when getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: { + + BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + DynamicallyAccessedMemberTypes requiredMemberTypes = getRuntimeMember 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 = GetMethodParameterValue (calledMethod, 0, requiredMemberTypes); + + foreach (var value in argumentValues[0]) { + if (value is SystemTypeValue systemTypeValue) { + foreach (var stringParam in argumentValues[1]) { + if (stringParam is KnownStringValue stringValue) { + switch (getRuntimeMember) { + 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: + 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 { + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + } + } + } else { + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + } + } + } + break; + case IntrinsicId.None: methodReturnValue = MultiValueLattice.Top; return false; @@ -188,6 +496,93 @@ namespace ILLink.Shared.TrimAnalysis } } + IEnumerable<MultiValue> ProcessGetMethodByName (TypeProxy type, string methodName, BindingFlags? bindingFlags) + { + bool foundAny = false; + foreach (var method in GetMethodsOnTypeHierarchy (type, methodName, bindingFlags)) { + MarkMethod (method.MethodRepresented); + 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; + } + + 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); + private partial bool MethodRequiresDataFlowAnalysis (MethodProxy method); private partial DynamicallyAccessedMemberTypes GetReturnValueAnnotation (MethodProxy method); @@ -198,8 +593,24 @@ namespace ILLink.Shared.TrimAnalysis private partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes); + private partial MethodParameterValue GetMethodParameterValue (MethodProxy method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes); + + private partial IEnumerable<SystemReflectionMethodBaseValue> GetMethodsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags); + + private partial IEnumerable<SystemTypeValue> GetNestedTypesOnType (TypeProxy type, string name, BindingFlags? bindingFlags); + 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 MarkMethod (MethodProxy method); + + private partial void MarkType (TypeProxy type); + // Only used for internal diagnostic purposes (not even for warning messages) private partial string GetContainingSymbolDisplayName (); } diff --git a/src/ILLink.Shared/TrimAnalysis/SystemReflectionMethodBaseValue.cs b/src/ILLink.Shared/TrimAnalysis/SystemReflectionMethodBaseValue.cs index 945b64262..a32d52fea 100644 --- a/src/ILLink.Shared/TrimAnalysis/SystemReflectionMethodBaseValue.cs +++ b/src/ILLink.Shared/TrimAnalysis/SystemReflectionMethodBaseValue.cs @@ -2,11 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. using ILLink.Shared.DataFlow; +using ILLink.Shared.TypeSystemProxy; namespace ILLink.Shared.TrimAnalysis { /// <summary> /// This is a known System.Reflection.MethodBase value. MethodRepresented is the 'value' of the MethodBase. /// </summary> - sealed partial record SystemReflectionMethodBaseValue : SingleValue; + sealed partial record SystemReflectionMethodBaseValue : SingleValue + { + public SystemReflectionMethodBaseValue (MethodProxy methodRepresented) => MethodRepresented = methodRepresented; + + public readonly MethodProxy MethodRepresented; + + public override string ToString () => this.ValueToString (MethodRepresented); + } } diff --git a/src/linker/Linker.Dataflow/HandleCallAction.cs b/src/linker/Linker.Dataflow/HandleCallAction.cs index b3f3d1c25..b88e6d8e9 100644 --- a/src/linker/Linker.Dataflow/HandleCallAction.cs +++ b/src/linker/Linker.Dataflow/HandleCallAction.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Reflection; using ILLink.Shared.TypeSystemProxy; using Mono.Cecil; using Mono.Linker; @@ -47,9 +49,43 @@ namespace ILLink.Shared.TrimAnalysis private partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) => new (method.Method, dynamicallyAccessedMemberTypes); + private partial MethodParameterValue GetMethodParameterValue (MethodProxy method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new ( + ReflectionMethodBodyScanner.ResolveToTypeDefinition (_context, method.Method.Parameters[parameterIndex].ParameterType), + method.Method, + parameterIndex, + dynamicallyAccessedMemberTypes); + + private partial IEnumerable<SystemReflectionMethodBaseValue> GetMethodsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) + { + foreach (var method in type.Type.GetMethodsOnTypeHierarchy (_context, m => m.Name == name, bindingFlags)) + yield return new SystemReflectionMethodBaseValue (new MethodProxy (method)); + } + + private partial IEnumerable<SystemTypeValue> GetNestedTypesOnType (TypeProxy type, string name, BindingFlags? bindingFlags) + { + foreach (var nestedType in type.Type.GetNestedTypesOnType (t => t.Name == name, bindingFlags)) + yield return new SystemTypeValue (new TypeProxy (nestedType)); + } + private partial void MarkStaticConstructor (TypeProxy type) => _reflectionMethodBodyScanner.MarkStaticConstructor (_analysisContext, type.Type); + private partial void MarkEventsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) + => _reflectionMethodBodyScanner.MarkEventsOnTypeHierarchy (_analysisContext, type.Type, e => e.Name == name, bindingFlags); + + private partial void MarkFieldsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) + => _reflectionMethodBodyScanner.MarkFieldsOnTypeHierarchy (_analysisContext, type.Type, f => f.Name == name, bindingFlags); + + private partial void MarkPropertiesOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) + => _reflectionMethodBodyScanner.MarkPropertiesOnTypeHierarchy (_analysisContext, type.Type, p => p.Name == name, bindingFlags); + + private partial void MarkMethod (MethodProxy method) + => _reflectionMethodBodyScanner.MarkMethod (_analysisContext, method.Method); + + private partial void MarkType (TypeProxy type) + => _reflectionMethodBodyScanner.MarkType (_analysisContext, type.Type); + private partial string GetContainingSymbolDisplayName () => _callingMethodDefinition.GetDisplayName (); } } diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index 4ae5b0b9a..1358a5ff3 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -269,7 +269,25 @@ namespace Mono.Linker.Dataflow case IntrinsicId.Type_get_TypeHandle: case IntrinsicId.Type_GetInterface: case IntrinsicId.Type_get_AssemblyQualifiedName: - case IntrinsicId.RuntimeHelpers_RunClassConstructor: { + case IntrinsicId.RuntimeHelpers_RunClassConstructor: + case var callType when (callType == IntrinsicId.Type_GetConstructors || callType == IntrinsicId.Type_GetMethods || callType == IntrinsicId.Type_GetFields || + callType == IntrinsicId.Type_GetProperties || callType == IntrinsicId.Type_GetEvents || callType == IntrinsicId.Type_GetNestedTypes || callType == IntrinsicId.Type_GetMembers) + && calledMethod.DeclaringType.Namespace == "System" + && calledMethod.DeclaringType.Name == "Type" + && calledMethod.Parameters[0].ParameterType.FullName == "System.Reflection.BindingFlags" + && calledMethod.HasThis: + case var fieldPropertyOrEvent when (fieldPropertyOrEvent == IntrinsicId.Type_GetField || fieldPropertyOrEvent == IntrinsicId.Type_GetProperty || fieldPropertyOrEvent == IntrinsicId.Type_GetEvent) + && calledMethod.DeclaringType.Namespace == "System" + && calledMethod.DeclaringType.Name == "Type" + && calledMethod.Parameters[0].ParameterType.FullName == "System.String" + && calledMethod.HasThis: + case var getRuntimeMember when getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: + case IntrinsicId.Type_GetMember: + case IntrinsicId.Type_GetMethod: + case IntrinsicId.Type_GetNestedType: { var instanceValue = MultiValueLattice.Top; IReadOnlyList<MultiValue> parameterValues = methodParams; if (calledMethodDefinition.HasImplicitThis ()) { @@ -344,61 +362,6 @@ namespace Mono.Linker.Dataflow 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 var getRuntimeMember when getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent - || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField - || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod - || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: { - - BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; - DynamicallyAccessedMemberTypes requiredMemberTypes = getRuntimeMember switch { - IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent => DynamicallyAccessedMemberTypes.PublicEvents, - IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField => DynamicallyAccessedMemberTypes.PublicFields, - IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod => DynamicallyAccessedMemberTypes.PublicMethods, - IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty => DynamicallyAccessedMemberTypes.PublicProperties, - _ => throw new InternalErrorException ($"Reflection call '{calledMethodDefinition.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is of unexpected member type."), - }; - - var targetValue = GetMethodParameterValue (calledMethodDefinition, 0, requiredMemberTypes); - - foreach (var value in methodParams[0]) { - if (value is SystemTypeValue systemTypeValue) { - foreach (var stringParam in methodParams[1]) { - if (stringParam is KnownStringValue stringValue) { - switch (getRuntimeMember) { - case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent: - MarkEventsOnTypeHierarchy (analysisContext, systemTypeValue.RepresentedType.Type, e => e.Name == stringValue.Contents, bindingFlags); - break; - case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField: - MarkFieldsOnTypeHierarchy (analysisContext, systemTypeValue.RepresentedType.Type, f => f.Name == stringValue.Contents, bindingFlags); - break; - case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod: - ProcessGetMethodByName (analysisContext, systemTypeValue.RepresentedType.Type, stringValue.Contents, bindingFlags, ref methodReturnValue); - break; - case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: - MarkPropertiesOnTypeHierarchy (analysisContext, systemTypeValue.RepresentedType.Type, p => p.Name == stringValue.Contents, bindingFlags); - break; - default: - throw new InternalErrorException ($"Error processing reflection call '{calledMethod.GetDisplayName ()}' inside {callingMethodDefinition.GetDisplayName ()}. Unexpected member kind."); - } - } else { - RequireDynamicallyAccessedMembers (analysisContext, value, targetValue); - } - } - } else { - RequireDynamicallyAccessedMembers (analysisContext, value, targetValue); - } - } - } - break; - - // // System.Linq.Expressions.Expression // // static Call (Type, String, Type[], Expression[]) @@ -460,7 +423,7 @@ namespace Mono.Linker.Dataflow // 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 (methodBaseValue.MethodRepresented.TryGetProperty (out PropertyDefinition? propertyDefinition)) { + if (methodBaseValue.MethodRepresented.Method.TryGetProperty (out PropertyDefinition? propertyDefinition)) { MarkProperty (analysisContext, propertyDefinition); continue; } @@ -673,105 +636,6 @@ namespace Mono.Linker.Dataflow 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: { - BindingFlags? bindingFlags; - if (calledMethod.Parameters.Count > 1 && calledMethodDefinition.Parameters[1].ParameterType.Name == "BindingFlags") - bindingFlags = GetBindingFlagsFromValue (methodParams[2]); - else if (calledMethod.Parameters.Count > 2 && calledMethodDefinition.Parameters[2].ParameterType.Name == "BindingFlags") - bindingFlags = GetBindingFlagsFromValue (methodParams[3]); - 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 = GetMethodParameterValue (calledMethodDefinition, 0, GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags)); - foreach (var value in methodParams[0]) { - if (value is SystemTypeValue systemTypeValue) { - foreach (var stringParam in methodParams[1]) { - if (stringParam is KnownStringValue stringValue && !BindingFlagsAreUnsupported (bindingFlags)) { - ProcessGetMethodByName (analysisContext, systemTypeValue.RepresentedType.Type, stringValue.Contents, bindingFlags, ref methodReturnValue); - } else { - // Otherwise fall back to the bitfield requirements - RequireDynamicallyAccessedMembers (analysisContext, value, targetValue); - } - } - } else { - // Otherwise fall back to the bitfield requirements - RequireDynamicallyAccessedMembers (analysisContext, value, targetValue); - } - } - } - break; - - // - // GetNestedType (string) - // GetNestedType (string, BindingFlags) - // - case IntrinsicId.Type_GetNestedType: { - BindingFlags? bindingFlags; - if (calledMethodDefinition.Parameters.Count > 1 && calledMethodDefinition.Parameters[1].ParameterType.Name == "BindingFlags") - bindingFlags = GetBindingFlagsFromValue (methodParams[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 = GetMethodParameterValue (calledMethodDefinition, 0, GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (bindingFlags)); - bool everyParentTypeHasAll = true; - foreach (var value in methodParams[0]) { - if (value is SystemTypeValue systemTypeValue) { - foreach (var stringParam in methodParams[1]) { - if (stringParam is KnownStringValue stringValue && !BindingFlagsAreUnsupported (bindingFlags)) { - TypeDefinition[]? matchingNestedTypes = MarkNestedTypesOnType (analysisContext, systemTypeValue.RepresentedType.Type, m => m.Name == stringValue.Contents, bindingFlags); - - if (matchingNestedTypes != null) { - for (int i = 0; i < matchingNestedTypes.Length; i++) - methodReturnValue = MultiValueLattice.Meet (methodReturnValue, new SystemTypeValue (matchingNestedTypes[i])); - } - } else { - // Otherwise fall back to the bitfield requirements - RequireDynamicallyAccessedMembers (analysisContext, value, targetValue); - } - } - } else { - // Otherwise fall back to the bitfield requirements - RequireDynamicallyAccessedMembers (analysisContext, value, targetValue); - } - - if (value is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers) { - if (valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.All) - everyParentTypeHasAll = false; - } else if (!(value is NullValue || value is SystemTypeValue)) { - // Known Type values are always OK - either they're fully resolved above and thus the return value - // is set to the known resolved type, or if they're not resolved, they won't exist at runtime - // and will cause exceptions - and thus don't introduce new requirements on marking. - // nulls are intentionally ignored as they will lead to exceptions at runtime - // and thus don't introduce new requirements on marking. - everyParentTypeHasAll = false; - } - } - - // If the parent type (all the possible values) has DynamicallyAccessedMemberTypes.All it means its nested types are also fully marked - // (see MarkStep.MarkEntireType - it will recursively mark entire type on nested types). In that case we can annotate - // the returned type (the nested type) with DynamicallyAccessedMemberTypes.All as well. - // Note it's OK to blindly overwrite any potential annotation on the return value from the method definition - // since DynamicallyAccessedMemberTypes.All is a superset of any other annotation. - if (everyParentTypeHasAll && methodReturnValue.IsEmpty ()) - methodReturnValue = GetMethodReturnValue (calledMethodDefinition, DynamicallyAccessedMemberTypes.All); - } - break; - - // // Type.BaseType // case IntrinsicId.Type_get_BaseType: { @@ -821,165 +685,6 @@ namespace Mono.Linker.Dataflow 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 var fieldPropertyOrEvent when (fieldPropertyOrEvent == IntrinsicId.Type_GetField || fieldPropertyOrEvent == IntrinsicId.Type_GetProperty || fieldPropertyOrEvent == IntrinsicId.Type_GetEvent) - && calledMethod.DeclaringType.Namespace == "System" - && calledMethod.DeclaringType.Name == "Type" - && calledMethod.Parameters[0].ParameterType.FullName == "System.String" - && calledMethod.HasThis: { - - BindingFlags? bindingFlags; - if (calledMethodDefinition.Parameters.Count > 1 && calledMethodDefinition.Parameters[1].ParameterType.Name == "BindingFlags") - bindingFlags = GetBindingFlagsFromValue (methodParams[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; - - DynamicallyAccessedMemberTypes memberTypes = fieldPropertyOrEvent switch { - IntrinsicId.Type_GetEvent => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents (bindingFlags), - IntrinsicId.Type_GetField => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields (bindingFlags), - IntrinsicId.Type_GetProperty => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (bindingFlags), - _ => throw new ArgumentException ($"Reflection call '{calledMethodDefinition.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is of unexpected member type."), - }; - - var targetValue = GetMethodParameterValue (calledMethodDefinition, 0, memberTypes); - foreach (var value in methodParams[0]) { - if (value is SystemTypeValue systemTypeValue) { - foreach (var stringParam in methodParams[1]) { - if (stringParam is KnownStringValue stringValue && !BindingFlagsAreUnsupported (bindingFlags)) { - switch (fieldPropertyOrEvent) { - case IntrinsicId.Type_GetEvent: - MarkEventsOnTypeHierarchy (analysisContext, systemTypeValue.RepresentedType.Type, filter: e => e.Name == stringValue.Contents, bindingFlags); - break; - case IntrinsicId.Type_GetField: - MarkFieldsOnTypeHierarchy (analysisContext, systemTypeValue.RepresentedType.Type, filter: f => f.Name == stringValue.Contents, bindingFlags); - break; - case IntrinsicId.Type_GetProperty: - MarkPropertiesOnTypeHierarchy (analysisContext, systemTypeValue.RepresentedType.Type, filter: p => p.Name == stringValue.Contents, bindingFlags); - break; - default: - Debug.Fail ("Unreachable."); - break; - } - } else { - RequireDynamicallyAccessedMembers (analysisContext, value, targetValue); - } - } - } else { - RequireDynamicallyAccessedMembers (analysisContext, value, targetValue); - } - } - } - break; - - // - // GetConstructors (BindingFlags) - // GetMethods (BindingFlags) - // GetFields (BindingFlags) - // GetEvents (BindingFlags) - // GetProperties (BindingFlags) - // GetNestedTypes (BindingFlags) - // GetMembers (BindingFlags) - // - case var callType when (callType == IntrinsicId.Type_GetConstructors || callType == IntrinsicId.Type_GetMethods || callType == IntrinsicId.Type_GetFields || - callType == IntrinsicId.Type_GetProperties || callType == IntrinsicId.Type_GetEvents || callType == IntrinsicId.Type_GetNestedTypes || callType == IntrinsicId.Type_GetMembers) - && calledMethod.DeclaringType.Namespace == "System" - && calledMethod.DeclaringType.Name == "Type" - && calledMethod.Parameters[0].ParameterType.FullName == "System.Reflection.BindingFlags" - && calledMethod.HasThis: { - - BindingFlags? bindingFlags; - bindingFlags = GetBindingFlagsFromValue (methodParams[1]); - DynamicallyAccessedMemberTypes memberTypes = DynamicallyAccessedMemberTypes.None; - if (BindingFlagsAreUnsupported (bindingFlags)) { - memberTypes = callType switch { - IntrinsicId.Type_GetConstructors => DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors, - IntrinsicId.Type_GetMethods => DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods, - IntrinsicId.Type_GetEvents => DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents, - IntrinsicId.Type_GetFields => DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields, - IntrinsicId.Type_GetProperties => DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties, - IntrinsicId.Type_GetNestedTypes => DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, - IntrinsicId.Type_GetMembers => 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 '{calledMethodDefinition.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is of unexpected member type."), - }; - } else { - memberTypes = callType switch { - IntrinsicId.Type_GetConstructors => GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags), - IntrinsicId.Type_GetMethods => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags), - IntrinsicId.Type_GetEvents => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents (bindingFlags), - IntrinsicId.Type_GetFields => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields (bindingFlags), - IntrinsicId.Type_GetProperties => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (bindingFlags), - IntrinsicId.Type_GetNestedTypes => GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (bindingFlags), - IntrinsicId.Type_GetMembers => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers (bindingFlags), - _ => throw new ArgumentException ($"Reflection call '{calledMethodDefinition.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is of unexpected member type."), - }; - } - - var targetValue = GetMethodParameterValue (calledMethodDefinition, 0, memberTypes); - foreach (var value in methodParams[0]) { - RequireDynamicallyAccessedMembers (analysisContext, value, targetValue); - } - } - break; - - - // - // GetMember (String) - // GetMember (String, BindingFlags) - // GetMember (String, MemberTypes, BindingFlags) - // - case IntrinsicId.Type_GetMember: { - var parameters = calledMethodDefinition.Parameters; - BindingFlags? bindingFlags; - if (parameters.Count == 1) { - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Public | BindingFlags.Instance; - } else if (parameters.Count == 2 && calledMethodDefinition.Parameters[1].ParameterType.Name == "BindingFlags") - bindingFlags = GetBindingFlagsFromValue (methodParams[2]); - else if (parameters.Count == 3 && calledMethodDefinition.Parameters[2].ParameterType.Name == "BindingFlags") { - bindingFlags = GetBindingFlagsFromValue (methodParams[3]); - } else // Non recognized intrinsic - throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is an unexpected intrinsic."); - - DynamicallyAccessedMemberTypes requiredMemberTypes = DynamicallyAccessedMemberTypes.None; - 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 = GetMethodParameterValue (calledMethodDefinition, 0, requiredMemberTypes); - - // Go over all types we've seen - foreach (var value in methodParams[0]) { - // Mark based on bitfield requirements - RequireDynamicallyAccessedMembers (analysisContext, value, targetValue); - } - } - break; - - // // System.Activator // // static CreateInstance (System.Type type) @@ -1147,7 +852,7 @@ namespace Mono.Linker.Dataflow foreach (var methodValue in methodParams[0]) { if (methodValue is SystemReflectionMethodBaseValue methodBaseValue) { - ValidateGenericMethodInstantiation (analysisContext, methodBaseValue.MethodRepresented, methodParams[1], calledMethodDefinition); + ValidateGenericMethodInstantiation (analysisContext, methodBaseValue.MethodRepresented.Method, methodParams[1], calledMethodDefinition); } else if (methodValue == NullValue.Instance) { // Nothing to do } else { @@ -1384,65 +1089,17 @@ namespace Mono.Linker.Dataflow } } - void ProcessGetMethodByName ( - in AnalysisContext analysisContext, - TypeDefinition typeDefinition, - string methodName, - BindingFlags? bindingFlags, - ref MultiValue methodReturnValue) - { - bool foundAny = false; - foreach (var method in typeDefinition.GetMethodsOnTypeHierarchy (_context, m => m.Name == methodName, bindingFlags)) { - MarkMethod (analysisContext, method); - methodReturnValue = MultiValueLattice.Meet (methodReturnValue, new SystemReflectionMethodBaseValue (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) - methodReturnValue = MultiValueLattice.Meet (methodReturnValue, NullValue.Instance); - } - void RequireDynamicallyAccessedMembers (in AnalysisContext analysisContext, in MultiValue value, ValueWithDynamicallyAccessedMembers targetValue) { var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (_context, this, analysisContext); requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); } - static BindingFlags? GetBindingFlagsFromValue (in MultiValue parameter) => (BindingFlags?) parameter.AsConstInt (); + static BindingFlags? GetBindingFlagsFromValue (in MultiValue parameter) => HandleCallAction.GetBindingFlagsFromValue (parameter); - 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; - } + static bool BindingFlagsAreUnsupported (BindingFlags? bindingFlags) => HandleCallAction.BindingFlagsAreUnsupported (bindingFlags); - static bool HasBindingFlag (BindingFlags? bindingFlags, BindingFlags? search) => bindingFlags != null && (bindingFlags & search) == search; + static bool HasBindingFlag (BindingFlags? bindingFlags, BindingFlags? search) => HandleCallAction.HasBindingFlag (bindingFlags, search); internal void MarkTypeForDynamicallyAccessedMembers (in AnalysisContext analysisContext, TypeDefinition typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes, DependencyKind dependencyKind, bool declaredOnly = false) { @@ -1476,7 +1133,7 @@ namespace Mono.Linker.Dataflow _markStep.MarkTypeVisibleToReflection (typeReference, type, new DependencyInfo (dependencyKind, analysisContext.Origin.Provider)); } - void MarkMethod (in AnalysisContext analysisContext, MethodDefinition method, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + internal void MarkMethod (in AnalysisContext analysisContext, MethodDefinition method, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) { _markStep.MarkMethodVisibleToReflection (method, new DependencyInfo (dependencyKind, analysisContext.Origin.Provider)); } @@ -1507,31 +1164,19 @@ namespace Mono.Linker.Dataflow MarkMethod (analysisContext, ctor); } - void MarkFieldsOnTypeHierarchy (in AnalysisContext analysisContext, TypeDefinition type, Func<FieldDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default) + internal void MarkFieldsOnTypeHierarchy (in AnalysisContext analysisContext, TypeDefinition type, Func<FieldDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default) { foreach (var field in type.GetFieldsOnTypeHierarchy (_context, filter, bindingFlags)) MarkField (analysisContext, field); } - TypeDefinition[]? MarkNestedTypesOnType (in AnalysisContext analysisContext, TypeDefinition type, Func<TypeDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default) - { - var result = new ArrayBuilder<TypeDefinition> (); - - foreach (var nestedType in type.GetNestedTypesOnType (filter, bindingFlags)) { - result.Add (nestedType); - MarkType (analysisContext, nestedType); - } - - return result.ToArray (); - } - - void MarkPropertiesOnTypeHierarchy (in AnalysisContext analysisContext, TypeDefinition type, Func<PropertyDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default) + internal void MarkPropertiesOnTypeHierarchy (in AnalysisContext analysisContext, TypeDefinition type, Func<PropertyDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default) { foreach (var property in type.GetPropertiesOnTypeHierarchy (_context, filter, bindingFlags)) MarkProperty (analysisContext, property); } - void MarkEventsOnTypeHierarchy (in AnalysisContext analysisContext, TypeDefinition type, Func<EventDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default) + internal void MarkEventsOnTypeHierarchy (in AnalysisContext analysisContext, TypeDefinition type, Func<EventDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default) { foreach (var @event in type.GetEventsOnTypeHierarchy (_context, filter, bindingFlags)) MarkEvent (analysisContext, @event); @@ -1557,42 +1202,11 @@ namespace Mono.Linker.Dataflow } } - 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); - 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); + HandleCallAction.GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags); 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); - - 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); - - 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); - - 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); - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers (BindingFlags? bindingFlags) => - GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents (bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields (bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (bindingFlags); + HandleCallAction.GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags); internal readonly struct AnalysisContext { diff --git a/src/linker/Linker.Dataflow/ValueNode.cs b/src/linker/Linker.Dataflow/ValueNode.cs index 29bc92cc6..a079e777f 100644 --- a/src/linker/Linker.Dataflow/ValueNode.cs +++ b/src/linker/Linker.Dataflow/ValueNode.cs @@ -120,18 +120,6 @@ namespace ILLink.Shared.TrimAnalysis } /// <summary> - /// This is a known System.Reflection.MethodBase value. MethodRepresented is the 'value' of the MethodBase. - /// </summary> - partial record SystemReflectionMethodBaseValue - { - public SystemReflectionMethodBaseValue (MethodDefinition methodRepresented) => MethodRepresented = methodRepresented; - - public readonly MethodDefinition MethodRepresented; - - public override string ToString () => this.ValueToString (MethodRepresented); - } - - /// <summary> /// A value that came from a method parameter - such as the result of a ldarg. /// </summary> partial record MethodParameterValue : IValueWithStaticType diff --git a/test/ILLink.RoslynAnalyzer.Tests/DynamicallyAccessedMembersAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/DynamicallyAccessedMembersAnalyzerTests.cs index d24ad8aa7..9e250aabc 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/DynamicallyAccessedMembersAnalyzerTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/DynamicallyAccessedMembersAnalyzerTests.cs @@ -196,14 +196,13 @@ class C // (17,9): warning IL2070: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String)'. // The parameter 'type' of method 'C.M(Type)' does not have matching annotations. // The source value must declare at least the same requirements as those declared on the target location it is assigned to. - return VerifyDynamicallyAccessedMembersAnalyzer (TargetMethodWithAnnotations); - /*, - VerifyCS.Diagnostic (DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsThisParameter) - .WithSpan (17, 9, 17, 30) - .WithArguments ("System.Type.GetMethod(String)", - "type", - "C.M(Type)", - "'DynamicallyAccessedMemberTypes.PublicMethods'")*/ + return VerifyDynamicallyAccessedMembersAnalyzer (TargetMethodWithAnnotations, + VerifyCS.Diagnostic (DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsThisParameter) + .WithSpan (17, 9, 17, 30) + .WithArguments ("System.Type.GetMethod(String)", + "type", + "C.M(Type)", + "'DynamicallyAccessedMemberTypes.PublicMethods'")); } #endregion @@ -351,11 +350,10 @@ class C // (12,9): warning IL2075: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String)'. // The return value of method 'C.GetT()' does not have matching annotations. // The source value must declare at least the same requirements as those declared on the target location it is assigned to. - return VerifyDynamicallyAccessedMembersAnalyzer (TargetMethodWithAnnotations - /*, + return VerifyDynamicallyAccessedMembersAnalyzer (TargetMethodWithAnnotations, VerifyCS.Diagnostic (DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsThisParameter) .WithSpan (12, 9, 12, 34) - .WithArguments ("System.Type.GetMethod(String)", "C.GetFoo()", "'DynamicallyAccessedMemberTypes.PublicMethods'")*/); + .WithArguments ("System.Type.GetMethod(String)", "C.GetFoo()", "'DynamicallyAccessedMemberTypes.PublicMethods'")); } #endregion @@ -493,13 +491,12 @@ class C // (14,9): warning IL2080: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String)'. // The field 'C.f' does not have matching annotations. // The source value must declare at least the same requirements as those declared on the target location it is assigned to. - return VerifyDynamicallyAccessedMembersAnalyzer (TargetMethodWithAnnotations); - /*, - VerifyCS.Diagnostic (DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsThisParameter) - .WithSpan (14, 9, 14, 27) - .WithArguments ("System.Type.GetMethod(String)", - "C.f", - "'DynamicallyAccessedMemberTypes.PublicMethods'")*/ + return VerifyDynamicallyAccessedMembersAnalyzer (TargetMethodWithAnnotations, + VerifyCS.Diagnostic (DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsThisParameter) + .WithSpan (14, 9, 14, 27) + .WithArguments ("System.Type.GetMethod(String)", + "C.f", + "'DynamicallyAccessedMemberTypes.PublicMethods'")); } #endregion diff --git a/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs b/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs index 581d5a279..7dc1e1639 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs @@ -25,8 +25,7 @@ namespace ILLink.RoslynAnalyzer.Tests [Fact] public Task ConstructorsUsedViaReflection () { - // https://github.com/dotnet/linker/issues/2578 - return RunTest (allowMissingWarnings: true); + return RunTest (); } [Fact] @@ -37,10 +36,15 @@ namespace ILLink.RoslynAnalyzer.Tests } [Fact] + public Task EventUsedViaReflection () + { + return RunTest (); + } + + [Fact] public Task EventsUsedViaReflection () { - // https://github.com/dotnet/linker/issues/2578 - return RunTest (allowMissingWarnings: true); + return RunTest (); } [Fact] @@ -63,38 +67,63 @@ namespace ILLink.RoslynAnalyzer.Tests } [Fact] + public Task FieldUsedViaReflection () + { + return RunTest (); + } + + [Fact] public Task FieldsUsedViaReflection () { - // https://github.com/dotnet/linker/issues/2578 - return RunTest (allowMissingWarnings: true); + return RunTest (); } [Fact] public Task MembersUsedViaReflection () { - // https://github.com/dotnet/linker/issues/2578 - return RunTest (allowMissingWarnings: true); + return RunTest (); } [Fact] public Task MemberUsedViaReflection () { - // https://github.com/dotnet/linker/issues/2578 - return RunTest (allowMissingWarnings: true); + return RunTest (); + } + + [Fact] + public Task MethodUsedViaReflection () + { + return RunTest (); + } + + [Fact] + public Task MethodUsedViaReflectionAndLocal () + { + return RunTest (); + } + + [Fact] + public Task MethodUsedViaReflectionWithDefaultBindingFlags () + { + return RunTest (); } [Fact] public Task MethodsUsedViaReflection () { - // https://github.com/dotnet/linker/issues/2578 - return RunTest (allowMissingWarnings: true); + return RunTest (); + } + + [Fact] + public Task NestedTypeUsedViaReflection () + { + return RunTest (); } [Fact] public Task NestedTypesUsedViaReflection () { - // https://github.com/dotnet/linker/issues/2578 - return RunTest (allowMissingWarnings: true); + return RunTest (); } [Fact] @@ -105,10 +134,21 @@ namespace ILLink.RoslynAnalyzer.Tests } [Fact] + public Task PropertyUsedViaReflection () + { + return RunTest (); + } + + [Fact] public Task PropertiesUsedViaReflection () { - // https://github.com/dotnet/linker/issues/2578 - return RunTest (allowMissingWarnings: true); + return RunTest (); + } + + [Fact] + public Task RuntimeReflectionExtensionsCalls () + { + return RunTest (); } [Fact] diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs index 486718f26..2ef192d11 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/ReflectionTests.g.cs @@ -38,12 +38,6 @@ namespace ILLink.RoslynAnalyzer.Tests } [Fact] - public Task EventUsedViaReflection () - { - return RunTest (allowMissingWarnings: true); - } - - [Fact] public Task ExpressionCallStringAndLocals () { return RunTest (allowMissingWarnings: true); @@ -62,36 +56,6 @@ namespace ILLink.RoslynAnalyzer.Tests } [Fact] - public Task FieldUsedViaReflection () - { - return RunTest (allowMissingWarnings: true); - } - - [Fact] - public Task MethodUsedViaReflection () - { - return RunTest (allowMissingWarnings: true); - } - - [Fact] - public Task MethodUsedViaReflectionAndLocal () - { - return RunTest (allowMissingWarnings: true); - } - - [Fact] - public Task MethodUsedViaReflectionWithDefaultBindingFlags () - { - return RunTest (allowMissingWarnings: true); - } - - [Fact] - public Task NestedTypeUsedViaReflection () - { - return RunTest (allowMissingWarnings: true); - } - - [Fact] public Task ObjectGetTypeLibraryMode () { return RunTest (allowMissingWarnings: true); @@ -104,24 +68,12 @@ namespace ILLink.RoslynAnalyzer.Tests } [Fact] - public Task PropertyUsedViaReflection () - { - return RunTest (allowMissingWarnings: true); - } - - [Fact] public Task RunClassConstructorUsedViaReflection () { return RunTest (allowMissingWarnings: true); } [Fact] - public Task RuntimeReflectionExtensionsCalls () - { - return RunTest (allowMissingWarnings: true); - } - - [Fact] public Task TypeBaseTypeUseViaReflection () { return RunTest (allowMissingWarnings: true); diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs b/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs index 07a450776..ef5339d7f 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs @@ -39,8 +39,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] public static Type _annotatedField; - // Intrinsic is disabled https://github.com/dotnet/linker/issues/2559 - [ExpectedWarning ("IL2110", nameof (_annotatedField), ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2110", nameof (_annotatedField))] static void Reflection () { typeof (AnnotatedField).GetField ("_annotatedField").SetValue (null, typeof (TestType)); @@ -52,8 +51,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow typeof (AnnotatedField).GetField ("_annotatedField").SetValue (null, typeof (TestType)); } - // Intrinsic is disabled https://github.com/dotnet/linker/issues/2559 - [ExpectedWarning ("IL2110", nameof (_annotatedField), ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2110", nameof (_annotatedField))] static void ReflectionReadOnly () { typeof (AnnotatedField).GetField ("_annotatedField").GetValue (null); @@ -160,8 +158,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow { } } - // Intrinsic is disabled https://github.com/dotnet/linker/issues/2559 - [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter), ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter))] static void Reflection () { typeof (AnnotatedMethodParameters).GetMethod (nameof (MethodWithSingleAnnotatedParameter)).Invoke (null, null); @@ -294,8 +291,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow typeof (AnnotatedMethodReturnValue).GetMethod (nameof (InstanceMethodWithAnnotatedReturnValue)).Invoke (null, null); } - // Intrinsic is disabled https://github.com/dotnet/linker/issues/2559 - [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue), ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue))] static void ReflectionOnVirtual () { typeof (AnnotatedMethodReturnValue).GetMethod (nameof (VirtualMethodWithAnnotatedReturnValue)).Invoke (null, null); @@ -423,8 +419,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow public Type PropertyWithAnnotation { get; set; } } - // Intrinsic is disabled https://github.com/dotnet/linker/issues/2559 - [ExpectedWarning ("IL2111", nameof (Property1WithAnnotation) + ".set", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2111", nameof (Property1WithAnnotation) + ".set")] static void ReflectionOnPropertyItself () { typeof (AnnotatedProperty).GetProperty (nameof (Property1WithAnnotation)); @@ -441,8 +436,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow typeof (AnnotatedProperty).GetProperty (nameof (Property2WithAnnotationGetterOnly)); } - // Intrinsic is disabled https://github.com/dotnet/linker/issues/2559 - [ExpectedWarning ("IL2111", nameof (VirtualProperty3WithAnnotationGetterOnly), ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2111", nameof (VirtualProperty3WithAnnotationGetterOnly))] static void ReflectionOnPropertyWithGetterOnlyOnVirtual () { typeof (AnnotatedProperty).GetProperty (nameof (VirtualProperty3WithAnnotationGetterOnly)); @@ -453,15 +447,13 @@ namespace Mono.Linker.Tests.Cases.DataFlow typeof (AnnotatedProperty).GetMethod ("get_" + nameof (Property1WithAnnotation)); } - // Intrinsic is disabled https://github.com/dotnet/linker/issues/2559 - [ExpectedWarning ("IL2111", nameof (Property1WithAnnotation) + ".set", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2111", nameof (Property1WithAnnotation) + ".set")] static void ReflectionOnSetter () { typeof (AnnotatedProperty).GetMethod ("set_" + nameof (Property1WithAnnotation)); } - // Intrinsic is disabled https://github.com/dotnet/linker/issues/2559 - [ExpectedWarning ("IL2111", nameof (VirtualProperty3WithAnnotationGetterOnly) + ".get", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2111", nameof (VirtualProperty3WithAnnotationGetterOnly) + ".get")] static void ReflectionOnVirtualGetter () { typeof (AnnotatedProperty).GetMethod ("get_" + nameof (VirtualProperty3WithAnnotationGetterOnly)); @@ -662,8 +654,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow var _ = new Action<Type> (GenericWithAnnotatedMethod<TestType>.AnnotatedMethod); } - // Intrinsic is disabled https://github.com/dotnet/linker/issues/2559 - [ExpectedWarning ("IL2111", nameof (GenericMethodWithAnnotation), ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2111", nameof (GenericMethodWithAnnotation))] public static void GenericMethodWithAnnotationReflection () { typeof (AnnotationOnGenerics).GetMethod (nameof (GenericMethodWithAnnotation)); diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/DynamicDependencyDataflow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/DynamicDependencyDataflow.cs index 8237bc4db..029e5848b 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/DynamicDependencyDataflow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/DynamicDependencyDataflow.cs @@ -18,18 +18,16 @@ namespace Mono.Linker.Tests.Cases.DataFlow [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] static Type TypeWithPublicMethods; - // Intrinsic is disabled https://github.com/dotnet/linker/issues/2559 [Kept] - [ExpectedWarning ("IL2080", nameof (Type.GetField), ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2080", nameof (Type.GetField))] [DynamicDependency ("DynamicDependencyTo")] static void DynamicDependencyFrom () { _ = TypeWithPublicMethods.GetField ("f"); } - // Intrinsic is disabled https://github.com/dotnet/linker/issues/2559 [Kept] - [ExpectedWarning ("IL2080", nameof (Type.GetProperty), ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2080", nameof (Type.GetProperty))] static void DynamicDependencyTo () { _ = TypeWithPublicMethods.GetProperty ("p"); diff --git a/test/Mono.Linker.Tests.Cases/Reflection/MethodsUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/MethodsUsedViaReflection.cs index fcede014b..2ffbde48e 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/MethodsUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/MethodsUsedViaReflection.cs @@ -24,6 +24,10 @@ namespace Mono.Linker.Tests.Cases.Reflection TestIgnoreCaseBindingFlags (); TestIgnorableBindingFlags (); TestUnsupportedBindingFlags (); + + HandlingOfComplexExpressionForBindingFlags.Test (); + HandlingOfBindingFlagsAsNumbers.Test (); + HandlingOfBindingFlagsFromConstants.Test (); } [Kept] @@ -307,5 +311,82 @@ namespace Mono.Linker.Tests.Cases.Reflection return true; } } + + [Kept] + class HandlingOfComplexExpressionForBindingFlags + { + [Kept] + class TestClassWithRUCMethods + { + [Kept] + public void Method () { } + + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode (nameof (HandlingOfComplexExpressionForBindingFlags) + "--" + nameof (TestClassWithRUCMethods))] + private void PrivateMethodWithRUC () { } + } + + [Kept] + // https://github.com/dotnet/linker/issues/2638 + [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)] + public static void Test () + { + BindingFlags left = BindingFlags.Instance | BindingFlags.Static; + BindingFlags right = BindingFlags.Public; + int result = (int) left | (int) right; + typeof (TestClassWithRUCMethods).GetMethods ((BindingFlags) result); + } + } + + [Kept] + class HandlingOfBindingFlagsAsNumbers + { + [Kept] + class TestClassWithRUCMethods + { + [Kept] + public static void Method () { } + + [RequiresUnreferencedCode (nameof (HandlingOfBindingFlagsAsNumbers) + "--" + nameof (TestClassWithRUCMethods))] + private static void PrivateMethodWithRUC () { } + } + + [Kept] + // https://github.com/dotnet/linker/issues/2638 + [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Analyzer)] + public static void Test () + { + typeof (TestClassWithRUCMethods).GetMethods ((BindingFlags) 24); + + // Analyzer currently can't figure this out + int bindingFlagsNumber = 24; + typeof (TestClassWithRUCMethods).GetMethods ((BindingFlags) bindingFlagsNumber); + } + } + + [Kept] + class HandlingOfBindingFlagsFromConstants + { + [Kept] + class TestClassWithRUCMethods + { + [Kept] + public static void Method () { } + + [RequiresUnreferencedCode (nameof (HandlingOfBindingFlagsAsNumbers) + "--" + nameof (TestClassWithRUCMethods))] + private static void PrivateMethodWithRUC () { } + } + + const BindingFlags PublicStaticFlags = BindingFlags.Public | BindingFlags.Static; + const BindingFlags PublicOnlyFlags = BindingFlags.Public; + + [Kept] + public static void Test () + { + typeof (TestClassWithRUCMethods).GetMethods (PublicStaticFlags); + typeof (TestClassWithRUCMethods).GetMethods (PublicOnlyFlags | BindingFlags.Static); + } + } } } diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresAccessedThrough.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresAccessedThrough.cs index 14b70f2a8..758d7a744 100644 --- a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresAccessedThrough.cs +++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresAccessedThrough.cs @@ -39,7 +39,7 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability { } - [ExpectedWarning ("IL2026", "--RequiresOnlyThroughReflection--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "--RequiresOnlyThroughReflection--")] static void TestRequiresOnlyThroughReflection () { typeof (RequiresAccessedThrough) @@ -54,7 +54,7 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability { } - [ExpectedWarning ("IL2026", "--GenericType.RequiresOnlyThroughReflection--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "--GenericType.RequiresOnlyThroughReflection--")] public static void Test () { typeof (AccessedThroughReflectionOnGenericType<T>) diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs index 2b7bfae37..240fd2d67 100644 --- a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs +++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs @@ -464,10 +464,10 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability typeof (DerivedWithoutRequiresOnType).RequiresPublicMethods (); } - [ExpectedWarning ("IL2026", "BaseWithoutRequiresOnType.Method()", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method(Int32)", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method()", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "ImplementationWithRequiresOnType.Method()", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "BaseWithoutRequiresOnType.Method()")] + [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method(Int32)")] + [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method()")] + [ExpectedWarning ("IL2026", "ImplementationWithRequiresOnType.Method()")] static void TestDirectReflectionAccess () { // Requires on the method itself @@ -600,16 +600,16 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability typeof (DerivedWithRequires).RequiresPublicFields (); } - [ExpectedWarning ("IL2026", "WithRequires.StaticField", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "WithRequires.PrivateStaticField", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticField", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "WithRequires.StaticField")] + [ExpectedWarning ("IL2026", "WithRequires.PrivateStaticField")] + [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticField")] static void TestDirectReflectionAccess () { typeof (WithRequires).GetField (nameof (WithRequires.StaticField)); typeof (WithRequires).GetField (nameof (WithRequires.InstanceField)); // Doesn't warn typeof (WithRequires).GetField ("PrivateStaticField", BindingFlags.NonPublic); typeof (WithRequiresOnlyInstanceFields).GetField (nameof (WithRequiresOnlyInstanceFields.InstanceField)); // Doesn't warn - typeof (DerivedWithoutRequires).GetField (nameof (DerivedWithRequires.DerivedStaticField)); // Doesn't warn + typeof (DerivedWithoutRequires).GetField (nameof (DerivedWithoutRequires.DerivedStaticField)); // Doesn't warn typeof (DerivedWithRequires).GetField (nameof (DerivedWithRequires.DerivedStaticField)); } @@ -676,7 +676,9 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability public static event EventHandler StaticEvent; } - [ExpectedWarning ("IL2026", "StaticEvent.add", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "StaticEvent.add")] + // https://github.com/mono/linker/issues/2218 + [ExpectedWarning ("IL2026", "StaticEvent.remove", ProducedBy = ProducedBy.Analyzer)] static void TestDirectReflectionAccess () { typeof (WithRequires).GetEvent (nameof (WithRequires.StaticEvent)); @@ -735,12 +737,12 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability typeof (DerivedWithRequires).RequiresPublicProperties (); } - [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.get", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.set", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "WithRequires.PrivateStaticProperty.get", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "WithRequires.PrivateStaticProperty.set", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticProperty.get", ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticProperty.set", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.get")] + [ExpectedWarning ("IL2026", "WithRequires.StaticProperty.set")] + [ExpectedWarning ("IL2026", "WithRequires.PrivateStaticProperty.get")] + [ExpectedWarning ("IL2026", "WithRequires.PrivateStaticProperty.set")] + [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticProperty.get")] + [ExpectedWarning ("IL2026", "DerivedWithRequires.DerivedStaticProperty.set")] static void TestDirectReflectionAccess () { typeof (WithRequires).GetProperty (nameof (WithRequires.StaticProperty)); diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresWithCopyAssembly.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresWithCopyAssembly.cs index facd4cf73..b2e1e0f39 100644 --- a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresWithCopyAssembly.cs +++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresWithCopyAssembly.cs @@ -50,7 +50,7 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability tmp.Method (); } - [ExpectedWarning ("IL2026", "--MethodCalledThroughReflection--", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", "--MethodCalledThroughReflection--")] static void TestRequiresThroughReflectionInMethodFromCopiedAssembly () { typeof (RequiresInCopyAssembly) |