Skip to content

Commit

Permalink
Annotate required APIs with DynamicallyAccessedMemberTypes.Interfaces (
Browse files Browse the repository at this point in the history
…dotnet#52461)

After the basic annotation of `Type.GetInterface` and `Type.GetInterfaces` this propagates this annotation to all places which require it. It also resolves warnings in all the places where this shows up.

For the most part this is just blindly propagating the annotations.

Most of the suppressions rely on the fact that is `IFoo` is referenced (and thus the `Type` instance of it exists at runtime), trimmer guarantees that it will keep it on all types which implement that interface. And also on additional fact that if `IFoo<>` is preserved, then all instantiations of it are also preserved. This is true for linker, but not necessarily true for AOT - that is we can do better if we don't have to make that assumption. For now I rely on that assumption.
  • Loading branch information
vitek-karas authored May 12, 2021
1 parent 4ca5d81 commit e43bd13
Show file tree
Hide file tree
Showing 37 changed files with 326 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -411,15 +411,17 @@ private static AssemblyLoadContext GetALC(string assemblyPath)
private sealed class BasicClassFactory : IClassFactory
{
private readonly Guid _classId;

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
private readonly Type _classType;

public BasicClassFactory(Guid clsid, Type classType)
public BasicClassFactory(Guid clsid, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type classType)
{
_classId = clsid;
_classType = classType;
}

public static Type GetValidatedInterfaceType(Type classType, ref Guid riid, object? outer)
public static Type GetValidatedInterfaceType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type classType, ref Guid riid, object? outer)
{
Debug.Assert(classType != null);
if (riid == Marshal.IID_IUnknown)
Expand Down Expand Up @@ -519,9 +521,11 @@ private sealed class LicenseClassFactory : IClassFactory2
{
private readonly LicenseInteropProxy _licenseProxy = new LicenseInteropProxy();
private readonly Guid _classId;

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
private readonly Type _classType;

public LicenseClassFactory(Guid clsid, Type classType)
public LicenseClassFactory(Guid clsid, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type classType)
{
_classId = clsid;
_classType = classType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,14 @@ public override FieldInfo[] GetFields(BindingFlags bindingAttr)
return m_typeBuilder.GetFields(bindingAttr);
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
public override Type? GetInterface(string name, bool ignoreCase)
{
return m_typeBuilder.GetInterface(name, ignoreCase);
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
public override Type[] GetInterfaces()
{
return m_typeBuilder.GetInterfaces();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,13 @@ public override Type MakeArrayType(int rank)
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
public override FieldInfo[] GetFields(BindingFlags bindingAttr) { throw new NotSupportedException(); }

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063:UnrecognizedReflectionPattern",
Justification = "Linker doesn't recognize always throwing method. https://github.com/mono/linker/issues/2025")]
public override Type GetInterface(string name, bool ignoreCase) { throw new NotSupportedException(); }

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
public override Type[] GetInterfaces() { throw new NotSupportedException(); }

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,11 +397,16 @@ public override FieldInfo[] GetFields(BindingFlags bindingAttr)
throw new NotSupportedException(SR.NotSupported_NonReflectedType);
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063:UnrecognizedReflectionPattern",
Justification = "Linker doesn't recognize always throwing method. https://github.com/mono/linker/issues/2025")]
public override Type GetInterface(string name, bool ignoreCase)
{
throw new NotSupportedException(SR.NotSupported_NonReflectedType);
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
public override Type[] GetInterfaces()
{
throw new NotSupportedException(SR.NotSupported_NonReflectedType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,8 @@ public override FieldInfo[] GetFields(BindingFlags bindingAttr)
return m_bakedRuntimeType.GetFields(bindingAttr);
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
public override Type? GetInterface(string name, bool ignoreCase)
{
if (!IsCreated())
Expand All @@ -845,6 +847,7 @@ public override FieldInfo[] GetFields(BindingFlags bindingAttr)
return m_bakedRuntimeType.GetInterface(name, ignoreCase);
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
public override Type[] GetInterfaces()
{
if (m_bakedRuntimeType != null)
Expand Down Expand Up @@ -947,6 +950,9 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
return m_bakedRuntimeType.GetMembers(bindingAttr);
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The GetInterfaces technically requires all interfaces to be preserved" +
"But in this case it acts only on TypeBuilder which is never trimmed (as it's runtime created).")]
public override bool IsAssignableFrom([NotNullWhen(true)] Type? c)
{
if (IsTypeEqual(c, this))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,13 @@ public override Type? BaseType
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
public override FieldInfo[] GetFields(BindingFlags bindingAttr) { throw new NotSupportedException(); }

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063:UnrecognizedReflectionPattern",
Justification = "Linker doesn't recognize always throwing method. https://github.com/mono/linker/issues/2025")]
public override Type GetInterface(string name, bool ignoreCase) { throw new NotSupportedException(); }

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
public override Type[] GetInterfaces() { throw new NotSupportedException(); }

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,10 @@ private RuntimeConstructorInfo[] PopulateConstructors(Filter filter)
return list.ToArray();
}

[UnconditionalSuppressMessage ("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
Justification = "Calls to GetInterfaces technically require all interfaces on ReflectedType" +
"But this is not a public API to enumerate reflection items, all the public APIs which do that" +
"should be annotated accordingly.")]
private RuntimeFieldInfo[] PopulateFields(Filter filter)
{
ListBuilder<RuntimeFieldInfo> list = default;
Expand Down Expand Up @@ -983,7 +987,11 @@ private void PopulateLiteralFields(Filter filter, RuntimeType declaringType, ref
}
}

private void AddSpecialInterface(ref ListBuilder<RuntimeType> list, Filter filter, RuntimeType iList, bool addSubInterface)
private void AddSpecialInterface(
ref ListBuilder<RuntimeType> list,
Filter filter,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] RuntimeType iList,
bool addSubInterface)
{
if (iList.IsAssignableFrom(ReflectedType))
{
Expand All @@ -1003,6 +1011,10 @@ private void AddSpecialInterface(ref ListBuilder<RuntimeType> list, Filter filte
}
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2065:UnrecognizedReflectionPattern",
Justification = "Calls to GetInterfaces technically require all interfaces on ReflectedType" +
"But this is not a public API to enumerate reflection items, all the public APIs which do that" +
"should be annotated accordingly.")]
private RuntimeType[] PopulateInterfaces(Filter filter)
{
ListBuilder<RuntimeType> list = default;
Expand Down Expand Up @@ -2611,6 +2623,7 @@ public override FieldInfo[] GetFields(BindingFlags bindingAttr)
return GetFieldCandidates(null, bindingAttr, false).ToArray();
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
public override Type[] GetInterfaces()
{
RuntimeType[] candidates = Cache.GetInterfaceList(MemberListType.All, null);
Expand Down Expand Up @@ -2903,6 +2916,8 @@ public override InterfaceMapping GetInterfaceMap(Type ifaceType)
return match;
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
public override Type? GetInterface(string fullname, bool ignoreCase)
{
if (fullname is null) throw new ArgumentNullException(nameof(fullname));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,11 @@
<property name="Scope">member</property>
<property name="Target">M:Microsoft.Extensions.Configuration.ConfigurationBinder.GetAllProperties(System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2070</argument>
<property name="Scope">member</property>
<property name="Target">M:Microsoft.Extensions.Configuration.ConfigurationBinder.FindOpenGenericInterface(System.Type,System.Type)</property>
</attribute>
</assembly>
</linker>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ItemGroup>
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\DynamicallyAccessedMembersAttribute.cs" />
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\DynamicallyAccessedMemberTypes.cs" />
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\UnconditionalSuppressMessageAttribute.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ public static IServiceCollection PostConfigureAll<TOptions>(this IServiceCollect

private static IEnumerable<Type> FindConfigurationServices(Type type)
{
foreach (Type t in type.GetInterfaces())
foreach (Type t in GetInterfacesOnType(type))
{
if (t.IsGenericType)
{
Expand All @@ -159,6 +159,16 @@ private static IEnumerable<Type> FindConfigurationServices(Type type)
}
}
}

// Extracted the suppression to a local function as trimmer currently doesn't handle suppressions
// on iterator methods correctly.
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification="This method only looks for interfaces referenced in its code. " +
"The trimmer will keep the interface and thus all of its implementations in that case. " +
"The call to GetInterfaces may return less results in trimmed apps, but it will " +
"include the interfaces this method looks for if they should be there.")]
static Type[] GetInterfacesOnType(Type t)
=> t.GetInterfaces();
}

private static void ThrowNoConfigServices(Type type) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ Namespace Microsoft.VisualBasic.CompilerServices
Debug.Assert(TypeCode.String = 18, "wrong value!")
End Sub

<RequiresUnreferencedCode("Calls ClassifyUserDefinedConversion")>
<RequiresUnreferencedCode("Calls ClassifyUserDefinedConversion and ClassifyPredefinedConversion")>
Friend Shared Function ClassifyConversion(ByVal targetType As System.Type, ByVal sourceType As System.Type, ByRef operatorMethod As Method) As ConversionClass
'This function classifies the nature of the conversion from the source type to the target
'type. If such a conversion requires a user-defined conversion, it will be supplied as an
Expand Down Expand Up @@ -205,6 +205,7 @@ Namespace Microsoft.VisualBasic.CompilerServices
Return s_conversionTable(targetTypeCode)(sourceTypeCode)
End Function

<RequiresUnreferencedCode("Calls GetInterfaceConstraints but does so recursively on various types")>
Friend Shared Function ClassifyPredefinedCLRConversion(ByVal targetType As System.Type, ByVal sourceType As System.Type) As ConversionClass
' This function classifies all intrinsic CLR conversions, such as inheritance,
' implementation, and array covariance.
Expand Down Expand Up @@ -355,6 +356,7 @@ Namespace Microsoft.VisualBasic.CompilerServices

End Function

<RequiresUnreferencedCode("Calls ClassifyPredefinedCLRConversion")>
Private Shared Function ClassifyCLRArrayToInterfaceConversion(ByVal targetInterface As System.Type, ByVal sourceArrayType As System.Type) As ConversionClass

Debug.Assert(IsInterface(targetInterface), "Non-Interface type unexpected!!!")
Expand Down Expand Up @@ -424,6 +426,7 @@ Namespace Microsoft.VisualBasic.CompilerServices
End Function


<RequiresUnreferencedCode("Calls ClassifyPredefinedCLRConversion")>
Private Shared Function ClassifyCLRConversionForArrayElementTypes(ByVal targetElementType As System.Type, ByVal sourceElementType As System.Type) As ConversionClass

' The element types must either be the same or
Expand Down Expand Up @@ -467,6 +470,7 @@ Namespace Microsoft.VisualBasic.CompilerServices
End Function


<RequiresUnreferencedCode("Calls ClassifyPredefinedCLRConversion")>
Friend Shared Function ClassifyPredefinedConversion(ByVal targetType As System.Type, ByVal sourceType As System.Type) As ConversionClass
' This function classifies all intrinsic language conversions, such as inheritance,
' implementation, array covariance, and conversions between intrinsic types.
Expand Down Expand Up @@ -553,6 +557,7 @@ Namespace Microsoft.VisualBasic.CompilerServices
Return result
End Function

<RequiresUnreferencedCode("Calls ClassifyPredefinedConversion")>
Private Shared Function Encompasses(ByVal larger As System.Type, ByVal smaller As System.Type) As Boolean
'Definition: LARGER is said to encompass SMALLER if SMALLER widens to or is LARGER.

Expand All @@ -564,6 +569,7 @@ Namespace Microsoft.VisualBasic.CompilerServices
Return result = ConversionClass.Widening OrElse result = ConversionClass.Identity
End Function

<RequiresUnreferencedCode("Calls ClassifyPredefinedConversion")>
Private Shared Function NotEncompasses(ByVal larger As System.Type, ByVal smaller As System.Type) As Boolean
'Definition: LARGER is said to not encompass SMALLER if SMALLER narrows to or is LARGER.

Expand All @@ -575,7 +581,7 @@ Namespace Microsoft.VisualBasic.CompilerServices
Return result = ConversionClass.Narrowing OrElse result = ConversionClass.Identity
End Function


<RequiresUnreferencedCode("Calls Encompasses")>
Private Shared Function MostEncompassing(ByVal types As List(Of System.Type)) As System.Type
'Given a set TYPES, determine the most encompassing type. An element
'CANDIDATE of TYPES is said to be most encompassing if no other element of
Expand All @@ -601,7 +607,7 @@ Namespace Microsoft.VisualBasic.CompilerServices
Return maxEncompassing
End Function


<RequiresUnreferencedCode("Calls Encompasses")>
Private Shared Function MostEncompassed(ByVal types As List(Of System.Type)) As System.Type
'Given a set TYPES, determine the most encompassed type. An element
'CANDIDATE of TYPES is said to be most encompassed if CANDIDATE encompasses
Expand Down Expand Up @@ -687,6 +693,7 @@ Namespace Microsoft.VisualBasic.CompilerServices
operatorList.Add(operatorToInsert)
End Sub

<RequiresUnreferencedCode("Calls ClassifyPredefinedConversion")>
Private Shared Function ResolveConversion(
ByVal targetType As System.Type,
ByVal sourceType As System.Type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,7 @@ nextcandidate:
Return False
End Function

<RequiresUnreferencedCode("Calls GetInterfaces on argument type recursively")>
Private Shared Function InferTypeArgumentsFromArgument(
ByVal argumentType As Type,
ByVal parameterType As Type,
Expand Down Expand Up @@ -1104,6 +1105,7 @@ RetryInference:
End Function


<RequiresUnreferencedCode("Calls InferTypeArgumentsFromArgument")>
Private Shared Function InferTypeArgumentsFromArgumentDirectly(
ByVal argumentType As Type,
ByVal parameterType As Type,
Expand Down Expand Up @@ -1296,6 +1298,7 @@ RetryInference:

End Function

<RequiresUnreferencedCode("Calls InferTypArgumentsFromArgument")>
Friend Shared Function InferTypeArgumentsFromArgument(
ByVal targetProcedure As Method,
ByVal argument As Object,
Expand Down Expand Up @@ -1839,6 +1842,7 @@ skipargument:
Return
End Sub

<RequiresUnreferencedCode("Calls InferTypeArgumentsFromArgument")>
Private Shared Function InferTypeArguments(
ByVal targetProcedure As Method,
ByVal arguments As Object(),
Expand Down
Loading

0 comments on commit e43bd13

Please sign in to comment.