-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Implements more advanced ILLink/ILCompiler analysis for Type.GetMember #94879
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ced1292
d0df383
65a5a1c
74e87f2
b15124a
f9c437f
b3c8d5f
8439ea2
21e41b0
3093fe5
1053805
02b6c45
40044cd
0646bb1
b665d99
dd6379b
ec45c87
ac1066a
4d8ccf3
f8cfd86
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
| } | ||
|
|
||
hamarb123 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you also please add test coverage for the various member types (ensure that when MemberTypes are passed, we keep exactly those member types on a reflected type)?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't implemented the changes to the tests yet, will close these comments when I do. I want to get the current tests to not fail first, then I'll change them to be what the new code should expect. |
||
|
|
||
| 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) | | ||
hamarb123 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| (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); | ||
|
|
||
| /// <Summary> | ||
| /// Returns true if the method is a .ctor for System.Type or a type that derives from System.Type (i.e. fields and params of this type can have DynamicallyAccessedMembers annotations) | ||
| /// </Summary> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changes the default behavior (looks correct to me). Could we make sure there are tests for this part of the change? So add a test that checks that static methods are kept when not passing binding flags.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will take a look through this next week some time (away currently).