File: Linker\TypeReferenceWalker.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;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
 
namespace Mono.Linker
{
	abstract class TypeReferenceWalker
	{
		protected readonly AssemblyDefinition assembly;
 
		protected HashSet<TypeReference> Visited { get; } = new HashSet<TypeReference> ();
 
		readonly bool walkSymbols;
 
		public TypeReferenceWalker (AssemblyDefinition assembly, bool walkSymbols)
		{
			this.assembly = assembly;
			this.walkSymbols = walkSymbols;
		}
 
		// Traverse the assembly and mark the scopes of discovered type references (but not exported types).
		// This includes scopes referenced by Cecil TypeReference objects that don't represent rows in the typeref table,
		// such as references to built-in types, or attribute arguments which encode type references as strings.
		public virtual void Process ()
		{
			if (Visited.Count > 0)
				throw new InvalidOperationException ();
 
			WalkCustomAttributesTypesScopes (assembly);
			WalkSecurityAttributesTypesScopes (assembly);
 
			foreach (var module in assembly.Modules)
				WalkCustomAttributesTypesScopes (module);
 
			var mmodule = assembly.MainModule;
			if (mmodule.HasTypes) {
				foreach (var type in mmodule.Types) {
					WalkScopes (type);
				}
			}
 
			if (mmodule.HasExportedTypes)
				WalkTypeScope (mmodule.ExportedTypes);
 
			ProcessExtra ();
		}
 
		protected virtual void ProcessExtra () { }
 
		void WalkScopes (TypeDefinition typeDefinition)
		{
			WalkCustomAttributesTypesScopes (typeDefinition);
			WalkSecurityAttributesTypesScopes (typeDefinition);
 
			if (typeDefinition.BaseType != null)
				WalkScopeOfTypeReference (typeDefinition.BaseType);
 
			if (typeDefinition.HasInterfaces) {
				foreach (var iface in typeDefinition.Interfaces) {
					WalkCustomAttributesTypesScopes (iface);
					WalkScopeOfTypeReference (iface.InterfaceType);
				}
			}
 
			if (typeDefinition.HasGenericParameters)
				WalkTypeScope (typeDefinition.GenericParameters);
 
			if (typeDefinition.HasEvents) {
				foreach (var e in typeDefinition.Events) {
					WalkCustomAttributesTypesScopes (e);
					// e.EventType is not saved
				}
			}
 
			if (typeDefinition.HasFields) {
				foreach (var f in typeDefinition.Fields) {
					WalkCustomAttributesTypesScopes (f);
					WalkScopeOfTypeReference (f.FieldType);
					WalkMarshalInfoTypeScope (f);
				}
			}
 
			if (typeDefinition.HasMethods) {
				foreach (var m in typeDefinition.Methods) {
					WalkCustomAttributesTypesScopes (m);
					WalkSecurityAttributesTypesScopes (m);
					if (m.HasGenericParameters)
						WalkTypeScope (m.GenericParameters);
 
					WalkCustomAttributesTypesScopes (m.MethodReturnType);
					WalkScopeOfTypeReference (m.MethodReturnType.ReturnType);
					WalkMarshalInfoTypeScope (m.MethodReturnType);
					if (m.HasOverrides) {
						foreach (var mo in m.Overrides)
							WalkMethodReference (mo);
					}
#pragma warning disable RS0030 // MethodReference.Parameters is banned - It's best to leave this as is
					if (m.HasMetadataParameters ())
						WalkTypeScope (m.Parameters);
#pragma warning restore RS0030
 
					if (m.HasBody)
						WalkTypeScope (m.Body);
 
					if (walkSymbols && m.DebugInformation?.Scope?.Import is ImportDebugInformation import)
						WalkDebugInfoImportScope (import);
				}
			}
 
			if (typeDefinition.HasProperties) {
				foreach (var p in typeDefinition.Properties) {
					WalkCustomAttributesTypesScopes (p);
					// p.PropertyType is not saved
				}
			}
 
			if (typeDefinition.HasNestedTypes) {
				foreach (var nestedType in typeDefinition.NestedTypes) {
					WalkScopes (nestedType);
				}
			}
		}
 
