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
26 changes: 23 additions & 3 deletions docs/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@ error and warning codes.

- The linker found a call to a method which is annotated with 'RequiresUnreferencedCodeAttribute' which can break functionality of a trimmed application.

#### `IL2027`: Attribute 'attribute' should only be used once on 'method'.
#### `IL2027`: Attribute 'attribute' should only be used once on 'member'.

- The linker found multiple instances of attribute 'attribute' on 'method'. This attribute is only allowed to have one instance, linker will only use the fist instance and ignore the rest.
- The linker found multiple instances of attribute 'attribute' on 'member'. This attribute is only allowed to have one instance, linker will only use the fist instance and ignore the rest.

#### `IL2028`: Attribute 'attribute' on 'method' doesn't have a required constructor argument.

Expand All @@ -185,4 +185,24 @@ error and warning codes.

#### `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.
- 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.

#### `IL2033`: Deprecated PreserveDependencyAttribute on 'member'. Use DynamicDependencyAttribute instead.

- PreserveDependencyAttribute was an internal attribute that was never officially supported. Instead, use the similar DynamicDependencyAttribute.

#### `IL2034`: Invalid DynamicDependencyAttribute on 'member'

- The input contains an invalid use of DynamicDependencyAttribute. Ensure that you are using one of the officially supported constructors.

#### `IL2035`: Unresolved assembly 'assemblyName' in DynamicDependencyAttribute on 'member'

- The assembly string given in a DynamicDependencyAttribute constructor could not be resolved. Ensure that the argument specifies a valid asembly name, and that the assembly is available to the linker.

#### `IL2036`: Unresolved type 'typeName' in DynamicDependencyAttribute on 'member'

- The type in a DynamicDependencyAttribute constructor could not be resolved. Ensure that the argument specifies a valid type name or type reference, that the type exists in the specified assembly, and that the assembly is available to the linker.

### `IL2037`: No members were resolved for 'memberSignature/memberTypes' in DynamicDependencyAttribute on 'member'

- The member signature or DynamicallyAccessedMemberTypes in a DynamicDependencyAttribute constructor did not resolve to any members on the type. If you using a signature, ensure that it refers to an existing member, and that it uses the format defined at https://github.com/dotnet/csharplang/blob/master/spec/documentation-comments.md#id-string-format. If using DynamicallyAccessedMemberTypes, ensure that the type contains members of the specified member types.
98 changes: 98 additions & 0 deletions src/linker/Linker.Steps/DynamicDependencyLookupStep.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// 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;
using System.Diagnostics;
using Mono.Cecil;
using System.Diagnostics.CodeAnalysis;

#nullable enable

