Skip to content

Commit

Permalink
Nullable annotations part 1 (dotnet/linker#2253)
Browse files Browse the repository at this point in the history
* Nullable annotations part 1

Enable nullable annotations for LinkContext and the XML processing classes.


Commit migrated from dotnet/linker@39505a8
  • Loading branch information
sbomer authored Sep 3, 2021
1 parent 8babc3a commit 481dc8f
Show file tree
Hide file tree
Showing 16 changed files with 212 additions and 288 deletions.
13 changes: 7 additions & 6 deletions src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,9 @@ static bool IsDynamicallyAccessedMembersAttribute (CustomAttribute attribute)
return attributeType.Name == "DynamicallyAccessedMembersAttribute" && attributeType.Namespace == "System.Diagnostics.CodeAnalysis";
}

DynamicallyAccessedMemberTypes GetMemberTypesForDynamicallyAccessedMembersAttribute (ICustomAttributeProvider provider, IMemberDefinition locationMember = null)
DynamicallyAccessedMemberTypes GetMemberTypesForDynamicallyAccessedMembersAttribute (IMemberDefinition member, ICustomAttributeProvider providerIfNotMember = null)
{
ICustomAttributeProvider provider = providerIfNotMember ?? member;
if (!_context.CustomAttributes.HasAny (provider))
return DynamicallyAccessedMemberTypes.None;
foreach (var attribute in _context.CustomAttributes.GetCustomAttributes (provider)) {
Expand All @@ -171,7 +172,7 @@ DynamicallyAccessedMemberTypes GetMemberTypesForDynamicallyAccessedMembersAttrib
else
_context.LogWarning (
$"Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' doesn't have the required number of parameters specified.",
2028, locationMember ?? (provider as IMemberDefinition));
2028, member);
}
return DynamicallyAccessedMemberTypes.None;
}
Expand Down Expand Up @@ -242,7 +243,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type)

for (int i = 0; i < method.Parameters.Count; i++) {
var methodParameter = method.Parameters[i];
DynamicallyAccessedMemberTypes pa = GetMemberTypesForDynamicallyAccessedMembersAttribute (methodParameter, method);
DynamicallyAccessedMemberTypes pa = GetMemberTypesForDynamicallyAccessedMembersAttribute (method, providerIfNotMember: methodParameter);
if (pa == DynamicallyAccessedMemberTypes.None)
continue;

Expand All @@ -259,7 +260,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type)
paramAnnotations[i + offset] = pa;
}

