| File: Linker\TypeReferenceWalker.cs | Web Access |
| Project: src\runtime\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); } }