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