Skip to content

Commit

Permalink
Add more DataFlow tests from linker to NativeAOT (#72777)
Browse files Browse the repository at this point in the history
This adds several dataflow tests from linker - only those which are passing with the fixes in this change. More will be added later.

Fixes:
- Correctly handle Requires attributes in generic types
- Recognize `ref Type` as interesting for data flow
- Fix generic parameter propagation for compiler generated types
- Treat multi-dimensional arrays as "Unknown" values (to match linker behavior)
- Improve origin of warnings generated from attributes
  • Loading branch information
vitek-karas committed Aug 1, 2022
1 parent 01444f7 commit 05c6f3d
Show file tree
Hide file tree
Showing 35 changed files with 6,348 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ internal static bool TryGetRequiresAttribute(TypeSystemEntity member, string req
decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName);
break;
case MetadataType type:
var ecmaType = type as EcmaType;
var ecmaType = type.GetTypeDefinition() as EcmaType;
if (ecmaType == null)
return false;
decoded = ecmaType.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ public static bool IsTypeInterestingForDataflow(TypeDesc type)
if (type.IsWellKnownType(WellKnownType.String))
return true;

// ByRef over an interesting type is itself interesting
if (type is ByRefType byRefType)
type = byRefType.ParameterType;

if (!type.IsDefType)
return false;

Expand Down Expand Up @@ -584,7 +588,7 @@ protected override TypeAnnotations CreateValueFromKey(TypeDesc key)
{
if (typeGenericParameterAnnotations == null)
typeGenericParameterAnnotations = new DynamicallyAccessedMemberTypes[ecmaType.Instantiation.Length];
typeGenericParameterAnnotations[genericParameter.Index] = annotation;
typeGenericParameterAnnotations[genericParameterIndex] = annotation;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -993,18 +993,7 @@ private void ScanIndirectStore(
StackSlot valueToStore = PopUnknown(currentStack, 1, methodBody, offset);
StackSlot destination = PopUnknown(currentStack, 1, methodBody, offset);

foreach (var uniqueDestination in destination.Value)
{
if (uniqueDestination is FieldValue fieldDestination)
{
HandleStoreField(methodBody, offset, fieldDestination, valueToStore.Value);
}
else if (uniqueDestination is MethodParameterValue parameterDestination)
{
HandleStoreParameter(methodBody, offset, parameterDestination, valueToStore.Value);
}
}

StoreInReference(destination.Value, valueToStore.Value, methodBody, offset, locals, curBasicBlock);
}

/// <summary>
Expand Down Expand Up @@ -1237,6 +1226,14 @@ private void HandleCall(
ValueNodeList methodArguments = PopCallArguments(currentStack, calledMethod, callingMethodBody, isNewObj,
offset, out newObjValue);

// Multi-dimensional array access is represented as a call to a special Get method on the array (runtime provided method)
// We don't track multi-dimensional arrays in any way, so return unknown value.
if (calledMethod is ArrayMethod { Kind: ArrayMethodKind.Get or ArrayMethodKind.Address })
{
currentStack.Push(new StackSlot(UnknownValue.Instance));
return;
}

var dereferencedMethodParams = new List<MultiValue>();
foreach (var argument in methodArguments)
dereferencedMethodParams.Add(DereferenceValue(argument, locals, ref interproceduralState));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,20 @@ public static void AddDependenciesDueToCustomAttributes(ref DependencyList depen
MethodDefinition methodDef = reader.GetMethodDefinition(methodHandle);

// Handle custom attributes on the method
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, methodDef.GetCustomAttributes());
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, methodDef.GetCustomAttributes(), method);

// Handle custom attributes on method parameters
foreach (ParameterHandle parameterHandle in methodDef.GetParameters())
{
Parameter parameter = reader.GetParameter(parameterHandle);
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes());
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes(), method);
}

// Handle custom attributes on generic method parameters
foreach (GenericParameterHandle genericParameterHandle in methodDef.GetGenericParameters())
{
GenericParameter parameter = reader.GetGenericParameter(genericParameterHandle);
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes());
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes(), method);
}

// We don't model properties and events as separate entities within the compiler, so ensuring
Expand All @@ -59,7 +59,7 @@ public static void AddDependenciesDueToCustomAttributes(ref DependencyList depen
PropertyAccessors accessors = property.GetAccessors();

if (accessors.Getter == methodHandle || accessors.Setter == methodHandle)
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, property.GetCustomAttributes());
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, property.GetCustomAttributes(), new PropertyPseudoDesc((EcmaType)method.OwningType, propertyHandle));
}

