Skip to content

Nullable annotations part 1 #2253

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 4 commits into from
Sep 3, 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
13 changes: 7 additions & 6 deletions 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
4 changes: 2 additions & 2 deletions src/linker/Linker.Dataflow/MethodBodyScanner.cs
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/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/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/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