Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSven Boemer <sbomer@gmail.com>2020-03-27 10:44:24 +0300
committerGitHub <noreply@github.com>2020-03-27 10:44:24 +0300
commit83a48f3ca59d9587669712291119ef730a6551d4 (patch)
tree55e52704dee9ec5300c5b9eae539ad93f9a3f17c
parent42b986633a3b78ea547a3f4ac36c259ff05566c2 (diff)
Add tracing reasons (#1012)
* Add tracing reasons This supplies a "reason" every time that we mark a piece of IL. A "reason" is an instance of a new type, DependencyInfo, which tracks a single source object and a flag (DependencyKind) indicating what kind of dependency this is. Annotations.Mark now expects a DependencyInfo, and the dependency reporting interface is extended to report these reasons. The mark sites within MarkStep and other steps have been updated with specific reasons, and the corresponding Mark* methods have been modified to take a reason that is passed through as many layers of the marking logic as necessary until it reaches a call to Annotations.Mark. The marking logic has been modified to more frequently allow marking Cecil objects multiple times for different reasons - while still ensuring that any expensive operations are done only once per object (usually under an IsProcessed check, rather than an IsMarked check). In a few cases, the marking logic will process a Cecil object and mark its dependencies, without marking the original Cecil object. This can happen, for example, for custom attributes in some cases, or for generic instantiations (since we only mark definitions). In these cases, there are extra calls that report these intermediate dependencies to the tracing interface without actually marking them. Some of the DependencyKinds (for example AlreadyMarked) are only used for internal tracking, and will not be reported to the dependency tracing interface. The set of DependencyKinds has been selected based on what seems like an intuitive reason to report for each object, though sometimes the choice is not obvious since the reasons are restricted to supplying a single source object. Some interesting cases where potentially non-obvious choices were made follow: - When marking overrides on instantiated type, we choose as the reason the instantiated type, rather than the base method (unless the override removal optimization is disabled, in which case instantiations are not taken into account when marking overrides - so we blame the base method). - When marking static constructors, we keep track of cases where they are marked due to a field access or method call from another method, and report these separately. For cases where we track enough information to do so, this results in a more direct dependency chain like: "method A triggers static constructor B", rather than "method A access field B, field B has declaring type C, declaring type C has static constructor D". - When marking custom attributes on assemblies or modules, we record a dependency from the assembly/module to the attribute, even though the assembly/module may not have been marked or even recorded. Since the attributes and their dependencies are kept regardless of what happens to the assembly, these are conceptually entry points to the analysis. This also includes a change that moves the reflection reporter from LinkContext to Tracer, and extended to support multiple sinks, similar to the dependency reporting. I have also added assertions in a few places where I think it makes sense to check invariants when we are running tests in Debug mode. * Make DependencyKind arrays static, add more comments * Number the DependencyKind enum * Pass DependencyInfo as in parameter where it makes sense The methods that reuse the "reason" variable were not modified. * Use static fields for well-known DependencyInfo without source Instead of a ctor that just sets the source to null. * Undo ReflectionPatternRecorder changes Move the reflection pattern recorder back to LinkContext * Remove stack-based dependency recording * Add DependencyKind.Custom * Workaround for ReducedTracing * Use -1 for DependencyKind.Custom * Add back obsolete Mark method for monolinker build
-rw-r--r--src/linker/Linker.Steps/ClearInitLocals.cs2
-rw-r--r--src/linker/Linker.Steps/MarkStep.cs932
-rw-r--r--src/linker/Linker.Steps/OutputStep.cs2
-rw-r--r--src/linker/Linker.Steps/ReflectionBlockedStep.cs2
-rw-r--r--src/linker/Linker.Steps/ResolveFromAssemblyStep.cs25
-rw-r--r--src/linker/Linker.Steps/ResolveFromXmlStep.cs39
-rw-r--r--src/linker/Linker/Annotations.cs18
-rw-r--r--src/linker/Linker/DependencyInfo.cs144
-rw-r--r--src/linker/Linker/DirectoryAssemblyResolver.cs1
-rw-r--r--src/linker/Linker/Driver.cs2
-rw-r--r--src/linker/Linker/IDependencyRecorder.cs11
-rw-r--r--src/linker/Linker/LinkContext.cs2
-rw-r--r--src/linker/Linker/MarkingHelpers.cs6
-rw-r--r--src/linker/Linker/MethodBodyScanner.cs8
-rw-r--r--src/linker/Linker/Pipeline.cs2
-rw-r--r--src/linker/Linker/Tracer.cs39
-rw-r--r--src/linker/Linker/XmlDependencyRecorder.cs24
-rw-r--r--test/Mono.Linker.Tests/TestCasesRunner/TestDependencyRecorder.cs9
18 files changed, 783 insertions, 485 deletions
diff --git a/src/linker/Linker.Steps/ClearInitLocals.cs b/src/linker/Linker.Steps/ClearInitLocals.cs
index d858c5d98..53a999f85 100644
--- a/src/linker/Linker.Steps/ClearInitLocals.cs
+++ b/src/linker/Linker.Steps/ClearInitLocals.cs
@@ -54,7 +54,7 @@ namespace Mono.Linker.Steps
}
if (changed && (Annotations.GetAction (assembly) == AssemblyAction.Copy))
- Annotations.SetAction (assembly, AssemblyAction.Save);
+ Annotations.SetAction (assembly, AssemblyAction.Save);
}
}
}
diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs
index 608505b60..2a9baffd9 100644
--- a/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/linker/Linker.Steps/MarkStep.cs
@@ -42,19 +42,108 @@ namespace Mono.Linker.Steps {
public partial class MarkStep : IStep {
protected LinkContext _context;
- protected Queue<MethodDefinition> _methods;
+ protected Queue<(MethodDefinition, DependencyInfo)> _methods;
protected List<MethodDefinition> _virtual_methods;
protected Queue<AttributeProviderPair> _assemblyLevelAttributes;
- protected Queue<AttributeProviderPair> _lateMarkedAttributes;
+ protected Queue<(AttributeProviderPair, DependencyInfo)> _lateMarkedAttributes;
protected List<TypeDefinition> _typesWithInterfaces;
protected List<MethodBody> _unreachableBodies;
+#if DEBUG
+ static DependencyKind [] _entireTypeReasons = new DependencyKind [] {
+ DependencyKind.NestedType,
+ DependencyKind.PreservedDependency,
+ DependencyKind.TypeInAssembly,
+ };
+
+ static DependencyKind [] _fieldReasons = new DependencyKind [] {
+ DependencyKind.AccessedViaReflection,
+ DependencyKind.AlreadyMarked,
+ DependencyKind.Custom,
+ DependencyKind.CustomAttributeField,
+ DependencyKind.EventSourceProviderField,
+ DependencyKind.FieldAccess,
+ DependencyKind.FieldOnGenericInstance,
+ DependencyKind.InteropMethodDependency,
+ DependencyKind.Ldtoken,
+ DependencyKind.MemberOfType,
+ DependencyKind.PreservedDependency,
+ DependencyKind.ReferencedBySpecialAttribute,
+ DependencyKind.TypePreserve,
+ };
+
+ static DependencyKind [] _typeReasons = new DependencyKind [] {
+ DependencyKind.AccessedViaReflection,
+ DependencyKind.AlreadyMarked,
+ DependencyKind.AttributeType,
+ DependencyKind.BaseType,
+ DependencyKind.CatchType,
+ DependencyKind.Custom,
+ DependencyKind.CustomAttributeArgumentType,
+ DependencyKind.CustomAttributeArgumentValue,
+ DependencyKind.DeclaringType,
+ DependencyKind.DeclaringTypeOfCalledMethod,
+ DependencyKind.ElementType,
+ DependencyKind.FieldType,
+ DependencyKind.GenericArgumentType,
+ DependencyKind.GenericParameterConstraintType,
+ DependencyKind.InterfaceImplementationInterfaceType,
+ DependencyKind.Ldtoken,
+ DependencyKind.ModifierType,
+ DependencyKind.InstructionTypeRef,
+ DependencyKind.ParameterType,
+ DependencyKind.ReferencedBySpecialAttribute,
+ DependencyKind.ReturnType,
+ DependencyKind.UnreachableBodyRequirement,
+ DependencyKind.VariableType,
+ };
+
+ static DependencyKind [] _methodReasons = new DependencyKind [] {
+ DependencyKind.AccessedViaReflection,
+ DependencyKind.AlreadyMarked,
+ DependencyKind.AttributeConstructor,
+ DependencyKind.AttributeProperty,
+ DependencyKind.BaseDefaultCtorForStubbedMethod,
+ DependencyKind.BaseMethod,
+ DependencyKind.CctorForType,
+ DependencyKind.CctorForField,
+ DependencyKind.Custom,
+ DependencyKind.DefaultCtorForNewConstrainedGenericArgument,
+ DependencyKind.DirectCall,
+ DependencyKind.ElementMethod,
+ DependencyKind.EventMethod,
+ DependencyKind.EventOfEventMethod,
+ DependencyKind.InteropMethodDependency,
+ DependencyKind.KeptForSpecialAttribute,
+ DependencyKind.Ldftn,
+ DependencyKind.Ldtoken,
+ DependencyKind.Ldvirtftn,
+ DependencyKind.MemberOfType,
+ DependencyKind.MethodForInstantiatedType,
+ DependencyKind.MethodForSpecialType,
+ DependencyKind.MethodImplOverride,
+ DependencyKind.MethodOnGenericInstance,
+ DependencyKind.Newobj,
+ DependencyKind.Override,
+ DependencyKind.OverrideOnInstantiatedType,
+ DependencyKind.PreservedDependency,
+ DependencyKind.ReferencedBySpecialAttribute,
+ DependencyKind.SerializationMethodForType,
+ DependencyKind.TriggersCctorForCalledMethod,
+ DependencyKind.TriggersCctorThroughFieldAccess,
+ DependencyKind.TypePreserve,
+ DependencyKind.UnreachableBodyRequirement,
+ DependencyKind.VirtualCall,
+ DependencyKind.VirtualNeededDueToPreservedScope,
+ };
+#endif
+
public MarkStep ()
{
- _methods = new Queue<MethodDefinition> ();
+ _methods = new Queue<(MethodDefinition, DependencyInfo)> ();
_virtual_methods = new List<MethodDefinition> ();
_assemblyLevelAttributes = new Queue<AttributeProviderPair> ();
- _lateMarkedAttributes = new Queue<AttributeProviderPair> ();
+ _lateMarkedAttributes = new Queue<(AttributeProviderPair, DependencyInfo)> ();
_typesWithInterfaces = new List<TypeDefinition> ();
_unreachableBodies = new List<MethodBody> ();
}
@@ -79,25 +168,22 @@ namespace Mono.Linker.Steps {
protected virtual void InitializeAssembly (AssemblyDefinition assembly)
{
- Tracer.Push (assembly);
- try {
- switch (_context.Annotations.GetAction (assembly)) {
- case AssemblyAction.Copy:
- case AssemblyAction.Save:
- MarkEntireAssembly (assembly);
- break;
- case AssemblyAction.Link:
- case AssemblyAction.AddBypassNGen:
- case AssemblyAction.AddBypassNGenUsed:
- MarkAssembly (assembly);
+ var action = _context.Annotations.GetAction (assembly);
+ switch (action) {
+ case AssemblyAction.Copy:
+ case AssemblyAction.Save:
+ Tracer.AddDirectDependency (assembly, new DependencyInfo (DependencyKind.AssemblyAction, action), marked: false);
+ MarkEntireAssembly (assembly);
+ break;
+ case AssemblyAction.Link:
+ case AssemblyAction.AddBypassNGen:
+ case AssemblyAction.AddBypassNGenUsed:
+ MarkAssembly (assembly);
- foreach (TypeDefinition type in assembly.MainModule.Types)
- InitializeType (type);
+ foreach (TypeDefinition type in assembly.MainModule.Types)
+ InitializeType (type);
- break;
- }
- } finally {
- Tracer.Pop ();
+ break;
}
}
@@ -118,7 +204,10 @@ namespace Mono.Linker.Steps {
if (!Annotations.IsMarked (type))
return;
- MarkType (type);
+ // We may get here for a type marked by an earlier step, or by a type
+ // marked indirectly as the result of some other InitializeType call.
+ // Just track this as already marked, and don't include a new source.
+ MarkType (type, DependencyInfo.AlreadyMarked);
if (type.HasFields)
InitializeFields (type);
@@ -147,30 +236,35 @@ namespace Mono.Linker.Steps {
{
foreach (FieldDefinition field in type.Fields)
if (Annotations.IsMarked (field))
- MarkField (field);
+ MarkField (field, DependencyInfo.AlreadyMarked);
}
void InitializeMethods (Collection<MethodDefinition> methods)
{
foreach (MethodDefinition method in methods)
if (Annotations.IsMarked (method))
- EnqueueMethod (method);
+ EnqueueMethod (method, DependencyInfo.AlreadyMarked);
}
- void MarkEntireType (TypeDefinition type)
+ void MarkEntireType (TypeDefinition type, in DependencyInfo reason)
{
+#if DEBUG
+ if (!_entireTypeReasons.Contains (reason.Kind))
+ throw new ArgumentOutOfRangeException ($"Internal error: unsupported type dependency {reason.Kind}");
+#endif
+
if (type.HasNestedTypes) {
foreach (TypeDefinition nested in type.NestedTypes)
- MarkEntireType (nested);
+ MarkEntireType (nested, new DependencyInfo (DependencyKind.NestedType, type));
}
- Annotations.Mark (type);
- MarkCustomAttributes (type);
+ Annotations.Mark (type, reason);
+ MarkCustomAttributes (type, new DependencyInfo (DependencyKind.CustomAttribute, type));
MarkTypeSpecialCustomAttributes (type);
if (type.HasInterfaces) {
foreach (InterfaceImplementation iface in type.Interfaces) {
- MarkInterfaceImplementation (iface);
+ MarkInterfaceImplementation (iface, type);
}
}
@@ -178,27 +272,28 @@ namespace Mono.Linker.Steps {
if (type.HasFields) {
foreach (FieldDefinition field in type.Fields) {
- MarkField (field);
+ MarkField (field, new DependencyInfo (DependencyKind.MemberOfType, type));
}
}
if (type.HasMethods) {
foreach (MethodDefinition method in type.Methods) {
- Annotations.Mark (method);
+ // Probably redundant since we EnqueueMethod below anyway.
+ Annotations.Mark (method, new DependencyInfo (DependencyKind.MemberOfType, type));
Annotations.SetAction (method, MethodAction.ForceParse);
- EnqueueMethod (method);
+ EnqueueMethod (method, new DependencyInfo (DependencyKind.MemberOfType, type));
}
}
if (type.HasProperties) {
foreach (var property in type.Properties) {
- MarkProperty (property);
+ MarkProperty (property, new DependencyInfo (DependencyKind.MemberOfType, type));
}
}
if (type.HasEvents) {
foreach (var ev in type.Events) {
- MarkEvent (ev);
+ MarkEvent (ev, new DependencyInfo (DependencyKind.MemberOfType, type));
}
}
}
@@ -227,12 +322,7 @@ namespace Mono.Linker.Steps {
continue;
if (!Annotations.IsMarked (type))
continue;
- Tracer.Push (type);
- try {
- _context.MarkingHelpers.MarkExportedType (exported, assembly.MainModule);
- } finally {
- Tracer.Pop ();
- }
+ _context.MarkingHelpers.MarkExportedType (exported, assembly.MainModule, new DependencyInfo (DependencyKind.ExportedType, type));
}
}
}
@@ -256,14 +346,11 @@ namespace Mono.Linker.Steps {
void ProcessQueue ()
{
while (!QueueIsEmpty ()) {
- MethodDefinition method = _methods.Dequeue ();
- Tracer.Push (method);
+ (MethodDefinition method, DependencyInfo reason) = _methods.Dequeue ();
try {
- ProcessMethod (method);
+ ProcessMethod (method, reason);
} catch (Exception e) {
throw new MarkException (string.Format ("Error processing method: '{0}' in assembly: '{1}'", method.FullName, method.Module.Name), e, method);
- } finally {
- Tracer.Pop ();
}
}
}
@@ -273,17 +360,15 @@ namespace Mono.Linker.Steps {
return _methods.Count == 0;
}
- protected virtual void EnqueueMethod (MethodDefinition method)
+ protected virtual void EnqueueMethod (MethodDefinition method, in DependencyInfo reason)
{
- _methods.Enqueue (method);
+ _methods.Enqueue ((method, reason));
}
void ProcessVirtualMethods ()
{
foreach (MethodDefinition method in _virtual_methods) {
- Tracer.Push (method);
ProcessVirtualMethod (method);
- Tracer.Pop ();
}
}
@@ -349,7 +434,16 @@ namespace Mono.Linker.Steps {
if (!isInstantiated && !@base.IsAbstract && _context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, method))
return;
- MarkMethod (method);
+ // Only track instantiations if override removal is enabled and the type is instantiated.
+ // If it's disabled, all overrides are kept, so there's no instantiation site to blame.
+ if (_context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, method) && isInstantiated) {
+ MarkMethod (method, new DependencyInfo (DependencyKind.OverrideOnInstantiatedType, method.DeclaringType));
+ } else {
+ // If the optimization is disabled or it's an abstract type, we just mark it as a normal override.
+ Debug.Assert (!_context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, method) || @base.IsAbstract);
+ MarkMethod (method, new DependencyInfo (DependencyKind.Override, @base));
+ }
+
ProcessVirtualMethod (method);
}
@@ -385,49 +479,47 @@ namespace Mono.Linker.Steps {
return type.HasInterface (@interfaceType, out InterfaceImplementation implementation) && Annotations.IsMarked (implementation);
}
- void MarkMarshalSpec (IMarshalInfoProvider spec)
+ void MarkMarshalSpec (IMarshalInfoProvider spec, in DependencyInfo reason)
{
if (!spec.HasMarshalInfo)
return;
if (spec.MarshalInfo is CustomMarshalInfo marshaler)
- MarkType (marshaler.ManagedType);
+ MarkType (marshaler.ManagedType, reason);
}
- void MarkCustomAttributes (ICustomAttributeProvider provider)
+ void MarkCustomAttributes (ICustomAttributeProvider provider, in DependencyInfo reason)
{
if (!provider.HasCustomAttributes)
return;
bool markOnUse = _context.KeepUsedAttributeTypesOnly && Annotations.GetAction (GetAssemblyFromCustomAttributeProvider (provider)) == AssemblyAction.Link;
- Tracer.Push (provider);
- try {
- foreach (CustomAttribute ca in provider.CustomAttributes) {
- if (ProcessLinkerSpecialAttribute (ca, provider)) {
- continue;
- }
-
- if (markOnUse) {
- _lateMarkedAttributes.Enqueue (new AttributeProviderPair (ca, provider));
- continue;
- }
+ foreach (CustomAttribute ca in provider.CustomAttributes) {
+ if (ProcessLinkerSpecialAttribute (ca, provider, reason)) {
+ continue;
+ }
- MarkCustomAttribute (ca);
- MarkSpecialCustomAttributeDependencies (ca);
+ if (markOnUse) {
+ _lateMarkedAttributes.Enqueue ((new AttributeProviderPair (ca, provider), reason));
+ continue;
}
- } finally {
- Tracer.Pop ();
+
+ MarkCustomAttribute (ca, reason);
+ MarkSpecialCustomAttributeDependencies (ca, provider);
}
}
- protected virtual bool ProcessLinkerSpecialAttribute (CustomAttribute ca, ICustomAttributeProvider provider)
+ protected virtual bool ProcessLinkerSpecialAttribute (CustomAttribute ca, ICustomAttributeProvider provider, in DependencyInfo reason)
{
if (IsUserDependencyMarker (ca.AttributeType) && provider is MemberReference mr) {
MarkUserDependency (mr, ca);
if (_context.KeepDependencyAttributes || Annotations.GetAction (mr.Module.Assembly) != AssemblyAction.Link) {
- MarkCustomAttribute (ca);
+ 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;
@@ -513,14 +605,14 @@ namespace Mono.Linker.Steps {
}
if (member == "*") {
- MarkEntireType (td);
+ MarkEntireType (td, new DependencyInfo (DependencyKind.PreservedDependency, ca));
return;
}
- if (MarkDependencyMethod (td, member, signature))
+ if (MarkDependencyMethod (td, member, signature, new DependencyInfo (DependencyKind.PreservedDependency, ca)))
return;
- if (MarkDependencyField (td, member))
+ if (MarkDependencyField (td, member, new DependencyInfo (DependencyKind.PreservedDependency, ca)))
return;
_context.LogMessage (MessageImportance.High, $"Could not resolve dependency member '{member}' declared in type '{td.FullName}'");
@@ -534,7 +626,7 @@ namespace Mono.Linker.Steps {
return type?.Resolve ();
}
- bool MarkDependencyMethod (TypeDefinition type, string name, string[] signature)
+ bool MarkDependencyMethod (TypeDefinition type, string name, string[] signature, in DependencyInfo reason)
{
bool marked = false;
@@ -553,7 +645,7 @@ namespace Mono.Linker.Steps {
continue;
if (signature == null) {
- MarkIndirectlyCalledMethod (m);
+ MarkIndirectlyCalledMethod (m, reason);
marked = true;
continue;
}
@@ -573,18 +665,18 @@ namespace Mono.Linker.Steps {
if (i < 0)
continue;
- MarkIndirectlyCalledMethod (m);
+ MarkIndirectlyCalledMethod (m, reason);
marked = true;
}
return marked;
}
- bool MarkDependencyField (TypeDefinition type, string name)
+ bool MarkDependencyField (TypeDefinition type, string name, in DependencyInfo reason)
{
foreach (var f in type.Fields) {
if (f.Name == name) {
- MarkField (f);
+ MarkField (f, reason);
return true;
}
}
@@ -592,37 +684,32 @@ namespace Mono.Linker.Steps {
return false;
}
- void LazyMarkCustomAttributes (ICustomAttributeProvider provider, AssemblyDefinition assembly)
+ void LazyMarkCustomAttributes (ICustomAttributeProvider provider, ModuleDefinition module)
{
if (!provider.HasCustomAttributes)
return;
foreach (CustomAttribute ca in provider.CustomAttributes)
- _assemblyLevelAttributes.Enqueue (new AttributeProviderPair (ca, assembly));
+ _assemblyLevelAttributes.Enqueue (new AttributeProviderPair (ca, module));
}
- protected virtual void MarkCustomAttribute (CustomAttribute ca)
+ protected virtual void MarkCustomAttribute (CustomAttribute ca, in DependencyInfo reason)
{
- Tracer.Push ((object)ca.AttributeType ?? (object)ca);
- try {
- Annotations.Mark (ca);
- MarkMethod (ca.Constructor);
+ Annotations.Mark (ca, reason);
+ MarkMethod (ca.Constructor, new DependencyInfo (DependencyKind.AttributeConstructor, ca));
- MarkCustomAttributeArguments (ca);
+ MarkCustomAttributeArguments (ca);
- TypeReference constructor_type = ca.Constructor.DeclaringType;
- TypeDefinition type = constructor_type.Resolve ();
-
- if (type == null) {
- HandleUnresolvedType (constructor_type);
- return;
- }
+ TypeReference constructor_type = ca.Constructor.DeclaringType;
+ TypeDefinition type = constructor_type.Resolve ();
- MarkCustomAttributeProperties (ca, type);
- MarkCustomAttributeFields (ca, type);
- } finally {
- Tracer.Pop ();
+ if (type == null) {
+ HandleUnresolvedType (constructor_type);
+ return;
}
+
+ MarkCustomAttributeProperties (ca, type);
+ MarkCustomAttributeFields (ca, type);
}
protected virtual bool ShouldMarkCustomAttribute (CustomAttribute ca, ICustomAttributeProvider provider)
@@ -663,9 +750,9 @@ namespace Mono.Linker.Steps {
return true;
}
- protected void MarkStaticConstructor (TypeDefinition type)
+ protected void MarkStaticConstructor (TypeDefinition type, in DependencyInfo reason)
{
- if (MarkMethodIf (type.Methods, IsNonEmptyStaticConstructor) != null)
+ if (MarkMethodIf (type.Methods, IsNonEmptyStaticConstructor, reason) != null)
Annotations.SetPreservedStaticCtor (type);
}
@@ -689,13 +776,13 @@ namespace Mono.Linker.Steps {
var displayTargetType = GetDebuggerAttributeTargetType (app.Attribute, (AssemblyDefinition) app.Provider);
if (displayTargetType == null || !Annotations.IsMarked (displayTargetType))
return false;
- }
+ }
}
return true;
}
- protected void MarkSecurityDeclarations (ISecurityDeclarationProvider provider)
+ protected void MarkSecurityDeclarations (ISecurityDeclarationProvider provider, in DependencyInfo reason)
{
// most security declarations are removed (if linked) but user code might still have some
// and if the attributes references types then they need to be marked too
@@ -703,19 +790,19 @@ namespace Mono.Linker.Steps {
return;
foreach (var sd in provider.SecurityDeclarations)
- MarkSecurityDeclaration (sd);
+ MarkSecurityDeclaration (sd, reason);
}
- protected virtual void MarkSecurityDeclaration (SecurityDeclaration sd)
+ protected virtual void MarkSecurityDeclaration (SecurityDeclaration sd, in DependencyInfo reason)
{
if (!sd.HasSecurityAttributes)
return;
foreach (var sa in sd.SecurityAttributes)
- MarkSecurityAttribute (sa);
+ MarkSecurityAttribute (sa, reason);
}
- protected virtual void MarkSecurityAttribute (SecurityAttribute sa)
+ protected virtual void MarkSecurityAttribute (SecurityAttribute sa, in DependencyInfo reason)
{
TypeReference security_type = sa.AttributeType;
TypeDefinition type = security_type.Resolve ();
@@ -723,48 +810,30 @@ namespace Mono.Linker.Steps {
HandleUnresolvedType (security_type);
return;
}
-
- MarkType (security_type);
- MarkSecurityAttributeProperties (sa, type);
- MarkSecurityAttributeFields (sa, type);
- }
-
- protected void MarkSecurityAttributeProperties (SecurityAttribute sa, TypeDefinition attribute)
- {
- if (!sa.HasProperties)
- return;
- foreach (var named_argument in sa.Properties)
- MarkCustomAttributeProperty (named_argument, attribute);
+ // Security attributes participate in inference logic without being marked.
+ Tracer.AddDirectDependency (sa, reason, marked: false);
+ MarkType (security_type, new DependencyInfo (DependencyKind.AttributeType, sa));
+ MarkCustomAttributeProperties (sa, type);
+ MarkCustomAttributeFields (sa, type);
}
- protected void MarkSecurityAttributeFields (SecurityAttribute sa, TypeDefinition attribute)
- {
- if (!sa.HasFields)
- return;
-
- foreach (var named_argument in sa.Fields)
- MarkCustomAttributeField (named_argument, attribute);
- }
-
- protected void MarkCustomAttributeProperties (CustomAttribute ca, TypeDefinition attribute)
+ protected void MarkCustomAttributeProperties (ICustomAttribute ca, TypeDefinition attribute)
{
if (!ca.HasProperties)
return;
foreach (var named_argument in ca.Properties)
- MarkCustomAttributeProperty (named_argument, attribute);
+ MarkCustomAttributeProperty (named_argument, attribute, ca, new DependencyInfo (DependencyKind.AttributeProperty, ca));
}
- protected void MarkCustomAttributeProperty (CustomAttributeNamedArgument namedArgument, TypeDefinition attribute)
+ protected void MarkCustomAttributeProperty (CustomAttributeNamedArgument namedArgument, TypeDefinition attribute, ICustomAttribute ca, in DependencyInfo reason)
{
PropertyDefinition property = GetProperty (attribute, namedArgument.Name);
- Tracer.Push (property);
if (property != null)
- MarkMethod (property.SetMethod);
+ MarkMethod (property.SetMethod, reason);
- MarkCustomAttributeArgument (namedArgument.Argument);
- Tracer.Pop ();
+ MarkCustomAttributeArgument (namedArgument.Argument, ca);
}
PropertyDefinition GetProperty (TypeDefinition type, string propertyname)
@@ -774,28 +843,30 @@ namespace Mono.Linker.Steps {
if (property != null)
return property;
+ // This would neglect to mark parameters for generic instances.
+ Debug.Assert (!(type.BaseType is GenericInstanceType));
type = type.BaseType?.Resolve ();
}
return null;
}
- protected void MarkCustomAttributeFields (CustomAttribute ca, TypeDefinition attribute)
+ protected void MarkCustomAttributeFields (ICustomAttribute ca, TypeDefinition attribute)
{
if (!ca.HasFields)
return;
foreach (var named_argument in ca.Fields)
- MarkCustomAttributeField (named_argument, attribute);
+ MarkCustomAttributeField (named_argument, attribute, ca);
}
- protected void MarkCustomAttributeField (CustomAttributeNamedArgument namedArgument, TypeDefinition attribute)
+ protected void MarkCustomAttributeField (CustomAttributeNamedArgument namedArgument, TypeDefinition attribute, ICustomAttribute ca)
{
FieldDefinition field = GetField (attribute, namedArgument.Name);
if (field != null)
- MarkField (field);
+ MarkField (field, new DependencyInfo (DependencyKind.CustomAttributeField, ca));
- MarkCustomAttributeArgument (namedArgument.Argument);
+ MarkCustomAttributeArgument (namedArgument.Argument, ca);
}
FieldDefinition GetField (TypeDefinition type, string fieldname)
@@ -805,6 +876,8 @@ namespace Mono.Linker.Steps {
if (field != null)
return field;
+ // This would neglect to mark parameters for generic instances.
+ Debug.Assert (!(type.BaseType is GenericInstanceType));
type = type.BaseType?.Resolve ();
}
@@ -818,6 +891,8 @@ namespace Mono.Linker.Steps {
if (method != null)
return method;
+ // This would neglect to mark parameters for generic instances.
+ Debug.Assert (!(type.BaseType is GenericInstanceType));
type = type.BaseType.Resolve ();
}
@@ -830,22 +905,24 @@ namespace Mono.Linker.Steps {
return;
foreach (var argument in ca.ConstructorArguments)
- MarkCustomAttributeArgument (argument);
+ MarkCustomAttributeArgument (argument, ca);
}
- void MarkCustomAttributeArgument (CustomAttributeArgument argument)
+ void MarkCustomAttributeArgument (CustomAttributeArgument argument, ICustomAttribute ca)
{
var at = argument.Type;
if (at.IsArray) {
var et = at.GetElementType ();
- MarkType (et);
+ MarkType (et, new DependencyInfo (DependencyKind.CustomAttributeArgumentType, ca));
if (argument.Value == null)
return;
+ // Array arguments are modeled as a CustomAttributeArgument [], and will mark the
+ // Type once for each element in the array.
foreach (var caa in (CustomAttributeArgument [])argument.Value)
- MarkCustomAttributeArgument (caa);
+ MarkCustomAttributeArgument (caa, ca);
return;
}
@@ -853,14 +930,14 @@ namespace Mono.Linker.Steps {
if (at.Namespace == "System") {
switch (at.Name) {
case "Type":
- MarkType (argument.Type);
- MarkType ((TypeReference)argument.Value);
+ MarkType (argument.Type, new DependencyInfo (DependencyKind.CustomAttributeArgumentType, ca));
+ MarkType ((TypeReference)argument.Value, new DependencyInfo (DependencyKind.CustomAttributeArgumentValue, ca));
return;
case "Object":
var boxed_value = (CustomAttributeArgument)argument.Value;
- MarkType (boxed_value.Type);
- MarkCustomAttributeArgument (boxed_value);
+ MarkType (boxed_value.Type, new DependencyInfo (DependencyKind.CustomAttributeArgumentType, ca));
+ MarkCustomAttributeArgument (boxed_value, ca);
return;
}
}
@@ -884,23 +961,23 @@ namespace Mono.Linker.Steps {
MarkAssemblyCustomAttributes (assembly);
- MarkSecurityDeclarations (assembly);
+ MarkSecurityDeclarations (assembly, new DependencyInfo (DependencyKind.AssemblyOrModuleAttribute, assembly));
foreach (ModuleDefinition module in assembly.Modules)
- LazyMarkCustomAttributes (module, assembly);
+ LazyMarkCustomAttributes (module, module);
}
void MarkEntireAssembly (AssemblyDefinition assembly)
{
- MarkCustomAttributes (assembly);
- MarkCustomAttributes (assembly.MainModule);
+ MarkCustomAttributes (assembly, new DependencyInfo (DependencyKind.AssemblyOrModuleAttribute, assembly));
+ MarkCustomAttributes (assembly.MainModule, new DependencyInfo (DependencyKind.AssemblyOrModuleAttribute, assembly.MainModule));
if (assembly.MainModule.HasExportedTypes) {
// TODO: This needs more work accross all steps
}
foreach (TypeDefinition type in assembly.MainModule.Types)
- MarkEntireType (type);
+ MarkEntireType (type, new DependencyInfo (DependencyKind.TypeInAssembly, assembly));
}
void ProcessModule (AssemblyDefinition assembly)
@@ -911,7 +988,7 @@ namespace Mono.Linker.Steps {
{
if (type.Name == "<Module>" && type.HasMethods)
{
- MarkType (type);
+ MarkType (type, new DependencyInfo (DependencyKind.TypeInAssembly, assembly));
break;
}
}
@@ -944,6 +1021,10 @@ namespace Mono.Linker.Steps {
continue;
}
+
+ markOccurred = true;
+ MarkCustomAttribute (customAttribute, new DependencyInfo (DependencyKind.AssemblyOrModuleAttribute, assemblyLevelAttribute.Provider));
+
string attributeFullName = customAttribute.Constructor.DeclaringType.FullName;
switch (attributeFullName) {
case "System.Diagnostics.DebuggerDisplayAttribute":
@@ -953,9 +1034,6 @@ namespace Mono.Linker.Steps {
MarkTypeWithDebuggerTypeProxyAttribute (GetDebuggerAttributeTargetType (assemblyLevelAttribute.Attribute, (AssemblyDefinition) assemblyLevelAttribute.Provider), customAttribute);
break;
}
-
- markOccurred = true;
- MarkCustomAttribute (customAttribute);
}
// requeue the items we skipped in case we need to make another pass
@@ -971,12 +1049,13 @@ namespace Mono.Linker.Steps {
if (startingQueueCount == 0)
return false;
- var skippedItems = new List<AttributeProviderPair> ();
+ var skippedItems = new List<(AttributeProviderPair, DependencyInfo)> ();
var markOccurred = false;
while (_lateMarkedAttributes.Count != 0) {
- var attributeProviderPair = _lateMarkedAttributes.Dequeue ();
+ var (attributeProviderPair, reason) = _lateMarkedAttributes.Dequeue ();
var customAttribute = attributeProviderPair.Attribute;
+ var provider = attributeProviderPair.Provider;
var resolved = customAttribute.Constructor.Resolve ();
if (resolved == null) {
@@ -984,14 +1063,14 @@ namespace Mono.Linker.Steps {
continue;
}
- if (!ShouldMarkCustomAttribute (customAttribute, attributeProviderPair.Provider)) {
- skippedItems.Add (attributeProviderPair);
+ if (!ShouldMarkCustomAttribute (customAttribute, provider)) {
+ skippedItems.Add ((attributeProviderPair, reason));
continue;
}
markOccurred = true;
- MarkCustomAttribute (customAttribute);
- MarkSpecialCustomAttributeDependencies (customAttribute);
+ MarkCustomAttribute (customAttribute, reason);
+ MarkSpecialCustomAttributeDependencies (customAttribute, provider);
}
// requeue the items we skipped in case we need to make another pass
@@ -1001,10 +1080,17 @@ namespace Mono.Linker.Steps {
return markOccurred;
}
- protected void MarkField (FieldReference reference)
+ protected void MarkField (FieldReference reference, DependencyInfo reason)
{
- if (reference.DeclaringType is GenericInstanceType)
- MarkType (reference.DeclaringType);
+ if (reference.DeclaringType is GenericInstanceType) {
+ Debug.Assert (reason.Kind == DependencyKind.FieldAccess || reason.Kind == DependencyKind.Ldtoken);
+ // Blame the field reference (without actually marking) on the original reason.
+ Tracer.AddDirectDependency (reference, reason, marked: false);
+ MarkType (reference.DeclaringType, new DependencyInfo (DependencyKind.DeclaringType, reference));
+
+ // Blame the field definition that we will resolve on the field reference.
+ reason = new DependencyInfo (DependencyKind.FieldOnGenericInstance, reference);
+ }
FieldDefinition field = reference.Resolve ();
@@ -1013,30 +1099,46 @@ namespace Mono.Linker.Steps {
return;
}
- MarkField (field);
+ MarkField (field, reason);
}
- void MarkField (FieldDefinition field)
+ void MarkField (FieldDefinition field, in DependencyInfo reason)
{
+#if DEBUG
+ if (!_fieldReasons.Contains (reason.Kind))
+ throw new ArgumentOutOfRangeException ($"Internal error: unsupported field dependency {reason.Kind}");
+#endif
+
if (CheckProcessed (field))
return;
- MarkType (field.DeclaringType);
- MarkType (field.FieldType);
- MarkCustomAttributes (field);
- MarkMarshalSpec (field);
+ MarkType (field.DeclaringType, new DependencyInfo (DependencyKind.DeclaringType, field));
+ MarkType (field.FieldType, new DependencyInfo (DependencyKind.FieldType, field));
+ MarkCustomAttributes (field, new DependencyInfo (DependencyKind.CustomAttribute, field));
+ MarkMarshalSpec (field, new DependencyInfo (DependencyKind.FieldMarshalSpec, field));
DoAdditionalFieldProcessing (field);
var parent = field.DeclaringType;
- if (!Annotations.HasPreservedStaticCtor (parent))
- MarkStaticConstructor (parent);
+ if (!Annotations.HasPreservedStaticCtor (parent)) {
+ var cctorReason = reason.Kind switch {
+ // Report an edge directly from the method accessing the field to the static ctor it triggers
+ DependencyKind.FieldAccess => new DependencyInfo (DependencyKind.TriggersCctorThroughFieldAccess, reason.Source),
+ _ => new DependencyInfo (DependencyKind.CctorForField, field)
+ };
+ MarkStaticConstructor (parent, cctorReason);
+ }
if (Annotations.HasSubstitutedInit (field)) {
Annotations.SetPreservedStaticCtor (parent);
Annotations.SetSubstitutedInit (parent);
}
- Annotations.Mark (field);
+ if (reason.Kind == DependencyKind.AlreadyMarked) {
+ Debug.Assert (Annotations.IsMarked (field));
+ return;
+ }
+
+ Annotations.Mark (field, reason);
}
protected virtual bool IgnoreScope (IMetadataScope scope)
@@ -1045,25 +1147,29 @@ namespace Mono.Linker.Steps {
return Annotations.GetAction (assembly) != AssemblyAction.Link;
}
- void MarkScope (IMetadataScope scope)
+ void MarkScope (IMetadataScope scope, TypeDefinition type)
{
- if (scope is IMetadataTokenProvider provider)
- Annotations.Mark (provider);
+ Annotations.Mark (scope, new DependencyInfo (DependencyKind.ScopeOfType, type));
}
protected virtual void MarkSerializable (TypeDefinition type)
{
- MarkDefaultConstructor (type);
+ // Keep default ctor for XmlSerializer support. See https://github.com/mono/linker/issues/957
+ MarkDefaultConstructor (type, new DependencyInfo (DependencyKind.SerializationMethodForType, type));
if (!_context.IsFeatureExcluded ("deserialization"))
- MarkMethodsIf (type.Methods, IsSpecialSerializationConstructor);
+ MarkMethodsIf (type.Methods, IsSpecialSerializationConstructor, new DependencyInfo (DependencyKind.SerializationMethodForType, type));
}
- protected virtual TypeDefinition MarkType (TypeReference reference)
+ protected virtual TypeDefinition MarkType (TypeReference reference, DependencyInfo reason)
{
+#if DEBUG
+ if (!_typeReasons.Contains (reason.Kind))
+ throw new ArgumentOutOfRangeException ($"Internal error: unsupported type dependency {reason.Kind}");
+#endif
if (reference == null)
return null;
- reference = GetOriginalType (reference);
+ (reference, reason) = GetOriginalType (reference, reason);
if (reference is FunctionPointerType)
return null;
@@ -1081,16 +1187,28 @@ namespace Mono.Linker.Steps {
return null;
}
+ // Track a mark reason for each call to MarkType.
+ switch (reason.Kind) {
+ case DependencyKind.AlreadyMarked:
+ Debug.Assert (Annotations.IsMarked (type));
+ break;
+ default:
+ Annotations.Mark (type, reason);
+ break;
+ }
+
+ // Treat cctors triggered by a called method specially and mark this case up-front.
+ if (type.HasMethods && ShouldMarkTypeStaticConstructor (type) && reason.Kind == DependencyKind.DeclaringTypeOfCalledMethod)
+ MarkStaticConstructor (type, new DependencyInfo (DependencyKind.TriggersCctorForCalledMethod, reason.Source));
+
if (CheckProcessed (type))
return null;
- Tracer.Push (type);
-
- MarkScope (type.Scope);
- MarkType (type.BaseType);
- MarkType (type.DeclaringType);
- MarkCustomAttributes (type);
- MarkSecurityDeclarations (type);
+ MarkScope (type.Scope, type);
+ MarkType (type.BaseType, new DependencyInfo (DependencyKind.BaseType, type));
+ MarkType (type.DeclaringType, new DependencyInfo (DependencyKind.DeclaringType, type));
+ MarkCustomAttributes (type, new DependencyInfo (DependencyKind.CustomAttribute, type));
+ MarkSecurityDeclarations (type, new DependencyInfo (DependencyKind.CustomAttribute, type));
if (type.IsMulticastDelegate ()) {
MarkMulticastDelegate (type);
@@ -1099,17 +1217,20 @@ namespace Mono.Linker.Steps {
if (type.IsSerializable ())
MarkSerializable (type);
+ // TODO: This needs work to ensure we handle EventSource appropriately.
+ // This marks static fields of KeyWords/OpCodes/Tasks subclasses of an EventSource type.
if (!_context.IsFeatureExcluded ("etw") && BCL.EventTracingForWindows.IsEventSourceImplementation (type, _context)) {
MarkEventSourceProviders (type);
}
+ // This marks properties for [EventData] types as well as other attribute dependencies.
MarkTypeSpecialCustomAttributes (type);
MarkGenericParameterProvider (type);
// keep fields for value-types and for classes with LayoutKind.Sequential or Explicit
if (type.IsValueType || !type.IsAutoLayout)
- MarkFields (type, type.IsEnum);
+ MarkFields (type, includeStatic: type.IsEnum, reason: new DependencyInfo (DependencyKind.MemberOfType, type));
// There are a number of markings we can defer until later when we know it's possible a reference type could be instantiated
// For example, if no instance of a type exist, then we don't need to mark the interfaces on that type
@@ -1140,22 +1261,19 @@ namespace Mono.Linker.Steps {
_typesWithInterfaces.Add (type);
if (type.HasMethods) {
- MarkMethodsIf (type.Methods, IsVirtualNeededByTypeDueToPreservedScope);
- if (ShouldMarkTypeStaticConstructor (type))
- MarkStaticConstructor (type);
+ // For virtuals that must be preserved, blame the declaring type.
+ MarkMethodsIf (type.Methods, IsVirtualNeededByTypeDueToPreservedScope, new DependencyInfo (DependencyKind.VirtualNeededDueToPreservedScope, type));
+ if (ShouldMarkTypeStaticConstructor (type) && reason.Kind != DependencyKind.TriggersCctorForCalledMethod)
+ MarkStaticConstructor (type, new DependencyInfo (DependencyKind.CctorForType, type));
if (_context.IsFeatureExcluded ("deserialization"))
- MarkMethodsIf (type.Methods, HasOnSerializeAttribute);
+ MarkMethodsIf (type.Methods, HasOnSerializeAttribute, new DependencyInfo (DependencyKind.SerializationMethodForType, type));
else
- MarkMethodsIf (type.Methods, HasOnSerializeOrDeserializeAttribute);
+ MarkMethodsIf (type.Methods, HasOnSerializeOrDeserializeAttribute, new DependencyInfo (DependencyKind.SerializationMethodForType, type));
}
DoAdditionalTypeProcessing (type);
- Tracer.Pop ();
-
- Annotations.Mark (type);
-
ApplyPreserveInfo (type);
return type;
@@ -1241,10 +1359,11 @@ namespace Mono.Linker.Steps {
MarkTypeWithDebuggerTypeProxyAttribute (type, attribute);
break;
case "EventDataAttribute" when attrType.Namespace == "System.Diagnostics.Tracing":
- MarkMethodsIf (type.Methods, MethodDefinitionExtensions.IsPublicInstancePropertyMethod);
+ if (MarkMethodsIf (type.Methods, MethodDefinitionExtensions.IsPublicInstancePropertyMethod, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, type)))
+ Tracer.AddDirectDependency (attribute, new DependencyInfo (DependencyKind.CustomAttribute, type), marked: false);
break;
case "TypeDescriptionProviderAttribute" when attrType.Namespace == "System.ComponentModel":
- MarkTypeConverterLikeDependency (attribute, l => l.IsDefaultConstructor ());
+ MarkTypeConverterLikeDependency (attribute, l => l.IsDefaultConstructor (), type);
break;
}
}
@@ -1253,13 +1372,14 @@ namespace Mono.Linker.Steps {
//
// Used for known framework attributes which can be applied to any element
//
- bool MarkSpecialCustomAttributeDependencies (CustomAttribute ca)
+ bool MarkSpecialCustomAttributeDependencies (CustomAttribute ca, ICustomAttributeProvider provider)
{
var dt = ca.Constructor.DeclaringType;
if (dt.Name == "TypeConverterAttribute" && dt.Namespace == "System.ComponentModel") {
MarkTypeConverterLikeDependency (ca, l =>
l.IsDefaultConstructor () ||
- l.Parameters.Count == 1 && l.Parameters [0].ParameterType.IsTypeOf ("System", "Type"));
+ l.Parameters.Count == 1 && l.Parameters [0].ParameterType.IsTypeOf ("System", "Type"),
+ provider);
return true;
}
@@ -1282,11 +1402,13 @@ namespace Mono.Linker.Steps {
void MarkXmlSchemaProvider (TypeDefinition type, CustomAttribute attribute)
{
- if (TryGetStringArgument (attribute, out string name))
- MarkNamedMethod (type, name);
+ if (TryGetStringArgument (attribute, out string name)) {
+ Tracer.AddDirectDependency (attribute, new DependencyInfo (DependencyKind.CustomAttribute, type), marked: false);
+ MarkNamedMethod (type, name, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute));
+ }
}
- protected virtual void MarkTypeConverterLikeDependency (CustomAttribute attribute, Func<MethodDefinition, bool> predicate)
+ protected virtual void MarkTypeConverterLikeDependency (CustomAttribute attribute, Func<MethodDefinition, bool> predicate, ICustomAttributeProvider provider)
{
var args = attribute.ConstructorArguments;
if (args.Count < 1)
@@ -1305,13 +1427,18 @@ namespace Mono.Linker.Steps {
if (tdef == null)
return;
- MarkMethodsIf (tdef.Methods, predicate);
+ Tracer.AddDirectDependency (attribute, new DependencyInfo (DependencyKind.CustomAttribute, provider), marked: false);
+ MarkMethodsIf (tdef.Methods, predicate, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute));
}
void MarkTypeWithDebuggerDisplayAttribute (TypeDefinition type, CustomAttribute attribute)
{
if (_context.KeepMembersForDebugger) {
+ // Members referenced by the DebuggerDisplayAttribute are kept even if the attribute may not be.
+ // Record a logical dependency on the attribute so that we can blame it for the kept members below.
+ Tracer.AddDirectDependency (attribute, new DependencyInfo (DependencyKind.CustomAttribute, type), marked: false);
+
string displayString = (string) attribute.ConstructorArguments[0].Value;
Regex regex = new Regex ("{[^{}]+}", RegexOptions.Compiled);
@@ -1330,31 +1457,34 @@ namespace Mono.Linker.Steps {
string methodName = realMatch.Substring (0, realMatch.Length - 2);
MethodDefinition method = GetMethodWithNoParameters (type, methodName);
if (method != null) {
- MarkMethod (method);
+ MarkMethod (method, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute));
continue;
}
} else {
FieldDefinition field = GetField (type, realMatch);
if (field != null) {
- MarkField (field);
+ MarkField (field, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute));
continue;
}
PropertyDefinition property = GetProperty (type, realMatch);
if (property != null) {
if (property.GetMethod != null) {
- MarkMethod (property.GetMethod);
+ MarkMethod (property.GetMethod, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute));
}
if (property.SetMethod != null) {
- MarkMethod (property.SetMethod);
+ MarkMethod (property.SetMethod, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute));
}
continue;
}
}
while (type != null) {
- MarkMethods (type);
- MarkFields (type, includeStatic: true);
+ // TODO: Non-understood DebuggerDisplayAttribute causes us to keep everything. Should this be a warning?
+ MarkMethods (type, new DependencyInfo (DependencyKind.KeptForSpecialAttribute, attribute));
+ MarkFields (type, includeStatic: true, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute));
+ // This logic would miss generic parameters used in methods/fields for generic types
+ Debug.Assert (!(type.BaseType is GenericInstanceType));
type = type.BaseType?.Resolve ();
}
return;
@@ -1377,12 +1507,13 @@ namespace Mono.Linker.Steps {
return;
}
- MarkType (proxyTypeReference);
+ Tracer.AddDirectDependency (attribute, new DependencyInfo (DependencyKind.CustomAttribute, type), marked: false);
+ MarkType (proxyTypeReference, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute));
TypeDefinition proxyType = proxyTypeReference.Resolve ();
if (proxyType != null) {
- MarkMethods (proxyType);
- MarkFields (proxyType, includeStatic: true);
+ MarkMethods (proxyType, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute));
+ MarkFields (proxyType, includeStatic: true, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute));
}
}
}
@@ -1399,7 +1530,7 @@ namespace Mono.Linker.Steps {
return argument != null;
}
- protected int MarkNamedMethod (TypeDefinition type, string method_name)
+ protected int MarkNamedMethod (TypeDefinition type, string method_name, in DependencyInfo reason)
{
if (!type.HasMethods)
return 0;
@@ -1409,7 +1540,7 @@ namespace Mono.Linker.Steps {
if (method.Name != method_name)
continue;
- MarkMethod (method);
+ MarkMethod (method, reason);
count++;
}
@@ -1421,11 +1552,12 @@ namespace Mono.Linker.Steps {
if (!TryGetStringArgument (attribute, out string member_name))
return;
- MarkNamedField (method.DeclaringType, member_name);
- MarkNamedProperty (method.DeclaringType, member_name);
+ MarkNamedField (method.DeclaringType, member_name, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute));
+ MarkNamedProperty (method.DeclaringType, member_name, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute));
}
- void MarkNamedField (TypeDefinition type, string field_name)
+ // TODO: combine with MarkDependencyField?
+ void MarkNamedField (TypeDefinition type, string field_name, in DependencyInfo reason)
{
if (!type.HasFields)
return;
@@ -1434,11 +1566,11 @@ namespace Mono.Linker.Steps {
if (field.Name != field_name)
continue;
- MarkField (field);
+ MarkField (field, reason);
}
}
- void MarkNamedProperty (TypeDefinition type, string property_name)
+ void MarkNamedProperty (TypeDefinition type, string property_name, in DependencyInfo reason)
{
if (!type.HasProperties)
return;
@@ -1447,10 +1579,9 @@ namespace Mono.Linker.Steps {
if (property.Name != property_name)
continue;
- Tracer.Push (property);
- MarkMethod (property.GetMethod);
- MarkMethod (property.SetMethod);
- Tracer.Pop ();
+ // This marks methods directly without reporting the property.
+ MarkMethod (property.GetMethod, reason);
+ MarkMethod (property.SetMethod, reason);
}
}
@@ -1469,7 +1600,7 @@ namespace Mono.Linker.Steps {
}
if (ShouldMarkInterfaceImplementation (type, iface, resolvedInterfaceType))
- MarkInterfaceImplementation (iface);
+ MarkInterfaceImplementation (iface, type);
}
}
@@ -1484,13 +1615,13 @@ namespace Mono.Linker.Steps {
void MarkGenericParameter (GenericParameter parameter)
{
- MarkCustomAttributes (parameter);
+ MarkCustomAttributes (parameter, new DependencyInfo (DependencyKind.GenericParameterCustomAttribute, parameter.Owner));
if (!parameter.HasConstraints)
return;
foreach (var constraint in parameter.Constraints) {
- MarkCustomAttributes (constraint);
- MarkType (constraint.ConstraintType);
+ MarkCustomAttributes (constraint, new DependencyInfo (DependencyKind.GenericParameterConstraintCustomAttribute, parameter.Owner));
+ MarkType (constraint.ConstraintType, new DependencyInfo (DependencyKind.GenericParameterConstraintType, parameter.Owner));
}
}
@@ -1558,30 +1689,35 @@ namespace Mono.Linker.Steps {
parameters [1].ParameterType.Name == "StreamingContext";
}
- protected void MarkMethodsIf (Collection<MethodDefinition> methods, Func<MethodDefinition, bool> predicate)
+ protected bool MarkMethodsIf (Collection<MethodDefinition> methods, Func<MethodDefinition, bool> predicate, in DependencyInfo reason)
{
- foreach (MethodDefinition method in methods)
- if (predicate (method))
- MarkMethod (method);
+ bool marked = false;
+ foreach (MethodDefinition method in methods) {
+ if (predicate (method)) {
+ MarkMethod (method, reason);
+ marked = true;
+ }
+ }
+ return marked;
}
- protected MethodDefinition MarkMethodIf (Collection<MethodDefinition> methods, Func<MethodDefinition, bool> predicate)
+ protected MethodDefinition MarkMethodIf (Collection<MethodDefinition> methods, Func<MethodDefinition, bool> predicate, in DependencyInfo reason)
{
foreach (MethodDefinition method in methods) {
if (predicate (method)) {
- return MarkMethod (method);
+ return MarkMethod (method, reason);
}
}
return null;
}
- protected bool MarkDefaultConstructor (TypeDefinition type)
+ protected bool MarkDefaultConstructor (TypeDefinition type, in DependencyInfo reason)
{
if (type?.HasMethods != true)
return false;
- return MarkMethodIf (type.Methods, MethodDefinitionExtensions.IsDefaultConstructor) != null;
+ return MarkMethodIf (type.Methods, MethodDefinitionExtensions.IsDefaultConstructor, reason) != null;
}
static bool IsNonEmptyStaticConstructor (MethodDefinition method)
@@ -1654,13 +1790,13 @@ namespace Mono.Linker.Steps {
{
foreach (var nestedType in td.NestedTypes) {
if (BCL.EventTracingForWindows.IsProviderName (nestedType.Name))
- MarkStaticFields (nestedType);
+ MarkStaticFields (nestedType, new DependencyInfo (DependencyKind.EventSourceProviderField, td));
}
}
protected virtual void MarkMulticastDelegate (TypeDefinition type)
{
- MarkMethodCollection (type.Methods);
+ MarkMethodCollection (type.Methods, new DependencyInfo (DependencyKind.MethodForSpecialType, type));
}
TypeDefinition ResolveFullyQualifiedTypeName (string name)
@@ -1682,25 +1818,30 @@ namespace Mono.Linker.Steps {
return null;
}
- protected TypeReference GetOriginalType (TypeReference type)
+ protected (TypeReference, DependencyInfo) GetOriginalType (TypeReference type, DependencyInfo reason)
{
while (type is TypeSpecification specification) {
- if (type is GenericInstanceType git)
+ if (type is GenericInstanceType git) {
MarkGenericArguments (git);
+ Debug.Assert (!(specification.ElementType is TypeSpecification));
+ }
if (type is IModifierType mod)
MarkModifierType (mod);
if (type is FunctionPointerType fnptr) {
MarkParameters (fnptr);
- MarkType (fnptr.ReturnType);
+ MarkType (fnptr.ReturnType, new DependencyInfo (DependencyKind.ReturnType, fnptr));
break; // FunctionPointerType is the original type
}
- type = specification.ElementType;
+ // Blame the type reference (which isn't marked) on the original reason.
+ Tracer.AddDirectDependency (specification, reason, marked: false);
+ // Blame the outgoing element type on the specification.
+ (type, reason) = (specification.ElementType, new DependencyInfo (DependencyKind.ElementType, specification));
}
- return type;
+ return (type, reason);
}
void MarkParameters (FunctionPointerType fnptr)
@@ -1710,19 +1851,19 @@ namespace Mono.Linker.Steps {
for (int i = 0; i < fnptr.Parameters.Count; i++)
{
- MarkType (fnptr.Parameters[i].ParameterType);
+ MarkType (fnptr.Parameters[i].ParameterType, new DependencyInfo (DependencyKind.ParameterType, fnptr));
}
}
void MarkModifierType (IModifierType mod)
{
- MarkType (mod.ModifierType);
+ MarkType (mod.ModifierType, new DependencyInfo (DependencyKind.ModifierType, mod));
}
void MarkGenericArguments (IGenericInstance instance)
{
foreach (TypeReference argument in instance.GenericArguments)
- MarkType (argument);
+ MarkType (argument, new DependencyInfo (DependencyKind.GenericArgumentType, instance));
MarkGenericArgumentConstructors (instance);
}
@@ -1748,7 +1889,7 @@ namespace Mono.Linker.Steps {
continue;
var argument_definition = argument.Resolve ();
- MarkDefaultConstructor (argument_definition);
+ MarkDefaultConstructor (argument_definition, new DependencyInfo (DependencyKind.DefaultCtorForNewConstrainedGenericArgument, instance));
}
}
@@ -1772,15 +1913,17 @@ namespace Mono.Linker.Steps {
switch (preserve) {
case TypePreserve.All:
- MarkFields (type, true);
- MarkMethods (type);
+ // TODO: it seems like PreserveAll on a type won't necessarily keep nested types,
+ // but PreserveAll on an assembly will. Is this correct?
+ MarkFields (type, true, new DependencyInfo (DependencyKind.TypePreserve, type));
+ MarkMethods (type, new DependencyInfo (DependencyKind.TypePreserve, type));
break;
case TypePreserve.Fields:
- if (!MarkFields (type, true, true))
+ if (!MarkFields (type, true, new DependencyInfo (DependencyKind.TypePreserve, type), true))
_context.LogMessage ($"Type {type.FullName} has no fields to preserve");
break;
case TypePreserve.Methods:
- if (!MarkMethods (type))
+ if (!MarkMethods (type, new DependencyInfo (DependencyKind.TypePreserve, type)))
_context.LogMessage ($"Type {type.FullName} has no methods to preserve");
break;
}
@@ -1792,7 +1935,7 @@ namespace Mono.Linker.Steps {
if (list == null)
return;
- MarkMethodCollection (list);
+ MarkMethodCollection (list, new DependencyInfo (DependencyKind.PreservedMethod, type));
}
void ApplyPreserveMethods (MethodDefinition method)
@@ -1801,10 +1944,10 @@ namespace Mono.Linker.Steps {
if (list == null)
return;
- MarkMethodCollection (list);
+ MarkMethodCollection (list, new DependencyInfo (DependencyKind.PreservedMethod, method));
}
- protected bool MarkFields (TypeDefinition type, bool includeStatic, bool markBackingFieldsOnlyIfPropertyMarked = false)
+ protected bool MarkFields (TypeDefinition type, bool includeStatic, in DependencyInfo reason, bool markBackingFieldsOnlyIfPropertyMarked = false)
{
if (!type.HasFields)
return false;
@@ -1825,7 +1968,7 @@ namespace Mono.Linker.Steps {
if (propertyDefinition != null && !Annotations.IsMarked (propertyDefinition))
continue;
}
- MarkField (field);
+ MarkField (field, reason);
}
return true;
@@ -1847,68 +1990,67 @@ namespace Mono.Linker.Steps {
return null;
}
- protected void MarkStaticFields (TypeDefinition type)
+ protected void MarkStaticFields (TypeDefinition type, in DependencyInfo reason)
{
if (!type.HasFields)
return;
foreach (FieldDefinition field in type.Fields) {
if (field.IsStatic)
- MarkField (field);
+ MarkField (field, reason);
}
}
- protected virtual bool MarkMethods (TypeDefinition type)
+ protected virtual bool MarkMethods (TypeDefinition type, in DependencyInfo reason)
{
if (!type.HasMethods)
return false;
- MarkMethodCollection (type.Methods);
+ MarkMethodCollection (type.Methods, reason);
return true;
}
- void MarkMethodCollection (IList<MethodDefinition> methods)
+ void MarkMethodCollection (IList<MethodDefinition> methods, in DependencyInfo reason)
{
foreach (MethodDefinition method in methods)
- MarkMethod (method);
+ MarkMethod (method, reason);
}
- protected void MarkIndirectlyCalledMethod (MethodDefinition method)
+ protected void MarkIndirectlyCalledMethod (MethodDefinition method, in DependencyInfo reason)
{
- MarkMethod (method);
+ MarkMethod (method, reason);
Annotations.MarkIndirectlyCalledMethod (method);
}
- protected virtual MethodDefinition MarkMethod (MethodReference reference)
+ protected virtual MethodDefinition MarkMethod (MethodReference reference, DependencyInfo reason)
{
- reference = GetOriginalMethod (reference);
+ (reference, reason) = GetOriginalMethod (reference, reason);
if (reference.DeclaringType is ArrayType)
return null;
- Tracer.Push (reference);
- if (reference.DeclaringType is GenericInstanceType)
- MarkType (reference.DeclaringType);
+ if (reference.DeclaringType is GenericInstanceType) {
+ // Blame the method reference on the original reason without marking it.
+ Tracer.AddDirectDependency (reference, reason, marked: false);
+ MarkType (reference.DeclaringType, new DependencyInfo (DependencyKind.DeclaringType, reference));
+ // Mark the resolved method definition as a dependency of the reference.
+ reason = new DependencyInfo (DependencyKind.MethodOnGenericInstance, reference);
+ }
// if (IgnoreScope (reference.DeclaringType.Scope))
// return;
MethodDefinition method = reference.Resolve ();
- try {
- if (method == null) {
- HandleUnresolvedMethod (reference);
- return null;
- }
+ if (method == null) {
+ HandleUnresolvedMethod (reference);
+ return null;
+ }
- if (Annotations.GetAction (method) == MethodAction.Nothing)
- Annotations.SetAction (method, MethodAction.Parse);
+ if (Annotations.GetAction (method) == MethodAction.Nothing)
+ Annotations.SetAction (method, MethodAction.Parse);
- EnqueueMethod (method);
- } finally {
- Tracer.Pop ();
- }
- Tracer.AddDependency (method);
+ EnqueueMethod (method, reason);
return method;
}
@@ -1920,52 +2062,85 @@ namespace Mono.Linker.Steps {
return assembly;
}
- protected MethodReference GetOriginalMethod (MethodReference method)
+ protected (MethodReference, DependencyInfo) GetOriginalMethod (MethodReference method, DependencyInfo reason)
{
while (method is MethodSpecification specification) {
+ // Blame the method reference (which isn't marked) on the original reason.
+ Tracer.AddDirectDependency (specification, reason, marked: false);
+ // Blame the outgoing element method on the specification.
if (method is GenericInstanceMethod gim)
MarkGenericArguments (gim);
- method = specification.ElementMethod;
+ (method, reason) = (specification.ElementMethod, new DependencyInfo (DependencyKind.ElementMethod, specification));
+ Debug.Assert (!(method is MethodSpecification));
}
- return method;
+ return (method, reason);
}
- protected virtual void ProcessMethod (MethodDefinition method)
+ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo reason)
{
+#if DEBUG
+ if (!_methodReasons.Contains (reason.Kind))
+ throw new ArgumentOutOfRangeException ($"Internal error: unsupported method dependency {reason.Kind}");
+#endif
+
+ // Record the reason for marking a method on each call. The logic under CheckProcessed happens
+ // only once per method.
+ switch (reason.Kind) {
+ case DependencyKind.AlreadyMarked:
+ Debug.Assert (Annotations.IsMarked (method));
+ break;
+ default:
+ Annotations.Mark (method, reason);
+ break;
+ }
+
+ bool markedForCall = (
+ reason.Kind == DependencyKind.DirectCall ||
+ reason.Kind == DependencyKind.VirtualCall ||
+ reason.Kind == DependencyKind.Newobj
+ );
+ if (markedForCall) {
+ // Record declaring type of a called method up-front as a special case so that we may
+ // track at least some method calls that trigger a cctor.
+ MarkType (method.DeclaringType, new DependencyInfo (DependencyKind.DeclaringTypeOfCalledMethod, method));
+ }
+
if (CheckProcessed (method))
return;
- Tracer.Push (method);
- MarkType (method.DeclaringType);
- MarkCustomAttributes (method);
- MarkSecurityDeclarations (method);
+ if (!markedForCall)
+ MarkType (method.DeclaringType, new DependencyInfo (DependencyKind.DeclaringType, method));
+ MarkCustomAttributes (method, new DependencyInfo (DependencyKind.CustomAttribute, method));
+ MarkSecurityDeclarations (method, new DependencyInfo (DependencyKind.CustomAttribute, method));
MarkGenericParameterProvider (method);
- if (ShouldMarkAsInstancePossible (method))
+ if (method.IsInstanceConstructor ()) {
MarkRequirementsForInstantiatedTypes (method.DeclaringType);
+ Tracer.AddDirectDependency (method.DeclaringType, new DependencyInfo (DependencyKind.InstantiatedByCtor, method), marked: false);
+ }
if (method.IsConstructor) {
if (!Annotations.ProcessSatelliteAssemblies && KnownMembers.IsSatelliteAssemblyMarker (method))
Annotations.ProcessSatelliteAssemblies = true;
} else if (method.IsPropertyMethod ())
- MarkProperty (method.GetProperty ());
+ MarkProperty (method.GetProperty (), new DependencyInfo (DependencyKind.PropertyOfPropertyMethod, method));
else if (method.IsEventMethod ())
- MarkEvent (method.GetEvent ());
+ MarkEvent (method.GetEvent (), new DependencyInfo (DependencyKind.EventOfEventMethod, method));
if (method.HasParameters) {
foreach (ParameterDefinition pd in method.Parameters) {
- MarkType (pd.ParameterType);
- MarkCustomAttributes (pd);
- MarkMarshalSpec (pd);
+ MarkType (pd.ParameterType, new DependencyInfo (DependencyKind.ParameterType, method));
+ MarkCustomAttributes (pd, new DependencyInfo (DependencyKind.ParameterAttribute, method));
+ MarkMarshalSpec (pd, new DependencyInfo (DependencyKind.ParameterMarshalSpec, method));
}
}
if (method.HasOverrides) {
foreach (MethodReference ov in method.Overrides) {
- MarkMethod (ov);
+ MarkMethod (ov, new DependencyInfo (DependencyKind.MethodImplOverride, method));
MarkExplicitInterfaceImplementation (method, ov);
}
}
@@ -1979,9 +2154,9 @@ namespace Mono.Linker.Steps {
MarkBaseMethods (method);
- MarkType (method.ReturnType);
- MarkCustomAttributes (method.MethodReturnType);
- MarkMarshalSpec (method.MethodReturnType);
+ MarkType (method.ReturnType, new DependencyInfo (DependencyKind.ReturnType, method));
+ MarkCustomAttributes (method.MethodReturnType, new DependencyInfo (DependencyKind.ReturnTypeAttribute, method));
+ MarkMarshalSpec (method.MethodReturnType, new DependencyInfo (DependencyKind.ReturnTypeMarshalSpec, method));
if (method.IsPInvokeImpl || method.IsInternalCall) {
ProcessInteropMethod (method);
@@ -1992,10 +2167,7 @@ namespace Mono.Linker.Steps {
DoAdditionalMethodProcessing (method);
- Annotations.Mark (method);
-
ApplyPreserveMethods (method);
- Tracer.Pop ();
}
// Allow subclassers to mark additional things when marking a method
@@ -2003,21 +2175,6 @@ namespace Mono.Linker.Steps {
{
}
- protected virtual bool ShouldMarkAsInstancePossible (MethodDefinition method)
- {
- // We don't need to mark it multiple times
- if (Annotations.IsInstantiated (method.DeclaringType))
- return false;
-
- if (method.IsInstanceConstructor ())
- return true;
-
- if (method.DeclaringType.IsInterface)
- return true;
-
- return false;
- }
-
protected virtual void MarkRequirementsForInstantiatedTypes (TypeDefinition type)
{
if (Annotations.IsInstantiated (type))
@@ -2028,7 +2185,7 @@ namespace Mono.Linker.Steps {
MarkInterfaceImplementations (type);
foreach (var method in GetRequiredMethodsForInstantiatedType (type))
- MarkMethod (method);
+ MarkMethod (method, new DependencyInfo (DependencyKind.MethodForInstantiatedType, type));
DoAdditionalInstantiatedTypeProcessing (type);
}
@@ -2066,7 +2223,7 @@ namespace Mono.Linker.Steps {
}
if (resolvedInterfaceType == resolvedOverride.DeclaringType) {
- MarkInterfaceImplementation (ifaceImpl);
+ MarkInterfaceImplementation (ifaceImpl, method.DeclaringType);
return;
}
}
@@ -2081,18 +2238,18 @@ namespace Mono.Linker.Steps {
return;
var baseType = method.DeclaringType.BaseType.Resolve ();
- if (!MarkDefaultConstructor (baseType))
+ if (!MarkDefaultConstructor (baseType, new DependencyInfo (DependencyKind.BaseDefaultCtorForStubbedMethod, method)))
throw new NotSupportedException ($"Cannot stub constructor on '{method.DeclaringType}' when base type does not have default constructor");
break;
case MethodAction.ConvertToThrow:
- MarkAndCacheConvertToThrowExceptionCtor ();
+ MarkAndCacheConvertToThrowExceptionCtor (new DependencyInfo (DependencyKind.UnreachableBodyRequirement, method));
break;
}
}
- protected virtual void MarkAndCacheConvertToThrowExceptionCtor ()
+ protected virtual void MarkAndCacheConvertToThrowExceptionCtor (DependencyInfo reason)
{
if (_context.MarkedKnownMembers.NotSupportedExceptionCtorString != null)
return;
@@ -2101,18 +2258,18 @@ namespace Mono.Linker.Steps {
if (nse == null)
throw new NotSupportedException ("Missing predefined 'System.NotSupportedException' type");
- MarkType (nse);
+ MarkType (nse, reason);
- var nseCtor = MarkMethodIf (nse.Methods, KnownMembers.IsNotSupportedExceptionCtorString);
+ var nseCtor = MarkMethodIf (nse.Methods, KnownMembers.IsNotSupportedExceptionCtorString, reason);
_context.MarkedKnownMembers.NotSupportedExceptionCtorString = nseCtor ?? throw new MarkException ($"Could not find constructor on '{nse.FullName}'");
var objectType = BCL.FindPredefinedType ("System", "Object", _context);
if (objectType == null)
throw new NotSupportedException ("Missing predefined 'System.Object' type");
- MarkType (objectType);
+ MarkType (objectType, reason);
- var objectCtor = MarkMethodIf (objectType.Methods, MethodDefinitionExtensions.IsDefaultConstructor);
+ var objectCtor = MarkMethodIf (objectType.Methods, MethodDefinitionExtensions.IsDefaultConstructor, reason);
_context.MarkedKnownMembers.ObjectCtor = objectCtor ?? throw new MarkException ($"Could not find constructor on '{objectType.FullName}'");
}
@@ -2121,14 +2278,14 @@ namespace Mono.Linker.Steps {
if (_context.MarkedKnownMembers.DisablePrivateReflectionAttributeCtor != null)
return false;
- var nse = BCL.FindPredefinedType ("System.Runtime.CompilerServices", "DisablePrivateReflectionAttribute", _context);
- if (nse == null)
+ var disablePrivateReflection = BCL.FindPredefinedType ("System.Runtime.CompilerServices", "DisablePrivateReflectionAttribute", _context);
+ if (disablePrivateReflection == null)
throw new NotSupportedException ("Missing predefined 'System.Runtime.CompilerServices.DisablePrivateReflectionAttribute' type");
- MarkType (nse);
+ MarkType (disablePrivateReflection, DependencyInfo.DisablePrivateReflectionRequirement);
- var ctor = MarkMethodIf (nse.Methods, MethodDefinitionExtensions.IsDefaultConstructor);
- _context.MarkedKnownMembers.DisablePrivateReflectionAttributeCtor = ctor ?? throw new MarkException ($"Could not find constructor on '{nse.FullName}'");
+ var ctor = MarkMethodIf (disablePrivateReflection.Methods, MethodDefinitionExtensions.IsDefaultConstructor, new DependencyInfo (DependencyKind.DisablePrivateReflectionRequirement, disablePrivateReflection));
+ _context.MarkedKnownMembers.DisablePrivateReflectionAttributeCtor = ctor ?? throw new MarkException ($"Could not find constructor on '{disablePrivateReflection.FullName}'");
return true;
}
@@ -2142,7 +2299,7 @@ namespace Mono.Linker.Steps {
if (base_method.DeclaringType.IsInterface && !method.DeclaringType.IsInterface)
continue;
- MarkMethod (base_method);
+ MarkMethod (base_method, new DependencyInfo (DependencyKind.BaseMethod, method));
MarkBaseMethods (base_method);
}
}
@@ -2162,12 +2319,12 @@ namespace Mono.Linker.Steps {
const bool includeStaticFields = false;
if (returnTypeDefinition != null && !returnTypeDefinition.IsImport) {
- MarkDefaultConstructor (returnTypeDefinition);
- MarkFields (returnTypeDefinition, includeStaticFields);
+ MarkDefaultConstructor (returnTypeDefinition, new DependencyInfo (DependencyKind.InteropMethodDependency, method));
+ MarkFields (returnTypeDefinition, includeStaticFields, new DependencyInfo (DependencyKind.InteropMethodDependency, method));
}
if (method.HasThis && !method.DeclaringType.IsImport) {
- MarkFields (method.DeclaringType, includeStaticFields);
+ MarkFields (method.DeclaringType, includeStaticFields, new DependencyInfo (DependencyKind.InteropMethodDependency, method));
}
foreach (ParameterDefinition pd in method.Parameters) {
@@ -2177,9 +2334,9 @@ namespace Mono.Linker.Steps {
}
TypeDefinition paramTypeDefinition = paramTypeReference.Resolve ();
if (paramTypeDefinition != null && !paramTypeDefinition.IsImport) {
- MarkFields (paramTypeDefinition, includeStaticFields);
+ MarkFields (paramTypeDefinition, includeStaticFields, new DependencyInfo (DependencyKind.InteropMethodDependency, method));
if (pd.ParameterType.IsByReference) {
- MarkDefaultConstructor (paramTypeDefinition);
+ MarkDefaultConstructor (paramTypeDefinition, new DependencyInfo (DependencyKind.InteropMethodDependency, method));
}
}
}
@@ -2210,46 +2367,50 @@ namespace Mono.Linker.Steps {
}
}
- protected void MarkProperty (PropertyDefinition prop)
+ protected void MarkProperty (PropertyDefinition prop, in DependencyInfo reason)
{
- MarkCustomAttributes (prop);
+ Tracer.AddDirectDependency (prop, reason, marked: false);
+ // Consider making this more similar to MarkEvent method?
+ MarkCustomAttributes (prop, new DependencyInfo (DependencyKind.CustomAttribute, prop));
DoAdditionalPropertyProcessing (prop);
}
- protected virtual void MarkEvent (EventDefinition evt)
+ protected virtual void MarkEvent (EventDefinition evt, in DependencyInfo reason)
{
- MarkCustomAttributes (evt);
- MarkMethodIfNotNull (evt.AddMethod);
- MarkMethodIfNotNull (evt.InvokeMethod);
- MarkMethodIfNotNull (evt.RemoveMethod);
+ // Record the event without marking it in Annotations.
+ Tracer.AddDirectDependency (evt, reason, marked: false);
+ MarkCustomAttributes (evt, new DependencyInfo (DependencyKind.CustomAttribute, evt));
+ MarkMethodIfNotNull (evt.AddMethod, new DependencyInfo (DependencyKind.EventMethod, evt));
+ MarkMethodIfNotNull (evt.InvokeMethod, new DependencyInfo (DependencyKind.EventMethod, evt));
+ MarkMethodIfNotNull (evt.RemoveMethod, new DependencyInfo (DependencyKind.EventMethod, evt));
DoAdditionalEventProcessing (evt);
}
- void MarkMethodIfNotNull (MethodReference method)
+ void MarkMethodIfNotNull (MethodReference method, in DependencyInfo reason)
{
if (method == null)
return;
- MarkMethod (method);
+ MarkMethod (method, reason);
}
protected virtual void MarkMethodBody (MethodBody body)
{
if (_context.IsOptimizationEnabled (CodeOptimizations.UnreachableBodies, body.Method) && IsUnreachableBody (body)) {
- MarkAndCacheConvertToThrowExceptionCtor ();
+ MarkAndCacheConvertToThrowExceptionCtor (new DependencyInfo (DependencyKind.UnreachableBodyRequirement, body.Method));
_unreachableBodies.Add (body);
return;
}
foreach (VariableDefinition var in body.Variables)
- MarkType (var.VariableType);
+ MarkType (var.VariableType, new DependencyInfo (DependencyKind.VariableType, body.Method));
foreach (ExceptionHandler eh in body.ExceptionHandlers)
if (eh.HandlerType == ExceptionHandlerType.Catch)
- MarkType (eh.CatchType);
+ MarkType (eh.CatchType, new DependencyInfo (DependencyKind.CatchType, body.Method));
foreach (Instruction instruction in body.Instructions)
- MarkInstruction (instruction);
+ MarkInstruction (instruction, body.Method);
MarkInterfacesNeededByBodyStack (body);
@@ -2277,30 +2438,45 @@ namespace Mono.Linker.Steps {
if (implementations == null)
return;
- foreach (var implementation in implementations)
- MarkInterfaceImplementation (implementation);
+ foreach (var (implementation, type) in implementations)
+ MarkInterfaceImplementation (implementation, type);
}
- protected virtual void MarkInstruction (Instruction instruction)
+ protected virtual void MarkInstruction (Instruction instruction, MethodDefinition method)
{
switch (instruction.OpCode.OperandType) {
case OperandType.InlineField:
- MarkField ((FieldReference) instruction.Operand);
+ MarkField ((FieldReference) instruction.Operand, new DependencyInfo (DependencyKind.FieldAccess, method));
break;
case OperandType.InlineMethod:
- MarkMethod ((MethodReference) instruction.Operand);
+ {
+ DependencyKind dependencyKind = instruction.OpCode.Code switch {
+ Code.Jmp => DependencyKind.DirectCall,
+ Code.Call => DependencyKind.DirectCall,
+ Code.Callvirt => DependencyKind.VirtualCall,
+ Code.Newobj => DependencyKind.Newobj,
+ Code.Ldvirtftn => DependencyKind.Ldvirtftn,
+ Code.Ldftn => DependencyKind.Ldftn,
+ _ => throw new Exception ($"unexpected opcode {instruction.OpCode}")
+ };
+ MarkMethod ((MethodReference) instruction.Operand, new DependencyInfo (dependencyKind, method));
break;
+ }
case OperandType.InlineTok:
+ {
object token = instruction.Operand;
+ Debug.Assert (instruction.OpCode.Code == Code.Ldtoken);
+ var reason = new DependencyInfo (DependencyKind.Ldtoken, method);
if (token is TypeReference typeReference)
- MarkType (typeReference);
+ MarkType (typeReference, reason);
else if (token is MethodReference methodReference)
- MarkMethod (methodReference);
+ MarkMethod (methodReference, reason);
else
- MarkField ((FieldReference) token);
+ MarkField ((FieldReference) token, reason);
break;
+ }
case OperandType.InlineType:
- MarkType ((TypeReference) instruction.Operand);
+ MarkType ((TypeReference) instruction.Operand, new DependencyInfo (DependencyKind.InstructionTypeRef, method));
break;
default:
break;
@@ -2347,11 +2523,13 @@ namespace Mono.Linker.Steps {
return IsFullyPreserved (type);
}
- protected virtual void MarkInterfaceImplementation (InterfaceImplementation iface)
+ protected virtual void MarkInterfaceImplementation (InterfaceImplementation iface, TypeDefinition type)
{
- MarkCustomAttributes (iface);
- MarkType (iface.InterfaceType);
- Annotations.Mark (iface);
+ // Blame the type that has the interfaceimpl, expecting the type itself to get marked for other reasons.
+ MarkCustomAttributes (iface, new DependencyInfo (DependencyKind.CustomAttribute, iface));
+ // Blame the interface type on the interfaceimpl itself.
+ MarkType (iface.InterfaceType, new DependencyInfo (DependencyKind.InterfaceImplementationInterfaceType, iface));
+ Annotations.Mark (iface, new DependencyInfo (DependencyKind.InterfaceImplementationOnType, type));
}
bool HasManuallyTrackedDependency (MethodBody methodBody)
@@ -2458,13 +2636,8 @@ namespace Mono.Linker.Steps {
_patternReported = true;
#endif
- _context.Tracer.Push ($"Reflection-{accessedItem}");
- try {
- mark ();
- _context.ReflectionPatternRecorder.RecognizedReflectionAccessPattern (MethodCalling, MethodCalled, accessedItem);
- } finally {
- _context.Tracer.Pop ();
- }
+ mark ();
+ _context.ReflectionPatternRecorder.RecognizedReflectionAccessPattern (MethodCalling, MethodCalled, accessedItem);
}
public void RecordUnrecognizedPattern (string message)
@@ -2623,7 +2796,8 @@ namespace Mono.Linker.Steps {
break;
}
- reflectionContext.RecordRecognizedPattern (foundType, () => _markStep.MarkType (foundType));
+ var methodCalling = reflectionContext.MethodCalling;
+ reflectionContext.RecordRecognizedPattern (foundType, () => _markStep.MarkType (foundType, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling)));
}
break;
}
@@ -3110,7 +3284,8 @@ namespace Mono.Linker.Steps {
continue;
foundMatch = true;
- reflectionContext.RecordRecognizedPattern (method, () => _markStep.MarkIndirectlyCalledMethod (method));
+ var methodCalling = reflectionContext.MethodCalling;
+ reflectionContext.RecordRecognizedPattern (method, () => _markStep.MarkIndirectlyCalledMethod (method, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling)));
}
if (!foundMatch)
@@ -3120,6 +3295,7 @@ namespace Mono.Linker.Steps {
void MarkPropertiesFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, bool staticOnly = false)
{
bool foundMatch = false;
+ var methodCalling = reflectionContext.MethodCalling;
foreach (var property in declaringType.Properties) {
if (property.Name != name)
continue;
@@ -3130,19 +3306,19 @@ namespace Mono.Linker.Steps {
// Be conservative and mark everything for the property.
var getter = property.GetMethod;
if (getter != null && (!staticOnly || staticOnly && getter.IsStatic)) {
- reflectionContext.RecordRecognizedPattern (getter, () => _markStep.MarkIndirectlyCalledMethod (getter));
+ reflectionContext.RecordRecognizedPattern (getter, () => _markStep.MarkIndirectlyCalledMethod (getter, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling)));
markedAny = true;
}
var setter = property.SetMethod;
if (setter != null && (!staticOnly || staticOnly && setter.IsStatic)) {
- reflectionContext.RecordRecognizedPattern (setter, () => _markStep.MarkIndirectlyCalledMethod (setter));
+ reflectionContext.RecordRecognizedPattern (setter, () => _markStep.MarkIndirectlyCalledMethod (setter, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling)));
markedAny = true;
}
if (markedAny) {
foundMatch = true;
- reflectionContext.RecordRecognizedPattern (property, () => _markStep.MarkProperty (property));
+ reflectionContext.RecordRecognizedPattern (property, () => _markStep.MarkProperty (property, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling)));
}
}
@@ -3153,6 +3329,7 @@ namespace Mono.Linker.Steps {
void MarkFieldsFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, bool staticOnly = false)
{
bool foundMatch = false;
+ var methodCalling = reflectionContext.MethodCalling;
foreach (var field in declaringType.Fields) {
if (field.Name != name)
continue;
@@ -3161,7 +3338,7 @@ namespace Mono.Linker.Steps {
continue;
foundMatch = true;
- reflectionContext.RecordRecognizedPattern (field, () => _markStep.MarkField (field));
+ reflectionContext.RecordRecognizedPattern (field, () => _markStep.MarkField (field, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling)));
break;
}
@@ -3172,12 +3349,13 @@ namespace Mono.Linker.Steps {
void MarkEventsFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name)
{
bool foundMatch = false;
+ var methodCalling = reflectionContext.MethodCalling;
foreach (var eventInfo in declaringType.Events) {
if (eventInfo.Name != name)
continue;
foundMatch = true;
- reflectionContext.RecordRecognizedPattern (eventInfo, () => _markStep.MarkEvent (eventInfo));
+ reflectionContext.RecordRecognizedPattern (eventInfo, () => _markStep.MarkEvent (eventInfo, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling)));
}
if (!foundMatch)
diff --git a/src/linker/Linker.Steps/OutputStep.cs b/src/linker/Linker.Steps/OutputStep.cs
index 06d2fba8e..c6cfd7bf0 100644
--- a/src/linker/Linker.Steps/OutputStep.cs
+++ b/src/linker/Linker.Steps/OutputStep.cs
@@ -139,13 +139,11 @@ namespace Mono.Linker.Steps {
case AssemblyAction.Save:
case AssemblyAction.Link:
case AssemblyAction.AddBypassNGen:
- Context.Tracer.AddDependency (assembly);
WriteAssembly (assembly, directory);
CopySatelliteAssembliesIfNeeded (assembly, directory);
assembliesWritten.Add (GetOriginalAssemblyFileInfo (assembly).Name);
break;
case AssemblyAction.Copy:
- Context.Tracer.AddDependency (assembly);
CloseSymbols (assembly);
CopyAssembly (assembly, directory);
CopySatelliteAssembliesIfNeeded (assembly, directory);
diff --git a/src/linker/Linker.Steps/ReflectionBlockedStep.cs b/src/linker/Linker.Steps/ReflectionBlockedStep.cs
index b11fbf69a..22b7e4633 100644
--- a/src/linker/Linker.Steps/ReflectionBlockedStep.cs
+++ b/src/linker/Linker.Steps/ReflectionBlockedStep.cs
@@ -61,7 +61,7 @@ namespace Mono.Linker.Steps
var ca = new CustomAttribute (ctor);
caProvider.CustomAttributes.Add (ca);
- Annotations.Mark (ca);
+ Annotations.Mark (ca, new DependencyInfo (DependencyKind.DisablePrivateReflection, ca));
}
}
} \ No newline at end of file
diff --git a/src/linker/Linker.Steps/ResolveFromAssemblyStep.cs b/src/linker/Linker.Steps/ResolveFromAssemblyStep.cs
index 7809fa7e3..08868fa3f 100644
--- a/src/linker/Linker.Steps/ResolveFromAssemblyStep.cs
+++ b/src/linker/Linker.Steps/ResolveFromAssemblyStep.cs
@@ -94,8 +94,6 @@ namespace Mono.Linker.Steps
var action = rootVisibility == RootVisibility.Any ? AssemblyAction.Copy : AssemblyAction.Link;
context.SetAction (assembly, action);
- context.Tracer.Push (assembly);
-
foreach (TypeDefinition type in assembly.MainModule.Types)
MarkType (context, type, rootVisibility);
@@ -132,11 +130,9 @@ namespace Mono.Linker.Steps
context.Resolve (resolvedExportedType.Scope);
MarkType (context, resolvedExportedType, rootVisibility);
- context.MarkingHelpers.MarkExportedType (exported, assembly.MainModule);
+ context.MarkingHelpers.MarkExportedType (exported, assembly.MainModule, new DependencyInfo (DependencyKind.ExportedType, resolvedExportedType));
}
}
-
- context.Tracer.Pop ();
}
static void MarkType (LinkContext context, TypeDefinition type, RootVisibility rootVisibility)
@@ -151,8 +147,7 @@ namespace Mono.Linker.Steps
return;
}
- context.Annotations.MarkAndPush (type);
-
+ context.Annotations.Mark (type, new DependencyInfo (DependencyKind.RootAssembly, type.Module.Assembly));
if (type.HasFields)
MarkFields (context, type.Fields, rootVisibility);
if (type.HasMethods)
@@ -160,21 +155,17 @@ namespace Mono.Linker.Steps
if (type.HasNestedTypes)
foreach (var nested in type.NestedTypes)
MarkType (context, nested, rootVisibility);
-
- context.Tracer.Pop ();
}
void ProcessExecutable (AssemblyDefinition assembly)
{
Context.SetAction (assembly, AssemblyAction.Link);
- Tracer.Push (assembly);
-
- Annotations.Mark (assembly.EntryPoint.DeclaringType);
-
- MarkMethod (Context, assembly.EntryPoint, MethodAction.Parse, RootVisibility.Any);
+ MethodDefinition entryPoint = assembly.EntryPoint;
+ TypeDefinition declaringType = entryPoint.DeclaringType;
+ Annotations.Mark (declaringType, new DependencyInfo (DependencyKind.RootAssembly, declaringType.Module.Assembly));
- Tracer.Pop ();
+ MarkMethod (Context, entryPoint, MethodAction.Parse, RootVisibility.Any);
}
static void MarkFields (LinkContext context, Collection<FieldDefinition> fields, RootVisibility rootVisibility)
@@ -186,7 +177,7 @@ namespace Mono.Linker.Steps
_ => true
};
if (markField) {
- context.Annotations.Mark (field);
+ context.Annotations.Mark (field, new DependencyInfo (DependencyKind.RootAssembly, field.Module.Assembly));
}
}
}
@@ -206,7 +197,7 @@ namespace Mono.Linker.Steps
};
if (markMethod) {
- context.Annotations.Mark (method);
+ context.Annotations.Mark (method, new DependencyInfo (DependencyKind.RootAssembly, method.Module.Assembly));
context.Annotations.SetAction (method, action);
}
}
diff --git a/src/linker/Linker.Steps/ResolveFromXmlStep.cs b/src/linker/Linker.Steps/ResolveFromXmlStep.cs
index 7da1d3eb6..35e7020c7 100644
--- a/src/linker/Linker.Steps/ResolveFromXmlStep.cs
+++ b/src/linker/Linker.Steps/ResolveFromXmlStep.cs
@@ -111,7 +111,6 @@ namespace Mono.Linker.Steps {
if (IsExcluded (iterator.Current))
return;
- Tracer.Push (assembly);
if (GetTypePreserve (iterator.Current) == TypePreserve.All) {
foreach (var type in assembly.MainModule.Types)
MarkAndPreserveAll (type);
@@ -119,7 +118,6 @@ namespace Mono.Linker.Steps {
ProcessTypes (assembly, iterator.Current.SelectChildren ("type", _ns));
ProcessNamespaces (assembly, iterator.Current.SelectChildren ("namespace", _ns));
}
- Tracer.Pop ();
}
void ProcessNamespaces (AssemblyDefinition assembly, XPathNodeIterator iterator)
@@ -137,18 +135,14 @@ namespace Mono.Linker.Steps {
void MarkAndPreserveAll (TypeDefinition type)
{
- Annotations.MarkAndPush (type);
+ Annotations.Mark (type, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation));
Annotations.SetPreserve (type, TypePreserve.All);
- if (!type.HasNestedTypes) {
- Tracer.Pop ();
+ if (!type.HasNestedTypes)
return;
- }
foreach (TypeDefinition nested in type.NestedTypes)
MarkAndPreserveAll (nested);
-
- Tracer.Pop ();
}
void ProcessTypes (AssemblyDefinition assembly, XPathNodeIterator iterator)
@@ -169,10 +163,8 @@ namespace Mono.Linker.Steps {
if (assembly.MainModule.HasExportedTypes) {
foreach (var exported in assembly.MainModule.ExportedTypes) {
if (fullname == exported.FullName) {
- Tracer.Push (exported);
- MarkingHelpers.MarkExportedType (exported, assembly.MainModule);
+ MarkingHelpers.MarkExportedType (exported, assembly.MainModule, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation));
var resolvedExternal = exported.Resolve ();
- Tracer.Pop ();
if (resolvedExternal != null) {
type = resolvedExternal;
break;
@@ -214,7 +206,7 @@ namespace Mono.Linker.Steps {
void MatchExportedType (ExportedType exportedType, ModuleDefinition module, Regex regex, XPathNavigator nav)
{
if (regex.Match (exportedType.FullName).Success) {
- MarkingHelpers.MarkExportedType (exportedType, module);
+ MarkingHelpers.MarkExportedType (exportedType, module, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation));
TypeDefinition type = exportedType.Resolve ();
if (type != null) {
ProcessType (type, nav);
@@ -257,21 +249,19 @@ namespace Mono.Linker.Steps {
Context.LogMessage ($"Duplicate preserve in {_xmlDocumentLocation} of {type.FullName} ({existingLevel}). Duplicate uses ({duplicateLevel})");
}
- Annotations.MarkAndPush (type);
- Tracer.AddDirectDependency (this, type);
+ Annotations.Mark (type, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation));
if (type.IsNested) {
- var parent = type;
- while (parent.IsNested) {
- parent = parent.DeclaringType;
- Annotations.Mark (parent);
+ var currentType = type;
+ while (currentType.IsNested) {
+ var parent = currentType.DeclaringType;
+ Context.Annotations.Mark (parent, new DependencyInfo (DependencyKind.DeclaringType, currentType));
+ currentType = parent;
}
}
if (preserve != TypePreserve.Nothing)
Annotations.SetPreserve (type, preserve);
-
- Tracer.Pop ();
}
void MarkSelectedFields (XPathNavigator nav, TypeDefinition type)
@@ -363,7 +353,7 @@ namespace Mono.Linker.Steps {
if (Annotations.IsMarked (field))
Context.LogMessage ($"Duplicate preserve in {_xmlDocumentLocation} of {field.FullName}");
- Annotations.Mark (field);
+ Context.Annotations.Mark (field, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation));
} else {
AddUnresolveMarker (string.Format ("T: {0}; F: {1}", type, signature));
}
@@ -435,9 +425,8 @@ namespace Mono.Linker.Steps {
if (Annotations.IsMarked (method))
Context.LogMessage ($"Duplicate preserve in {_xmlDocumentLocation} of {method.FullName}");
- Annotations.Mark (method);
+ Annotations.Mark (method, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation));
Annotations.MarkIndirectlyCalledMethod (method);
- Tracer.AddDirectDependency (this, method);
Annotations.SetAction (method, MethodAction.Parse);
}
@@ -525,7 +514,7 @@ namespace Mono.Linker.Steps {
if (Annotations.IsMarked (@event))
Context.LogMessage ($"Duplicate preserve in {_xmlDocumentLocation} of {@event.FullName}");
- Annotations.Mark (@event);
+ Annotations.Mark (@event, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation));
MarkMethod (@event.AddMethod);
MarkMethod (@event.RemoveMethod);
@@ -593,7 +582,7 @@ namespace Mono.Linker.Steps {
if (Annotations.IsMarked (property))
Context.LogMessage ($"Duplicate preserve in {_xmlDocumentLocation} of {property.FullName}");
- Annotations.Mark (property);
+ Annotations.Mark (type, new DependencyInfo (DependencyKind.XmlDescriptor, _xmlDocumentLocation));
MarkPropertyAccessors (type, property, accessors);
} else
diff --git a/src/linker/Linker/Annotations.cs b/src/linker/Linker/Annotations.cs
index 1b8af1763..cb1f3a49f 100644
--- a/src/linker/Linker/Annotations.cs
+++ b/src/linker/Linker/Annotations.cs
@@ -28,6 +28,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using Mono.Cecil;
using Mono.Cecil.Cil;
@@ -150,21 +151,30 @@ namespace Mono.Linker {
return fieldType_init.Contains (type);
}
+ [Obsolete ("Mark token providers with a reason instead.")]
public void Mark (IMetadataTokenProvider provider)
{
marked.Add (provider);
- Tracer.AddDependency (provider, true);
}
+ public void Mark (IMetadataTokenProvider provider, in DependencyInfo reason)
+ {
+ Debug.Assert (!(reason.Kind == DependencyKind.AlreadyMarked));
+ marked.Add (provider);
+ Tracer.AddDirectDependency (provider, reason, marked: true);
+ }
+
+ [Obsolete ("Mark attributes with a reason instead.")]
public void Mark (CustomAttribute attribute)
{
marked_attributes.Add (attribute);
}
- public void MarkAndPush (IMetadataTokenProvider provider)
+ public void Mark (CustomAttribute attribute, in DependencyInfo reason)
{
- Mark (provider);
- Tracer.Push (provider, false);
+ Debug.Assert (!(reason.Kind == DependencyKind.AlreadyMarked));
+ marked_attributes.Add (attribute);
+ Tracer.AddDirectDependency (attribute, reason, marked: true);
}
public bool IsMarked (IMetadataTokenProvider provider)
diff --git a/src/linker/Linker/DependencyInfo.cs b/src/linker/Linker/DependencyInfo.cs
new file mode 100644
index 000000000..73dc41c03
--- /dev/null
+++ b/src/linker/Linker/DependencyInfo.cs
@@ -0,0 +1,144 @@
+using System;
+
+namespace Mono.Linker
+{
+ public enum DependencyKind {
+ // For tracking any other kinds of dependencies in extensions to the core logic
+ Custom = -1, // the source is reserved to carry any dependency information tracked by the extender
+
+ // For use when we don't care about tracking a particular dependency
+ Unspecified = 0, // currently unused
+
+ // Entry points to the analysis
+ AssemblyAction = 1, // assembly action -> entry assembly
+ RootAssembly = 2, // assembly -> entry type
+ XmlDescriptor = 3, // xml document -> entry member
+ // Attributes on assemblies are marked whether or not we keep
+ // the assembly, so mark these as entry points.
+ AssemblyOrModuleAttribute = 4, // assembly/module -> entry attribute
+
+ // Membership and containment relationships
+ NestedType = 5, // parent type -> nested type
+ MemberOfType = 6, // type -> member
+ DeclaringType = 7, // member -> type
+ FieldOnGenericInstance = 8, // fieldref on instantiated generic -> field on generic typedef
+ MethodOnGenericInstance = 9, // methodref on instantiated generic -> method on generic typedef
+
+ // Type relationships
+ BaseType = 10, // type -> its base type
+ FieldType = 11, // field -> its type
+ ParameterType = 12, // method -> types of its parameters
+ ReturnType = 13, // method -> type it returns
+ VariableType = 14, // method -> types of its variables
+ CatchType = 15, // method -> types of its exception handlers
+
+ // Override relationships
+ BaseMethod = 16, // override -> base method
+ Override = 17, // base method -> override
+ MethodImplOverride = 18, // method -> .override on the method
+ VirtualNeededDueToPreservedScope = 19, // type -> virtuals kept because scope requires it
+ MethodForInstantiatedType = 20, // type -> methods kept because type is instantiated or scope requires it
+ BaseDefaultCtorForStubbedMethod = 21, // stubbed method -> default ctor of base type
+
+ // Generic type relationships
+ GenericArgumentType = 22, // generic instance -> argument type
+ GenericParameterConstraintType = 23, // generic typedef/methoddef -> parameter constraint type
+ DefaultCtorForNewConstrainedGenericArgument = 24, // generic instance -> argument ctor
+ ElementType = 25, // generic type instantiation -> generic typedef
+ ElementMethod = 26, // generic method instantiation -> generic methoddef
+ ModifierType = 27, // modified type -> type modifier
+
+ // Modules and assemblies
+ ScopeOfType = 28, // type -> module/assembly
+ TypeInAssembly = 29, // assembly -> type
+ ModuleOfExportedType = 30, // exported type -> module
+ ExportedType = 31, // type -> exported type
+
+ // Grouping of property/event methods
+ PropertyOfPropertyMethod = 32, // property method -> its property
+ EventOfEventMethod = 33, // event method -> its event
+ EventMethod = 34, // event -> its event methods
+ // PropertyMethod doesn't exist because property methods aren't always marked for a property
+
+ // Interface implementations
+ InterfaceImplementationInterfaceType = 35, // interfaceimpl -> interface type
+ InterfaceImplementationOnType = 36, // type -> interfaceimpl on it
+
+ // Interop methods
+ ReturnTypeMarshalSpec = 37, // interop method -> marshal spec of its return type
+ ParameterMarshalSpec = 38, // interop method -> marshal spec of its parameters
+ FieldMarshalSpec = 39, // field -> its marshal spec
+ InteropMethodDependency = 40, // interop method -> required members of its parameters, return type, declaring type
+
+ // Dependencies created by instructions
+ DirectCall = 41, // method -> method
+ VirtualCall = 42, // method -> method
+ Ldvirtftn = 43, // method -> method
+ Ldftn = 44, // method -> method
+ Newobj = 45, // method -> method
+ Ldtoken = 46, // method -> member referenced
+ FieldAccess = 47, // method -> field (for instructions that load/store fields)
+ InstructionTypeRef = 48, // other instructions that have an inline type token (method -> type)
+
+ // Custom attributes on various providers
+ CustomAttribute = 49, // attribute provider (type/field/method/etc...) -> attribute on it
+ ParameterAttribute = 50, // method parameter -> attribute on it
+ ReturnTypeAttribute = 51, // method return type -> attribute on it
+ GenericParameterCustomAttribute = 52, // generic parameter -> attribute on it
+ GenericParameterConstraintCustomAttribute = 53, // generic parameter constraint -> attribute on it
+
+ // Dependencies of custom attributes
+ AttributeConstructor = 54, // attribute -> its ctor
+ // used for security attributes, where we mark the type/properties directly
+ AttributeType = 55, // attribute -> attribute type
+ AttributeProperty = 56, // attribute -> attribute property
+ CustomAttributeArgumentType = 57, // attribute -> type of an argument to the attribute ctor
+ CustomAttributeArgumentValue = 58, // attribute -> type passed as an argument to the attribute ctor
+ CustomAttributeField = 59, // attribute -> field on the attribute
+
+ // Tracking cctors
+ TriggersCctorThroughFieldAccess = 60, // field-accessing method -> cctor of field's declaring type
+ TriggersCctorForCalledMethod = 61, // caller method -> callee method type cctor
+ DeclaringTypeOfCalledMethod = 62, // called method -> its declaring type (used to track when a cctor is triggered by a method call)
+ CctorForType = 63, // type -> cctor of type
+ CctorForField = 64, // field -> cctor of field's declaring type
+
+ // Tracking instantiations
+ InstantiatedByCtor = 65, // ctor -> its declaring type (indicating that it was marked instantiated due to the ctor)
+ OverrideOnInstantiatedType = 66, // instantiated type -> override method on the type
+
+ // Linker-specific behavior (preservation hints, patterns, user inputs, linker outputs, etc.)
+ PreservedDependency = 67, // PreserveDependency attribute -> member
+ AccessedViaReflection = 68, // method -> detected member accessed via reflection from that method
+ PreservedMethod = 69, // type/method -> preserved method (explicitly preserved in Annotations by XML or other steps)
+ TypePreserve = 70, // type -> field/method preserved for the type (explicitly set in Annotations by XML or other steps)
+ DisablePrivateReflection = 71, // type/method -> DisablePrivateReflection attribute added by linkerf
+
+ // Built-in knowledge of special runtime/diagnostic subsystems
+ // XmlSchemaProvider, DebuggerDisplay, DebuggerTypeProxy, SoapHeader, TypeDescriptionProvider
+ ReferencedBySpecialAttribute = 72, // attribute -> referenced members
+ KeptForSpecialAttribute = 73, // attribute -> kept members (used when the members are not explicitly referenced)
+ SerializationMethodForType = 74, // type -> method required for serialization
+ EventSourceProviderField = 75, // EventSource derived type -> fields on nested Keywords/Tasks/Opcodes provider classes
+ MethodForSpecialType = 76, // type -> methods kept (currently used for MulticastDelegate)
+
+ // Linker internals, requirements for certain optimizations
+ UnreachableBodyRequirement = 77, // method -> well-known type required for unreachable bodies optimization
+ DisablePrivateReflectionRequirement = 78, // null -> DisablePrivateReflectionAttribute type/methods (note that no specific source is reported)
+ AlreadyMarked = 79, // null -> member that has already been marked for a particular reason (used to propagate reasons internally, not reported)
+ }
+
+ readonly public struct DependencyInfo : IEquatable<DependencyInfo> {
+ public DependencyKind Kind { get; }
+ public object Source { get; }
+ public DependencyInfo (DependencyKind kind, object source) => (Kind, Source) = (kind, source);
+ public static readonly DependencyInfo Unspecified = new DependencyInfo (DependencyKind.Unspecified, null);
+ public static readonly DependencyInfo AlreadyMarked = new DependencyInfo (DependencyKind.AlreadyMarked, null);
+ public static readonly DependencyInfo DisablePrivateReflectionRequirement = new DependencyInfo (DependencyKind.DisablePrivateReflectionRequirement, null);
+ public bool Equals (DependencyInfo other) => (Kind, Source) == (other.Kind, other.Source);
+ public override bool Equals (Object obj) => obj is DependencyInfo info && this.Equals (info);
+ public override int GetHashCode() => (Kind, Source).GetHashCode ();
+ public static bool operator == (DependencyInfo lhs, DependencyInfo rhs) => lhs.Equals (rhs);
+ public static bool operator != (DependencyInfo lhs, DependencyInfo rhs) => !lhs.Equals (rhs);
+ }
+}
diff --git a/src/linker/Linker/DirectoryAssemblyResolver.cs b/src/linker/Linker/DirectoryAssemblyResolver.cs
index beae353eb..5b9a95be9 100644
--- a/src/linker/Linker/DirectoryAssemblyResolver.cs
+++ b/src/linker/Linker/DirectoryAssemblyResolver.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Reflection;
using System.Collections.Generic;
using System.IO;
using Mono.Collections.Generic;
diff --git a/src/linker/Linker/Driver.cs b/src/linker/Linker/Driver.cs
index faf90259d..5d037602a 100644
--- a/src/linker/Linker/Driver.cs
+++ b/src/linker/Linker/Driver.cs
@@ -405,7 +405,7 @@ namespace Mono.Linker {
case "x":
if (!GetStringParam (token, l => {
foreach (string file in GetFiles (l))
- p.PrependStep (new ResolveFromXmlStep (new XPathDocument (file)));
+ p.PrependStep (new ResolveFromXmlStep (new XPathDocument (file), file));
}))
return false;
diff --git a/src/linker/Linker/IDependencyRecorder.cs b/src/linker/Linker/IDependencyRecorder.cs
index de4e4616e..43d2391e0 100644
--- a/src/linker/Linker/IDependencyRecorder.cs
+++ b/src/linker/Linker/IDependencyRecorder.cs
@@ -40,5 +40,16 @@ namespace Mono.Linker
/// <remarks>The source and target are typically Cecil metadata objects (MethodDefinition, TypeDefinition, ...)
/// but they can also be the linker steps or really any other object.</remarks>
void RecordDependency (object source, object target, bool marked);
+
+ /// <summary>
+ /// Reports a dependency detected by the linker, with a well-defined reason for keeping the dependency.
+ /// </summary>
+ /// <param name="target">The target of the dependency (for example the callee method).</param>
+ /// <param name="reason">The reason for including the target dependency (for example a direct call from another method).</param>
+ /// <param name="marked">true if the target is also marked by the MarkStep as a result of this particular reason.</param>
+ /// <remarks>The target is typically a Cecil metadata object (MethodDefinition, TypeDefinition, ...)
+ /// but can also be the linker steps or really any other object. "marked" may be false for a target that
+ /// is still marked for some other reason.</remarks>
+ void RecordDependency (object target, in DependencyInfo reason, bool marked);
}
}
diff --git a/src/linker/Linker/LinkContext.cs b/src/linker/Linker/LinkContext.cs
index 3dec3f767..0a0e187ee 100644
--- a/src/linker/Linker/LinkContext.cs
+++ b/src/linker/Linker/LinkContext.cs
@@ -1,4 +1,4 @@
-//
+//
// LinkContext.cs
//
// Author:
diff --git a/src/linker/Linker/MarkingHelpers.cs b/src/linker/Linker/MarkingHelpers.cs
index b477a5ce3..3d0dfb9a3 100644
--- a/src/linker/Linker/MarkingHelpers.cs
+++ b/src/linker/Linker/MarkingHelpers.cs
@@ -10,11 +10,11 @@ namespace Mono.Linker {
_context = context;
}
- public void MarkExportedType (ExportedType type, ModuleDefinition module)
+ public void MarkExportedType (ExportedType type, ModuleDefinition module, in DependencyInfo reason)
{
- _context.Annotations.Mark (type);
+ _context.Annotations.Mark (type, reason);
if (_context.KeepTypeForwarderOnlyAssemblies)
- _context.Annotations.Mark (module);
+ _context.Annotations.Mark (module, new DependencyInfo (DependencyKind.ModuleOfExportedType, type));
}
}
}
diff --git a/src/linker/Linker/MethodBodyScanner.cs b/src/linker/Linker/MethodBodyScanner.cs
index 267a7c3ba..939bed5e2 100644
--- a/src/linker/Linker/MethodBodyScanner.cs
+++ b/src/linker/Linker/MethodBodyScanner.cs
@@ -49,7 +49,7 @@ namespace Mono.Linker {
return true;
}
- public static IEnumerable<InterfaceImplementation> GetReferencedInterfaces (AnnotationStore annotations, MethodBody body)
+ public static IEnumerable<(InterfaceImplementation, TypeDefinition)> GetReferencedInterfaces (AnnotationStore annotations, MethodBody body)
{
var possibleStackTypes = AllPossibleStackTypes (body.Method);
if (possibleStackTypes.Count == 0)
@@ -59,7 +59,7 @@ namespace Mono.Linker {
if (interfaceTypes.Length == 0)
return null;
- var interfaceImplementations = new HashSet<InterfaceImplementation> ();
+ var interfaceImplementations = new HashSet<(InterfaceImplementation, TypeDefinition)> ();
// If a type could be on the stack in the body and an interface it implements could be on the stack on the body
// then we need to mark that interface implementation. When this occurs it is not safe to remove the interface implementation from the type
@@ -126,11 +126,11 @@ namespace Mono.Linker {
return types;
}
- static void AddMatchingInterfaces (HashSet<InterfaceImplementation> results, TypeDefinition type, TypeDefinition [] interfaceTypes)
+ static void AddMatchingInterfaces (HashSet<(InterfaceImplementation, TypeDefinition)> results, TypeDefinition type, TypeDefinition [] interfaceTypes)
{
foreach (var interfaceType in interfaceTypes) {
if (type.HasInterface (interfaceType, out InterfaceImplementation implementation))
- results.Add (implementation);
+ results.Add ((implementation, type));
}
}
diff --git a/src/linker/Linker/Pipeline.cs b/src/linker/Linker/Pipeline.cs
index a28de3ba1..11fae6a7d 100644
--- a/src/linker/Linker/Pipeline.cs
+++ b/src/linker/Linker/Pipeline.cs
@@ -130,9 +130,7 @@ namespace Mono.Linker {
protected virtual void ProcessStep (LinkContext context, IStep step)
{
- context.Tracer.Push (step);
step.Process (context);
- context.Tracer.Pop ();
}
public IStep [] GetSteps ()
diff --git a/src/linker/Linker/Tracer.cs b/src/linker/Linker/Tracer.cs
index 1a6b7109f..43c8aa7df 100644
--- a/src/linker/Linker/Tracer.cs
+++ b/src/linker/Linker/Tracer.cs
@@ -66,49 +66,16 @@ namespace Mono.Linker
recorders.Add (recorder);
}
- public void Push (object o, bool addDependency = true)
- {
- if (!IsRecordingEnabled ())
- return;
-
- if (addDependency && dependency_stack.Count > 0)
- AddDependency (o);
-
- dependency_stack.Push (o);
- }
-
- public void Pop ()
- {
- if (!IsRecordingEnabled ())
- return;
-
- dependency_stack.Pop ();
- }
-
bool IsRecordingEnabled ()
{
return recorders != null;
}
- public void AddDirectDependency (object b, object e)
- {
- ReportDependency (b, e, false);
- }
-
- public void AddDependency (object o, bool marked = false)
- {
- if (!IsRecordingEnabled ())
- return;
-
- ReportDependency (dependency_stack.Count > 0 ? dependency_stack.Peek () : null, o, marked);
- }
-
- private void ReportDependency (object source, object target, bool marked)
+ public void AddDirectDependency (object target, in DependencyInfo reason, bool marked)
{
if (IsRecordingEnabled ()) {
- foreach (IDependencyRecorder recorder in recorders) {
- recorder.RecordDependency (source, target, marked);
- }
+ foreach (IDependencyRecorder recorder in recorders)
+ recorder.RecordDependency (target, reason, marked);
}
}
}
diff --git a/src/linker/Linker/XmlDependencyRecorder.cs b/src/linker/Linker/XmlDependencyRecorder.cs
index 637b94c2a..8f093a29c 100644
--- a/src/linker/Linker/XmlDependencyRecorder.cs
+++ b/src/linker/Linker/XmlDependencyRecorder.cs
@@ -89,23 +89,27 @@ namespace Mono.Linker
stream = null;
}
+ public void RecordDependency (object target, in DependencyInfo reason, bool marked)
+ {
+ // For now, just report a dependency from source to target without noting the DependencyKind.
+ RecordDependency (reason.Source, target, marked);
+ }
+
public void RecordDependency (object source, object target, bool marked)
{
if (!ShouldRecord (source) && !ShouldRecord (target))
return;
- // This is a hack to work around a quirk of MarkStep that results in outputting ~6k edges even with the above ShouldRecord checks.
- // What happens is that due to the method queueing in MarkStep, the dependency chain is broken in many cases. And in these cases
- // we end up adding an edge for MarkStep -> <queued Method>
- // This isn't particularly useful information since it's incomplete, but it's especially not useful in ReducedTracing mode when there is one of these for
- // every class library method that was queued.
- if (context.EnableReducedTracing && source is MarkStep && !ShouldRecord (target))
+ // We use a few hacks to work around MarkStep outputting thousands of edges even
+ // with the above ShouldRecord checks. Ideally we would format these into a meaningful format
+ // however I don't think that is worth the effort at the moment.
+
+ // Prevent useless logging of attributes like `e="Other:Mono.Cecil.CustomAttribute"`.
+ if (source is CustomAttribute || target is CustomAttribute)
return;
- // This is another hack to prevent useless information from being logged. With the introduction of interface sweeping there are a lot of edges such as
- // `e="InterfaceImpl:Mono.Cecil.InterfaceImplementation"` which are useless information. Ideally we would format the interface implementation into a meaningful format
- // however I don't think that is worth the effort at the moment.
- if (target is InterfaceImplementation)
+ // Prevent useless logging of interface implementations like `e="InterfaceImpl:Mono.Cecil.InterfaceImplementation"`.
+ if (source is InterfaceImplementation || target is InterfaceImplementation)
return;
if (source != target) {
diff --git a/test/Mono.Linker.Tests/TestCasesRunner/TestDependencyRecorder.cs b/test/Mono.Linker.Tests/TestCasesRunner/TestDependencyRecorder.cs
index 8df724fda..4f5f57ad0 100644
--- a/test/Mono.Linker.Tests/TestCasesRunner/TestDependencyRecorder.cs
+++ b/test/Mono.Linker.Tests/TestCasesRunner/TestDependencyRecorder.cs
@@ -21,5 +21,14 @@ namespace Mono.Linker.Tests.TestCasesRunner
Marked = marked
});
}
+
+ public void RecordDependency (object target, in DependencyInfo reason, bool marked)
+ {
+ Dependencies.Add (new Dependency () {
+ Source = reason.Source?.ToString (),
+ Target = target.ToString (),
+ Marked = marked
+ });
+ }
}
}