DynamicallyAccessedMemberTypes returnAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (method.MethodReturnType, method);
DynamicallyAccessedMemberTypes returnAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (method, providerIfNotMember: method.MethodReturnType);
if (returnAnnotation != DynamicallyAccessedMemberTypes.None && !IsTypeInterestingForDataflow (method.ReturnType)) {
_context.LogWarning (
$"Return type of method '{method.GetDisplayName ()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to properties of type 'System.Type' or 'System.String'.",
Expand All @@ -270,7 +271,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type)
if (method.HasGenericParameters) {
for (int genericParameterIndex = 0; genericParameterIndex < method.GenericParameters.Count; genericParameterIndex++) {
var genericParameter = method.GenericParameters[genericParameterIndex];
var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (genericParameter, method);
var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (method, providerIfNotMember: genericParameter);
if (annotation != DynamicallyAccessedMemberTypes.None) {
if (genericParameterAnnotations == null)
genericParameterAnnotations = new DynamicallyAccessedMemberTypes[method.GenericParameters.Count];
Expand Down Expand Up @@ -392,7 +393,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type)
if (type.HasGenericParameters) {
for (int genericParameterIndex = 0; genericParameterIndex < type.GenericParameters.Count; genericParameterIndex++) {
var genericParameter = type.GenericParameters[genericParameterIndex];
var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (genericParameter, type);
var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (type, providerIfNotMember: genericParameter);
if (annotation != DynamicallyAccessedMemberTypes.None) {
if (typeGenericParameterAnnotations == null)
typeGenericParameterAnnotations = new DynamicallyAccessedMemberTypes[type.GenericParameters.Count];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ private void ScanLdfld (

bool isByRef = code == Code.Ldflda || code == Code.Ldsflda;

FieldDefinition field = _context.TryResolve (operation.Operand as FieldReference);
FieldDefinition field = _context.TryResolve ((FieldReference) operation.Operand);
if (field != null) {
StackSlot slot = new StackSlot (GetFieldValue (thisMethod, field), isByRef);
currentStack.Push (slot);
Expand Down Expand Up @@ -819,7 +819,7 @@ private void ScanStfld (
if (operation.OpCode.Code == Code.Stfld)
PopUnknown (currentStack, 1, methodBody, operation.Offset);

FieldDefinition field = _context.TryResolve (operation.Operand as FieldReference);
FieldDefinition field = _context.TryResolve ((FieldReference) operation.Operand);
if (field != null) {
HandleStoreField (thisMethod, field, operation, valueToStoreSlot.Value);
}
Expand Down
96 changes: 36 additions & 60 deletions src/tools/illink/src/linker/Linker.Steps/BodySubstitutionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
using System.Xml.XPath;
using Mono.Cecil;

#nullable enable

namespace Mono.Linker.Steps
{
public class BodySubstitutionParser : ProcessLinkerXmlBase
{
SubstitutionInfo _substitutionInfo;
SubstitutionInfo? _substitutionInfo;

public BodySubstitutionParser (LinkContext context, Stream documentStream, string xmlDocumentLocation)
: base (context, documentStream, xmlDocumentLocation)
Expand All @@ -24,74 +26,49 @@ public BodySubstitutionParser (LinkContext context, Stream documentStream, Embed
public void Parse (SubstitutionInfo xmlInfo)
{
_substitutionInfo = xmlInfo;
bool stripSubstitutions = _context.IsOptimizationEnabled (CodeOptimizations.RemoveSubstitutions, _resourceAssembly);
bool stripSubstitutions = _context.IsOptimizationEnabled (CodeOptimizations.RemoveSubstitutions, _resource?.Assembly);
ProcessXml (stripSubstitutions, _context.IgnoreSubstitutions);
}

protected override void ProcessAssembly (AssemblyDefinition assembly, XPathNavigator nav, bool warnOnUnresolvedTypes)
{
ProcessTypes (assembly, nav, warnOnUnresolvedTypes);
ProcessResources (assembly, nav.SelectChildren ("resource", ""));
ProcessResources (assembly, nav);
}

protected override TypeDefinition ProcessExportedType (ExportedType exported, AssemblyDefinition assembly) => null;
protected override TypeDefinition? ProcessExportedType (ExportedType exported, AssemblyDefinition assembly) => null;

protected override bool ProcessTypePattern (string fullname, AssemblyDefinition assembly, XPathNavigator nav) => false;

protected override void ProcessType (TypeDefinition type, XPathNavigator nav)
{
Debug.Assert (ShouldProcessElement (nav));

if (!nav.HasChildren)
return;

XPathNodeIterator methods = nav.SelectChildren ("method", "");
if (methods.Count > 0)
ProcessMethods (type, methods);

var fields = nav.SelectChildren ("field", "");
if (fields.Count > 0) {
while (fields.MoveNext ()) {
if (!ShouldProcessElement (fields.Current))
continue;

ProcessField (type, fields);
}
}
ProcessTypeChildren (type, nav);
}

void ProcessMethods (TypeDefinition type, XPathNodeIterator iterator)
protected override void ProcessMethod (TypeDefinition type, XPathNavigator methodNav, object? _customData)
{
while (iterator.MoveNext ()) {
if (!ShouldProcessElement (iterator.Current))
continue;

ProcessMethod (type, iterator);
}
}

void ProcessMethod (TypeDefinition type, XPathNodeIterator iterator)
{
string signature = GetAttribute (iterator.Current, "signature");
Debug.Assert (_substitutionInfo != null);
string signature = GetSignature (methodNav);
if (string.IsNullOrEmpty (signature))
return;

MethodDefinition method = FindMethod (type, signature);
MethodDefinition? method = FindMethod (type, signature);
if (method == null) {
LogWarning ($"Could not find method '{signature}' on type '{type.GetDisplayName ()}'.", 2009, iterator.Current);
LogWarning ($"Could not find method '{signature}' on type '{type.GetDisplayName ()}'.", 2009, methodNav);
return;
}

string action = GetAttribute (iterator.Current, "body");
string action = GetAttribute (methodNav, "body");
switch (action) {
case "remove":
_substitutionInfo.SetMethodAction (method, MethodAction.ConvertToThrow);
return;
case "stub":
string value = GetAttribute (iterator.Current, "value");
string value = GetAttribute (methodNav, "value");
if (!string.IsNullOrEmpty (value)) {
if (!TryConvertValue (value, method.ReturnType, out object res)) {
LogWarning ($"Invalid value for '{method.GetDisplayName ()}' stub.", 2010, iterator.Current);
if (!TryConvertValue (value, method.ReturnType, out object? res)) {
LogWarning ($"Invalid value for '{method.GetDisplayName ()}' stub.", 2010, methodNav);
return;
}

Expand All @@ -101,77 +78,76 @@ void ProcessMethod (TypeDefinition type, XPathNodeIterator iterator)
_substitutionInfo.SetMethodAction (method, MethodAction.ConvertToStub);
return;
default:
LogWarning ($"Unknown body modification '{action}' for '{method.GetDisplayName ()}'.", 2011, iterator.Current);
LogWarning ($"Unknown body modification '{action}' for '{method.GetDisplayName ()}'.", 2011, methodNav);
return;
}
}

void ProcessField (TypeDefinition type, XPathNodeIterator iterator)
protected override void ProcessField (TypeDefinition type, XPathNavigator fieldNav)
{
string name = GetAttribute (iterator.Current, "name");
Debug.Assert (_substitutionInfo != null);
string name = GetAttribute (fieldNav, "name");
if (string.IsNullOrEmpty (name))
return;

var field = type.Fields.FirstOrDefault (f => f.Name == name);
if (field == null) {
LogWarning ($"Could not find field '{name}' on type '{type.GetDisplayName ()}'.", 2012, iterator.Current);
LogWarning ($"Could not find field '{name}' on type '{type.GetDisplayName ()}'.", 2012, fieldNav);
return;
}

if (!field.IsStatic || field.IsLiteral) {
LogWarning ($"Substituted field '{field.GetDisplayName ()}' needs to be static field.", 2013, iterator.Current);
LogWarning ($"Substituted field '{field.GetDisplayName ()}' needs to be static field.", 2013, fieldNav);
return;
}

string value = GetAttribute (iterator.Current, "value");
string value = GetAttribute (fieldNav, "value");
if (string.IsNullOrEmpty (value)) {
LogWarning ($"Missing 'value' attribute for field '{field.GetDisplayName ()}'.", 2014, iterator.Current);
LogWarning ($"Missing 'value' attribute for field '{field.GetDisplayName ()}'.", 2014, fieldNav);
return;
}
if (!TryConvertValue (value, field.FieldType, out object res)) {
LogWarning ($"Invalid value '{value}' for '{field.GetDisplayName ()}'.", 2015, iterator.Current);
if (!TryConvertValue (value, field.FieldType, out object? res)) {
LogWarning ($"Invalid value '{value}' for '{field.GetDisplayName ()}'.", 2015, fieldNav);
return;
}

_substitutionInfo.SetFieldValue (field, res);

string init = GetAttribute (iterator.Current, "initialize");
string init = GetAttribute (fieldNav, "initialize");
if (init?.ToLowerInvariant () == "true") {
_substitutionInfo.SetFieldInit (field);
}
}

void ProcessResources (AssemblyDefinition assembly, XPathNodeIterator iterator)
void ProcessResources (AssemblyDefinition assembly, XPathNavigator nav)
{
while (iterator.MoveNext ()) {
XPathNavigator nav = iterator.Current;

if (!ShouldProcessElement (nav))
foreach (XPathNavigator resourceNav in nav.SelectChildren ("resource", "")) {
if (!ShouldProcessElement (resourceNav))
continue;

string name = GetAttribute (nav, "name");
string name = GetAttribute (resourceNav, "name");
if (String.IsNullOrEmpty (name)) {
LogWarning ($"Missing 'name' attribute for resource.", 2038, iterator.Current);
LogWarning ($"Missing 'name' attribute for resource.", 2038, resourceNav);
continue;
}

string action = GetAttribute (nav, "action");
string action = GetAttribute (resourceNav, "action");
if (action != "remove") {
LogWarning ($"Invalid value '{action}' for attribute 'action' for resource '{name}'.", 2039, iterator.Current);
LogWarning ($"Invalid value '{action}' for attribute 'action' for resource '{name}'.", 2039, resourceNav);
continue;
}

EmbeddedResource resource = assembly.FindEmbeddedResource (name);
if (resource == null) {
LogWarning ($"Could not find embedded resource '{name}' to remove in assembly '{assembly.Name.Name}'.", 2040, iterator.Current);
LogWarning ($"Could not find embedded resource '{name}' to remove in assembly '{assembly.Name.Name}'.", 2040, resourceNav);
continue;
}

_context.Annotations.AddResourceToRemove (assembly, resource);
}
}

static MethodDefinition FindMethod (TypeDefinition type, string signature)
static MethodDefinition? FindMethod (TypeDefinition type, string signature)
{
if (!type.HasMethods)
return null;
Expand Down
3 changes: 3 additions & 0 deletions src/tools/illink/src/linker/Linker.Steps/CodeRewriterStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ public static Instruction CreateConstantResultInstruction (LinkContext context,
break;
}

if (rtype == null)
return null;

switch (rtype.MetadataType) {
case MetadataType.Boolean:
if (value is int bintValue && bintValue == 1)
Expand Down
27 changes: 14 additions & 13 deletions src/tools/illink/src/linker/Linker.Steps/DescriptorMarker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

using Mono.Cecil;

#nullable enable

namespace Mono.Linker.Steps
{
public class DescriptorMarker : ProcessLinkerXmlBase
Expand All @@ -35,7 +37,7 @@ public DescriptorMarker (LinkContext context, Stream documentStream, EmbeddedRes

public void Mark ()
{
bool stripDescriptors = _context.IsOptimizationEnabled (CodeOptimizations.RemoveDescriptors, _resourceAssembly);
bool stripDescriptors = _context.IsOptimizationEnabled (CodeOptimizations.RemoveDescriptors, _resource?.Assembly);
ProcessXml (stripDescriptors, _context.IgnoreDescriptors);
}

Expand All @@ -57,12 +59,11 @@ protected override void ProcessAssembly (AssemblyDefinition assembly, XPathNavig

void ProcessNamespaces (AssemblyDefinition assembly, XPathNavigator nav)
{
var iterator = nav.SelectChildren (NamespaceElementName, XmlNamespace);
while (iterator.MoveNext ()) {
if (!ShouldProcessElement (iterator.Current))
foreach (XPathNavigator namespaceNav in nav.SelectChildren (NamespaceElementName, XmlNamespace)) {
if (!ShouldProcessElement (namespaceNav))
continue;

string fullname = GetFullName (iterator.Current);
string fullname = GetFullName (namespaceNav);
bool foundMatch = false;
foreach (TypeDefinition type in assembly.MainModule.Types) {
if (type.Namespace != fullname)
Expand All @@ -73,7 +74,7 @@ void ProcessNamespaces (AssemblyDefinition assembly, XPathNavigator nav)
}

if (!foundMatch) {
LogWarning ($"Could not find any type in namespace '{fullname}'.", 2044, iterator.Current);
LogWarning ($"Could not find any type in namespace '{fullname}'.", 2044, namespaceNav);
}
}
}
Expand All @@ -90,7 +91,7 @@ void MarkAndPreserveAll (TypeDefinition type)
MarkAndPreserveAll (nested);
}

protected override TypeDefinition ProcessExportedType (ExportedType exported, AssemblyDefinition assembly)
protected override TypeDefinition? ProcessExportedType (ExportedType exported, AssemblyDefinition assembly)
{
_context.MarkingHelpers.MarkExportedType (exported, assembly.MainModule, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation));
return base.ProcessExportedType (exported, assembly);
Expand Down Expand Up @@ -154,30 +155,30 @@ protected override void ProcessField (TypeDefinition type, FieldDefinition field
_context.Annotations.Mark (field, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation));
}

protected override void ProcessMethod (TypeDefinition type, MethodDefinition method, XPathNavigator nav, object customData)
protected override void ProcessMethod (TypeDefinition type, MethodDefinition method, XPathNavigator nav, object? customData)
{
if (_context.Annotations.IsMarked (method))
LogWarning ($"Duplicate preserve of '{method.GetDisplayName ()}'.", 2025, nav);

_context.Annotations.MarkIndirectlyCalledMethod (method);
_context.Annotations.SetAction (method, MethodAction.Parse);

if (!(bool) customData) {
if (customData is bool required && !required) {
_context.Annotations.AddPreservedMethod (type, method);
} else {
_context.Annotations.Mark (method, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation));
}
}

void ProcessMethodIfNotNull (TypeDefinition type, MethodDefinition method, XPathNavigator nav, object customData)
void ProcessMethodIfNotNull (TypeDefinition type, MethodDefinition method, XPathNavigator nav, object? customData)
{
if (method == null)
return;

ProcessMethod (type, method, nav, customData);
}

protected override MethodDefinition GetMethod (TypeDefinition type, string signature)
protected override MethodDefinition? GetMethod (TypeDefinition type, string signature)
{
if (type.HasMethods)
foreach (MethodDefinition meth in type.Methods)
Expand Down Expand Up @@ -211,7 +212,7 @@ public static string GetMethodSignature (MethodDefinition meth, bool includeGene
return sb.ToString ();
}

protected override void ProcessEvent (TypeDefinition type, EventDefinition @event, XPathNavigator nav, object customData)
protected override void ProcessEvent (TypeDefinition type, EventDefinition @event, XPathNavigator nav, object? customData)
{
if (_context.Annotations.IsMarked (@event))
LogWarning ($"Duplicate preserve of '{@event.FullName}'.", 2025, nav);
Expand All @@ -221,7 +222,7 @@ protected override void ProcessEvent (TypeDefinition type, EventDefinition @even
ProcessMethodIfNotNull (type, @event.InvokeMethod, nav, customData);
}

protected override void ProcessProperty (TypeDefinition type, PropertyDefinition property, XPathNavigator nav, object customData, bool fromSignature)
protected override void ProcessProperty (TypeDefinition type, PropertyDefinition property, XPathNavigator nav, object? customData, bool fromSignature)
{
string[] accessors = fromSignature ? GetAccessors (nav) : _accessorsAll;

Expand Down
Loading

0 comments on commit 481dc8f

Please sign in to comment.