Skip to content

Add more functionality to Value Nodes #2

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

Merged
merged 5 commits into from
Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/linker/Linker.Dataflow/FlowAnnotations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ public DynamicallyAccessedMemberTypes GetFieldAnnotation (FieldDefinition field)
return DynamicallyAccessedMemberTypes.None;
}

public DynamicallyAccessedMemberTypes GetTypeAnnotation (TypeDefinition type)
{
if (GetAnnotations (type).TryGetAnnotation (type, out var annotation)) {
return annotation;
}
return DynamicallyAccessedMemberTypes.None;
}

public DynamicallyAccessedMemberTypes GetGenericParameterAnnotation (GenericParameter genericParameter)
{
TypeDefinition declaringType = genericParameter.DeclaringType?.Resolve ();
Expand Down Expand Up @@ -541,7 +549,7 @@ public bool TryGetAnnotation (TypeDefinition type, out DynamicallyAccessedMember
annotation = default;

//@TODO - using type parameter to keep up with the usage pattern
if (type ==null || _typeAnnotation == DynamicallyAccessedMemberTypes.None)
if (type == null || _typeAnnotation == DynamicallyAccessedMemberTypes.None)
return false;

annotation = _typeAnnotation;
Expand Down
84 changes: 53 additions & 31 deletions src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,16 @@ public void ProcessAttributeDataflow (IMemberDefinition source, FieldDefinition
RequireDynamicallyAccessedMembers (ref reflectionContext, annotation, valueNode, field);
}

public void ProcessAttributeDataflow (IMemberDefinition source, TypeDefinition type, CustomAttributeArgument value)
{
var annotation = _context.Annotations.FlowAnnotations.GetTypeAnnotation (type);
Debug.Assert (annotation != DynamicallyAccessedMemberTypes.None);
ValueNode valueNode = GetValueNodeForCustomAttributeArgument (value);
var reflectionContext = new ReflectionPatternContext (_context, true, source, type);
reflectionContext.AnalyzingPattern ();
RequireDynamicallyAccessedMembers (ref reflectionContext, annotation, valueNode, type);
}

static ValueNode GetValueNodeForCustomAttributeArgument (CustomAttributeArgument argument)
{
ValueNode valueNode;
Expand Down Expand Up @@ -910,37 +920,16 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
// GetType()
//
case IntrinsicId.Object_GetType: {
// We could do better here if we start tracking the static types of values within the method body.
// Right now, this can only analyze a couple cases for which we have static information for.
TypeDefinition staticType = null;
if (methodParams[0] is MethodParameterValue methodParam) {
if (callingMethodDefinition.HasThis) {
if (methodParam.ParameterIndex == 0) {
staticType = callingMethodDefinition.DeclaringType;
} else {
staticType = callingMethodDefinition.Parameters[methodParam.ParameterIndex - 1].ParameterType.ResolveToMainTypeDefinition ();
}
} else {
staticType = callingMethodDefinition.Parameters[methodParam.ParameterIndex].ParameterType.ResolveToMainTypeDefinition ();
}
} else if (methodParams[0] is LoadFieldValue loadedField) {
staticType = loadedField.Field.FieldType.ResolveToMainTypeDefinition ();
}

if (staticType != null) {
// We can only analyze the Object.GetType call with the precise type if the type is sealed.
// The type could be a descendant of the type in question, making us miss reflection.
bool canUse = staticType.IsSealed;

if (!canUse) {
// We can allow Object.GetType to be modeled as System.Delegate because we keep all methods
// on delegates anyway so reflection on something this approximation would miss is actually safe.
canUse = staticType.IsTypeOf ("System", "Delegate");
}

if (canUse) {
methodReturnValue = new SystemTypeValue (staticType);
}
if (methodParams[0].StaticType is null) {
// We don�t know anything about the type GetType was called on. Track this as a usual �result of a method call without any annotations�
methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (calledMethod.MethodReturnType, DynamicallyAccessedMemberTypes.None));
} else if (methodParams[0].StaticType.IsSealed) {
// We can treat this one the same as if it was a typeof() expression
methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (methodParams[0].StaticType));
} else {
// Now we fall back to the annotations enumeration value of this type
var annotations = GetMemberTypesForDynamicallyAccessedMembersAttribute (methodParams[0].StaticType);
methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (calledMethod.MethodReturnType, annotations));
}
}
break;
Expand Down Expand Up @@ -1756,6 +1745,39 @@ void ProcessGetMethodByName (
methodReturnValue = MergePointValue.MergeValues (methodReturnValue, NullValue.Instance);
}

DynamicallyAccessedMemberTypes GetMemberTypesForDynamicallyAccessedMembersAttribute (TypeDefinition type)
{
DynamicallyAccessedMemberTypes AggregatedAnnotation = DynamicallyAccessedMemberTypes.None;
var baseTypeDefinition = type.BaseType?.Resolve ();
if (baseTypeDefinition != null)
AggregatedAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (baseTypeDefinition);
if (type.HasInterfaces) {
foreach (InterfaceImplementation iface in type.Interfaces) {
var interfaceTypeDefinition = iface.InterfaceType.Resolve ();
if (interfaceTypeDefinition != null)
AggregatedAnnotation |= GetMemberTypesForDynamicallyAccessedMembersAttribute (interfaceTypeDefinition);
}
}
if (!_context.CustomAttributes.HasCustomAttributes (type))
return AggregatedAnnotation;
foreach (var attribute in _context.CustomAttributes.GetCustomAttributes (type)) {
if (!IsDynamicallyAccessedMembersAttribute (attribute))
continue;
if (attribute.ConstructorArguments.Count == 1)
return AggregatedAnnotation |= (DynamicallyAccessedMemberTypes) (int) attribute.ConstructorArguments[0].Value;
else
_context.LogWarning (
$"Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' doesn't have the required number of parameters specified", 2028, type);
}
return AggregatedAnnotation;
}

static bool IsDynamicallyAccessedMembersAttribute (CustomAttribute attribute)
{
var attributeType = attribute.AttributeType;
return attributeType.Name == "DynamicallyAccessedMembersAttribute" && attributeType.Namespace == "System.Diagnostics.CodeAnalysis";
}

void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionContext, DynamicallyAccessedMemberTypes requiredMemberTypes, ValueNode value, IMetadataTokenProvider targetContext)
{
foreach (var uniqueValue in value.UniqueValues ()) {
Expand Down
23 changes: 23 additions & 0 deletions src/linker/Linker.Dataflow/ValueNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ public ValueNode ()
/// </summary>
public ValueNodeKind Kind { get; protected set; }

/// <summary>
/// The IL type of the value, represented as closely as possible, but not always exact. It can be null, for
/// example, when the analysis is imprecise or operating on malformed IL.
/// </summary>
public TypeDefinition StaticType { get; protected set; }

/// <summary>
/// Allows the enumeration of the direct children of this node. The ChildCollection struct returned here
/// supports 'foreach' without allocation.
Expand Down Expand Up @@ -486,6 +492,7 @@ class UnknownValue : LeafValueNode
private UnknownValue ()
{
Kind = ValueNodeKind.Unknown;
StaticType = null;
}

public static UnknownValue Instance { get; } = new UnknownValue ();
Expand Down Expand Up @@ -518,6 +525,7 @@ class NullValue : LeafValueNode
private NullValue ()
{
Kind = ValueNodeKind.Null;
StaticType = null;
}

public override bool Equals (ValueNode other)
Expand Down Expand Up @@ -553,6 +561,7 @@ class SystemTypeValue : LeafValueNode
public SystemTypeValue (TypeDefinition typeRepresented)
{
Kind = ValueNodeKind.SystemType;
StaticType = typeRepresented;
TypeRepresented = typeRepresented;
}

Expand Down Expand Up @@ -587,6 +596,7 @@ class RuntimeTypeHandleValue : LeafValueNode
public RuntimeTypeHandleValue (TypeDefinition typeRepresented)
{
Kind = ValueNodeKind.RuntimeTypeHandle;
StaticType = typeRepresented;
TypeRepresented = typeRepresented;
}

Expand Down Expand Up @@ -622,6 +632,7 @@ class SystemTypeForGenericParameterValue : LeafValueWithDynamicallyAccessedMembe
public SystemTypeForGenericParameterValue (GenericParameter genericParameter, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
{
Kind = ValueNodeKind.SystemTypeForGenericParameter;
StaticType = null;
GenericParameter = genericParameter;
DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes;
SourceContext = genericParameter;
Expand Down Expand Up @@ -659,6 +670,7 @@ class RuntimeTypeHandleForGenericParameterValue : LeafValueNode
public RuntimeTypeHandleForGenericParameterValue (GenericParameter genericParameter)
{
Kind = ValueNodeKind.RuntimeTypeHandleForGenericParameter;
StaticType = null;
GenericParameter = genericParameter;
}

Expand Down Expand Up @@ -693,6 +705,7 @@ class RuntimeMethodHandleValue : LeafValueNode
public RuntimeMethodHandleValue (MethodDefinition methodRepresented)
{
Kind = ValueNodeKind.RuntimeMethodHandle;
StaticType = null;
MethodRepresented = methodRepresented;
}

Expand Down Expand Up @@ -727,6 +740,7 @@ class SystemReflectionMethodBaseValue : LeafValueNode
public SystemReflectionMethodBaseValue (MethodDefinition methodRepresented)
{
Kind = ValueNodeKind.SystemReflectionMethodBase;
StaticType = null;
MethodRepresented = methodRepresented;
}

Expand Down Expand Up @@ -761,6 +775,7 @@ class KnownStringValue : LeafValueNode
public KnownStringValue (string contents)
{
Kind = ValueNodeKind.KnownString;
StaticType = null;
Contents = contents;
}

Expand Down Expand Up @@ -808,6 +823,8 @@ class MethodParameterValue : LeafValueWithDynamicallyAccessedMemberNode
public MethodParameterValue (int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, IMetadataTokenProvider sourceContext)
{
Kind = ValueNodeKind.MethodParameter;
var parameter = (ParameterDefinition) sourceContext;
StaticType = parameter.ParameterType.ResolveToMainTypeDefinition ();
ParameterIndex = parameterIndex;
DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes;
SourceContext = sourceContext;
Expand Down Expand Up @@ -845,6 +862,7 @@ class AnnotatedStringValue : LeafValueWithDynamicallyAccessedMemberNode
public AnnotatedStringValue (IMetadataTokenProvider sourceContext, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
{
Kind = ValueNodeKind.AnnotatedString;
StaticType = null;
DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes;
SourceContext = sourceContext;
}
Expand Down Expand Up @@ -879,6 +897,7 @@ class MethodReturnValue : LeafValueWithDynamicallyAccessedMemberNode
public MethodReturnValue (MethodReturnType methodReturnType, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
{
Kind = ValueNodeKind.MethodReturn;
StaticType = methodReturnType.ReturnType.ResolveToMainTypeDefinition ();
DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes;
SourceContext = methodReturnType;
}
Expand Down Expand Up @@ -916,6 +935,7 @@ class MergePointValue : ValueNode
private MergePointValue (ValueNode one, ValueNode two)
{
Kind = ValueNodeKind.MergePoint;
StaticType = null;
m_values = new ValueNodeHashSet ();

if (one.Kind == ValueNodeKind.MergePoint) {
Expand Down Expand Up @@ -1036,6 +1056,7 @@ public GetTypeFromStringValue (TypeResolver resolver, ValueNode assemblyIdentity
{
_resolver = resolver;
Kind = ValueNodeKind.GetTypeFromString;
StaticType = null;
AssemblyIdentity = assemblyIdentity;
NameString = nameString;
}
Expand Down Expand Up @@ -1122,6 +1143,7 @@ class LoadFieldValue : LeafValueWithDynamicallyAccessedMemberNode
public LoadFieldValue (FieldDefinition fieldToLoad, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
{
Kind = ValueNodeKind.LoadField;
StaticType = fieldToLoad.FieldType.ResolveToMainTypeDefinition ();
Field = fieldToLoad;
DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes;
SourceContext = fieldToLoad;
Expand Down Expand Up @@ -1199,6 +1221,7 @@ class ArrayValue : ValueNode
public ArrayValue (ValueNode size)
{
Kind = ValueNodeKind.Array;
StaticType = null;
Size = size ?? UnknownValue.Instance;
}

Expand Down
12 changes: 11 additions & 1 deletion test/Mono.Linker.Tests.Cases/DataFlow/AnnotationsOnType.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@
<linker>
<assembly fullname="Test">
<type fullname="Mono.Linker.Tests.Cases.DataFlow.TypeDataflow">
<type name="AnnotationsOnThisTypeViaXml">
<type name="AnnotatedViaXmlTypeInstance">
<attribute fullname="System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute">
<argument>PublicMethods</argument>
</attribute>
</type>
<type name="InterfaceWithAnnotation">
<attribute fullname="System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute">
<argument>PublicMethods</argument>
</attribute>
</type>
<type name="ImplementsInterfaceWithAnnotationAndHasDifferentAnnotation">
<attribute fullname="System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute">
<argument>PublicConstructors</argument>
</attribute>
Expand Down
87 changes: 82 additions & 5 deletions test/Mono.Linker.Tests.Cases/DataFlow/TypeDataflow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,91 @@ public class TypeDataflow
[Kept]
public static void Main ()
{
_ = new AnnotationsOnThisTypeViaXml ();
CallUnannotatedSealedTypeInstance (new UnannotatedSealedTypeInstance ());
CallUnannotatedTypeInstance (new UnannotatedTypeInstance ());
CallAnnotatedTypeInstance (new AnnotatedViaXmlTypeInstance ());
CallAnnotatedTypeInstanceThatImplementsInterfaceWithDifferentAnnotations (new ImplementsInterfaceWithAnnotationAndHasDifferentAnnotation ());
TestIfElse (true);
}

// We are inserting [DynamicallyAccessedMembers(DynamicallyAccessedMembersTypes.PublicConstructors)] via xml
class AnnotationsOnThisTypeViaXml
[Kept]
[RecognizedReflectionAccessPattern]
public static void CallUnannotatedSealedTypeInstance (UnannotatedSealedTypeInstance instance)
{
instance.GetType ().GetMethod ("Foo");
}

[Kept]
[UnrecognizedReflectionAccessPattern (typeof (Type), "GetMethod", new Type[] { typeof (string) }, messageCode: "IL2075")]
public static void CallUnannotatedTypeInstance (UnannotatedTypeInstance instance)
{
public AnnotationsOnThisTypeViaXml () { }
private AnnotationsOnThisTypeViaXml (int i) { }
instance.GetType ().GetMethod ("Foo");
}

[Kept]
[RecognizedReflectionAccessPattern]
public static void CallAnnotatedTypeInstance (AnnotatedViaXmlTypeInstance instance)
{
instance.GetType ().GetMethod ("Foo");
}

[Kept]
[RecognizedReflectionAccessPattern]
public static void CallAnnotatedTypeInstanceThatImplementsInterfaceWithDifferentAnnotations (ImplementsInterfaceWithAnnotationAndHasDifferentAnnotation instance)
{
instance.GetType ().GetMethod ("Foo");
}

[Kept]
[RecognizedReflectionAccessPattern]
[UnrecognizedReflectionAccessPattern (typeof (Type), "GetMethod", new Type[] { typeof (string) }, messageCode: "IL2075")]
public static void TestIfElse (bool decision)
{
Type t;
if (decision) {
UnannotatedTypeInstance instance = new UnannotatedTypeInstance ();
t = instance.GetType ();
} else {
AnnotatedViaXmlTypeInstance instance = new AnnotatedViaXmlTypeInstance ();
t = instance.GetType ();
}
t.GetMethod ("Foo");
}

[Kept]
[KeptMember (".ctor()")]
public sealed class UnannotatedSealedTypeInstance
{
[Kept]
public void Foo () { }
}

[Kept]
[KeptMember (".ctor()")]
public class UnannotatedTypeInstance
{
public void Foo () { }
}

[Kept]
[KeptMember (".ctor()")]
public class AnnotatedViaXmlTypeInstance
{
public void Foo () { }
protected int field;
}

public interface InterfaceWithAnnotation
{
public void Foo () { }
}

[Kept]
[KeptMember (".ctor()")]
public class ImplementsInterfaceWithAnnotationAndHasDifferentAnnotation : InterfaceWithAnnotation
{
public void Foo () { }
}

}
}