namespace Mono.Linker.Steps
{
public class DynamicDependencyLookupStep : LoadReferencesStep
{
protected override void ProcessAssembly (AssemblyDefinition assembly)
{
var module = assembly.MainModule;

foreach (var type in module.Types) {
ProcessType (type);
}
}

void ProcessType (TypeDefinition type)
{
if (type.HasMethods) {
foreach (var method in type.GetMethods ()) {
var methodDefinition = method.Resolve ();
if (methodDefinition?.HasCustomAttributes != true)
continue;

ProcessDynamicDependencyAttributes (methodDefinition);
}
}

if (type.HasFields) {
foreach (var field in type.Fields) {
var fieldDefinition = field.Resolve ();
if (fieldDefinition?.HasCustomAttributes != true)
continue;

ProcessDynamicDependencyAttributes (fieldDefinition);
}
}

if (type.HasNestedTypes) {
foreach (var nestedType in type.NestedTypes) {
ProcessType (nestedType);
}
}
}

void ProcessDynamicDependencyAttributes (IMemberDefinition member)
{
Debug.Assert (member is MethodDefinition || member is FieldDefinition);

foreach (var ca in member.CustomAttributes) {
if (!IsPreserveDependencyAttribute (ca.AttributeType))
continue;
#if FEATURE_ILLINK
Context.LogWarning ($"Deprecated PreserveDependencyAttribute on '{member}'. Use DynamicDependencyAttribute instead.", 2033, MessageOrigin.TryGetOrigin (member));
#endif
if (ca.ConstructorArguments.Count != 3)
continue;

if (!(ca.ConstructorArguments[2].Value is string assemblyName))
continue;

var assembly = Context.Resolve (new AssemblyNameReference (assemblyName, new Version ()));
if (assembly == null)
continue;
ProcessReferences (assembly);
}

var dynamicDependencies = Context.Annotations.GetLinkerAttributes<DynamicDependency> (member);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you should use GetLinkerAttributes here, you already iterate all attributes above so this is doing the same loop and also building cache for member which might not be reached at all.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally optimized this a little more, but removed it based on this discussion: #1215 (comment). If you think it is worth it, I could add back the check for this callsite, and only call GetLinkerAttributes if the previous loop found any DynamicDependencyAttributes.

Debug.Assert (dynamicDependencies != null);

foreach (var dynamicDependency in dynamicDependencies) {
if (dynamicDependency.AssemblyName == null)
continue;

var assembly = Context.Resolve (new AssemblyNameReference (dynamicDependency.AssemblyName, new Version ()));
if (assembly == null) {
Context.LogWarning ($"Unresolved assembly '{dynamicDependency.AssemblyName}' in DynamicDependencyAttribute on '{member}'", 2035, MessageOrigin.TryGetOrigin (member));
continue;
}

ProcessReferences (assembly);
}
}

public static bool IsPreserveDependencyAttribute (TypeReference tr)
{
return tr.Name == "PreserveDependencyAttribute" && tr.Namespace == "System.Runtime.CompilerServices";
}
}
}
158 changes: 121 additions & 37 deletions src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.RegularExpressions;
using Mono.Cecil;
Expand All @@ -54,7 +55,10 @@ public partial class MarkStep : IStep
#if DEBUG
static readonly DependencyKind[] _entireTypeReasons = new DependencyKind[] {
DependencyKind.NestedType,
#if !FEATURE_ILLINK
DependencyKind.PreservedDependency,
#endif
DependencyKind.DynamicDependency,
DependencyKind.TypeInAssembly,
DependencyKind.AccessedViaReflection,
DependencyKind.BaseType,
Expand All @@ -71,7 +75,10 @@ public partial class MarkStep : IStep
DependencyKind.InteropMethodDependency,
DependencyKind.Ldtoken,
DependencyKind.MemberOfType,
#if !FEATURE_ILLINK
DependencyKind.PreservedDependency,
#endif
DependencyKind.DynamicDependency,
DependencyKind.ReferencedBySpecialAttribute,
DependencyKind.TypePreserve,
};
Expand All @@ -87,6 +94,7 @@ public partial class MarkStep : IStep
DependencyKind.CustomAttributeArgumentValue,
DependencyKind.DeclaringType,
DependencyKind.DeclaringTypeOfCalledMethod,
DependencyKind.DynamicDependency,
DependencyKind.ElementType,
DependencyKind.FieldType,
DependencyKind.GenericArgumentType,
Expand Down Expand Up @@ -130,7 +138,10 @@ public partial class MarkStep : IStep
DependencyKind.Newobj,
DependencyKind.Override,
DependencyKind.OverrideOnInstantiatedType,
#if !FEATURE_ILLINK
DependencyKind.PreservedDependency,
#endif
DependencyKind.DynamicDependency,
DependencyKind.PreservedMethod,
DependencyKind.ReferencedBySpecialAttribute,
DependencyKind.SerializationMethodForType,
Expand Down Expand Up @@ -367,7 +378,7 @@ void ProcessQueue ()
} catch (Exception e) when (!(e is LinkerFatalErrorException)) {
throw new LinkerFatalErrorException (
MessageContainer.CreateErrorMessage ($"Error processing method '{method.FullName}' in assembly '{method.Module.Name}'", 1005,
origin: MessageOrigin.TryGetOrigin (method, 0)), e);
origin: MessageOrigin.TryGetOrigin (method)), e);
}
}
}
Expand Down Expand Up @@ -522,9 +533,8 @@ void MarkCustomAttributes (ICustomAttributeProvider provider, in DependencyInfo
bool markOnUse = _context.KeepUsedAttributeTypesOnly && Annotations.GetAction (GetAssemblyFromCustomAttributeProvider (provider)) == AssemblyAction.Link;

foreach (CustomAttribute ca in provider.CustomAttributes) {
if (ProcessLinkerSpecialAttribute (ca, provider, reason)) {
if (ProcessLinkerSpecialAttribute (ca, provider, reason))
continue;
}

if (markOnUse) {
_lateMarkedAttributes.Enqueue ((new AttributeProviderPair (ca, provider), reason));
Expand All @@ -534,24 +544,120 @@ void MarkCustomAttributes (ICustomAttributeProvider provider, in DependencyInfo
MarkCustomAttribute (ca, reason);
MarkSpecialCustomAttributeDependencies (ca, provider);
}

if (!(provider is MethodDefinition || provider is FieldDefinition))
return;

var dynamicDependencies = _context.Annotations.GetLinkerAttributes<DynamicDependency> ((IMemberDefinition) provider);

foreach (var dynamicDependency in dynamicDependencies)
MarkDynamicDependency (dynamicDependency, (MemberReference) provider);
}

protected virtual bool ProcessLinkerSpecialAttribute (CustomAttribute ca, ICustomAttributeProvider provider, in DependencyInfo reason)
{
if (IsUserDependencyMarker (ca.AttributeType) && provider is MemberReference mr) {
var isPreserveDependency = IsUserDependencyMarker (ca.AttributeType);
var isDynamicDependency = ca.AttributeType.IsTypeOf<DynamicDependencyAttribute> ();

if (!((isPreserveDependency || isDynamicDependency) && provider is MemberReference mr))
return false;

if (isPreserveDependency)
MarkUserDependency (mr, ca);

if (_context.KeepDependencyAttributes || Annotations.GetAction (mr.Module.Assembly) != AssemblyAction.Link) {
MarkCustomAttribute (ca, reason);
} else {
// Record the custom attribute so that it has a reason, without actually marking it.
Tracer.AddDirectDependency (ca, reason, marked: false);
if (_context.KeepDependencyAttributes || Annotations.GetAction (mr.Module.Assembly) != AssemblyAction.Link) {
MarkCustomAttribute (ca, reason);
} else {
// Record the custom attribute so that it has a reason, without actually marking it.
Tracer.AddDirectDependency (ca, reason, marked: false);
}

return true;
}

void MarkDynamicDependency (DynamicDependency dynamicDependency, MemberReference context)
{
Debug.Assert (context is MethodDefinition || context is FieldDefinition);
AssemblyDefinition assembly;
if (dynamicDependency.AssemblyName != null) {
assembly = _context.GetLoadedAssembly (dynamicDependency.AssemblyName);
if (assembly == null) {
_context.LogWarning ($"Unresolved assembly '{dynamicDependency.AssemblyName}' in DynamicDependencyAttribute on '{context}'", 2035, MessageOrigin.TryGetOrigin (context.Resolve ()));
return;
}
} else {
assembly = context.Module.Assembly;
Debug.Assert (assembly != null);
}

return true;
TypeDefinition type;
if (dynamicDependency.TypeName is string typeName) {
type = DocumentationSignatureParser.GetTypeByDocumentationSignature (assembly, typeName);
if (type == null) {
_context.LogWarning ($"Unresolved type '{typeName}' in DynamicDependencyAttribute on '{context}'", 2036, MessageOrigin.TryGetOrigin (context.Resolve ()));
return;
}
} else if (dynamicDependency.Type is TypeReference typeReference) {
type = typeReference.Resolve ();
if (type == null) {
_context.LogWarning ($"Unresolved type '{typeReference}' in DynamicDependencyAtribute on '{context}'", 2036, MessageOrigin.TryGetOrigin (context.Resolve ()));
return;
}
} else {
type = context.DeclaringType.Resolve ();
if (type == null) {
_context.LogWarning ($"Unresolved type '{context.DeclaringType}' in DynamicDependencyAttribute on '{context}'", 2036, MessageOrigin.TryGetOrigin (context.Resolve ()));
return;
}
}

return false;
IEnumerable<IMemberDefinition> members;
if (dynamicDependency.MemberSignature is string memberSignature) {
members = DocumentationSignatureParser.GetMembersByDocumentationSignature (type, memberSignature);
if (!members.Any ()) {
_context.LogWarning ($"No members were resolved for '{memberSignature}' in DynamicDependencyAttribute on '{context}'", 2037, MessageOrigin.TryGetOrigin (context.Resolve ()));
return;
}
} else {
var memberTypes = dynamicDependency.MemberTypes;
members = DynamicallyAccessedMembersBinder.GetDynamicallyAccessedMembers (type, memberTypes);
if (!members.Any ()) {
_context.LogWarning ($"No members were resolved for '{memberTypes}' in DynamicDependencyAttribute on '{context}'", 2037, MessageOrigin.TryGetOrigin (context.Resolve ()));
return;
}
}

MarkMembers (type, members, new DependencyInfo (DependencyKind.DynamicDependency, dynamicDependency.OriginalAttribute));
}

void MarkMembers (TypeDefinition typeDefinition, IEnumerable<IMemberDefinition> members, DependencyInfo reason)
{
foreach (var member in members) {
switch (member) {
case TypeDefinition type:
MarkType (type, reason);
break;
case MethodDefinition method:
MarkMethod (method, reason);
break;
case FieldDefinition field:
MarkField (field, reason);
break;
case PropertyDefinition property:
MarkProperty (property, reason);
MarkMethodIfNotNull (property.GetMethod, reason);
MarkMethodIfNotNull (property.SetMethod, reason);
MarkMethodsIf (property.OtherMethods, m => true, reason);
break;
case EventDefinition @event:
MarkEvent (@event, reason);
MarkMethodsIf (@event.OtherMethods, m => true, reason);
break;
case null:
MarkEntireType (typeDefinition, includeBaseTypes: true, reason);
break;
}
}
}

protected static AssemblyDefinition GetAssemblyFromCustomAttributeProvider (ICustomAttributeProvider provider)
Expand All @@ -571,27 +677,13 @@ protected static AssemblyDefinition GetAssemblyFromCustomAttributeProvider (ICus

protected virtual bool IsUserDependencyMarker (TypeReference type)
{
return PreserveDependencyLookupStep.IsPreserveDependencyAttribute (type);
return DynamicDependencyLookupStep.IsPreserveDependencyAttribute (type);
}

protected virtual void MarkUserDependency (MemberReference context, CustomAttribute ca)
{
if (ca.HasProperties && ca.Properties[0].Name == "Condition") {
var condition = ca.Properties[0].Argument.Value as string;
switch (condition) {
case "":
case null:
break;
case "DEBUG":
if (!_context.KeepMembersForDebugger)
return;

break;
default:
// Don't have yet a way to match the general condition so everything is excluded
return;
}
}
if (!DynamicDependency.ShouldProcess (_context, ca))
return;

AssemblyDefinition assembly;
var args = ca.ConstructorArguments;
Expand Down Expand Up @@ -2284,7 +2376,7 @@ void MarkNewCodeDependencies (MethodDefinition method)
var baseType = method.DeclaringType.BaseType.Resolve ();
if (!MarkDefaultConstructor (baseType, new DependencyInfo (DependencyKind.BaseDefaultCtorForStubbedMethod, method)))
throw new LinkerFatalErrorException (MessageContainer.CreateErrorMessage ($"Cannot stub constructor on '{method.DeclaringType}' when base type does not have default constructor",
1006, origin: MessageOrigin.TryGetOrigin (method, 0)));
1006, origin: MessageOrigin.TryGetOrigin (method)));

break;

Expand Down Expand Up @@ -2608,11 +2700,6 @@ protected virtual void MarkInterfaceImplementation (InterfaceImplementation ifac
Annotations.Mark (iface, new DependencyInfo (DependencyKind.InterfaceImplementationOnType, type));
}

bool HasManuallyTrackedDependency (MethodBody methodBody)
{
return PreserveDependencyLookupStep.HasPreserveDependencyAttribute (methodBody.Method);
}

//
// Extension point for reflection logic handling customization
//
Expand All @@ -2626,9 +2713,6 @@ protected virtual bool ProcessReflectionDependency (MethodBody body, Instruction
//
protected virtual void MarkReflectionLikeDependencies (MethodBody body, bool requiresReflectionMethodBodyScanner)
{
if (HasManuallyTrackedDependency (body))
return;

if (requiresReflectionMethodBodyScanner) {
var scanner = new ReflectionMethodBodyScanner (_context, this, _flowAnnotations);
scanner.ScanAndProcessReturnValue (body);
Expand Down
Loading