From ced1292e4ad94fd76bf027368b4d16e28b1eb5a6 Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Fri, 17 Nov 2023 08:07:06 +1100 Subject: [PATCH 01/17] Implement masking based on MemberTypes - Implements masking the required member types based on the MemberTypes parameter --- .../TrimAnalysis/HandleCallAction.cs | 74 ++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index 4e3f1e98fb14ab..d3bb508af9a9f9 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -373,12 +373,18 @@ GenericParameterValue genericParam } BindingFlags? bindingFlags; + MemberTypes? memberTypes; if (calledMethod.HasMetadataParametersCount (1)) { + // Assume a default value for MemberTypes for methods that don't use MemberTypes as a parameter + memberTypes = MemberTypes.All; // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter bindingFlags = BindingFlags.Public | BindingFlags.Instance; - } else if (calledMethod.HasMetadataParametersCount (2) && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.Reflection.BindingFlags")) + } else if (calledMethod.HasMetadataParametersCount (2) && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.Reflection.BindingFlags")) { + // Assume a default value for MemberTypes for methods that don't use MemberTypes as a parameter + memberTypes = MemberTypes.All; bindingFlags = GetBindingFlagsFromValue (argumentValues[1]); - else if (calledMethod.HasMetadataParametersCount (3) && calledMethod.HasParameterOfType ((ParameterIndex) 3, "System.Reflection.BindingFlags")) { + } else if (calledMethod.HasMetadataParametersCount (3) && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.Reflection.MemberTypes") && calledMethod.HasParameterOfType ((ParameterIndex) 3, "System.Reflection.BindingFlags")) { + memberTypes = GetMemberTypesFromValue (argumentValues[1]); bindingFlags = GetBindingFlagsFromValue (argumentValues[2]); } else // Non recognized intrinsic throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{GetContainingSymbolDisplayName ()}' is an unexpected intrinsic."); @@ -394,6 +400,11 @@ GenericParameterValue genericParam } else { requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers (bindingFlags); } + if (!MemberTypesAreUnsupported(memberTypes)) { + DynamicallyAccessedMemberTypes requiredMemberTypesMaskFromMemberTypes; + requiredMemberTypesMaskFromMemberTypes = GetDynamicallyAccessedMemberTypesFromMemberTypesForMembers (memberTypes); + requiredMemberTypes &= requiredMemberTypesMaskFromMemberTypes; + } var targetValue = _annotations.GetMethodThisParameterValue (calledMethod, requiredMemberTypes); @@ -1352,6 +1363,7 @@ private void ProcessCreateInstanceByName (MethodProxy calledMethod, IReadOnlyLis } internal static BindingFlags? GetBindingFlagsFromValue (in MultiValue parameter) => (BindingFlags?) parameter.AsConstInt (); + internal static MemberTypes? GetMemberTypesFromValue (in MultiValue parameter) => (MemberTypes?) parameter.AsConstInt (); internal static bool BindingFlagsAreUnsupported (BindingFlags? bindingFlags) { @@ -1381,6 +1393,29 @@ internal static bool BindingFlagsAreUnsupported (BindingFlags? bindingFlags) return (flags & ~(UnderstoodBindingFlags | IgnorableBindingFlags)) != 0; } + internal static bool MemberTypesAreUnsupported (MemberTypes? memberTypes) + { + if (memberTypes == null) + return true; + + // Member types we understand + const MemberTypes UnderstoodMemberTypes = + MemberTypes.Constructor | + MemberTypes.Event | + MemberTypes.Field | + MemberTypes.Method | + MemberTypes.Property | + MemberTypes.TypeInfo | + MemberTypes.NestedType; + + // Member types that don't affect binding outside InvokeMember (that we don't analyze). + const MemberTypes IgnorableMemberTypes = + MemberTypes.Custom; + + MemberTypes flags = memberTypes.Value; + return (flags & ~(UnderstoodMemberTypes | IgnorableMemberTypes)) != 0; + } + internal static bool HasBindingFlag (BindingFlags? bindingFlags, BindingFlags? search) => bindingFlags != null && (bindingFlags & search) == search; internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (BindingFlags? bindingFlags) => @@ -1421,6 +1456,41 @@ internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (bindingFlags) | GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (bindingFlags); + internal static bool HasMemberType (MemberTypes? memberTypes, MemberTypes? search) => memberTypes != null && (memberTypes & search) == search; + + internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForNestedTypes (MemberTypes? memberTypes) => + (HasMemberType (memberTypes, MemberTypes.TypeInfo) ? DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None) | + (HasMemberType (memberTypes, MemberTypes.NestedType) ? DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None) | + (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None); + + internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForConstructors (MemberTypes? memberTypes) => + (HasMemberType (memberTypes, MemberTypes.Constructor) ? DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None) | + (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None); + + internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForMethods (MemberTypes? memberTypes) => + (HasMemberType (memberTypes, MemberTypes.Constructor) ? DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None) | + (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None); + + internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForFields (MemberTypes? memberTypes) => + (HasMemberType (memberTypes, MemberTypes.Constructor) ? DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None) | + (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None); + + internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForProperties (MemberTypes? memberTypes) => + (HasMemberType (memberTypes, MemberTypes.Constructor) ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None) | + (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None); + + internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForEvents (MemberTypes? memberTypes) => + (HasMemberType (memberTypes, MemberTypes.Constructor) ? DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None) | + (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None); + + internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForMembers (MemberTypes? memberTypes) => + GetDynamicallyAccessedMemberTypesFromMemberTypesForConstructors (memberTypes) | + GetDynamicallyAccessedMemberTypesFromMemberTypesForEvents (memberTypes) | + GetDynamicallyAccessedMemberTypesFromMemberTypesForFields (memberTypes) | + GetDynamicallyAccessedMemberTypesFromMemberTypesForMethods (memberTypes) | + GetDynamicallyAccessedMemberTypesFromMemberTypesForProperties (memberTypes) | + GetDynamicallyAccessedMemberTypesFromMemberTypesForNestedTypes (memberTypes); + /// /// Returns true if the method is a .ctor for System.Type or a type that derives from System.Type (i.e. fields and params of this type can have DynamicallyAccessedMembers annotations) /// From d0df38312755bc14db6ffeccfd69d6be4fa5f8af Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Fri, 17 Nov 2023 08:24:47 +1100 Subject: [PATCH 02/17] Update HandleCallAction.cs --- .../src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index d3bb508af9a9f9..49a89d56036a9b 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -1468,19 +1468,19 @@ internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypes (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None); internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForMethods (MemberTypes? memberTypes) => - (HasMemberType (memberTypes, MemberTypes.Constructor) ? DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None) | + (HasMemberType (memberTypes, MemberTypes.Method) ? DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None) | (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None); internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForFields (MemberTypes? memberTypes) => - (HasMemberType (memberTypes, MemberTypes.Constructor) ? DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None) | + (HasMemberType (memberTypes, MemberTypes.Field) ? DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None) | (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None); internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForProperties (MemberTypes? memberTypes) => - (HasMemberType (memberTypes, MemberTypes.Constructor) ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None) | + (HasMemberType (memberTypes, MemberTypes.Property) ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None) | (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None); internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForEvents (MemberTypes? memberTypes) => - (HasMemberType (memberTypes, MemberTypes.Constructor) ? DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None) | + (HasMemberType (memberTypes, MemberTypes.Event) ? DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None) | (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None); internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForMembers (MemberTypes? memberTypes) => From 65a5a1ce9d9e5348e969c333e8175f953ed8f073 Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Fri, 17 Nov 2023 09:01:12 +1100 Subject: [PATCH 03/17] Implement matching by name - Matching by name is implemented here, including logic to handle the prefix name lookup scheme of GetMember & special handling for constructors --- .../Compiler/Dataflow/HandleCallAction.cs | 21 +++++ .../TrimAnalysis/HandleCallAction.cs | 82 ++++++++++++++++++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs index f09a76221ef37b..ec2118491b8c9c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs @@ -124,5 +124,26 @@ private partial bool MarkAssociatedProperty(MethodProxy method) } private partial string GetContainingSymbolDisplayName() => _callingMethod.GetDisplayName(); + + private partial void MarkEventsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionMarker.MarkEventsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (e => e.Name.StartsWith(name)) : (e => e.Name == name), _reason, bindingFlags); + + private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionMarker.MarkFieldsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (f => f.Name.StartsWith(name)) : (f => f.Name == name), _reason, bindingFlags); + + private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionMarker.MarkPropertiesOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (p => p.Name.StartsWith(name)) : (p => p.Name == name), _reason, bindingFlags); + + private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var method in type.Type.GetMethodsOnTypeHierarchy(nameIsPrefix ? (m => m.Name.StartsWith(name)) : (m => m.Name == name), bindingFlags)) + yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); + } + + private partial IEnumerable GetNestedTypesOnType(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) + yield return new SystemTypeValue(new TypeProxy(nestedType)); + } } } diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index 49a89d56036a9b..3943050ff69a71 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -410,8 +410,59 @@ GenericParameterValue genericParam // Go over all types we've seen foreach (var value in instanceValue) { - // Mark based on bitfield requirements - _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + if (value is SystemTypeValue systemTypeValue) { + foreach (var stringParam in argumentValues[0]) { + if (stringParam is KnownStringValue stringValue && !BindingFlagsAreUnsupported (bindingFlags) && !MemberTypesAreUnsupported (memberTypes)) { + // determine if we've got a prefix (for example, abc* searches for anything starting with abc) + var isPrefix = stringValue.Contents.EndsWith('*'); + var name = stringValue.Contents; + if (isPrefix) name = name.Substring(0, name.Length - 1); + + // we may not need to search for constructors depending on the value of 'name', since they're always named '.ctor' (II.10.5.1) + DynamicallyAccessedMemberTypes requiredMemberTypes2 = requiredMemberTypes; + if (isPrefix ? !".ctor".StartsWith(name) : name != ".ctor") { + requiredMemberTypes2 &= ~(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors); + } + + AddReturnValue (MultiValueLattice.Top); // Initialize return value (so that it's not autofilled if there are no matching members) + + // search for all the things we want by name + if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)) != 0) { + MarkConstructorsOnType (systemTypeValue.RepresentedType, bindingFlags, parameterCount: null); + } + if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)) != 0) { + MarkEventsOnTypeHierarchyWithPrefix (systemTypeValue.RepresentedType, name, isPrefix, bindingFlags); + } + if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)) != 0) { + MarkFieldsOnTypeHierarchyWithPrefix (systemTypeValue.RepresentedType, name, isPrefix, bindingFlags); + } + if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)) != 0) { + foreach (var methodValue in ProcessGetMethodByNameWithPrefix (systemTypeValue.RepresentedType, name, isPrefix, bindingFlags)) + AddReturnValue (methodValue); + } + if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)) != 0) { + MarkPropertiesOnTypeHierarchyWithPrefix (systemTypeValue.RepresentedType, name, isPrefix, bindingFlags); + } + if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)) != 0) { + foreach (var nestedTypeValue in GetNestedTypesOnTypeWithPrefix (systemTypeValue.RepresentedType, name, isPrefix, bindingFlags)) { + MarkType (nestedTypeValue.RepresentedType); + AddReturnValue (nestedTypeValue); + } + } + } else if (stringParam is NullValue) { + // type.GetMember(null, ...) throws - so track empty value set as its result + AddReturnValue (MultiValueLattice.Top); + } else { + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + } + } + } else if (value is NullValue) { + // null.GetMember(...) throws - so track empty value set as its result + AddReturnValue (MultiValueLattice.Top); + } else { + // Mark based on bitfield requirements + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + } } } break; @@ -1227,6 +1278,23 @@ private IEnumerable ProcessGetMethodByName (TypeProxy type, string m yield return NullValue.Instance; } + private IEnumerable ProcessGetMethodByNameWithPrefix (TypeProxy type, string methodName, bool nameIsPrefix, BindingFlags? bindingFlags) + { + bool foundAny = false; + foreach (var method in GetMethodsOnTypeHierarchyWithPrefix (type, methodName, nameIsPrefix, bindingFlags)) { + MarkMethod (method.RepresentedMethod); + yield return method; + foundAny = true; + } + + // If there were no methods found the API will return null at runtime, so we should + // track the null as a return value as well. + // This also prevents warnings in such case, since if we don't set the return value it will be + // "unknown" and consumers may warn. + if (!foundAny) + yield return NullValue.Instance; + } + private bool AnalyzeGenericInstantiationTypeArray (in MultiValue arrayParam, in MethodProxy calledMethod, ImmutableArray genericParameters) { bool hasRequirements = false; @@ -1524,5 +1592,15 @@ internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypes // Only used for internal diagnostic purposes (not even for warning messages) private partial string GetContainingSymbolDisplayName (); + + private partial void MarkEventsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags); + + private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags); + + private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags); + + private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix (TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags); + + private partial IEnumerable GetNestedTypesOnTypeWithPrefix (TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags); } } From b15124a3e408af2e05d1990a25aacb1c2770fcab Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Fri, 8 Dec 2023 07:11:21 +1100 Subject: [PATCH 04/17] Implement methods in other files --- .../TrimAnalysis/HandleCallAction.cs | 21 +++++++++++++++++++ .../TrimAnalysis/ReflectionAccessAnalyzer.cs | 18 ++++++++++++++++ .../Linker.Dataflow/HandleCallAction.cs | 21 +++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs index 96c4a07a92a79a..02a5ebfac246d5 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs @@ -114,5 +114,26 @@ private partial bool MarkAssociatedProperty (MethodProxy method) } private partial string GetContainingSymbolDisplayName () => _operation.FindContainingSymbol (_owningSymbol).GetDisplayName (); + + private partial void MarkEventsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForEventsOnTypeHierarchyWithPrefix (_diagnosticContext, type.Type, name, nameIsPrefix, bindingFlags); + + private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForFieldsOnTypeHierarchyWithPrefix (_diagnosticContext, type.Type, name, nameIsPrefix, bindingFlags); + + private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchyWithPrefix (_diagnosticContext, type.Type, name, nameIsPrefix, bindingFlags); + + private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var method in type.Type.GetMethodsOnTypeHierarchy(nameIsPrefix ? (m => m.Name.StartsWith(name)) : (m => m.Name == name), bindingFlags)) + yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); + } + + private partial IEnumerable GetNestedTypesOnType(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) + yield return new SystemTypeValue(new TypeProxy(nestedType)); + } } } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs index 1431399b5623e1..a289ef4b5e62d1 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs @@ -60,6 +60,24 @@ internal void GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchy (in Dia GetReflectionAccessDiagnosticsForProperty (diagnosticContext, prop); } + internal void GetReflectionAccessDiagnosticsForEventsOnTypeHierarchyWithPrefix (in DiagnosticContext diagnosticContext, ITypeSymbol typeSymbol, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var @event in typeSymbol.GetEventsOnTypeHierarchy (nameIsPrefix ? (e => e.Name.StartsWith(name)) : (e => e.Name == name), bindingFlags)) + GetDiagnosticsForEvent (diagnosticContext, @event); + } + + internal void GetReflectionAccessDiagnosticsForFieldsOnTypeHierarchyWithPrefix (in DiagnosticContext diagnosticContext, ITypeSymbol typeSymbol, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var field in typeSymbol.GetFieldsOnTypeHierarchy (nameIsPrefix ? (f => f.Name.StartsWith(name)) : (f => f.Name == name), bindingFlags)) + GetDiagnosticsForField (diagnosticContext, field); + } + + internal void GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchyWithPrefix (in DiagnosticContext diagnosticContext, ITypeSymbol typeSymbol, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var prop in typeSymbol.GetPropertiesOnTypeHierarchy (p => p.Name.StartsWith(name)) : (p => p.Name == name), bindingFlags)) + GetReflectionAccessDiagnosticsForProperty (diagnosticContext, prop); + } + internal void GetReflectionAccessDiagnosticsForConstructorsOnType (in DiagnosticContext diagnosticContext, ITypeSymbol typeSymbol, BindingFlags? bindingFlags, int? parameterCount) { foreach (var c in typeSymbol.GetConstructorsOnType (filter: parameterCount.HasValue ? c => c.Parameters.Length == parameterCount.Value : null, bindingFlags: bindingFlags)) diff --git a/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs b/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs index 108b79356a0228..80eeb19e12af1e 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs @@ -128,5 +128,26 @@ private partial bool MarkAssociatedProperty (MethodProxy method) } private partial string GetContainingSymbolDisplayName () => _callingMethodDefinition.GetDisplayName (); + + private partial void MarkEventsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionMarker.MarkEventsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (e => e.Name.StartsWith(name)) : (e => e.Name == name), _reason, bindingFlags); + + private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionMarker.MarkFieldsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (f => f.Name.StartsWith(name)) : (f => f.Name == name), _reason, bindingFlags); + + private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionMarker.MarkPropertiesOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (p => p.Name.StartsWith(name)) : (p => p.Name == name), _reason, bindingFlags); + + private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var method in type.Type.GetMethodsOnTypeHierarchy(nameIsPrefix ? (m => m.Name.StartsWith(name)) : (m => m.Name == name), bindingFlags)) + yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); + } + + private partial IEnumerable GetNestedTypesOnType(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) + yield return new SystemTypeValue(new TypeProxy(nestedType)); + } } } From b3c8d5f10ef5f15a1f73c17488bd4298d6518335 Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Fri, 8 Dec 2023 09:00:42 +1100 Subject: [PATCH 05/17] Fix compile errors --- .../ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs | 2 +- .../src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs | 2 +- .../TrimAnalysis/ReflectionAccessAnalyzer.cs | 2 +- src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs index ec2118491b8c9c..2410cda3f0da97 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs @@ -140,7 +140,7 @@ private partial IEnumerable GetMethodsOnTypeHie yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); } - private partial IEnumerable GetNestedTypesOnType(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + private partial IEnumerable GetNestedTypesOnTypeWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) { foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) yield return new SystemTypeValue(new TypeProxy(nestedType)); diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs index 02a5ebfac246d5..bed7e7764fbc83 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs @@ -130,7 +130,7 @@ private partial IEnumerable GetMethodsOnTypeHie yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); } - private partial IEnumerable GetNestedTypesOnType(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + private partial IEnumerable GetNestedTypesOnTypeWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) { foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) yield return new SystemTypeValue(new TypeProxy(nestedType)); diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs index a289ef4b5e62d1..b4bc5623842f9c 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs @@ -74,7 +74,7 @@ internal void GetReflectionAccessDiagnosticsForFieldsOnTypeHierarchyWithPrefix ( internal void GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchyWithPrefix (in DiagnosticContext diagnosticContext, ITypeSymbol typeSymbol, string name, bool nameIsPrefix, BindingFlags? bindingFlags) { - foreach (var prop in typeSymbol.GetPropertiesOnTypeHierarchy (p => p.Name.StartsWith(name)) : (p => p.Name == name), bindingFlags)) + foreach (var prop in typeSymbol.GetPropertiesOnTypeHierarchy (nameIsPrefix ? (p => p.Name.StartsWith(name)) : (p => p.Name == name), bindingFlags)) GetReflectionAccessDiagnosticsForProperty (diagnosticContext, prop); } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs b/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs index 80eeb19e12af1e..7565b1a1fc96d5 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs @@ -144,7 +144,7 @@ private partial IEnumerable GetMethodsOnTypeHie yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); } - private partial IEnumerable GetNestedTypesOnType(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + private partial IEnumerable GetNestedTypesOnTypeWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) { foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) yield return new SystemTypeValue(new TypeProxy(nestedType)); From 8439ea2bf04c4911db4a12b9a18cdce65a4b2dbc Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Fri, 8 Dec 2023 09:16:29 +1100 Subject: [PATCH 06/17] Fix some compile errors and spacing - We use EndsWith(string) because some platforms don't have the char overload --- .../TrimAnalysis/HandleCallAction.cs | 30 +++++++++---------- .../TrimAnalysis/HandleCallAction.cs | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs index bed7e7764fbc83..740d85e96d0271 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs @@ -116,24 +116,24 @@ private partial bool MarkAssociatedProperty (MethodProxy method) private partial string GetContainingSymbolDisplayName () => _operation.FindContainingSymbol (_owningSymbol).GetDisplayName (); private partial void MarkEventsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForEventsOnTypeHierarchyWithPrefix (_diagnosticContext, type.Type, name, nameIsPrefix, bindingFlags); + => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForEventsOnTypeHierarchyWithPrefix (_diagnosticContext, type.Type, name, nameIsPrefix, bindingFlags); - private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForFieldsOnTypeHierarchyWithPrefix (_diagnosticContext, type.Type, name, nameIsPrefix, bindingFlags); + private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForFieldsOnTypeHierarchyWithPrefix (_diagnosticContext, type.Type, name, nameIsPrefix, bindingFlags); - private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchyWithPrefix (_diagnosticContext, type.Type, name, nameIsPrefix, bindingFlags); + private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchyWithPrefix (_diagnosticContext, type.Type, name, nameIsPrefix, bindingFlags); - private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var method in type.Type.GetMethodsOnTypeHierarchy(nameIsPrefix ? (m => m.Name.StartsWith(name)) : (m => m.Name == name), bindingFlags)) - yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); - } + private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var method in type.Type.GetMethodsOnTypeHierarchy(nameIsPrefix ? (m => m.Name.StartsWith(name)) : (m => m.Name == name), bindingFlags)) + yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); + } - private partial IEnumerable GetNestedTypesOnTypeWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) - yield return new SystemTypeValue(new TypeProxy(nestedType)); - } + private partial IEnumerable GetNestedTypesOnTypeWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) + yield return new SystemTypeValue(new TypeProxy(nestedType)); + } } } diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index e576e2554db5ac..28b13422dc8fe2 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -414,7 +414,7 @@ GenericParameterValue genericParam foreach (var stringParam in argumentValues[0].AsEnumerable ()) { if (stringParam is KnownStringValue stringValue && !BindingFlagsAreUnsupported (bindingFlags) && !MemberTypesAreUnsupported (memberTypes)) { // determine if we've got a prefix (for example, abc* searches for anything starting with abc) - var isPrefix = stringValue.Contents.EndsWith('*'); + var isPrefix = stringValue.Contents.EndsWith("*"); var name = stringValue.Contents; if (isPrefix) name = name.Substring(0, name.Length - 1); From 21e41b046a2054e839f231a30e20aed7136c31c3 Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Fri, 8 Dec 2023 09:18:17 +1100 Subject: [PATCH 07/17] Update HandleCallAction.cs --- .../Linker.Dataflow/HandleCallAction.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs b/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs index 7565b1a1fc96d5..a2bf2b03890c2d 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs @@ -130,24 +130,24 @@ private partial bool MarkAssociatedProperty (MethodProxy method) private partial string GetContainingSymbolDisplayName () => _callingMethodDefinition.GetDisplayName (); private partial void MarkEventsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionMarker.MarkEventsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (e => e.Name.StartsWith(name)) : (e => e.Name == name), _reason, bindingFlags); + => _reflectionMarker.MarkEventsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (e => e.Name.StartsWith(name)) : (e => e.Name == name), bindingFlags); - private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionMarker.MarkFieldsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (f => f.Name.StartsWith(name)) : (f => f.Name == name), _reason, bindingFlags); + private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionMarker.MarkFieldsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (f => f.Name.StartsWith(name)) : (f => f.Name == name), bindingFlags); - private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionMarker.MarkPropertiesOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (p => p.Name.StartsWith(name)) : (p => p.Name == name), _reason, bindingFlags); + private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + => _reflectionMarker.MarkPropertiesOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (p => p.Name.StartsWith(name)) : (p => p.Name == name), bindingFlags); - private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var method in type.Type.GetMethodsOnTypeHierarchy(nameIsPrefix ? (m => m.Name.StartsWith(name)) : (m => m.Name == name), bindingFlags)) - yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); - } + private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var method in type.Type.GetMethodsOnTypeHierarchy(nameIsPrefix ? (m => m.Name.StartsWith(name)) : (m => m.Name == name), bindingFlags)) + yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); + } - private partial IEnumerable GetNestedTypesOnTypeWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) - yield return new SystemTypeValue(new TypeProxy(nestedType)); - } + private partial IEnumerable GetNestedTypesOnTypeWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) + yield return new SystemTypeValue(new TypeProxy(nestedType)); + } } } From 3093fe5171be345ada0bb6ecb6fe96477123e5f9 Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Fri, 8 Dec 2023 09:44:21 +1100 Subject: [PATCH 08/17] Update HandleCallAction.cs --- .../src/linker/Linker.Dataflow/HandleCallAction.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs b/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs index a2bf2b03890c2d..ffe3c5ba7d0040 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs @@ -138,11 +138,11 @@ private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) => _reflectionMarker.MarkPropertiesOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (p => p.Name.StartsWith(name)) : (p => p.Name == name), bindingFlags); - private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var method in type.Type.GetMethodsOnTypeHierarchy(nameIsPrefix ? (m => m.Name.StartsWith(name)) : (m => m.Name == name), bindingFlags)) - yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); - } + private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) + { + foreach (var method in type.Type.GetMethodsOnTypeHierarchy(_context, nameIsPrefix ? (m => m.Name.StartsWith(name)) : (m => m.Name == name), bindingFlags)) + yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); + } private partial IEnumerable GetNestedTypesOnTypeWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) { From 1053805d704a69994a2be87436398c5a29174b77 Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Sun, 10 Dec 2023 07:14:09 +1100 Subject: [PATCH 09/17] Add some unit tests & fix some code - Update the `[Kept]` attributes on the unit tests to reflect what we should now expect to be kept - Add some more unit tests - Fix missing `| BindingFlags.Static` for the default binding flags --- .../TrimAnalysis/HandleCallAction.cs | 4 +- .../Reflection/MemberUsedViaReflection.cs | 168 ++++++++++++++++-- 2 files changed, 153 insertions(+), 19 deletions(-) diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index 28b13422dc8fe2..935ee84987cd99 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -377,8 +377,8 @@ GenericParameterValue genericParam if (calledMethod.HasMetadataParametersCount (1)) { // Assume a default value for MemberTypes for methods that don't use MemberTypes as a parameter memberTypes = MemberTypes.All; - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Public | BindingFlags.Instance; + // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter (Type.DefaultLookup) + bindingFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; } else if (calledMethod.HasMetadataParametersCount (2) && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.Reflection.BindingFlags")) { // Assume a default value for MemberTypes for methods that don't use MemberTypes as a parameter memberTypes = MemberTypes.All; diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs index 9ddc8fa45debe4..841ebf6dc37ef3 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs @@ -12,14 +12,15 @@ public class MemberUsedViaReflection { public static void Main () { - // Normally calls to GetMember use prefix lookup to match multiple values, we took a conservative approach - // and preserve not based on the string passed but on the binding flags requirements TestWithName (); TestWithNullName (); TestWithEmptyName (); TestWithNoValueName (); TestWithPrefixLookup (); TestWithBindingFlags (); + TestConstructorPrefix (); + TestConstructorName (); + TestMemberTypeNamed (); TestWithUnknownBindingFlags (BindingFlags.Public); TestWithMemberTypes (); TestNullType (); @@ -32,7 +33,7 @@ public static void Main () [Kept] static void TestWithName () { - var members = typeof (SimpleType).GetMember ("memberKept"); + var members = typeof (SpecificName).GetMember ("memberKept"); } [Kept] @@ -67,18 +68,35 @@ static void TestWithBindingFlags () var members = typeof (BindingFlagsType).GetMember ("PrefixLookup*", BindingFlags.Public | BindingFlags.NonPublic); } + [Kept] + static void TestConstructorPrefix () + { + var members = typeof (ConstructorPrefixClass).GetMember (".ct*"); + } + + [Kept] + static void TestConstructorName () + { + var members = typeof (ConstructorNameClass).GetMember (".ctor"); + } + + [Kept] + static void TestMemberTypeNamed () + { + var members1 = typeof (MemberTypeNamedClass1).GetMember ("FieldName", MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance); + var members2 = typeof (MemberTypeNamedClass2).GetMember ("FieldName", MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance); + } + [Kept] static void TestWithUnknownBindingFlags (BindingFlags bindingFlags) { - // Since the binding flags are not known trimming tools should mark all members on the type + // The binding flags are not known trimming tools should mark all members on the type with that prefix var members = typeof (UnknownBindingFlags).GetMember ("PrefixLookup*", bindingFlags); } [Kept] static void TestWithMemberTypes () { - // Here we took the same conservative approach, instead of understanding MemberTypes we only use - // the information in the binding flags requirements and keep all the MemberTypes var members = typeof (TestMemberTypes).GetMember ("PrefixLookup*", MemberTypes.Method, BindingFlags.Public); } @@ -135,6 +153,17 @@ static void TestIfElse (bool decision) var members = myType.GetMember ("PrefixLookup*", BindingFlags.Public); } + private class SpecificName + { + [Kept] + public static int field; + + public SimpleType() + { } + + public int otherMember() { } + } + [Kept] private class SimpleType { @@ -172,6 +201,8 @@ private PrefixLookupType (int i) private static int PrefixLookup_privatefield; + public static int IncorrectPrefix_field; + [Kept] public int PrefixLookupProperty { [Kept] @@ -185,11 +216,18 @@ private int PrefixLookupPrivateProperty { set { PrefixLookup_privatefield = value; } } + public int IncorrectPrefixProperty { + get { return IncorrectPrefix_field; } + set { IncorrectPrefix_field = value; } + } + [Kept] public void PrefixLookupMethod () { } private void PrefixLookupPrivateMethod () { } + public void IncorrectPrefixMethod() { } + [Kept] [KeptBackingField] [KeptEventAddMethod] @@ -198,10 +236,14 @@ private void PrefixLookupPrivateMethod () { } private event EventHandler PrefixLookupPrivateEvent; + public event EventHandler IncorrectPrefixEvent; + [Kept] public static class PrefixLookupNestedType { } private static class PrefixLookupPrivateNestedType { } + + public static class IncorrectPrefixNestedType { } } [Kept] @@ -211,7 +253,6 @@ private class BindingFlagsType public BindingFlagsType () { } - [Kept] private BindingFlagsType (int i) { } @@ -221,6 +262,8 @@ private BindingFlagsType (int i) [Kept] private static int PrefixLookup_privatefield; + public static int IncorrectPrefix_field; + [Kept] public int PrefixLookupProperty { [Kept] @@ -237,12 +280,19 @@ private int PrefixLookupPrivateProperty { set { PrefixLookup_privatefield = value; } } + public int IncorrectPrefixProperty { + get { return IncorrectPrefix_field; } + set { IncorrectPrefix_field = value; } + } + [Kept] public void PrefixLookupMethod () { } [Kept] private void PrefixLookupPrivateMethod () { } + public void IncorrectPrefixMethod() { } + [Kept] [KeptBackingField] [KeptEventAddMethod] @@ -255,11 +305,66 @@ private void PrefixLookupPrivateMethod () { } [KeptEventRemoveMethod] private event EventHandler PrefixLookupPrivateEvent; + public event EventHandler IncorrectPrefixEvent; + [Kept] public static class PrefixLookupNestedType { } [Kept] private static class PrefixLookupPrivateNestedType { } + + public static class IncorrectPrefixNestedType { } + } + + [Kept] + private class ConstructorPrefixClass + { + [Kept] + public ConstructorPrefixClass() + { } + + [Kept] + public ConstructorPrefixClass(int a) + { } + + private ConstructorPrefixClass(string a) + { } + } + + [Kept] + private class ConstructorNameClass + { + [Kept] + public ConstructorNameClass() + { } + + [Kept] + public ConstructorNameClass(int a) + { } + + private ConstructorNameClass(string a) + { } + } + + [Kept] + private class MemberTypeNamedClass1 + { + [Kept] + public MemberTypeNamedClass1() + { } + + [Kept] + public int FieldName; + + public int IncorrectFieldName; + } + + private class MemberTypeNamedClass2 + { + public MemberTypeNamedClass2() + { } + + public void FieldName() { } } [Kept] @@ -269,7 +374,6 @@ private class UnknownBindingFlags public UnknownBindingFlags () { } - [Kept] private UnknownBindingFlags (int i) { } @@ -279,6 +383,8 @@ private UnknownBindingFlags (int i) [Kept] private static int PrefixLookup_privatefield; + public static int IncorrectPrefix_field; + [Kept] public int PrefixLookupProperty { [Kept] @@ -295,12 +401,19 @@ private int PrefixLookupPrivateProperty { set { PrefixLookup_privatefield = value; } } + public int IncorrectPrefixProperty { + get { return IncorrectPrefix_field; } + set { IncorrectPrefix_field = value; } + } + [Kept] public void PrefixLookupMethod () { } [Kept] private void PrefixLookupPrivateMethod () { } + public void IncorrectPrefixMethod() { } + [Kept] [KeptBackingField] [KeptEventAddMethod] @@ -313,11 +426,15 @@ private void PrefixLookupPrivateMethod () { } [KeptEventRemoveMethod] private event EventHandler PrefixLookupPrivateEvent; + public event EventHandler IncorrectPrefixEvent; + [Kept] public static class PrefixLookupNestedType { } [Kept] private static class PrefixLookupPrivateNestedType { } + + public static class IncorrectPrefixNestedType { } } [Kept] @@ -330,16 +447,12 @@ public TestMemberTypes () private TestMemberTypes (int i) { } - [Kept] public static int PrefixLookup_field; private static int PrefixLookup_privatefield; - [Kept] public int PrefixLookupProperty { - [Kept] get { return PrefixLookup_field; } - [Kept] set { PrefixLookup_field = value; } } @@ -353,15 +466,10 @@ public void PrefixLookupMethod () { } private void PrefixLookupPrivateMethod () { } - [Kept] - [KeptBackingField] - [KeptEventAddMethod] - [KeptEventRemoveMethod] public event EventHandler PrefixLookupEvent; private event EventHandler PrefixLookupPrivateEvent; - [Kept] public static class PrefixLookupNestedType { } private static class PrefixLookupPrivateNestedType { } @@ -429,6 +537,8 @@ private IfMember (int i) private static int PrefixLookup_privatefield; + public static int IncorrectPrefix_field; + [Kept] public int PrefixLookupProperty { [Kept] @@ -442,11 +552,18 @@ private int PrefixLookupPrivateProperty { set { PrefixLookup_privatefield = value; } } + public int IncorrectPrefixProperty { + get { return IncorrectPrefix_field; } + set { IncorrectPrefix_field = value; } + } + [Kept] public void PrefixLookupMethod () { } private void PrefixLookupPrivateMethod () { } + public void IncorrectPrefixMethod () { } + [Kept] [KeptBackingField] [KeptEventAddMethod] @@ -455,10 +572,14 @@ private void PrefixLookupPrivateMethod () { } private event EventHandler PrefixLookupPrivateEvent; + public event EventHandler IncorrectPrefixEvent; + [Kept] public static class PrefixLookupNestedType { } private static class PrefixLookupPrivateNestedType { } + + public static class IncorrectPrefixNestedType { } } [Kept] @@ -476,6 +597,8 @@ private ElseMember (int i) private static int PrefixLookup_privatefield; + public static int IncorrectPrefix_field; + [Kept] public int PrefixLookupProperty { [Kept] @@ -489,11 +612,18 @@ private int PrefixLookupPrivateProperty { set { PrefixLookup_privatefield = value; } } + public int IncorrectPrefixProperty { + get { return IncorrectPrefix_field; } + set { IncorrectPrefix_field = value; } + } + [Kept] public void PrefixLookupMethod () { } private void PrefixLookupPrivateMethod () { } + public void IncorrectPrefixMethod () { } + [Kept] [KeptBackingField] [KeptEventAddMethod] @@ -502,10 +632,14 @@ private void PrefixLookupPrivateMethod () { } private event EventHandler PrefixLookupPrivateEvent; + public event EventHandler IncorrectPrefixEvent; + [Kept] public static class PrefixLookupNestedType { } private static class PrefixLookupPrivateNestedType { } + + public static class IncorrectPrefixNestedType { } } } } \ No newline at end of file From 02b6c45f3109032ffd707ae23a1a5fe3306fcb0d Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Sun, 10 Dec 2023 07:25:27 +1100 Subject: [PATCH 10/17] Fix typo & move ctors to top --- .../Reflection/MemberUsedViaReflection.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs index 841ebf6dc37ef3..b5718661b759fd 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs @@ -156,17 +156,22 @@ static void TestIfElse (bool decision) private class SpecificName { [Kept] - public static int field; - - public SimpleType() + public SpecificName() { } + [Kept] + public static int field; + public int otherMember() { } } [Kept] private class SimpleType { + [Kept] + public SimpleType () + { } + [Kept] public static int field; @@ -178,10 +183,6 @@ public int memberKept { set { field = value; } } - [Kept] - public SimpleType () - { } - [Kept] public void someMethod () { } } From 40044cd05b8ec7312d3bbefe6a4c1de935fbad8d Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Sun, 10 Dec 2023 07:35:25 +1100 Subject: [PATCH 11/17] Update MemberUsedViaReflection.cs --- .../Reflection/MemberUsedViaReflection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs index b5718661b759fd..29d66dc6f044ec 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs @@ -162,7 +162,7 @@ public SpecificName() [Kept] public static int field; - public int otherMember() { } + public void otherMember() { } } [Kept] From 0646bb1d31232dbe32bb85e76ab70318a9024bd6 Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Sun, 10 Dec 2023 09:33:55 +1100 Subject: [PATCH 12/17] Update MemberUsedViaReflection.cs --- .../Reflection/MemberUsedViaReflection.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs index 29d66dc6f044ec..60783cd152b5af 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs @@ -162,6 +162,14 @@ public SpecificName() [Kept] public static int field; + [Kept] + public int memberKept { + [Kept] + get { return field; } + [Kept] + set { field = value; } + } + public void otherMember() { } } From b665d992f082c59716bd53269e5ececeb1a04315 Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Sun, 10 Dec 2023 18:59:13 +1100 Subject: [PATCH 13/17] Update some `[Kept]` attributes - And add another .IsEmpty check like similar APIs have --- .../src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs | 2 +- .../Reflection/MemberUsedViaReflection.cs | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index 935ee84987cd99..f7d111cea34eb2 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -367,7 +367,7 @@ GenericParameterValue genericParam // GetMember (String, MemberTypes, BindingFlags) // case IntrinsicId.Type_GetMember: { - if (instanceValue.IsEmpty ()) { + if (instanceValue.IsEmpty () || argumentValues[0].IsEmpty ()) { returnValue = MultiValueLattice.Top; break; } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs index 60783cd152b5af..26b52bd3bd6602 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/MemberUsedViaReflection.cs @@ -155,7 +155,6 @@ static void TestIfElse (bool decision) private class SpecificName { - [Kept] public SpecificName() { } @@ -198,7 +197,6 @@ public void someMethod () { } [Kept] private class PrefixLookupType { - [Kept] public PrefixLookupType () { } @@ -258,7 +256,6 @@ public static class IncorrectPrefixNestedType { } [Kept] private class BindingFlagsType { - [Kept] public BindingFlagsType () { } @@ -358,7 +355,6 @@ private ConstructorNameClass(string a) [Kept] private class MemberTypeNamedClass1 { - [Kept] public MemberTypeNamedClass1() { } @@ -379,7 +375,6 @@ public void FieldName() { } [Kept] private class UnknownBindingFlags { - [Kept] public UnknownBindingFlags () { } @@ -449,7 +444,6 @@ public static class IncorrectPrefixNestedType { } [Kept] private class TestMemberTypes { - [Kept] public TestMemberTypes () { } @@ -487,7 +481,6 @@ private static class PrefixLookupPrivateNestedType { } [Kept] private class MyType { - [Kept] public MyType () { } @@ -534,7 +527,6 @@ private static class PrefixLookupPrivateNestedType { } [Kept] private class IfMember { - [Kept] public IfMember () { } @@ -594,7 +586,6 @@ public static class IncorrectPrefixNestedType { } [Kept] private class ElseMember { - [Kept] public ElseMember () { } From dd6379bfe26a75c711856509c70edf18cddf2fce Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Thu, 18 Jan 2024 08:46:22 +1100 Subject: [PATCH 14/17] Apply some feedback (incomplete) - Remove prefix versions of APIs - Remove ignoring of .Custom - Add missing space --- .../Compiler/Dataflow/HandleCallAction.cs | 21 ----------- .../TrimAnalysis/HandleCallAction.cs | 21 ----------- .../TrimAnalysis/ReflectionAccessAnalyzer.cs | 18 ---------- .../TrimAnalysis/HandleCallAction.cs | 35 ++----------------- .../Linker.Dataflow/HandleCallAction.cs | 21 ----------- 5 files changed, 2 insertions(+), 114 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs index 2410cda3f0da97..f09a76221ef37b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs @@ -124,26 +124,5 @@ private partial bool MarkAssociatedProperty(MethodProxy method) } private partial string GetContainingSymbolDisplayName() => _callingMethod.GetDisplayName(); - - private partial void MarkEventsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionMarker.MarkEventsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (e => e.Name.StartsWith(name)) : (e => e.Name == name), _reason, bindingFlags); - - private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionMarker.MarkFieldsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (f => f.Name.StartsWith(name)) : (f => f.Name == name), _reason, bindingFlags); - - private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionMarker.MarkPropertiesOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (p => p.Name.StartsWith(name)) : (p => p.Name == name), _reason, bindingFlags); - - private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var method in type.Type.GetMethodsOnTypeHierarchy(nameIsPrefix ? (m => m.Name.StartsWith(name)) : (m => m.Name == name), bindingFlags)) - yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); - } - - private partial IEnumerable GetNestedTypesOnTypeWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) - yield return new SystemTypeValue(new TypeProxy(nestedType)); - } } } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs index 740d85e96d0271..96c4a07a92a79a 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs @@ -114,26 +114,5 @@ private partial bool MarkAssociatedProperty (MethodProxy method) } private partial string GetContainingSymbolDisplayName () => _operation.FindContainingSymbol (_owningSymbol).GetDisplayName (); - - private partial void MarkEventsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForEventsOnTypeHierarchyWithPrefix (_diagnosticContext, type.Type, name, nameIsPrefix, bindingFlags); - - private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForFieldsOnTypeHierarchyWithPrefix (_diagnosticContext, type.Type, name, nameIsPrefix, bindingFlags); - - private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionAccessAnalyzer.GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchyWithPrefix (_diagnosticContext, type.Type, name, nameIsPrefix, bindingFlags); - - private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var method in type.Type.GetMethodsOnTypeHierarchy(nameIsPrefix ? (m => m.Name.StartsWith(name)) : (m => m.Name == name), bindingFlags)) - yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); - } - - private partial IEnumerable GetNestedTypesOnTypeWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) - yield return new SystemTypeValue(new TypeProxy(nestedType)); - } } } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs index b4bc5623842f9c..1431399b5623e1 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/ReflectionAccessAnalyzer.cs @@ -60,24 +60,6 @@ internal void GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchy (in Dia GetReflectionAccessDiagnosticsForProperty (diagnosticContext, prop); } - internal void GetReflectionAccessDiagnosticsForEventsOnTypeHierarchyWithPrefix (in DiagnosticContext diagnosticContext, ITypeSymbol typeSymbol, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var @event in typeSymbol.GetEventsOnTypeHierarchy (nameIsPrefix ? (e => e.Name.StartsWith(name)) : (e => e.Name == name), bindingFlags)) - GetDiagnosticsForEvent (diagnosticContext, @event); - } - - internal void GetReflectionAccessDiagnosticsForFieldsOnTypeHierarchyWithPrefix (in DiagnosticContext diagnosticContext, ITypeSymbol typeSymbol, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var field in typeSymbol.GetFieldsOnTypeHierarchy (nameIsPrefix ? (f => f.Name.StartsWith(name)) : (f => f.Name == name), bindingFlags)) - GetDiagnosticsForField (diagnosticContext, field); - } - - internal void GetReflectionAccessDiagnosticsForPropertiesOnTypeHierarchyWithPrefix (in DiagnosticContext diagnosticContext, ITypeSymbol typeSymbol, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var prop in typeSymbol.GetPropertiesOnTypeHierarchy (nameIsPrefix ? (p => p.Name.StartsWith(name)) : (p => p.Name == name), bindingFlags)) - GetReflectionAccessDiagnosticsForProperty (diagnosticContext, prop); - } - internal void GetReflectionAccessDiagnosticsForConstructorsOnType (in DiagnosticContext diagnosticContext, ITypeSymbol typeSymbol, BindingFlags? bindingFlags, int? parameterCount) { foreach (var c in typeSymbol.GetConstructorsOnType (filter: parameterCount.HasValue ? c => c.Parameters.Length == parameterCount.Value : null, bindingFlags: bindingFlags)) diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index f7d111cea34eb2..3321d09dfc333f 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -400,7 +400,7 @@ GenericParameterValue genericParam } else { requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers (bindingFlags); } - if (!MemberTypesAreUnsupported(memberTypes)) { + if (!MemberTypesAreUnsupported (memberTypes)) { DynamicallyAccessedMemberTypes requiredMemberTypesMaskFromMemberTypes; requiredMemberTypesMaskFromMemberTypes = GetDynamicallyAccessedMemberTypesFromMemberTypesForMembers (memberTypes); requiredMemberTypes &= requiredMemberTypesMaskFromMemberTypes; @@ -1278,23 +1278,6 @@ private IEnumerable ProcessGetMethodByName (TypeProxy type, string m yield return NullValue.Instance; } - private IEnumerable ProcessGetMethodByNameWithPrefix (TypeProxy type, string methodName, bool nameIsPrefix, BindingFlags? bindingFlags) - { - bool foundAny = false; - foreach (var method in GetMethodsOnTypeHierarchyWithPrefix (type, methodName, nameIsPrefix, bindingFlags)) { - MarkMethod (method.RepresentedMethod); - yield return method; - foundAny = true; - } - - // If there were no methods found the API will return null at runtime, so we should - // track the null as a return value as well. - // This also prevents warnings in such case, since if we don't set the return value it will be - // "unknown" and consumers may warn. - if (!foundAny) - yield return NullValue.Instance; - } - private bool AnalyzeGenericInstantiationTypeArray (in MultiValue arrayParam, in MethodProxy calledMethod, ImmutableArray genericParameters) { bool hasRequirements = false; @@ -1476,12 +1459,8 @@ internal static bool MemberTypesAreUnsupported (MemberTypes? memberTypes) MemberTypes.TypeInfo | MemberTypes.NestedType; - // Member types that don't affect binding outside InvokeMember (that we don't analyze). - const MemberTypes IgnorableMemberTypes = - MemberTypes.Custom; - MemberTypes flags = memberTypes.Value; - return (flags & ~(UnderstoodMemberTypes | IgnorableMemberTypes)) != 0; + return (flags & ~UnderstoodMemberTypes) != 0; } internal static bool HasBindingFlag (BindingFlags? bindingFlags, BindingFlags? search) => bindingFlags != null && (bindingFlags & search) == search; @@ -1592,15 +1571,5 @@ internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypes // Only used for internal diagnostic purposes (not even for warning messages) private partial string GetContainingSymbolDisplayName (); - - private partial void MarkEventsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags); - - private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags); - - private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags); - - private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix (TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags); - - private partial IEnumerable GetNestedTypesOnTypeWithPrefix (TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags); } } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs b/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs index ffe3c5ba7d0040..108b79356a0228 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs @@ -128,26 +128,5 @@ private partial bool MarkAssociatedProperty (MethodProxy method) } private partial string GetContainingSymbolDisplayName () => _callingMethodDefinition.GetDisplayName (); - - private partial void MarkEventsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionMarker.MarkEventsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (e => e.Name.StartsWith(name)) : (e => e.Name == name), bindingFlags); - - private partial void MarkFieldsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionMarker.MarkFieldsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (f => f.Name.StartsWith(name)) : (f => f.Name == name), bindingFlags); - - private partial void MarkPropertiesOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - => _reflectionMarker.MarkPropertiesOnTypeHierarchy(_diagnosticContext.Origin, type.Type, nameIsPrefix ? (p => p.Name.StartsWith(name)) : (p => p.Name == name), bindingFlags); - - private partial IEnumerable GetMethodsOnTypeHierarchyWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var method in type.Type.GetMethodsOnTypeHierarchy(_context, nameIsPrefix ? (m => m.Name.StartsWith(name)) : (m => m.Name == name), bindingFlags)) - yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); - } - - private partial IEnumerable GetNestedTypesOnTypeWithPrefix(TypeProxy type, string name, bool nameIsPrefix, BindingFlags? bindingFlags) - { - foreach (var nestedType in type.Type.GetNestedTypesOnType(nameIsPrefix ? (t => t.Name.StartsWith(name)) : (t => t.Name == name), bindingFlags)) - yield return new SystemTypeValue(new TypeProxy(nestedType)); - } } } From ec45c87c0cee69e6116b692c1743d64658a61a18 Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Thu, 18 Jan 2024 08:55:24 +1100 Subject: [PATCH 15/17] Update HandleCallAction.cs - Remove remaining prefix code - Don't call AddReturnValue --- .../TrimAnalysis/HandleCallAction.cs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index 3321d09dfc333f..7585a8a7c30b5b 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -415,12 +415,17 @@ GenericParameterValue genericParam if (stringParam is KnownStringValue stringValue && !BindingFlagsAreUnsupported (bindingFlags) && !MemberTypesAreUnsupported (memberTypes)) { // determine if we've got a prefix (for example, abc* searches for anything starting with abc) var isPrefix = stringValue.Contents.EndsWith("*"); + if (isPrefix) + { + // Mark based on bitfield requirements + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + continue; + } var name = stringValue.Contents; - if (isPrefix) name = name.Substring(0, name.Length - 1); // we may not need to search for constructors depending on the value of 'name', since they're always named '.ctor' (II.10.5.1) DynamicallyAccessedMemberTypes requiredMemberTypes2 = requiredMemberTypes; - if (isPrefix ? !".ctor".StartsWith(name) : name != ".ctor") { + if (name != ".ctor") { requiredMemberTypes2 &= ~(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors); } @@ -431,22 +436,20 @@ GenericParameterValue genericParam MarkConstructorsOnType (systemTypeValue.RepresentedType, bindingFlags, parameterCount: null); } if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)) != 0) { - MarkEventsOnTypeHierarchyWithPrefix (systemTypeValue.RepresentedType, name, isPrefix, bindingFlags); + MarkEventsOnTypeHierarchy (systemTypeValue.RepresentedType, name, bindingFlags); } if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)) != 0) { - MarkFieldsOnTypeHierarchyWithPrefix (systemTypeValue.RepresentedType, name, isPrefix, bindingFlags); + MarkFieldsOnTypeHierarchy (systemTypeValue.RepresentedType, name, bindingFlags); } if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)) != 0) { - foreach (var methodValue in ProcessGetMethodByNameWithPrefix (systemTypeValue.RepresentedType, name, isPrefix, bindingFlags)) - AddReturnValue (methodValue); + foreach (var methodValue in ProcessGetMethodByName (systemTypeValue.RepresentedType, name, bindingFlags)) /*mark method*/; } if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)) != 0) { - MarkPropertiesOnTypeHierarchyWithPrefix (systemTypeValue.RepresentedType, name, isPrefix, bindingFlags); + MarkPropertiesOnTypeHierarchy (systemTypeValue.RepresentedType, name, bindingFlags); } if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)) != 0) { - foreach (var nestedTypeValue in GetNestedTypesOnTypeWithPrefix (systemTypeValue.RepresentedType, name, isPrefix, bindingFlags)) { + foreach (var nestedTypeValue in GetNestedTypesOnType (systemTypeValue.RepresentedType, name, bindingFlags)) { MarkType (nestedTypeValue.RepresentedType); - AddReturnValue (nestedTypeValue); } } } else if (stringParam is NullValue) { From 4d8ccf38d3ca4b7a707a65255ee5502d6ec8a81c Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Tue, 30 Jan 2024 21:54:01 +1100 Subject: [PATCH 16/17] Update HandleCallAction.cs - Implement feedback for .ctor - Remove calls to `AddReturnValue (MultiValueLattice.Top);`, since this returns an array --- .../TrimAnalysis/HandleCallAction.cs | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index 7585a8a7c30b5b..464dcf27796e0d 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -423,16 +423,8 @@ GenericParameterValue genericParam } var name = stringValue.Contents; - // we may not need to search for constructors depending on the value of 'name', since they're always named '.ctor' (II.10.5.1) - DynamicallyAccessedMemberTypes requiredMemberTypes2 = requiredMemberTypes; - if (name != ".ctor") { - requiredMemberTypes2 &= ~(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors); - } - - AddReturnValue (MultiValueLattice.Top); // Initialize return value (so that it's not autofilled if there are no matching members) - // search for all the things we want by name - if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)) != 0) { + if (name == ".ctor" && (requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)) != 0) { MarkConstructorsOnType (systemTypeValue.RepresentedType, bindingFlags, parameterCount: null); } if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)) != 0) { @@ -452,17 +444,12 @@ GenericParameterValue genericParam MarkType (nestedTypeValue.RepresentedType); } } - } else if (stringParam is NullValue) { - // type.GetMember(null, ...) throws - so track empty value set as its result - AddReturnValue (MultiValueLattice.Top); - } else { + } else if (stringParam is not NullValue) { + // Mark based on bitfield requirements _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); } } - } else if (value is NullValue) { - // null.GetMember(...) throws - so track empty value set as its result - AddReturnValue (MultiValueLattice.Top); - } else { + } else if (value is not NullValue) { // Mark based on bitfield requirements _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); } From f8cfd863f74f75eb35385340c3ea43381e5103ca Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Wed, 31 Jan 2024 07:45:17 +1100 Subject: [PATCH 17/17] Update HandleCallAction.cs --- .../ILLink.Shared/TrimAnalysis/HandleCallAction.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index 464dcf27796e0d..a2bec61ee624b2 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -424,22 +424,22 @@ GenericParameterValue genericParam var name = stringValue.Contents; // search for all the things we want by name - if (name == ".ctor" && (requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)) != 0) { + if (name == ".ctor" && (requiredMemberTypes & (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)) != 0) { MarkConstructorsOnType (systemTypeValue.RepresentedType, bindingFlags, parameterCount: null); } - if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)) != 0) { + if ((requiredMemberTypes & (DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)) != 0) { MarkEventsOnTypeHierarchy (systemTypeValue.RepresentedType, name, bindingFlags); } - if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)) != 0) { + if ((requiredMemberTypes & (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)) != 0) { MarkFieldsOnTypeHierarchy (systemTypeValue.RepresentedType, name, bindingFlags); } - if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)) != 0) { + if ((requiredMemberTypes & (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)) != 0) { foreach (var methodValue in ProcessGetMethodByName (systemTypeValue.RepresentedType, name, bindingFlags)) /*mark method*/; } - if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)) != 0) { + if ((requiredMemberTypes & (DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)) != 0) { MarkPropertiesOnTypeHierarchy (systemTypeValue.RepresentedType, name, bindingFlags); } - if ((requiredMemberTypes2 & (DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)) != 0) { + if ((requiredMemberTypes & (DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)) != 0) { foreach (var nestedTypeValue in GetNestedTypesOnType (systemTypeValue.RepresentedType, name, bindingFlags)) { MarkType (nestedTypeValue.RepresentedType); }