Skip to content
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
22 changes: 19 additions & 3 deletions docs/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,17 +252,17 @@ the error code. For example:

- CustomAttribute 'type' is being referenced in the code but the 'type' has been removed using the "remove" attribute tag on a type inside the LinkAttributes xml

#### `IL2046`: Presence of RequiresUnreferencedCodeAttribute on method '<method>' doesn't match overridden method 'base method'. All overridden methods must have RequiresUnreferencedCodeAttribute.
#### `IL2046`: Presence of RequiresUnreferencedCodeAttribute on method 'method' doesn't match overridden method 'base method'. All overridden methods must have RequiresUnreferencedCodeAttribute.

- All overrides of a virtual method including the base method must either have or not have the RequiresUnreferencedCodeAttribute.

#### `IL2047`: DynamicallyAccessedMemberTypes in DynamicallyAccessedMembersAttribute on <member> don't match overridden <base member>. All overriden members must have the same DynamicallyAccessedMembersAttribute usage.

- All overrides of a virtual method including the base method must have the same DynamicallyAccessedMemberAttribute usage on all it's components (return value, parameters and generic parameters).

#### `IL2048`: Internal attribute 'RemoveAttributeInstances' can only be used on a type, but is being used on 'member type' 'member'
#### `IL2048`: Internal attribute 'RemoveAttributeInstances' can only be used on a type, but is being used on 'member'

- Internal attribute 'RemoveAttributeInstances' is a special attribute that should only be used on custom attribute types and is being used on'member type' 'member'.
- Internal attribute 'RemoveAttributeInstances' is a special attribute that should only be used on custom attribute types and is being used on 'member'.

#### `IL2049`: Unrecognized internal attribute 'attribute'

Expand All @@ -271,3 +271,19 @@ the error code. For example:
#### `IL2050`: Correctness of COM interop cannot be guaranteed

- P/invoke method 'method' declares a parameter with COM marshalling. Correctness of COM interop cannot be guaranteed after trimming. Interfaces and interface members might be removed.

#### `IL2051`: Property element does not contain attribute 'name'

- An attribute element declares a property but this does not specify its name or is empty.

#### `IL2052`: Property 'propertyName' could not be found

- An attribute element has property 'propertyName' but this could not be found.

#### `IL2053`: Invalid value 'propertyValue' for property 'propertyName'

- The value 'propertyValue' used in a custom attribute annotation does not match the type of the attribute's property 'propertyName'.

#### `IL2054`: Invalid argument value 'argumentValue' for attribute 'attribute'

- The value 'argumentValue' used in a custom attribute annotation does not match the type of one of the attribute's constructor arguments. The arguments used for a custom attribute annotation should be declared in the same order the constructor uses.
101 changes: 0 additions & 101 deletions src/linker/Linker.Steps/BodySubstituterStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,107 +167,6 @@ void ProcessResources (AssemblyDefinition assembly, XPathNodeIterator iterator)
}
}

static bool TryConvertValue (string value, TypeReference target, out object result)
{
switch (target.MetadataType) {
case MetadataType.Boolean:
if (bool.TryParse (value, out bool bvalue)) {
result = bvalue ? 1 : 0;
return true;
}

goto case MetadataType.Int32;

case MetadataType.Byte:
if (!byte.TryParse (value, NumberStyles.Integer, CultureInfo.InvariantCulture, out byte byteresult))
break;

result = (int) byteresult;
return true;

case MetadataType.SByte:
if (!sbyte.TryParse (value, NumberStyles.Integer, CultureInfo.InvariantCulture, out sbyte sbyteresult))
break;

result = (int) sbyteresult;
return true;

case MetadataType.Int16:
if (!short.TryParse (value, NumberStyles.Integer, CultureInfo.InvariantCulture, out short shortresult))
break;

result = (int) shortresult;
return true;

case MetadataType.UInt16:
if (!ushort.TryParse (value, NumberStyles.Integer, CultureInfo.InvariantCulture, out ushort ushortresult))
break;

result = (int) ushortresult;
return true;

case MetadataType.Int32:
if (!int.TryParse (value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int iresult))
break;

result = iresult;
return true;

case MetadataType.UInt32:
if (!uint.TryParse (value, NumberStyles.Integer, CultureInfo.InvariantCulture, out uint uresult))
break;

result = (int) uresult;
return true;

case MetadataType.Double:
if (!double.TryParse (value, NumberStyles.Float, CultureInfo.InvariantCulture, out double dresult))
break;

result = dresult;
return true;

case MetadataType.Single:
if (!float.TryParse (value, NumberStyles.Float, CultureInfo.InvariantCulture, out float fresult))
break;

result = fresult;
return true;

case MetadataType.Int64:
if (!long.TryParse (value, NumberStyles.Integer, CultureInfo.InvariantCulture, out long lresult))
break;

result = lresult;
return true;

case MetadataType.UInt64:
if (!ulong.TryParse (value, NumberStyles.Integer, CultureInfo.InvariantCulture, out ulong ulresult))
break;

result = (long) ulresult;
return true;

case MetadataType.Char:
if (!char.TryParse (value, out char chresult))
break;

result = (int) chresult;
return true;

case MetadataType.String:
if (value is string || value == null) {
result = value;
return true;
}

break;
}

result = null;
return false;
}

