diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs index 9063ac7368dbe4..a2bec61ee624b2 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/HandleCallAction.cs @@ -367,18 +367,24 @@ GenericParameterValue genericParam // GetMember (String, MemberTypes, BindingFlags) // case IntrinsicId.Type_GetMember: { - if (instanceValue.IsEmpty ()) { + if (instanceValue.IsEmpty () || argumentValues[0].IsEmpty ()) { returnValue = MultiValueLattice.Top; break; } BindingFlags? bindingFlags; + MemberTypes? memberTypes; if (calledMethod.HasMetadataParametersCount (1)) { - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Public | BindingFlags.Instance; - } else if (calledMethod.HasMetadataParametersCount (2) && calledMethod.HasParameterOfType ((ParameterIndex) 2, "System.Reflection.BindingFlags")) + // 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 (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; 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,13 +400,59 @@ GenericParameterValue genericParam } else { requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers (bindingFlags); } + if (!MemberTypesAreUnsupported (memberTypes)) { + DynamicallyAccessedMemberTypes requiredMemberTypesMaskFromMemberTypes; + requiredMemberTypesMaskFromMemberTypes = GetDynamicallyAccessedMemberTypesFromMemberTypesForMembers (memberTypes); + requiredMemberTypes &= requiredMemberTypesMaskFromMemberTypes; + } var targetValue = _annotations.GetMethodThisParameterValue (calledMethod, requiredMemberTypes); // Go over all types we've seen foreach (var value in instanceValue.AsEnumerable ()) { - // Mark based on bitfield requirements - _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + if (value is SystemTypeValue systemTypeValue) { + 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("*"); + if (isPrefix) + { + // Mark based on bitfield requirements + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + continue; + } + var name = stringValue.Contents; + + // search for all the things we want by name + if (name == ".ctor" && (requiredMemberTypes & (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)) != 0) { + MarkConstructorsOnType (systemTypeValue.RepresentedType, bindingFlags, parameterCount: null); + } + if ((requiredMemberTypes & (DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)) != 0) { + MarkEventsOnTypeHierarchy (systemTypeValue.RepresentedType, name, bindingFlags); + } + if ((requiredMemberTypes & (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)) != 0) { + MarkFieldsOnTypeHierarchy (systemTypeValue.RepresentedType, name, bindingFlags); + } + if ((requiredMemberTypes & (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)) != 0) { + foreach (var methodValue in ProcessGetMethodByName (systemTypeValue.RepresentedType, name, bindingFlags)) /*mark method*/; + } + if ((requiredMemberTypes & (DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)) != 0) { + MarkPropertiesOnTypeHierarchy (systemTypeValue.RepresentedType, name, bindingFlags); + } + if ((requiredMemberTypes & (DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)) != 0) { + foreach (var nestedTypeValue in GetNestedTypesOnType (systemTypeValue.RepresentedType, name, bindingFlags)) { + MarkType (nestedTypeValue.RepresentedType); + } + } + } else if (stringParam is not NullValue) { + // Mark based on bitfield requirements + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + } + } + } else if (value is not NullValue) { + // Mark based on bitfield requirements + _requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); + } } } break; @@ -1352,6 +1404,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 +1434,25 @@ 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; + + MemberTypes flags = memberTypes.Value; + return (flags & ~UnderstoodMemberTypes) != 0; + } + internal static bool HasBindingFlag (BindingFlags? bindingFlags, BindingFlags? search) => bindingFlags != null && (bindingFlags & search) == search; internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (BindingFlags? bindingFlags) => @@ -1421,6 +1493,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.Method) ? DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None) | + (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None); + + internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForFields (MemberTypes? memberTypes) => + (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.Property) ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None) | + (MemberTypesAreUnsupported (memberTypes) ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None); + + internal static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromMemberTypesForEvents (MemberTypes? memberTypes) => + (HasMemberType (memberTypes, MemberTypes.Event) ? 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) /// 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..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 @@ -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,9 +153,11 @@ static void TestIfElse (bool decision) var members = myType.GetMember ("PrefixLookup*", BindingFlags.Public); } - [Kept] - private class SimpleType + private class SpecificName { + public SpecificName() + { } + [Kept] public static int field; @@ -149,10 +169,27 @@ public int memberKept { set { field = value; } } + public void otherMember() { } + } + + [Kept] + private class SimpleType + { [Kept] public SimpleType () { } + [Kept] + public static int field; + + [Kept] + public int memberKept { + [Kept] + get { return field; } + [Kept] + set { field = value; } + } + [Kept] public void someMethod () { } } @@ -160,7 +197,6 @@ public void someMethod () { } [Kept] private class PrefixLookupType { - [Kept] public PrefixLookupType () { } @@ -172,6 +208,8 @@ private PrefixLookupType (int i) private static int PrefixLookup_privatefield; + public static int IncorrectPrefix_field; + [Kept] public int PrefixLookupProperty { [Kept] @@ -185,11 +223,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,20 +243,22 @@ 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] private class BindingFlagsType { - [Kept] public BindingFlagsType () { } - [Kept] private BindingFlagsType (int i) { } @@ -221,6 +268,8 @@ private BindingFlagsType (int i) [Kept] private static int PrefixLookup_privatefield; + public static int IncorrectPrefix_field; + [Kept] public int PrefixLookupProperty { [Kept] @@ -237,12 +286,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,21 +311,73 @@ 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 UnknownBindingFlags + private class ConstructorPrefixClass { [Kept] - public UnknownBindingFlags () + 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 + { + public MemberTypeNamedClass1() + { } + + [Kept] + public int FieldName; + + public int IncorrectFieldName; + } + + private class MemberTypeNamedClass2 + { + public MemberTypeNamedClass2() + { } + + public void FieldName() { } + } + + [Kept] + private class UnknownBindingFlags + { + public UnknownBindingFlags () + { } + private UnknownBindingFlags (int i) { } @@ -279,6 +387,8 @@ private UnknownBindingFlags (int i) [Kept] private static int PrefixLookup_privatefield; + public static int IncorrectPrefix_field; + [Kept] public int PrefixLookupProperty { [Kept] @@ -295,12 +405,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,33 +430,32 @@ 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 TestMemberTypes { - [Kept] 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 +469,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 { } @@ -370,7 +481,6 @@ private static class PrefixLookupPrivateNestedType { } [Kept] private class MyType { - [Kept] public MyType () { } @@ -417,7 +527,6 @@ private static class PrefixLookupPrivateNestedType { } [Kept] private class IfMember { - [Kept] public IfMember () { } @@ -429,6 +538,8 @@ private IfMember (int i) private static int PrefixLookup_privatefield; + public static int IncorrectPrefix_field; + [Kept] public int PrefixLookupProperty { [Kept] @@ -442,11 +553,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,16 +573,19 @@ 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] private class ElseMember { - [Kept] public ElseMember () { } @@ -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