File: Linker.Steps\SealerStep.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.Collections.Generic;
using System.Diagnostics;
using Mono.Cecil;
using Mono.Collections.Generic;
 
namespace Mono.Linker.Steps
{
	public class SealerStep : BaseStep
	{
		HashSet<TypeDefinition>? referencedBaseTypeCache;
 
		public SealerStep ()
		{
		}
 
		protected override void ProcessAssembly (AssemblyDefinition assembly)
		{
			if (!Context.CanApplyOptimization (CodeOptimizations.Sealer, assembly))
				return;
 
			foreach (var type in assembly.MainModule.Types)
				ProcessType (type);
		}
 
		protected override void EndProcess ()
		{
			referencedBaseTypeCache = null;
		}
 
		bool IsSubclassed (TypeDefinition type)
		{
			if (referencedBaseTypeCache == null) {
				referencedBaseTypeCache = new HashSet<TypeDefinition> ();
				foreach (var a in Context.GetAssemblies ()) {
					if (!Annotations.IsMarked (a))
						continue;
 
					PopulateCache (a.MainModule.Types);
 
					void PopulateCache (Collection<TypeDefinition> types)
					{
						foreach (var t in types) {
							var btd = Context.TryResolve (t.BaseType);
							if (btd != null)
								referencedBaseTypeCache.Add (btd);
 
							if (t.HasNestedTypes) {
								PopulateCache (t.NestedTypes);
							}
						}
					}
				}
			}
 
			var bt = Context.TryResolve (type);
			return bt != null && referencedBaseTypeCache.Contains (bt);
		}
 
		void ProcessType (TypeDefinition type)
		{
			if (type.HasNestedTypes) {
				foreach (var nt in type.NestedTypes) {
					ProcessType (nt);
				}
			}
 
			//
			// interface members are virtual (and we cannot change this)
			//
			if (type.IsInterface)
				return;
 
			//
			// the code does not include any subclass for this type
			//
			if (!type.IsAbstract && !type.IsSealed && !IsSubclassed (type))
				SealType (type);
 
			if (!type.HasMethods)
				return;
 
			// process methods to see if we can seal or devirtualize them
			foreach (var method in type.Methods) {
				if (method.IsFinal || !method.IsVirtual || method.IsAbstract || method.IsRuntime)
					continue;
 
				Debug.Assert (Annotations.IsMarked (method));
				if (!Annotations.IsMarked (method))
					continue;
 
				var overrides = Annotations.GetOverrides (method);
 
				//
				// cannot de-virtualize nor seal methods if something overrides them
				//
				if (IsAnyOverrideMarked (overrides))
					continue;
 
				SealMethod (method);
 
				// subclasses might need this method to satisfy an interface requirement
				// and requires dispatch/virtual support
				if (!type.IsSealed)
					continue;
 
				var bases = Annotations.GetBaseMethods (method);
				// Devirtualize if a method is not override to existing marked methods
				if (!IsAnyBaseMarked (bases))
					method.IsVirtual = method.IsFinal = method.IsNewSlot = false;
			}
		}
 
		protected virtual void SealType (TypeDefinition type)
		{
			type.IsSealed = true;
		}
 
		protected virtual void SealMethod (MethodDefinition method)
		{
			method.IsFinal = true;
		}
 
		bool IsAnyOverrideMarked (IEnumerable<OverrideInformation>? list)
		{
			if (list == null)
				return false;
 
			foreach (var m in list) {
				if (Annotations.IsMarked (m.Override))
					return true;
			}
			return false;
		}
 
		bool IsAnyBaseMarked (IEnumerable<OverrideInformation>? list)
		{
			if (list == null)
				return false;
 
			foreach (var m in list) {
				if (Annotations.IsMarked (m.Base))
					return true;
			}
			return false;
		}
	}
}