File: Linker.Steps\RootAssemblyInputStep.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.IO;
using ILLink.Shared;
using Mono.Cecil;
 
namespace Mono.Linker.Steps
{
	public class RootAssemblyInput : BaseStep
	{
		readonly string fileName;
		readonly AssemblyRootMode rootMode;
 
		public RootAssemblyInput (string fileName, AssemblyRootMode rootMode)
		{
			this.fileName = fileName;
			this.rootMode = rootMode;
		}
 
		protected override void Process ()
		{
			AssemblyDefinition? assembly = LoadAssemblyFile ();
			if (assembly == null)
				return;
 
			var di = new DependencyInfo (DependencyKind.RootAssembly, assembly);
			var origin = new MessageOrigin (assembly);
 
			AssemblyAction action = Context.Annotations.GetAction (assembly);
			switch (action) {
			case AssemblyAction.Copy:
				Annotations.Mark (assembly.MainModule, di, origin);
				// Mark Step will take care of marking whole assembly
				return;
			case AssemblyAction.CopyUsed:
			case AssemblyAction.Link:
				break;
			default:
				Context.LogError (null, DiagnosticId.RootAssemblyCannotUseAction, assembly.Name.ToString (), action.ToString ());
				return;
			}
 
			switch (rootMode) {
			case AssemblyRootMode.EntryPoint:
				var ep = assembly.MainModule.EntryPoint;
				if (ep == null) {
					Context.LogError (null, DiagnosticId.RootAssemblyDoesNotHaveEntryPoint, assembly.Name.ToString ());
					return;
				}
 
				Annotations.Mark (ep.DeclaringType, di, origin);
				Annotations.AddPreservedMethod (ep.DeclaringType, ep);
				break;
			case AssemblyRootMode.VisibleMembers:
				var preserve_visible = TypePreserveMembers.Visible;
				if (MarkInternalsVisibleTo (assembly))
					preserve_visible |= TypePreserveMembers.Internal;
 
				MarkAndPreserve (assembly, preserve_visible);
				break;
 
			case AssemblyRootMode.Library:
				var preserve_library = TypePreserveMembers.Visible | TypePreserveMembers.Library;
				if (MarkInternalsVisibleTo (assembly))
					preserve_library |= TypePreserveMembers.Internal;
 
				MarkAndPreserve (assembly, preserve_library);
 
				// Assembly root mode wins over any enabled optimization which
				// could conflict with library rooting behaviour
				Context.Optimizations.Disable (
					CodeOptimizations.Sealer |
					CodeOptimizations.UnusedTypeChecks |
					CodeOptimizations.UnreachableBodies |
					CodeOptimizations.UnusedInterfaces |
					CodeOptimizations.RemoveDescriptors |
					CodeOptimizations.RemoveLinkAttributes |
					CodeOptimizations.RemoveSubstitutions |
					CodeOptimizations.RemoveDynamicDependencyAttribute |
					CodeOptimizations.OptimizeTypeHierarchyAnnotations |
					CodeOptimizations.SubstituteFeatureGuards, assembly.Name.Name);
 
				// Enable EventSource special handling
				Context.DisableEventSourceSpecialHandling = false;
 
				// No metadata trimming
				Context.MetadataTrimming = MetadataTrimming.None;
				break;
			case AssemblyRootMode.AllMembers:
				Annotations.SetRootAssembly (assembly);
				Annotations.Mark (assembly.MainModule, di, origin);
				return;
			}
		}
 
		AssemblyDefinition? LoadAssemblyFile ()
		{
			AssemblyDefinition? assembly;
 
			if (File.Exists (fileName)) {
				assembly = Context.Resolver.GetAssembly (fileName);
				Context.Resolver.CacheAssembly (assembly);
				return assembly;
			}
 
			//
			// Quirks mode for netcore to support passing ambiguous assembly name
			//
			assembly = Context.TryResolve (fileName);
			if (assembly == null)
				Context.LogError (null, DiagnosticId.RootAssemblyCouldNotBeFound, fileName);
 
			return assembly;
		}
 
		void MarkAndPreserve (AssemblyDefinition assembly, TypePreserveMembers preserve)
		{
			var module = assembly.MainModule;
			if (module.HasExportedTypes)
				foreach (var type in module.ExportedTypes)
					MarkAndPreserve (assembly, type, preserve);
 
			foreach (var type in module.Types)
				MarkAndPreserve (type, preserve);
		}
 
		void MarkAndPreserve (TypeDefinition type, TypePreserveMembers preserve)
		{
			TypePreserveMembers preserve_anything = preserve;
			if ((preserve & TypePreserveMembers.Visible) != 0 && !IsTypeVisible (type))
				preserve_anything &= ~TypePreserveMembers.Visible;
 
			if ((preserve & TypePreserveMembers.Internal) != 0 && IsTypePrivate (type))
				preserve_anything &= ~TypePreserveMembers.Internal;
 
			// Keep all interfaces and interface members in library mode
			if ((preserve & TypePreserveMembers.Library) != 0 && type.IsInterface) {
				Annotations.Mark (type, new DependencyInfo (DependencyKind.RootAssembly, type.Module.Assembly), new MessageOrigin (type.Module.Assembly));
				Annotations.SetPreserve (type, TypePreserve.All);
			}
 
			switch (preserve_anything) {
			case 0:
				return;
			case TypePreserveMembers.Library:
				//
				// In library mode private type can have members kept for serialization if
				// the type is referenced
				//
				preserve = preserve_anything;
				Annotations.SetMembersPreserve (type, preserve);
				break;
			default:
				Annotations.Mark (type, new DependencyInfo (DependencyKind.RootAssembly, type.Module.Assembly), new MessageOrigin (type.Module.Assembly));
				Annotations.SetMembersPreserve (type, preserve);
				break;
			}
 
			if (!type.HasNestedTypes)
				return;
 
			foreach (TypeDefinition nested in type.NestedTypes)
				MarkAndPreserve (nested, preserve);
		}
 
		void MarkAndPreserve (AssemblyDefinition assembly, ExportedType type, TypePreserveMembers preserve)
		{
			var di = new DependencyInfo (DependencyKind.RootAssembly, assembly);
			var origin = new MessageOrigin (assembly);
			Context.Annotations.Mark (type, di, origin);
			Context.Annotations.Mark (assembly.MainModule, di, origin);
			Annotations.SetMembersPreserve (type, preserve);
		}
 
		static bool IsTypeVisible (TypeDefinition type)
		{
			return type.IsPublic || type.IsNestedPublic || type.IsNestedFamily || type.IsNestedFamilyOrAssembly;
		}
 
		static bool IsTypePrivate (TypeDefinition type)
		{
			return type.IsNestedPrivate;
		}
 
		bool MarkInternalsVisibleTo (AssemblyDefinition assembly)
		{
			foreach (CustomAttribute attribute in assembly.CustomAttributes) {
				if (attribute.Constructor.DeclaringType.IsTypeOf ("System.Runtime.CompilerServices", "InternalsVisibleToAttribute")) {
					Context.Annotations.Mark (attribute, new DependencyInfo (DependencyKind.RootAssembly, assembly));
					return true;
				}
			}
 
			return false;
		}
	}
}