		void WalkTypeScope (Collection<GenericParameter> genericParameters)
		{
			foreach (var gp in genericParameters) {
				WalkCustomAttributesTypesScopes (gp);
				if (gp.HasConstraints)
					WalkTypeScope (gp.Constraints);
			}
		}
 
		void WalkTypeScope (Collection<GenericParameterConstraint> constraints)
		{
			foreach (var gc in constraints) {
				WalkCustomAttributesTypesScopes (gc);
				WalkScopeOfTypeReference (gc.ConstraintType);
			}
		}
 
		void WalkTypeScope (Collection<ParameterDefinition> parameters)
		{
			foreach (var p in parameters) {
				WalkCustomAttributesTypesScopes (p);
				WalkScopeOfTypeReference (p.ParameterType);
				WalkMarshalInfoTypeScope (p);
			}
		}
 
		void WalkTypeScope (Collection<ExportedType> forwarders)
		{
			foreach (var f in forwarders)
				ProcessExportedType (f);
		}
 
		void WalkTypeScope (MethodBody body)
		{
#pragma warning disable RS0030 // Processing type references should not trigger method marking/processing, so access Cecil directly
			if (body.HasVariables) {
				foreach (var v in body.Variables) {
					WalkScopeOfTypeReference (v.VariableType);
				}
			}
 
			if (body.HasExceptionHandlers) {
				foreach (var eh in body.ExceptionHandlers) {
					if (eh.CatchType != null)
						WalkScopeOfTypeReference (eh.CatchType);
				}
			}
 
			foreach (var instr in body.Instructions) {
				switch (instr.OpCode.OperandType) {
 
				case OperandType.InlineMethod: {
						var mr = (MethodReference) instr.Operand;
						WalkMethodReference (mr);
						break;
					}
 
				case OperandType.InlineField: {
						var fr = (FieldReference) instr.Operand;
						WalkFieldReference (fr);
						break;
					}
 
				case OperandType.InlineTok: {
						switch (instr.Operand) {
						case TypeReference tr:
							WalkScopeOfTypeReference (tr);
							break;
						case FieldReference fr:
							WalkFieldReference (fr);
							break;
						case MethodReference mr:
							WalkMethodReference (mr);
							break;
						}
 
						break;
					}
 
				case OperandType.InlineType: {
						var tr = (TypeReference) instr.Operand;
						WalkScopeOfTypeReference (tr);
						break;
					}
				}
			}
#pragma warning restore RS0030 // Do not used banned APIs
		}
 
		void WalkMethodReference (MethodReference mr)
		{
			WalkScopeOfTypeReference (mr.ReturnType);
			WalkScopeOfTypeReference (mr.DeclaringType);
 
			if (mr is GenericInstanceMethod gim) {
				foreach (var tr in gim.GenericArguments)
					WalkScopeOfTypeReference (tr);
			}
 
			if (mr.HasMetadataParameters ()) {
#pragma warning disable RS0030 // MethedReference.Parameters is banned. Best to leave working code as is.
				WalkTypeScope (mr.Parameters);
#pragma warning restore RS0030 // Do not used banned APIs
			}
		}
 
		void WalkFieldReference (FieldReference fr)
		{
			WalkScopeOfTypeReference (fr.FieldType);
			WalkScopeOfTypeReference (fr.DeclaringType);
		}
 
		void WalkMarshalInfoTypeScope (IMarshalInfoProvider provider)
		{
			if (!provider.HasMarshalInfo)
				return;
 
			if (provider.MarshalInfo is CustomMarshalInfo cmi)
				WalkScopeOfTypeReference (cmi.ManagedType);
		}
 
