File: Linker\DependencyRecorderHelper.cs
Web Access
Project: src\src\tools\illink\src\linker\Mono.Linker.csproj (illink)
// 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;
		}
	}
}