static MethodDefinition FindMethod (TypeDefinition type, string signature)
{
if (!type.HasMethods)
Expand Down
200 changes: 120 additions & 80 deletions src/linker/Linker.Steps/LinkAttributesStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,103 +32,143 @@ IEnumerable<CustomAttribute> ProcessAttributes (XPathNavigator nav, ICustomAttri
if (!ShouldProcessElement (iterator.Current))
continue;

AssemblyDefinition assembly;
TypeDefinition attributeType;

string internalAttribute = GetAttribute (iterator.Current, "internal");
if (internalAttribute != String.Empty) {
if (internalAttribute == "RemoveAttributeInstances") {
if (provider.MetadataToken.TokenType == TokenType.TypeDef) {
if (!Annotations.IsMarked (provider)) {
IEnumerable<Attribute> removeAttributeInstance = new List<Attribute> { new RemoveAttributeInstancesAttribute () };
Context.CustomAttributes.AddInternalAttributes (provider, removeAttributeInstance);
}
continue;
} else {
Context.LogWarning ($"Internal attribute 'RemoveAttributeInstances' can only be used on a type, but is being used on '{nav.Name}' '{provider}'", 2048, _xmlDocumentLocation);
continue;
}
} else {
Context.LogWarning ($"Unrecognized internal attribute '{internalAttribute}'", 2049, _xmlDocumentLocation);
continue;
}
if (!string.IsNullOrEmpty (internalAttribute)) {
ProcessInternalAttribute (provider, internalAttribute);
continue;
}

string attributeFullName = GetFullName (iterator.Current);
if (attributeFullName == String.Empty) {
if (attributeFullName == string.Empty) {
Context.LogWarning ($"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.LogWarning ($"Could not resolve assembly '{assemblyName}' in attribute '{attributeFullName}' specified in the '{_xmlDocumentLocation}'", 2030, _xmlDocumentLocation);
continue;
}
attributeType = assembly.FindType (attributeFullName);
}
if (attributeType == null) {
Context.LogWarning ($"Attribute type '{attributeFullName}' could not be found", 2031, _xmlDocumentLocation);

if (!GetAttributeType (iterator, attributeFullName, out TypeDefinition attributeType))
continue;

CustomAttribute customAttribute = CreateCustomAttribute (iterator, attributeType);
if (customAttribute != null)
attributes.Add (customAttribute);
}

return attributes;
}

CustomAttribute CreateCustomAttribute (XPathNodeIterator iterator, TypeDefinition attributeType)
{
string[] attributeArguments = (GetAttributeChildren (iterator.Current.SelectChildren ("argument", string.Empty))).ToArray ();
MethodDefinition constructor = attributeType.Methods.Where (method => method.IsInstanceConstructor ()).FirstOrDefault (c => c.Parameters.Count == attributeArguments.Length);
if (constructor == null) {
Context.LogWarning ($"Could not find a constructor for type '{attributeType}' that receives '{attributeArguments.Length}' arguments as parameter", 2022, _xmlDocumentLocation);
return null;
}

CustomAttribute customAttribute = new CustomAttribute (constructor);
var arguments = ProcessAttributeArguments (constructor, attributeArguments);
if (arguments != null)
foreach (var argument in arguments)
customAttribute.ConstructorArguments.Add (argument);

var properties = ProcessAttributeProperties (iterator.Current.SelectChildren ("property", string.Empty), attributeType);
if (properties != null)
foreach (var property in properties)
customAttribute.Properties.Add (property);

return customAttribute;
}

List<CustomAttributeNamedArgument> ProcessAttributeProperties (XPathNodeIterator iterator, TypeDefinition attributeType)
{
List<CustomAttributeNamedArgument> attributeProperties = new List<CustomAttributeNamedArgument> ();
while (iterator.MoveNext ()) {
string propertyName = GetName (iterator.Current);
if (propertyName == string.Empty) {
Context.LogWarning ($"Property element does not contain attribute 'name'", 2051, _xmlDocumentLocation);
continue;
}

ArrayBuilder<string> 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.LogWarning ($"Could not find a constructor for type '{attributeType}' that receives '{arguments.Count}' arguments as parameter", 2022, _xmlDocumentLocation);
PropertyDefinition property = attributeType.Properties.Where (prop => prop.Name == propertyName).FirstOrDefault ();
if (property == null) {
Context.LogWarning ($"Property '{propertyName}' could not be found", 2052, _xmlDocumentLocation);
continue;
}
string[] xmlArguments = arguments.ToArray ();
bool recognizedArgument = true;

CustomAttribute attribute = new CustomAttribute (constructor);
if (xmlArguments == null) {
attributes.Add (attribute);
var propertyValue = iterator.Current.Value;
if (!TryConvertValue (propertyValue, property.PropertyType, out object value)) {
Context.LogWarning ($"Invalid value '{propertyValue}' for property '{propertyName}'", 2053, _xmlDocumentLocation);
continue;
}
for (int i = 0; i < xmlArguments.Length; i++) {
object argumentValue = null;

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 (argumentValue == null) {
Context.LogWarning ($"Could not parse argument '{xmlArguments[i]}' specified in '{_xmlDocumentLocation}' as a {constructor.Parameters[i].ParameterType.FullName}", 2021, _xmlDocumentLocation);
recognizedArgument = false;
}
} else {
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.LogWarning ($"Argument '{xmlArguments[i]}' specified in '{_xmlDocumentLocation}' could not be transformed to the constructor parameter type", 2032, _xmlDocumentLocation);
}
break;
default:
Context.LogWarning ($"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));

attributeProperties.Add (new CustomAttributeNamedArgument (property.Name,
new CustomAttributeArgument (property.PropertyType, value)));
}

return attributeProperties;
}

List<CustomAttributeArgument> ProcessAttributeArguments (MethodDefinition attributeConstructor, string[] arguments)
{
if (arguments == null)
return null;

List<CustomAttributeArgument> attributeArguments = new List<CustomAttributeArgument> ();
for (int i = 0; i < arguments.Length; i++) {
object argValue;
TypeDefinition parameterType = attributeConstructor.Parameters[i].ParameterType.Resolve ();
if (!TryConvertValue (arguments[i], parameterType, out argValue)) {
Context.LogWarning ($"Invalid argument value '{arguments[i]}' for attribute '{attributeConstructor.DeclaringType.GetDisplayName ()}'", 2054, _xmlDocumentLocation);
return null;
}
if (recognizedArgument)
attributes.Add (attribute);

attributeArguments.Add (new CustomAttributeArgument (parameterType, argValue));
}
return attributes;

return attributeArguments;
}

void ProcessInternalAttribute (ICustomAttributeProvider provider, string internalAttribute)
{
if (internalAttribute != "RemoveAttributeInstances") {
Context.LogWarning ($"Unrecognized internal attribute '{internalAttribute}'", 2049, _xmlDocumentLocation);
return;
}

if (provider.MetadataToken.TokenType != TokenType.TypeDef) {
Context.LogWarning ($"Internal attribute 'RemoveAttributeInstances' can only be used on a type, but is being used on '{provider}'", 2048, _xmlDocumentLocation);
return;
}

if (!Annotations.IsMarked (provider)) {
IEnumerable<Attribute> removeAttributeInstance = new List<Attribute> { new RemoveAttributeInstancesAttribute () };
Context.CustomAttributes.AddInternalAttributes (provider, removeAttributeInstance);
}
}

bool GetAttributeType (XPathNodeIterator iterator, string attributeFullName, out TypeDefinition attributeType)
{
string assemblyName = GetAttribute (iterator.Current, "assembly");
if (string.IsNullOrEmpty (assemblyName)) {
attributeType = Context.GetType (attributeFullName);
} else {
AssemblyDefinition assembly;
try {
assembly = GetAssembly (Context, AssemblyNameReference.Parse (assemblyName));
} catch (Exception) {
Context.LogWarning ($"Could not resolve assembly '{assemblyName}' in attribute '{attributeFullName}' specified in '{_xmlDocumentLocation}'", 2030, _xmlDocumentLocation);
attributeType = default;
return false;
}

attributeType = assembly.FindType (attributeFullName);
}

if (attributeType == null) {
Context.LogWarning ($"Attribute type '{attributeFullName}' could not be found", 2031, _xmlDocumentLocation);
return false;
}

return true;
}

ArrayBuilder<string> GetAttributeChildren (XPathNodeIterator iterator)
Expand Down
Loading