		void WalkCustomAttributesTypesScopes (ICustomAttributeProvider customAttributeProvider)
		{
			if (!customAttributeProvider.HasCustomAttributes)
				return;
 
			foreach (var ca in customAttributeProvider.CustomAttributes)
				WalkForwardedTypesScope (ca);
		}
 
		void WalkSecurityAttributesTypesScopes (ISecurityDeclarationProvider securityAttributeProvider)
		{
			if (!securityAttributeProvider.HasSecurityDeclarations)
				return;
 
			foreach (var ca in securityAttributeProvider.SecurityDeclarations) {
				if (!ca.HasSecurityAttributes)
					continue;
 
				foreach (var securityAttribute in ca.SecurityAttributes)
					WalkForwardedTypesScope (securityAttribute);
			}
		}
 
		void WalkForwardedTypesScope (CustomAttribute attribute)
		{
			WalkMethodReference (attribute.Constructor);
 
			if (attribute.HasConstructorArguments) {
				foreach (var ca in attribute.ConstructorArguments)
					WalkForwardedTypesScope (ca);
			}
 
			if (attribute.HasFields) {
				foreach (var field in attribute.Fields)
					WalkForwardedTypesScope (field.Argument);
			}
 
			if (attribute.HasProperties) {
				foreach (var property in attribute.Properties)
					WalkForwardedTypesScope (property.Argument);
			}
		}
 
		void WalkForwardedTypesScope (SecurityAttribute attribute)
		{
			if (attribute.HasFields) {
				foreach (var field in attribute.Fields)
					WalkForwardedTypesScope (field.Argument);
			}
 
			if (attribute.HasProperties) {
				foreach (var property in attribute.Properties)
					WalkForwardedTypesScope (property.Argument);
			}
		}
 
		void WalkForwardedTypesScope (CustomAttributeArgument attributeArgument)
		{
			WalkScopeOfTypeReference (attributeArgument.Type);
 
			switch (attributeArgument.Value) {
			case TypeReference tr:
				WalkScopeOfTypeReference (tr);
				break;
			case CustomAttributeArgument caa:
				WalkForwardedTypesScope (caa);
				break;
			case CustomAttributeArgument[] array:
				foreach (var item in array)
					WalkForwardedTypesScope (item);
				break;
			}
		}
 
		void WalkDebugInfoImportScope (ImportDebugInformation import)
		{
			if (import.HasTargets) {
				foreach (var target in import.Targets)
					WalkScopeOfTypeReference (target.Type);
			}
 
			if (import.Parent is ImportDebugInformation parent)
				WalkDebugInfoImportScope (parent);
		}
 
		void WalkScopeOfTypeReference (TypeReference type)
		{
			if (type == null)
				return;
 
			if (!Visited.Add (type))
				return;
 
			// Don't walk the scope of windows runtime projections
			if (type.IsWindowsRuntimeProjection)
				return;
 
			switch (type) {
			case GenericInstanceType git:
				WalkScopeOfTypeReference (git.ElementType);
				foreach (var ga in git.GenericArguments)
					WalkScopeOfTypeReference (ga);
				return;
			case FunctionPointerType fpt:
				WalkScopeOfTypeReference (fpt.ReturnType);
				if (fpt.HasParameters)
					WalkTypeScope (fpt.Parameters);
				return;
			case IModifierType imt:
				WalkScopeOfTypeReference (imt.ModifierType);
				WalkScopeOfTypeReference (imt.ElementType);
				return;
			case TypeSpecification ts:
				WalkScopeOfTypeReference (ts.ElementType);
				return;
			case TypeDefinition:
			case GenericParameter:
				// Nothing to walk
				return;
			}
 
			ProcessTypeReference (type);
		}
 
		protected abstract void ProcessTypeReference (TypeReference type);
 
		protected abstract void ProcessExportedType (ExportedType exportedType);
	}
 
}