|
// 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);
}
}
|