foreach (EventDefinitionHandle eventHandle in declaringType.GetEvents())
Expand All @@ -68,7 +68,7 @@ public static void AddDependenciesDueToCustomAttributes(ref DependencyList depen
EventAccessors accessors = @event.GetAccessors();

if (accessors.Adder == methodHandle || accessors.Remover == methodHandle || accessors.Raiser == methodHandle)
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, @event.GetCustomAttributes());
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, @event.GetCustomAttributes(), new EventPseudoDesc((EcmaType)method.OwningType, eventHandle));
}
}
}
Expand All @@ -77,32 +77,32 @@ public static void AddDependenciesDueToCustomAttributes(ref DependencyList depen
{
MetadataReader reader = type.MetadataReader;
TypeDefinition typeDef = reader.GetTypeDefinition(type.Handle);
AddDependenciesDueToCustomAttributes(ref dependencies, factory, type.EcmaModule, typeDef.GetCustomAttributes());
AddDependenciesDueToCustomAttributes(ref dependencies, factory, type.EcmaModule, typeDef.GetCustomAttributes(), type);

// Handle custom attributes on generic type parameters
foreach (GenericParameterHandle genericParameterHandle in typeDef.GetGenericParameters())
{
GenericParameter parameter = reader.GetGenericParameter(genericParameterHandle);
AddDependenciesDueToCustomAttributes(ref dependencies, factory, type.EcmaModule, parameter.GetCustomAttributes());
AddDependenciesDueToCustomAttributes(ref dependencies, factory, type.EcmaModule, parameter.GetCustomAttributes(), type);
}
}

public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaField field)
{
FieldDefinition fieldDef = field.MetadataReader.GetFieldDefinition(field.Handle);
AddDependenciesDueToCustomAttributes(ref dependencies, factory, field.Module, fieldDef.GetCustomAttributes());
AddDependenciesDueToCustomAttributes(ref dependencies, factory, field.Module, fieldDef.GetCustomAttributes(), field);
}

public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaAssembly assembly)
{
AssemblyDefinition asmDef = assembly.MetadataReader.GetAssemblyDefinition();
AddDependenciesDueToCustomAttributes(ref dependencies, factory, assembly, asmDef.GetCustomAttributes());
AddDependenciesDueToCustomAttributes(ref dependencies, factory, assembly, asmDef.GetCustomAttributes(), assembly);

ModuleDefinition moduleDef = assembly.MetadataReader.GetModuleDefinition();
AddDependenciesDueToCustomAttributes(ref dependencies, factory, assembly, moduleDef.GetCustomAttributes());
AddDependenciesDueToCustomAttributes(ref dependencies, factory, assembly, moduleDef.GetCustomAttributes(), assembly);
}

private static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaModule module, CustomAttributeHandleCollection attributeHandles)
private static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaModule module, CustomAttributeHandleCollection attributeHandles, TypeSystemEntity parent)
{
MetadataReader reader = module.MetadataReader;
var mdManager = (UsageBasedMetadataManager)factory.MetadataManager;
Expand All @@ -126,7 +126,7 @@ private static void AddDependenciesDueToCustomAttributes(ref DependencyList depe
CustomAttributeValue<TypeDesc> decodedValue = attribute.DecodeValue(attributeTypeProvider);

// Make a new list in case we need to abort.
var caDependencies = factory.MetadataManager.GetDependenciesForCustomAttribute(factory, constructor, decodedValue) ?? new DependencyList();
var caDependencies = factory.MetadataManager.GetDependenciesForCustomAttribute(factory, constructor, decodedValue, parent) ?? new DependencyList();

caDependencies.Add(factory.ReflectableMethod(constructor), "Attribute constructor");
caDependencies.Add(factory.ConstructedTypeSymbol(constructor.OwningType), "Attribute type");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ public virtual void GetDependenciesDueToAccess(ref DependencyList dependencies,
{
}

public virtual DependencyList GetDependenciesForCustomAttribute(NodeFactory factory, MethodDesc attributeCtor, CustomAttributeValue decodedValue)
public virtual DependencyList GetDependenciesForCustomAttribute(NodeFactory factory, MethodDesc attributeCtor, CustomAttributeValue decodedValue, TypeSystemEntity parent)
{
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -702,12 +702,12 @@ public override void GetDependenciesDueToAccess(ref DependencyList dependencies,
}
}

public override DependencyList GetDependenciesForCustomAttribute(NodeFactory factory, MethodDesc attributeCtor, CustomAttributeValue decodedValue)
public override DependencyList GetDependenciesForCustomAttribute(NodeFactory factory, MethodDesc attributeCtor, CustomAttributeValue decodedValue, TypeSystemEntity parent)
{
bool scanReflection = (_generationOptions & UsageBasedMetadataGenerationOptions.ReflectionILScanning) != 0;
if (scanReflection)
{
return (new AttributeDataFlow(Logger, factory, FlowAnnotations, new Logging.MessageOrigin(attributeCtor))).ProcessAttributeDataflow(attributeCtor, decodedValue);
return (new AttributeDataFlow(Logger, factory, FlowAnnotations, new Logging.MessageOrigin(parent))).ProcessAttributeDataflow(attributeCtor, decodedValue);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;

namespace Mono.Linker.Tests.Cases.Expectations.Assertions
{
[AttributeUsage (AttributeTargets.Class)]
public sealed class KeptPrivateImplementationDetailsAttribute : KeptAttribute
{
public KeptPrivateImplementationDetailsAttribute (string methodName)
{
if (string.IsNullOrEmpty (methodName))
throw new ArgumentException ("Value cannot be null or empty.", nameof (methodName));
}
}
}
Loading

0 comments on commit 05c6f3d

Please sign in to comment.