diff --git a/docs/data-formats.md b/docs/data-formats.md index 0e20fd4d1dbe..a875b0489504 100644 --- a/docs/data-formats.md +++ b/docs/data-formats.md @@ -229,7 +229,7 @@ are applied. ```xml - + Argument @@ -244,7 +244,7 @@ This allows to add a custom attribute to a class, interface, delegate, struct or - + Argument @@ -259,7 +259,7 @@ This allows to add a custom attribute to a class, interface, delegate, struct or - + DefaultConstructor @@ -275,7 +275,7 @@ This allows to add a custom attribute to a class, interface, delegate, struct or - + DefaultConstructor @@ -291,7 +291,7 @@ This allows to add a custom attribute to a class, interface, delegate, struct or - + DefaultConstructor @@ -308,21 +308,21 @@ This allows to add a custom attribute to a class, interface, delegate, struct or - + DefaultConstructor - + PublicConstructors - + DefaultConstructor @@ -340,17 +340,17 @@ This allows to add a custom attribute to a class, interface, delegate, struct or - + DefaultConstructor - + DefaultConstructor - + PublicConstructors @@ -368,7 +368,7 @@ This allows to add a custom attribute to a class, interface, delegate, struct or - + DefaultConstructor @@ -378,6 +378,20 @@ This allows to add a custom attribute to a class, interface, delegate, struct or ``` +### Custom attribute on type in all assemblies + +```xml + + + + + DefaultConstructor + + + + +``` + ### Conditional custom attributes The `feature` and `featurevalue` attributes are optional, but must be used together when used. @@ -391,7 +405,7 @@ attributes are applied. - + PublicConstructors @@ -403,6 +417,9 @@ attributes are applied. ### Custom attributes elements +The attribute element requires 'fullname' attribute without it linker will generate a warning and skip +the attribute. Optionally you can use the 'assembly' attribute to point to certain assembly to look +for the attribute, if not specified the linker will look the attribute in any loaded assembly. Inside an attribute element in the xml you can define argument, field and property elements. An attribute could have several arguments, several fields or several properties. When writing custom attribute with multiple arguments you need to write the xml elements in an order dependent @@ -411,11 +428,11 @@ second xml argument element correspond to the second custom attribute argument a For fields and properties, you need to include the name since they are not order dependent. ```xml - + Argument1 Argument2 Argument3 SomeValue SomeValue -``` +``` \ No newline at end of file diff --git a/docs/error-codes.md b/docs/error-codes.md index 559712ab2111..94169850a548 100644 --- a/docs/error-codes.md +++ b/docs/error-codes.md @@ -135,17 +135,17 @@ error and warning codes. - The 'XML document location' defined the set accessor of property 'property' on type 'type', but the accessor was not found. -#### `IL2020`: DynamicallyAccessedMembers attribute was specified but no argument was proportioned +#### `IL2020`: Argument 'argument' specified in 'XML document location' is of unsupported type 'type' -- The XML descriptor has a DynamicallyAccessedMembers attribute but the argument 'argument' does not match any of the existing DynamicallyAccessedMemberTypes +- The constructor parameter type is not supported in the XML reading code. -#### `IL2021`: Could not parse argument 'argument' specified in 'XML document location' as a DynamicallyAccessedMemberTypes +#### `IL2021`: Could not parse argument 'argument' specified in 'XML document location' as a 'type' -- The XML descriptor has a DynamicallyAccessedMembers attribute but the argument 'argument' does not match any of the existing DynamicallyAccessedMemberTypes +- The XML descriptor has a 'type' attribute but the argument 'argument' does not match any of the existing enum 'type' values -#### `IL2022`: DynamicallyAccessedMembers attribute was specified but there is more than one argument +#### `IL2022`: Could not find a constructor for type 'attribute type' that receives 'number of arguments' arguments as parameter -- The XML descriptor has more than one argument for a single DynamicallyAccessedMembers attribute, there can only be one argument in order to parse it +- The 'attribute type' 'number of arguments' doesnt match with the number of arguments in any of the constructor function described in 'attribute type' #### `IL2023`: There is more than one return parameter specified for 'method' in 'XML document location' @@ -169,4 +169,20 @@ error and warning codes. #### `IL2028`: Attribute 'attribute' on 'method' doesn't have a required constructor argument. -- The linker found an instance of attribute 'attribute' on 'method' but it lacks a required constructor argument. Linker will ignore this attribute. \ No newline at end of file +- The linker found an instance of attribute 'attribute' on 'method' but it lacks a required constructor argument. Linker will ignore this attribute. + +#### `IL2029`: Attribute element does not contain attribute 'fullname' + +- An attribute element was declared but does not contain the attribute 'fullname' or 'fullname' attribute is empty + +#### `IL2030`: Could not resolve assembly 'assembly' in attribute 'attribute' specified in the 'XML document location' + +- The assembly 'assembly' described as a attribute property of 'attribute' could not be resolved in 'XML document location' + +#### `IL2031`: Attribute type 'attribute type' could not be found + +- The described 'attribute type' could not be found in the assemblies + +#### `IL2032`: Argument 'argument' specified in 'XML document location' could not be transformed to the constructor parameter type + +- The number of arguments correspond to a certain type constructor, but the type of arguments specified in the xml does not match the type of arguments in the constructor. \ No newline at end of file diff --git a/src/linker/Linker.Dataflow/AggregateFlowAnnotationSource.cs b/src/linker/Linker.Dataflow/AggregateFlowAnnotationSource.cs deleted file mode 100644 index 906beb77de50..000000000000 --- a/src/linker/Linker.Dataflow/AggregateFlowAnnotationSource.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Mono.Cecil; - -namespace Mono.Linker.Dataflow -{ - class AggregateFlowAnnotationSource : IFlowAnnotationSource - { - private readonly List _sources; - - public AggregateFlowAnnotationSource (IEnumerable sources) - { - _sources = new List (sources); - } - - public DynamicallyAccessedMemberTypes GetFieldAnnotation (FieldDefinition field) - { - return _sources.Aggregate (DynamicallyAccessedMemberTypes.None, (r, s) => r | s.GetFieldAnnotation (field)); - } - - public DynamicallyAccessedMemberTypes GetParameterAnnotation (MethodDefinition method, int index) - { - return _sources.Aggregate (DynamicallyAccessedMemberTypes.None, (r, s) => r | s.GetParameterAnnotation (method, index)); - } - - public DynamicallyAccessedMemberTypes GetPropertyAnnotation (PropertyDefinition property) - { - return _sources.Aggregate (DynamicallyAccessedMemberTypes.None, (r, s) => r | s.GetPropertyAnnotation (property)); - } - - public DynamicallyAccessedMemberTypes GetReturnParameterAnnotation (MethodDefinition method) - { - return _sources.Aggregate (DynamicallyAccessedMemberTypes.None, (r, s) => r | s.GetReturnParameterAnnotation (method)); - } - - public DynamicallyAccessedMemberTypes GetThisParameterAnnotation (MethodDefinition method) - { - return _sources.Aggregate (DynamicallyAccessedMemberTypes.None, (r, s) => r | s.GetThisParameterAnnotation (method)); - } - } -} diff --git a/src/linker/Linker.Dataflow/AttributeFlowAnnotationSource.cs b/src/linker/Linker.Dataflow/AttributeFlowAnnotationSource.cs deleted file mode 100644 index 273538d28b3d..000000000000 --- a/src/linker/Linker.Dataflow/AttributeFlowAnnotationSource.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using Mono.Cecil; - -namespace Mono.Linker.Dataflow -{ - class AttributeFlowAnnotationSource : IFlowAnnotationSource - { - public DynamicallyAccessedMemberTypes GetFieldAnnotation (FieldDefinition field) - { - return Get (field); - } - - public DynamicallyAccessedMemberTypes GetParameterAnnotation (MethodDefinition method, int index) - { - return Get (method.Parameters[index]); - } - - public DynamicallyAccessedMemberTypes GetPropertyAnnotation (PropertyDefinition property) - { - return Get (property); - } - - public DynamicallyAccessedMemberTypes GetReturnParameterAnnotation (MethodDefinition method) - { - return Get (method.MethodReturnType); - } - - public DynamicallyAccessedMemberTypes GetThisParameterAnnotation (MethodDefinition method) - { - // We take the annotation from the attribute on the method itself for "this" - return Get (method); - } - - static bool IsDynamicallyAccessedMembersAttribute (CustomAttribute attribute) - { - var attributeType = attribute.AttributeType; - return attributeType.Name == "DynamicallyAccessedMembersAttribute" && attributeType.Namespace == "System.Diagnostics.CodeAnalysis"; - } - - static DynamicallyAccessedMemberTypes GetFromAttribute (CustomAttribute attribute) - { - Debug.Assert (IsDynamicallyAccessedMembersAttribute (attribute)); - - if (attribute.HasConstructorArguments) { - return (DynamicallyAccessedMemberTypes) (int) attribute.ConstructorArguments[0].Value; - } - - return DynamicallyAccessedMemberTypes.None; - } - - static DynamicallyAccessedMemberTypes Get (ICustomAttributeProvider attributeProvider) - { - if (!attributeProvider.HasCustomAttributes) - return DynamicallyAccessedMemberTypes.None; - - foreach (var attribute in attributeProvider.CustomAttributes) { - if (IsDynamicallyAccessedMembersAttribute (attribute)) { - return GetFromAttribute (attribute); - } - } - - return DynamicallyAccessedMemberTypes.None; - } - } -} diff --git a/src/linker/Linker.Dataflow/FlowAnnotations.cs b/src/linker/Linker.Dataflow/FlowAnnotations.cs index 73c634d02039..43eec09e5cba 100644 --- a/src/linker/Linker.Dataflow/FlowAnnotations.cs +++ b/src/linker/Linker.Dataflow/FlowAnnotations.cs @@ -13,12 +13,14 @@ namespace Mono.Linker.Dataflow class FlowAnnotations { - readonly IFlowAnnotationSource _source; + readonly LinkContext _context; + readonly CustomAttributeSource _source; readonly Dictionary _annotations = new Dictionary (); - public FlowAnnotations (IFlowAnnotationSource annotationSource) + public FlowAnnotations (LinkContext context, CustomAttributeSource annotationSource) { _source = annotationSource; + _context = context; } public bool RequiresDataFlowAnalysis (MethodDefinition method) @@ -73,6 +75,29 @@ TypeAnnotations GetAnnotations (TypeDefinition type) return value; } + static bool IsDynamicallyAccessedMembersAttribute (CustomAttribute attribute) + { + var attributeType = attribute.AttributeType; + return attributeType.Name == "DynamicallyAccessedMembersAttribute" && attributeType.Namespace == "System.Diagnostics.CodeAnalysis"; + } + + DynamicallyAccessedMemberTypes GetMemberTypesForDynamicallyAccessedMemberAttribute (ICustomAttributeProvider provider, IMemberDefinition locationMember = null) + { + if (!_source.HasCustomAttributes (provider)) + return DynamicallyAccessedMemberTypes.None; + foreach (var attribute in _source.GetCustomAttributes (provider)) { + if (!IsDynamicallyAccessedMembersAttribute (attribute)) + continue; + if (attribute.ConstructorArguments.Count == 1) + return (DynamicallyAccessedMemberTypes) (int) attribute.ConstructorArguments[0].Value; + else if (attribute.ConstructorArguments.Count == 0) + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"DynamicallyAccessedMembersAttribute was specified but no argument was proportioned", 2020, locationMember ?? (provider as IMemberDefinition))); + else + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"DynamicallyAccessedMembersAttribute was specified but there is more than one argument", 2022, locationMember ?? (provider as IMemberDefinition))); + } + return DynamicallyAccessedMemberTypes.None; + } + TypeAnnotations BuildTypeAnnotations (TypeDefinition type) { var annotatedFields = new ArrayBuilder (); @@ -83,7 +108,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) if (!IsTypeInterestingForDataflow (field.FieldType)) continue; - DynamicallyAccessedMemberTypes annotation = _source.GetFieldAnnotation (field); + DynamicallyAccessedMemberTypes annotation = GetMemberTypesForDynamicallyAccessedMemberAttribute (field); if (annotation == DynamicallyAccessedMemberTypes.None) { continue; } @@ -101,11 +126,13 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) // We convert indices from metadata space to IL space here. // IL space assigns index 0 to the `this` parameter on instance methods. + + int offset; if (method.HasImplicitThis ()) { offset = 1; if (IsTypeInterestingForDataflow (method.DeclaringType)) { - DynamicallyAccessedMemberTypes ta = _source.GetThisParameterAnnotation (method); + DynamicallyAccessedMemberTypes ta = GetMemberTypesForDynamicallyAccessedMemberAttribute (method); if (ta != DynamicallyAccessedMemberTypes.None) { paramAnnotations = new DynamicallyAccessedMemberTypes[method.Parameters.Count + offset]; paramAnnotations[0] = ta; @@ -120,7 +147,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) continue; } - DynamicallyAccessedMemberTypes pa = _source.GetParameterAnnotation (method, i); + DynamicallyAccessedMemberTypes pa = GetMemberTypesForDynamicallyAccessedMemberAttribute (method.Parameters[i], method); if (pa == DynamicallyAccessedMemberTypes.None) { continue; } @@ -132,7 +159,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) } DynamicallyAccessedMemberTypes returnAnnotation = IsTypeInterestingForDataflow (method.ReturnType) ? - _source.GetReturnParameterAnnotation (method) : DynamicallyAccessedMemberTypes.None; + GetMemberTypesForDynamicallyAccessedMemberAttribute (method.MethodReturnType, method) : DynamicallyAccessedMemberTypes.None; if (returnAnnotation != DynamicallyAccessedMemberTypes.None || paramAnnotations != null) { annotatedMethods.Add (new MethodAnnotations (method, paramAnnotations, returnAnnotation)); } @@ -160,7 +187,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) continue; } - DynamicallyAccessedMemberTypes annotation = _source.GetPropertyAnnotation (property); + DynamicallyAccessedMemberTypes annotation = GetMemberTypesForDynamicallyAccessedMemberAttribute (property); if (annotation == DynamicallyAccessedMemberTypes.None) { continue; } diff --git a/src/linker/Linker.Dataflow/IFlowAnnotationSource.cs b/src/linker/Linker.Dataflow/IFlowAnnotationSource.cs deleted file mode 100644 index 0177f14def07..000000000000 --- a/src/linker/Linker.Dataflow/IFlowAnnotationSource.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.CodeAnalysis; -using Mono.Cecil; - -namespace Mono.Linker.Dataflow -{ - interface IFlowAnnotationSource - { - DynamicallyAccessedMemberTypes GetPropertyAnnotation (PropertyDefinition property); - - // Index refers to the index in the formal parameter list (i.e. there's no index for `this` on instance methods) - DynamicallyAccessedMemberTypes GetParameterAnnotation (MethodDefinition method, int index); - - DynamicallyAccessedMemberTypes GetReturnParameterAnnotation (MethodDefinition method); - - // Should return annotation which applies to the "this" parameter of the method - // Note that this does not apply to the "this" parameter on extension methods, it's the this on instance methods. - DynamicallyAccessedMemberTypes GetThisParameterAnnotation (MethodDefinition method); - - DynamicallyAccessedMemberTypes GetFieldAnnotation (FieldDefinition field); - } -} diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index f3f2f9df2696..a8ba8ea4ae56 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -162,19 +162,9 @@ public virtual void Process (LinkContext context) { _context = context; - IFlowAnnotationSource annotationSource = new AttributeFlowAnnotationSource (); - if (_context.AttributeDefinitions != null && _context.AttributeDefinitions.Count > 0) { - annotationSource = new AggregateFlowAnnotationSource ( - _context.AttributeDefinitions.Select (xmlStringPath => { - var xmlAnnotations = new XmlFlowAnnotationSource (_context); - xmlAnnotations.ParseXml (xmlStringPath); - return xmlAnnotations; - }) - .Append (annotationSource)); - } - - _flowAnnotations = new FlowAnnotations (annotationSource); + CustomAttributeSource annotationSources = new CustomAttributeSource (_context); + _flowAnnotations = new FlowAnnotations (_context, annotationSources); Initialize (); Process (); diff --git a/src/linker/Linker/CustomAttributeSource.cs b/src/linker/Linker/CustomAttributeSource.cs new file mode 100644 index 000000000000..e9fd8198e1c6 --- /dev/null +++ b/src/linker/Linker/CustomAttributeSource.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Mono.Cecil; + +namespace Mono.Linker +{ + class CustomAttributeSource + { + readonly List _sources; + + public CustomAttributeSource (LinkContext context) + { + _sources = new List (); + if (context.AttributeDefinitions?.Count > 0) { + foreach (string a in context.AttributeDefinitions) { + XmlCustomAttributeSource xmlAnnotations = new XmlCustomAttributeSource (context); + xmlAnnotations.ParseXml (a); + _sources.Add (xmlAnnotations); + } + } + } + + public IEnumerable GetCustomAttributes (ICustomAttributeProvider provider) + { + if (provider.HasCustomAttributes) { + foreach (var customAttribute in provider.CustomAttributes) + yield return customAttribute; + } + + if (_sources.Count > 0) { + foreach (var source in _sources) { + if (source.HasCustomAttributes (provider)) { + foreach (var customAttribute in source.GetCustomAttributes (provider)) + yield return customAttribute; + } + } + } + } + + public bool HasCustomAttributes (ICustomAttributeProvider provider) + { + if (provider.HasCustomAttributes) + return true; + + if (_sources.Count > 0) { + foreach (var source in _sources) { + if (source.HasCustomAttributes (provider)) { + return true; + } + } + } + + return false; + } + } +} diff --git a/src/linker/Linker.Dataflow/XmlFlowAnnotationSource.cs b/src/linker/Linker/XmlCustomAttributeSource.cs similarity index 52% rename from src/linker/Linker.Dataflow/XmlFlowAnnotationSource.cs rename to src/linker/Linker/XmlCustomAttributeSource.cs index 20dec1cf6940..dd883da76f99 100644 --- a/src/linker/Linker.Dataflow/XmlFlowAnnotationSource.cs +++ b/src/linker/Linker/XmlCustomAttributeSource.cs @@ -4,104 +4,125 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Xml.XPath; - using Mono.Cecil; -namespace Mono.Linker.Dataflow +namespace Mono.Linker { - class XmlFlowAnnotationSource : IFlowAnnotationSource + class XmlCustomAttributeSource { - readonly Dictionary _methods = new Dictionary (); - readonly Dictionary _properties = new Dictionary (); - readonly Dictionary _fields = new Dictionary (); + readonly Dictionary> _attributes = new Dictionary> (); readonly LinkContext _context; XPathDocument _document; string _xmlDocumentLocation; - public XmlFlowAnnotationSource (LinkContext context) + public XmlCustomAttributeSource (LinkContext context) { _context = context; } - public DynamicallyAccessedMemberTypes GetFieldAnnotation (FieldDefinition field) + public IEnumerable GetCustomAttributes (ICustomAttributeProvider provider) { - return _fields.TryGetValue (field, out var ann) ? ann : DynamicallyAccessedMemberTypes.None; + return _attributes.TryGetValue (provider, out var ann) ? ann : null; } - public DynamicallyAccessedMemberTypes GetParameterAnnotation (MethodDefinition method, int index) + public bool HasCustomAttributes (ICustomAttributeProvider provider) { - DynamicallyAccessedMemberTypes parameterAnnotation = DynamicallyAccessedMemberTypes.None; - - if (_methods.TryGetValue (method, out var ann) && ann.ParameterAnnotations != null) { - string paramName = method.Parameters[index].Name; - - bool firstAppearance = true; - foreach (var (ParamName, Annotation) in ann.ParameterAnnotations) { - if (ParamName == paramName && firstAppearance) { - firstAppearance = false; - parameterAnnotation = Annotation; - } else if (ParamName == paramName && !firstAppearance) { - _context.LogMessage (MessageContainer.CreateWarningMessage (_context, - $"There are duplicate parameter names for '{paramName}' inside '{method.Name}' in '{_xmlDocumentLocation}'", 2024, _xmlDocumentLocation)); - } - } - } - - return parameterAnnotation; + return _attributes.ContainsKey (provider); } - public DynamicallyAccessedMemberTypes GetPropertyAnnotation (PropertyDefinition property) + IEnumerable ProcessAttributes (XPathNavigator nav) { - return _properties.TryGetValue (property, out var ann) ? ann : DynamicallyAccessedMemberTypes.None; - } + XPathNodeIterator iterator = nav.SelectChildren ("attribute", string.Empty); + var attributes = new List (); + while (iterator.MoveNext ()) { + AssemblyDefinition assembly; + TypeDefinition attributeType; - public DynamicallyAccessedMemberTypes GetReturnParameterAnnotation (MethodDefinition method) - { - return _methods.TryGetValue (method, out var ann) ? ann.ReturnAnnotation : DynamicallyAccessedMemberTypes.None; - } + string attributeFullName = GetFullName (iterator.Current); + if (attributeFullName == String.Empty) { + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Attribute element does not contain attribute 'fullname'", 2029, _xmlDocumentLocation)); + continue; + } + string assemblyName = GetAttribute (iterator.Current, "assembly"); + if (assemblyName == String.Empty) + attributeType = _context.GetType (attributeFullName); + else { + try { + assembly = GetAssembly (_context, AssemblyNameReference.Parse (assemblyName)); + } catch (Exception) { + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Could not resolve assembly '{assemblyName}' in attribute '{attributeFullName}' specified in the '{_xmlDocumentLocation}'", 2030, _xmlDocumentLocation)); + continue; + } + attributeType = assembly.FindType (attributeFullName); + } + if (attributeType == null) { + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Attribute type '{attributeFullName}' could not be found", 2031, _xmlDocumentLocation)); + continue; + } - public DynamicallyAccessedMemberTypes GetThisParameterAnnotation (MethodDefinition method) - { - if (_methods.TryGetValue (method, out var ann) && ann.ParameterAnnotations != null) { - foreach (var (ParamName, Annotation) in ann.ParameterAnnotations) - if (ParamName == "this") - return Annotation; - } + ArrayBuilder arguments = GetAttributeChildren (iterator.Current.SelectChildren ("argument", string.Empty)); + MethodDefinition constructor = attributeType.Methods.Where (method => method.IsInstanceConstructor ()).FirstOrDefault (c => c.Parameters.Count == arguments.Count); + if (constructor == null) { + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Could not find a constructor for type '{attributeType}' that receives '{arguments.Count}' arguments as parameter", 2022, _xmlDocumentLocation)); + continue; + } + string[] xmlArguments = arguments.ToArray (); + bool recognizedArgument = true; - return DynamicallyAccessedMemberTypes.None; - } + CustomAttribute attribute = new CustomAttribute (constructor); + for (int i = 0; i < xmlArguments.Length; i++) { + object argumentValue = null; - static DynamicallyAccessedMemberTypes GetMemberTypesForDynamicallyAccessedMemberAttribute (ArrayBuilder attributes, LinkContext _context, string _xmlDocumentLocation) - { - foreach (var attribute in attributes.ToArray ()) { - if (attribute.attributeName == "System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers") { - if (attribute.arguments.Count == 0) { - _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"DynamicallyAccessedMembers attribute was specified but no argument was proportioned", - 2020, _xmlDocumentLocation)); - } else if (attribute.arguments.Count == 1) { - DynamicallyAccessedMemberTypes result; - foreach (var argument in attribute.arguments.ToArray ()) { - if (argument == string.Empty) + if (constructor.Parameters[i].ParameterType.Resolve ().IsEnum) { + foreach (var field in constructor.Parameters[i].ParameterType.Resolve ().Fields) { + if (field.IsStatic && field.Name == xmlArguments[i]) { + argumentValue = Convert.ToInt32 (field.Constant); break; - if (Enum.TryParse (argument, false, out result)) { - return result; - } else { - _context.LogMessage (MessageContainer.CreateWarningMessage (_context, - $"Could not parse argument '{argument}' specified in '{_xmlDocumentLocation}' as a DynamicallyAccessedMemberTypes", 2021, _xmlDocumentLocation)); } } + if (argumentValue == null) { + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Could not parse argument '{xmlArguments[i]}' specified in '{_xmlDocumentLocation}' as a {constructor.Parameters[i].ParameterType.FullName}", 2021, _xmlDocumentLocation)); + recognizedArgument = false; + } } else { - _context.LogMessage (MessageContainer.CreateWarningMessage (_context, - $"DynamicallyAccessedMembers attribute was specified but there is more than one argument", 2022, _xmlDocumentLocation)); + switch (constructor.Parameters[i].ParameterType.MetadataType) { + case MetadataType.String: + argumentValue = xmlArguments[i]; + break; + case MetadataType.Int32: + int result; + if (int.TryParse (xmlArguments[i], out result)) + argumentValue = result; + else { + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Argument '{xmlArguments[i]}' specified in '{_xmlDocumentLocation}' could not be transformed to the constructor parameter type", 2032, _xmlDocumentLocation)); + } + break; + default: + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Argument '{xmlArguments[i]}' specified in '{_xmlDocumentLocation}' is of unsupported type '{constructor.Parameters[i].ParameterType}'", 2020, _xmlDocumentLocation)); + recognizedArgument = false; + break; + } } + attribute.ConstructorArguments.Add (new CustomAttributeArgument (constructor.Parameters[i].ParameterType, argumentValue)); } + if (recognizedArgument) + attributes.Add (attribute); } - return DynamicallyAccessedMemberTypes.None; + return attributes; + } + + ArrayBuilder GetAttributeChildren (XPathNodeIterator iterator) + { + ArrayBuilder children = new ArrayBuilder (); + while (iterator.MoveNext ()) { + children.Add (iterator.Current.Value); + } + return children; } public void ParseXml (string document) @@ -121,48 +142,34 @@ public void ParseXml (string document) } } - private void ProcessAssemblies (LinkContext context, XPathNodeIterator iterator) + void ProcessAssemblies (LinkContext context, XPathNodeIterator iterator) { while (iterator.MoveNext ()) { - if (!ShouldProcessElement (iterator.Current)) { + if (!ShouldProcessElement (iterator.Current)) return; - } - AssemblyDefinition assembly = GetAssembly (context, GetAssemblyName (iterator.Current)); + if (GetFullName (iterator.Current) == "*") { + foreach (AssemblyDefinition assemblyIterator in context.GetAssemblies ()) { + ProcessTypes (assemblyIterator, iterator, true); + } + } else { + AssemblyDefinition assembly = GetAssembly (context, GetAssemblyName (iterator.Current)); - if (assembly == null) { - _context.LogMessage (MessageContainer.CreateWarningMessage (_context, - $"Could not resolve assembly {GetAssemblyName (iterator.Current).Name} specified in {_xmlDocumentLocation}", 2007, _xmlDocumentLocation)); - continue; + if (assembly == null) { + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Could not resolve assembly {GetAssemblyName (iterator.Current).Name} specified in {_xmlDocumentLocation}", 2007, _xmlDocumentLocation)); + continue; + } + IEnumerable attributes = ProcessAttributes (iterator.Current); + if (attributes.Count () > 0) + _attributes[assembly] = attributes; + ProcessTypes (assembly, iterator); } - ProcessTypes (assembly, iterator.Current.SelectChildren ("type", string.Empty)); - } - } - - ArrayBuilder ProcessAttributes (XPathNodeIterator iterator) - { - iterator = iterator.Current.SelectChildren ("attribute", string.Empty); - var attributes = new ArrayBuilder (); - while (iterator.MoveNext ()) { - string attributeName = GetFullName (iterator.Current); - ArrayBuilder arguments = GetAttributeChildren (iterator.Current.SelectChildren ("argument", string.Empty)); - - attributes.Add (new Attribute (attributeName, arguments)); - } - return attributes; - } - - ArrayBuilder GetAttributeChildren (XPathNodeIterator iterator) - { - ArrayBuilder childs = new ArrayBuilder (); - while (iterator.MoveNext ()) { - childs.Add (iterator.Current.Value); } - return childs; } - void ProcessTypes (AssemblyDefinition assembly, XPathNodeIterator iterator) + void ProcessTypes (AssemblyDefinition assembly, XPathNodeIterator iterator, bool searchOnAllAssemblies = false) { + iterator = iterator.Current.SelectChildren ("type", string.Empty); while (iterator.MoveNext ()) { XPathNavigator nav = iterator.Current; @@ -191,7 +198,8 @@ void ProcessTypes (AssemblyDefinition assembly, XPathNodeIterator iterator) } if (type == null) { - _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Could not resolve type '{fullname}' specified in {_xmlDocumentLocation}", 2008, _xmlDocumentLocation)); + if (!searchOnAllAssemblies) + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Could not resolve type '{fullname}' specified in {_xmlDocumentLocation}", 2008, _xmlDocumentLocation)); continue; } @@ -233,6 +241,9 @@ void ProcessTypePattern (string fullname, AssemblyDefinition assembly, XPathNavi void ProcessType (TypeDefinition type, XPathNavigator nav) { + IEnumerable attributes = ProcessAttributes (nav); + if (attributes.Count () > 0) + _attributes[type] = attributes; ProcessTypeChildren (type, nav); if (!type.HasNestedTypes) @@ -242,7 +253,7 @@ void ProcessType (TypeDefinition type, XPathNavigator nav) while (iterator.MoveNext ()) { foreach (TypeDefinition nested in type.NestedTypes) { if (nested.Name == GetAttribute (iterator.Current, "name") && ShouldProcessElement (iterator.Current)) - ProcessTypeChildren (nested, iterator.Current); + ProcessType (nested, iterator.Current); } } } @@ -253,6 +264,7 @@ void ProcessTypeChildren (TypeDefinition type, XPathNavigator nav) ProcessSelectedFields (nav, type); ProcessSelectedMethods (nav, type); ProcessSelectedProperties (nav, type); + ProcessSelectedEvents (nav, type); } } @@ -317,18 +329,36 @@ void ProcessSelectedProperties (XPathNavigator nav, TypeDefinition type) } } + void ProcessSelectedEvents (XPathNavigator nav, TypeDefinition type) + { + XPathNodeIterator events = nav.SelectChildren ("event", string.Empty); + if (events.Count == 0) + return; + while (events.MoveNext ()) { + if (!ShouldProcessElement (events.Current)) { + return; + } + + string value = GetSignature (events.Current); + if (!String.IsNullOrEmpty (value)) + ProcessEventSignature (type, value, events); + + value = GetAttribute (events.Current, "name"); + if (!String.IsNullOrEmpty (value)) + ProcessEventName (type, value, events); + } + } + void ProcessFieldSignature (TypeDefinition type, string signature, XPathNodeIterator iterator) { FieldDefinition field = GetField (type, signature); if (field == null) { - _context.LogMessage (MessageContainer.CreateWarningMessage (_context, - $"Could not find field '{signature}' in type '{type.FullName}' specified in { _xmlDocumentLocation}", 2016, _xmlDocumentLocation)); + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Could not find field '{signature}' in type '{type.FullName}' specified in { _xmlDocumentLocation}", 2016, _xmlDocumentLocation)); return; } - ArrayBuilder attributes = ProcessAttributes (iterator); - DynamicallyAccessedMemberTypes fieldAnnotation = GetMemberTypesForDynamicallyAccessedMemberAttribute (attributes, _context, _xmlDocumentLocation); - if (fieldAnnotation != DynamicallyAccessedMemberTypes.None) - _fields[field] = fieldAnnotation; + IEnumerable attributes = ProcessAttributes (iterator.Current); + if (attributes.Count () > 0) + _attributes[field] = attributes; } void ProcessFieldName (TypeDefinition type, string name, XPathNodeIterator iterator) @@ -338,10 +368,9 @@ void ProcessFieldName (TypeDefinition type, string name, XPathNodeIterator itera foreach (FieldDefinition field in type.Fields) { if (field.Name == name) { - ArrayBuilder attributes = ProcessAttributes (iterator); - DynamicallyAccessedMemberTypes fieldAnnotation = GetMemberTypesForDynamicallyAccessedMemberAttribute (attributes, _context, _xmlDocumentLocation); - if (fieldAnnotation != DynamicallyAccessedMemberTypes.None) - _fields[field] = fieldAnnotation; + IEnumerable attributes = ProcessAttributes (iterator.Current); + if (attributes.Count () > 0) + _attributes[field] = attributes; } } } @@ -362,66 +391,54 @@ void ProcessMethodSignature (TypeDefinition type, string signature, XPathNodeIte { MethodDefinition method = GetMethod (type, signature); if (method == null) { - _context.LogMessage (MessageContainer.CreateWarningMessage (_context, - $"Could not find method '{signature}' in type '{type.FullName}' specified in '{_xmlDocumentLocation}'", 2009, _xmlDocumentLocation)); + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Could not find method '{signature}' in type '{type.FullName}' specified in '{_xmlDocumentLocation}'", 2009, _xmlDocumentLocation)); return; } - ProcessMethodChildren (type, method, iterator); + ProcessMethod (method, iterator); } - void ProcessMethodChildren (TypeDefinition type, MethodDefinition method, XPathNodeIterator iterator) + void ProcessMethod (MethodDefinition method, XPathNodeIterator iterator) { - ArrayBuilder attributes = ProcessAttributes (iterator); - ArrayBuilder<(string, ArrayBuilder)> parameterAnnotations = ProcessParameters (type, - method, iterator.Current.SelectChildren ("parameter", string.Empty)); - ArrayBuilder> returnParameterAnnotations = ProcessReturnParameters (type, - method, iterator.Current.SelectChildren ("return", string.Empty)); - - var parameterAnnotation = new ArrayBuilder<(string ParamName, DynamicallyAccessedMemberTypes Annotation)> (); - DynamicallyAccessedMemberTypes returnAnnotation = 0; - - if (parameterAnnotations.Count > 0) { - foreach (var parameter in parameterAnnotations.ToArray ()) { - DynamicallyAccessedMemberTypes paramAnnotation = GetMemberTypesForDynamicallyAccessedMemberAttribute (parameter.Item2, _context, _xmlDocumentLocation); - if (paramAnnotation != 0) - parameterAnnotation.Add ((parameter.Item1, paramAnnotation)); - } - } - - if (returnParameterAnnotations.Count == 1) { - foreach (var returnparameter in returnParameterAnnotations.ToArray ()) { - DynamicallyAccessedMemberTypes returnparamAnnotation = GetMemberTypesForDynamicallyAccessedMemberAttribute (returnparameter, _context, _xmlDocumentLocation); - if (returnparamAnnotation != 0) - returnAnnotation = returnparamAnnotation; - } - } else { - _context.LogMessage (MessageContainer.CreateWarningMessage (_context, - $"There is more than one return parameter specified for '{method.Name}' in '{_xmlDocumentLocation}'", 2023, _xmlDocumentLocation)); - } - if (returnAnnotation != 0 || parameterAnnotation.Count > 0) - _methods[method] = new AnnotatedMethod (returnAnnotation, parameterAnnotation.ToArray ()); - + IEnumerable attributes = ProcessAttributes (iterator.Current); + if (attributes.Count () > 0) + _attributes[method] = attributes; + ProcessReturnParameters (method, iterator); + ProcessParameters (method, iterator); } - ArrayBuilder<(string, ArrayBuilder)> ProcessParameters (TypeDefinition type, - MethodDefinition method, XPathNodeIterator iterator) + void ProcessParameters (MethodDefinition method, XPathNodeIterator iterator) { - var methodParameters = new ArrayBuilder<(string, ArrayBuilder)> (); + iterator = iterator.Current.SelectChildren ("parameter", string.Empty); while (iterator.MoveNext ()) { - methodParameters.Add ((GetAttribute (iterator.Current, "name"), ProcessAttributes (iterator))); + IEnumerable attributes = ProcessAttributes (iterator.Current); + if (attributes.Count () > 0) { + string paramName = GetAttribute (iterator.Current, "name"); + foreach (ParameterDefinition parameter in method.Parameters) { + if (paramName == parameter.Name) { + if (_attributes.ContainsKey (parameter)) + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"There are duplicate parameter names for '{paramName}' inside '{method.Name}' in '{_xmlDocumentLocation}'", 2024, _xmlDocumentLocation)); + _attributes[parameter] = attributes; + break; + } + } + } } - return methodParameters; } - ArrayBuilder> ProcessReturnParameters (TypeDefinition type, - MethodDefinition method, XPathNodeIterator iterator) + void ProcessReturnParameters (MethodDefinition method, XPathNodeIterator iterator) { - var methodParameters = new ArrayBuilder> (); - + iterator = iterator.Current.SelectChildren ("return", string.Empty); + bool firstAppearance = true; while (iterator.MoveNext ()) { - methodParameters.Add (ProcessAttributes (iterator)); + if (firstAppearance) { + firstAppearance = false; + IEnumerable attributes = ProcessAttributes (iterator.Current); + if (attributes.Count () > 0) + _attributes[method.MethodReturnType] = attributes; + } else { + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"There is more than one return parameter specified for '{method.Name}' in '{_xmlDocumentLocation}'", 2023, _xmlDocumentLocation)); + } } - return methodParameters; } void ProcessMethodName (TypeDefinition type, string name, XPathNodeIterator iterator) @@ -431,7 +448,7 @@ void ProcessMethodName (TypeDefinition type, string name, XPathNodeIterator iter foreach (MethodDefinition method in type.Methods) if (name == method.Name) - ProcessMethodChildren (type, method, iterator); + ProcessMethod (method, iterator); } static MethodDefinition GetMethod (TypeDefinition type, string signature) @@ -478,10 +495,9 @@ void ProcessPropertySignature (TypeDefinition type, string signature, XPathNodeI { PropertyDefinition property = GetProperty (type, signature); if (property != null) { - ArrayBuilder attributes = ProcessAttributes (iterator); - DynamicallyAccessedMemberTypes propertyAnnotation = GetMemberTypesForDynamicallyAccessedMemberAttribute (attributes, _context, _xmlDocumentLocation); - if (propertyAnnotation != DynamicallyAccessedMemberTypes.None) - _properties[property] = propertyAnnotation; + IEnumerable attributes = ProcessAttributes (iterator.Current); + if (attributes.Count () > 0) + _attributes[property] = attributes; } } @@ -492,24 +508,37 @@ void ProcessPropertyName (TypeDefinition type, string name, XPathNodeIterator it foreach (PropertyDefinition property in type.Properties) { if (property.Name == name) { - ArrayBuilder attributes = ProcessAttributes (iterator); - DynamicallyAccessedMemberTypes propertyAnnotation = GetMemberTypesForDynamicallyAccessedMemberAttribute (attributes, _context, _xmlDocumentLocation); - if (propertyAnnotation != DynamicallyAccessedMemberTypes.None) - _properties[property] = propertyAnnotation; + IEnumerable attributes = ProcessAttributes (iterator.Current); + if (attributes.Count () > 0) + _attributes[property] = attributes; } } } - static PropertyDefinition GetProperty (TypeDefinition type, string signature) + void ProcessEventSignature (TypeDefinition type, string signature, XPathNodeIterator iterator) { - if (!type.HasProperties) - return null; + EventDefinition @event = GetEvent (type, signature); + if (@event == null) { + _context.LogMessage (MessageContainer.CreateWarningMessage (_context, $"Could not find event '{signature}' in type '{type.FullName}' specified in {_xmlDocumentLocation}", 2016, _xmlDocumentLocation)); + return; + } + IEnumerable attributes = ProcessAttributes (iterator.Current); + if (attributes.Count () > 0) + _attributes[@event] = attributes; + } - foreach (PropertyDefinition property in type.Properties) - if (signature == property.PropertyType.FullName + " " + property.Name) - return property; + void ProcessEventName (TypeDefinition type, string name, XPathNodeIterator iterator) + { + if (!type.HasEvents) + return; - return null; + foreach (EventDefinition @event in type.Events) { + if (@event.Name == name) { + IEnumerable attributes = ProcessAttributes (iterator.Current); + if (attributes.Count () > 0) + _attributes[@event] = attributes; + } + } } AssemblyDefinition GetAssembly (LinkContext context, AssemblyNameReference assemblyName) @@ -539,27 +568,30 @@ static string GetAttribute (XPathNavigator nav, string attribute) return nav.GetAttribute (attribute, string.Empty); } - private struct AnnotatedMethod + protected static EventDefinition GetEvent (TypeDefinition type, string signature) { - public readonly DynamicallyAccessedMemberTypes ReturnAnnotation; - public readonly (string ParamName, DynamicallyAccessedMemberTypes Annotation)[] ParameterAnnotations; + if (!type.HasEvents) + return null; - public AnnotatedMethod (DynamicallyAccessedMemberTypes returnAnnotation, - (string ParamName, DynamicallyAccessedMemberTypes Annotation)[] paramAnnotations) - => (ReturnAnnotation, ParameterAnnotations) = (returnAnnotation, paramAnnotations); + foreach (EventDefinition @event in type.Events) + if (signature == @event.EventType.FullName + " " + @event.Name) + return @event; + + return null; } - private struct Attribute + static PropertyDefinition GetProperty (TypeDefinition type, string signature) { - public string attributeName { get; set; } - public ArrayBuilder arguments { get; set; } + if (!type.HasProperties) + return null; - public Attribute (string _attributeName, ArrayBuilder _arguments) - { - attributeName = _attributeName; - arguments = _arguments; - } + foreach (PropertyDefinition property in type.Properties) + if (signature == property.PropertyType.FullName + " " + property.Name) + return property; + + return null; } + bool ShouldProcessElement (XPathNavigator nav) { var feature = GetAttribute (nav, "feature"); @@ -583,4 +615,4 @@ bool ShouldProcessElement (XPathNavigator nav) return bValue == featureSetting; } } -} +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/XmlAnnotations.cs b/test/Mono.Linker.Tests.Cases/DataFlow/XmlAnnotations.cs index e654b499ba05..8a64c0dfffb4 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/XmlAnnotations.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/XmlAnnotations.cs @@ -13,7 +13,8 @@ namespace Mono.Linker.Tests.Cases.DataFlow { [SkipKeptItemsValidation] [SetupLinkerAttributeDefinitionsFile ("XmlAnnotations.xml")] - [LogContains ("warning IL2021: Could not parse argument 'NonValidArgument' specified in", true)] + [LogContains ("XmlAnnotations.xml: warning IL2031: Attribute type 'System.DoesNotExistattribute' could not be found")] + [LogContains ("XmlAnnotations.xml: warning IL2021: Could not parse argument 'NonValidArgument' specified in *", true)] class XmlAnnotations { public static void Main () @@ -38,6 +39,7 @@ public static void Main () [UnrecognizedReflectionAccessPattern (typeof (XmlAnnotations), nameof (RequirePublicConstructors), new Type[] { typeof (Type) })] [UnrecognizedReflectionAccessPattern (typeof (XmlAnnotations), nameof (RequireNonPublicConstructors), new Type[] { typeof (Type) })] + [RecognizedReflectionAccessPattern] private void ReadFromInstanceField () { RequireDefaultConstructor (_typeWithDefaultConstructor); @@ -46,6 +48,7 @@ private void ReadFromInstanceField () } [UnrecognizedReflectionAccessPattern (typeof (XmlAnnotations), nameof (RequirePublicConstructors), new Type[] { typeof (Type) })] + [RecognizedReflectionAccessPattern] private void TwoAnnotatedParameters ( Type type, Type type2) diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/XmlAnnotations.xml b/test/Mono.Linker.Tests.Cases/DataFlow/XmlAnnotations.xml index 0fad8df94030..56c3dce01a82 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/XmlAnnotations.xml +++ b/test/Mono.Linker.Tests.Cases/DataFlow/XmlAnnotations.xml @@ -3,54 +3,54 @@ - + 0 - + DefaultConstructor - + DefaultConstructor - + PublicConstructors - + NonValidArgument - + DefaultConstructor - + DefaultConstructor - + DefaultConstructor - + DefaultConstructor