|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Diagnostics;
using Mono.Cecil;
namespace Mono.Linker
{
/// <summary>
/// Class which implements IDependencyRecorder and writes the dependencies into an DGML file.
/// </summary>
public class DependencyRecorderHelper
{
static bool IsAssemblyBound(TypeDefinition td)
{
do
{
if (td.IsNestedPrivate || td.IsNestedAssembly || td.IsNestedFamilyAndAssembly)
return true;
td = td.DeclaringType;
} while (td != null);
return false;
}
public static string TokenString(LinkContext context, object? o)
{
if (o == null)
return "N:null";
if (o is TypeReference t)
{
bool addAssembly = true;
var td = context.TryResolve(t);
if (td != null)
{
addAssembly = td.IsNotPublic || IsAssemblyBound(td);
t = td;
}
var addition = addAssembly ? $":{t.Module}" : "";
return $"{((IMetadataTokenProvider)o).MetadataToken.TokenType}:{o}{addition}";
}
if (o is IMetadataTokenProvider provider)
return provider.MetadataToken.TokenType + ":" + o;
return "Other:" + o;
}
static bool ShouldRecordAssembly(LinkContext context, AssemblyDefinition assembly)
{
Debug.Assert(context.EnableReducedTracing || context.TraceAssembly != null);
if (context.TraceAssembly != null && !context.TraceAssembly.Contains(assembly.Name.Name))
return false; // We were asked to only trace a specific set of assemblies and this is not one of them
// We were either asked to trace in general, or to trace this assembly in particular.
// Note: with reduced tracing, we may still not trace an assembly if it's not linked.
if (context.EnableReducedTracing)
{
switch (context.Annotations.GetAction(assembly))
{
case AssemblyAction.Link:
case AssemblyAction.AddBypassNGen:
case AssemblyAction.AddBypassNGenUsed:
return true;
default:
return false;
}
}
return true;
}
public static bool ShouldRecord(LinkContext context, object? source, object target)
{
if (source == null || target == null)
return false;
// 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 false;
// Prevent useless logging of interface implementations like `e="InterfaceImpl:Mono.Cecil.InterfaceImplementation"`.
if (source is InterfaceImplementation || target is InterfaceImplementation)
return false;
if (!ShouldRecord(context, source) && !ShouldRecord(context, target))
{
return false;
}
return true;
}
public static bool ShouldRecord(LinkContext context, object? o)
{
// If tracing a specific set of assemblies (TraceAssembly != null),
// this takes precedence over the EnableReducedTracing setting.
if (!context.EnableReducedTracing && context.TraceAssembly == null)
return true;
if (o is TypeDefinition t)
return ShouldRecordAssembly(context, t.Module.Assembly);
if (o is IMemberDefinition m)
return ShouldRecordAssembly(context, m.DeclaringType.Module.Assembly);
if (o is TypeReference typeRef)
{
var resolved = context.TryResolve(typeRef);
// Err on the side of caution if we can't resolve
if (resolved == null)
return true;
return ShouldRecordAssembly(context, resolved.Module.Assembly);
}
if (o is MemberReference mRef)
{
var resolved = mRef.Resolve();
// Err on the side of caution if we can't resolve
if (resolved == null)
return true;
return ShouldRecordAssembly(context, resolved.DeclaringType.Module.Assembly);
}
if (o is ModuleDefinition module)
return ShouldRecordAssembly(context, module.Assembly);
if (o is AssemblyDefinition assembly)
return ShouldRecordAssembly(context, assembly);
if (o is ParameterDefinition parameter)
{
if (parameter.Method is MethodDefinition parameterMethodDefinition)
return ShouldRecordAssembly(context, parameterMethodDefinition.DeclaringType.Module.Assembly);
}
return true;
}
}
}
|