// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
///
/// A set of helpers for extracting elements from metadata.
/// This type is not responsible for managing the underlying storage
/// backing the PE image.
///
internal sealed class PEModule : IDisposable
{
///
/// We need to store reference to the module metadata to keep the metadata alive while
/// symbols have reference to PEModule.
///
private readonly ModuleMetadata _owner;
// Either we have PEReader or we have pointer and size of the metadata blob:
private readonly PEReader _peReaderOpt;
private readonly IntPtr _metadataPointerOpt;
private readonly int _metadataSizeOpt;
private MetadataReader _lazyMetadataReader;
private ImmutableArray _lazyAssemblyReferences;
///
/// This is a tuple for optimization purposes. In valid cases, we need to store
/// only one assembly index per type. However, if we found more than one, we
/// keep a second one as well to use it for error reporting.
/// We use -1 in case there was no forward.
///
private Dictionary _lazyForwardedTypesToAssemblyIndexMap;
private readonly Lazy _lazyTypeNameCollection;
private readonly Lazy _lazyNamespaceNameCollection;
private string _lazyName;
private bool _isDisposed;
///
/// Using as a type for atomicity.
///
private ThreeState _lazyContainsNoPiaLocalTypes;
///
/// If bitmap is not null, each bit indicates whether a TypeDef
/// with corresponding RowId has been checked if it is a NoPia
/// local type. If the bit is 1, local type will have an entry
/// in m_lazyTypeDefToTypeIdentifierMap.
///
private int[] _lazyNoPiaLocalTypeCheckBitMap;
///
/// For each TypeDef that has 1 in m_lazyNoPiaLocalTypeCheckBitMap,
/// this map stores corresponding TypeIdentifier AttributeInfo.
///
private ConcurrentDictionary _lazyTypeDefToTypeIdentifierMap;
// The module can be used by different compilations or different versions of the "same"
// compilation, which use different hash algorithms. Let's cache result for each distinct
// algorithm.
private readonly CryptographicHashProvider _hashesOpt;
private delegate bool AttributeValueExtractor(out T value, ref BlobReader sigReader);
private static readonly AttributeValueExtractor s_attributeStringValueExtractor = CrackStringInAttributeValue;
private static readonly AttributeValueExtractor s_attributeStringAndIntValueExtractor = CrackStringAndIntInAttributeValue;
private static readonly AttributeValueExtractor s_attributeShortValueExtractor = CrackShortInAttributeValue;
private static readonly AttributeValueExtractor s_attributeIntValueExtractor = CrackIntInAttributeValue;
private static readonly AttributeValueExtractor s_attributeLongValueExtractor = CrackLongInAttributeValue;
// Note: not a general purpose helper
private static readonly AttributeValueExtractor s_decimalValueInDecimalConstantAttributeExtractor = CrackDecimalInDecimalConstantAttribute;
private static readonly AttributeValueExtractor> s_attributeBoolArrayValueExtractor = CrackBoolArrayInAttributeValue;
private static readonly AttributeValueExtractor> s_attributeStringArrayValueExtractor = CrackStringArrayInAttributeValue;
private static readonly AttributeValueExtractor s_attributeObsoleteDataExtractor = CrackObsoleteAttributeData;
private static readonly AttributeValueExtractor s_attributeDeprecatedDataExtractor = CrackDeprecatedAttributeData;
// 'ignoreAssemblyRefs' is used by the EE only, when debugging
// .NET Native, where the corlib may have assembly references
// (see https://github.com/dotnet/roslyn/issues/13275).
internal PEModule(ModuleMetadata owner, PEReader peReader, IntPtr metadataOpt, int metadataSizeOpt, bool includeEmbeddedInteropTypes, bool ignoreAssemblyRefs)
{
// shall not throw
Debug.Assert((peReader == null) ^ (metadataOpt == IntPtr.Zero && metadataSizeOpt == 0));
Debug.Assert(metadataOpt == IntPtr.Zero || metadataSizeOpt > 0);
_owner = owner;
_peReaderOpt = peReader;
_metadataPointerOpt = metadataOpt;
_metadataSizeOpt = metadataSizeOpt;
_lazyTypeNameCollection = new Lazy(ComputeTypeNameCollection);
_lazyNamespaceNameCollection = new Lazy(ComputeNamespaceNameCollection);
_hashesOpt = (peReader != null) ? new PEHashProvider(peReader) : null;
_lazyContainsNoPiaLocalTypes = includeEmbeddedInteropTypes ? ThreeState.False : ThreeState.Unknown;
if (ignoreAssemblyRefs)
{
_lazyAssemblyReferences = ImmutableArray.Empty;
}
}
private sealed class PEHashProvider : CryptographicHashProvider
{
private readonly PEReader _peReader;
public PEHashProvider(PEReader peReader)
{
Debug.Assert(peReader != null);
_peReader = peReader;
}
internal unsafe override ImmutableArray ComputeHash(HashAlgorithm algorithm)
{
PEMemoryBlock block = _peReader.GetEntireImage();
byte[] hash;
using (var stream = new ReadOnlyUnmanagedMemoryStream(_peReader, (IntPtr)block.Pointer, block.Length))
{
hash = algorithm.ComputeHash(stream);
}
return ImmutableArray.Create(hash);
}
}
internal bool IsDisposed
{
get
{
return _isDisposed;
}
}
public void Dispose()
{
_isDisposed = true;
_peReaderOpt?.Dispose();
}
// for testing
internal PEReader PEReaderOpt
{
get
{
return _peReaderOpt;
}
}
internal MetadataReader MetadataReader
{
get
{
if (_lazyMetadataReader == null)
{
InitializeMetadataReader();
}
if (_isDisposed)
{
// Without locking, which might be expensive, we can't guarantee that the underlying memory
// won't be accessed after the metadata object is disposed. However we can do a cheap check here that
// handles most cases.
ThrowMetadataDisposed();
}
return _lazyMetadataReader;
}
}
private unsafe void InitializeMetadataReader()
{
MetadataReader newReader;
// PEModule is either created with metadata memory block or a PE reader.
if (_metadataPointerOpt != IntPtr.Zero)
{
newReader = new MetadataReader((byte*)_metadataPointerOpt, _metadataSizeOpt, MetadataReaderOptions.ApplyWindowsRuntimeProjections, StringTableDecoder.Instance);
}
else
{
Debug.Assert(_peReaderOpt != null);
// A workaround for https://github.com/dotnet/corefx/issues/1815
bool hasMetadata;
try
{
hasMetadata = _peReaderOpt.HasMetadata;
}
catch
{
hasMetadata = false;
}
if (!hasMetadata)
{
throw new BadImageFormatException(CodeAnalysisResources.PEImageDoesntContainManagedMetadata);
}
newReader = _peReaderOpt.GetMetadataReader(MetadataReaderOptions.ApplyWindowsRuntimeProjections, StringTableDecoder.Instance);
}
Interlocked.CompareExchange(ref _lazyMetadataReader, newReader, null);
}
private static void ThrowMetadataDisposed()
{
throw new ObjectDisposedException(nameof(ModuleMetadata));
}
#region Module level properties and methods
internal bool IsManifestModule
{
get
{
return MetadataReader.IsAssembly;
}
}
internal bool IsLinkedModule
{
get
{
return !MetadataReader.IsAssembly;
}
}
internal bool IsCOFFOnly
{
get
{
// default value if we only have metadata
if (_peReaderOpt == null)
{
return false;
}
return _peReaderOpt.PEHeaders.IsCoffOnly;
}
}
///
/// Target architecture of the machine.
///
internal Machine Machine
{
get
{
// platform agnostic if we only have metadata
if (_peReaderOpt == null)
{
return Machine.I386;
}
return _peReaderOpt.PEHeaders.CoffHeader.Machine;
}
}
///
/// Indicates that this PE file makes Win32 calls. See CorPEKind.pe32BitRequired for more information (http://msdn.microsoft.com/en-us/library/ms230275.aspx).
///
internal bool Bit32Required
{
get
{
// platform agnostic if we only have metadata
if (_peReaderOpt == null)
{
return false;
}
return (_peReaderOpt.PEHeaders.CorHeader.Flags & CorFlags.Requires32Bit) != 0;
}
}
internal ImmutableArray GetHash(AssemblyHashAlgorithm algorithmId)
{
Debug.Assert(_hashesOpt != null);
return _hashesOpt.GetHash(algorithmId);
}
#endregion
#region ModuleDef helpers
internal string Name
{
get
{
if (_lazyName == null)
{
_lazyName = MetadataReader.GetString(MetadataReader.GetModuleDefinition().Name);
}
return _lazyName;
}
}
/// An exception from metadata reader.
internal Guid GetModuleVersionIdOrThrow()
{
return MetadataReader.GetModuleVersionIdOrThrow();
}
#endregion
#region ModuleRef, File helpers
///
/// Returns the names of linked managed modules.
///
/// An exception from metadata reader.
internal ImmutableArray GetMetadataModuleNamesOrThrow()
{
var builder = ArrayBuilder.GetInstance();
try
{
foreach (var fileHandle in MetadataReader.AssemblyFiles)
{
var file = MetadataReader.GetAssemblyFile(fileHandle);
if (!file.ContainsMetadata)
{
continue;
}
string moduleName = MetadataReader.GetString(file.Name);
if (!MetadataHelpers.IsValidMetadataFileName(moduleName))
{
throw new BadImageFormatException(string.Format(CodeAnalysisResources.InvalidModuleName, this.Name, moduleName));
}
builder.Add(moduleName);
}
return builder.ToImmutable();
}
finally
{
builder.Free();
}
}
///
/// Returns names of referenced modules.
///
/// An exception from metadata reader.
internal IEnumerable GetReferencedManagedModulesOrThrow()
{
HashSet nameTokens = new HashSet();
foreach (var handle in MetadataReader.TypeReferences)
{
TypeReference typeRef = MetadataReader.GetTypeReference(handle);
EntityHandle scope = typeRef.ResolutionScope;
if (scope.Kind == HandleKind.ModuleReference)
{
nameTokens.Add(scope);
}
}
foreach (var token in nameTokens)
{
yield return this.GetModuleRefNameOrThrow((ModuleReferenceHandle)token);
}
}
internal ImmutableArray GetEmbeddedResourcesOrThrow()
{
if (MetadataReader.ManifestResources.Count == 0)
{
return ImmutableArray.Empty;
}
var builder = ImmutableArray.CreateBuilder();
foreach (var handle in MetadataReader.ManifestResources)
{
var resource = MetadataReader.GetManifestResource(handle);
if (resource.Implementation.IsNil)
{
string resourceName = MetadataReader.GetString(resource.Name);
builder.Add(new EmbeddedResource((uint)resource.Offset, resource.Attributes, resourceName));
}
}
return builder.ToImmutable();
}
/// An exception from metadata reader.
public string GetModuleRefNameOrThrow(ModuleReferenceHandle moduleRef)
{
return MetadataReader.GetString(MetadataReader.GetModuleReference(moduleRef).Name);
}
#endregion
#region AssemblyRef helpers
// The array is sorted by AssemblyRef RowId, starting with RowId=1 and doesn't have any RowId gaps.
public ImmutableArray ReferencedAssemblies
{
get
{
if (_lazyAssemblyReferences == null)
{
_lazyAssemblyReferences = this.MetadataReader.GetReferencedAssembliesOrThrow();
}
return _lazyAssemblyReferences;
}
}
#endregion
#region PE Header helpers
internal string MetadataVersion
{
get { return MetadataReader.MetadataVersion; }
}
#endregion
#region Heaps
/// An exception from metadata reader.
internal BlobReader GetMemoryReaderOrThrow(BlobHandle blob)
{
return MetadataReader.GetBlobReader(blob);
}
/// An exception from metadata reader.
internal string GetFullNameOrThrow(StringHandle namespaceHandle, StringHandle nameHandle)
{
var attributeTypeName = MetadataReader.GetString(nameHandle);
var attributeTypeNamespaceName = MetadataReader.GetString(namespaceHandle);
return MetadataHelpers.BuildQualifiedName(attributeTypeNamespaceName, attributeTypeName);
}
#endregion
#region AssemblyDef helpers
/// An exception from metadata reader.
internal AssemblyIdentity ReadAssemblyIdentityOrThrow()
{
return MetadataReader.ReadAssemblyIdentityOrThrow();
}
#endregion
#region TypeDef helpers
/// An exception from metadata reader.
public TypeDefinitionHandle GetContainingTypeOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).GetDeclaringType();
}
/// An exception from metadata reader.
public string GetTypeDefNameOrThrow(TypeDefinitionHandle typeDef)
{
TypeDefinition typeDefinition = MetadataReader.GetTypeDefinition(typeDef);
string name = MetadataReader.GetString(typeDefinition.Name);
Debug.Assert(name.Length == 0 || MetadataHelpers.IsValidMetadataIdentifier(name)); // Obfuscated assemblies can have types with empty names.
// The problem is that the mangled name for an static machine type looks like
// "<" + methodName + ">d__" + uniqueId.However, methodName will have dots in
// it for explicit interface implementations (e.g. "d__0"). Unfortunately,
// the native compiler emits such names in a very strange way: everything before
// the last dot goes in the namespace (!!) field of the typedef.Since state
// machine types are always nested types and since nested types never have
// explicit namespaces (since they are in the same namespaces as their containing
// types), it should be safe to check for a non-empty namespace name on a nested
// type and prepend the namespace name and a dot to the type name. After that,
// debugging support falls out.
if (IsNestedTypeDefOrThrow(typeDef))
{
string namespaceName = MetadataReader.GetString(typeDefinition.Namespace);
if (namespaceName.Length > 0)
{
// As explained above, this is not really the qualified name - the namespace
// name is actually the part of the name that preceded the last dot (in bad
// metadata).
name = namespaceName + "." + name;
}
}
return name;
}
/// An exception from metadata reader.
public string GetTypeDefNamespaceOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetString(MetadataReader.GetTypeDefinition(typeDef).Namespace);
}
/// An exception from metadata reader.
public EntityHandle GetTypeDefExtendsOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).BaseType;
}
/// An exception from metadata reader.
public TypeAttributes GetTypeDefFlagsOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).Attributes;
}
/// An exception from metadata reader.
public GenericParameterHandleCollection GetTypeDefGenericParamsOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).GetGenericParameters();
}
/// An exception from metadata reader.
public bool HasGenericParametersOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).GetGenericParameters().Count > 0;
}
/// An exception from metadata reader.
public void GetTypeDefPropsOrThrow(
TypeDefinitionHandle typeDef,
out string name,
out string @namespace,
out TypeAttributes flags,
out EntityHandle extends)
{
TypeDefinition row = MetadataReader.GetTypeDefinition(typeDef);
name = MetadataReader.GetString(row.Name);
@namespace = MetadataReader.GetString(row.Namespace);
flags = row.Attributes;
extends = row.BaseType;
}
/// An exception from metadata reader.
internal bool IsNestedTypeDefOrThrow(TypeDefinitionHandle typeDef)
{
return IsNestedTypeDefOrThrow(MetadataReader, typeDef);
}
/// An exception from metadata reader.
private static bool IsNestedTypeDefOrThrow(MetadataReader metadataReader, TypeDefinitionHandle typeDef)
{
return IsNested(metadataReader.GetTypeDefinition(typeDef).Attributes);
}
/// An exception from metadata reader.
internal bool IsInterfaceOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).Attributes.IsInterface();
}
private struct TypeDefToNamespace
{
internal readonly TypeDefinitionHandle TypeDef;
internal readonly NamespaceDefinitionHandle NamespaceHandle;
internal TypeDefToNamespace(TypeDefinitionHandle typeDef, NamespaceDefinitionHandle namespaceHandle)
{
TypeDef = typeDef;
NamespaceHandle = namespaceHandle;
}
}
/// An exception from metadata reader.
private IEnumerable GetTypeDefsOrThrow(bool topLevelOnly)
{
foreach (var typeDef in MetadataReader.TypeDefinitions)
{
var row = MetadataReader.GetTypeDefinition(typeDef);
if (topLevelOnly && IsNested(row.Attributes))
{
continue;
}
yield return new TypeDefToNamespace(typeDef, row.NamespaceDefinition);
}
}
///
/// The function groups types defined in the module by their fully-qualified namespace name.
/// The case-sensitivity of the grouping depends upon the provided StringComparer.
///
/// The sequence is sorted by name by using provided comparer. Therefore, if there are multiple
/// groups for a namespace name (e.g. because they differ in case), the groups are going to be
/// adjacent to each other.
///
/// Empty string is used as namespace name for types in the Global namespace. Therefore, all types
/// in the Global namespace, if any, should be in the first group (assuming a reasonable StringComparer).
///
/// Comparer to sort the groups.
///
///
/// A sorted list of TypeDef row ids, grouped by fully-qualified namespace name.
/// An exception from metadata reader.
internal IEnumerable> GroupTypesByNamespaceOrThrow(StringComparer nameComparer)
{
// TODO: Consider if we should cache the result (not the IEnumerable, but the actual values).
// NOTE: Rather than use a sorted dictionary, we accumulate the groupings in a normal dictionary
// and then sort the list. We do this so that namespaces with distinct names are not
// merged, even if they are equal according to the provided comparer. This improves the error
// experience because types retain their exact namespaces.
Dictionary> namespaces = new Dictionary>();
GetTypeNamespaceNamesOrThrow(namespaces);
GetForwardedTypeNamespaceNamesOrThrow(namespaces);
var result = new ArrayBuilder>();
foreach (var pair in namespaces)
{
result.Add(new Grouping(pair.Key, pair.Value ?? SpecializedCollections.EmptyEnumerable()));
}
result.Sort(new TypesByNamespaceSortComparer(nameComparer));
return result;
}
internal class TypesByNamespaceSortComparer : IComparer>
{
private readonly StringComparer _nameComparer;
public TypesByNamespaceSortComparer(StringComparer nameComparer)
{
_nameComparer = nameComparer;
}
public int Compare(IGrouping left, IGrouping right)
{
if (left == right)
{
return 0;
}
int result = _nameComparer.Compare(left.Key, right.Key);
if (result == 0)
{
var fLeft = left.FirstOrDefault();
var fRight = right.FirstOrDefault();
if (fLeft.IsNil ^ fRight.IsNil)
{
result = fLeft.IsNil ? +1 : -1;
}
else
{
result = HandleComparer.Default.Compare(fLeft, fRight);
}
if (result == 0)
{
// This can only happen when both are for forwarded types.
Debug.Assert(left.IsEmpty() && right.IsEmpty());
result = string.CompareOrdinal(left.Key, right.Key);
}
}
Debug.Assert(result != 0);
return result;
}
}
///
/// Groups together the RowIds of types in a given namespaces. The types considered are
/// those defined in this module.
///
/// An exception from metadata reader.
private void GetTypeNamespaceNamesOrThrow(Dictionary> namespaces)
{
// PERF: Group by namespace handle so we only have to allocate one string for every namespace
var namespaceHandles = new Dictionary>(NamespaceHandleEqualityComparer.Singleton);
foreach (TypeDefToNamespace pair in GetTypeDefsOrThrow(topLevelOnly: true))
{
NamespaceDefinitionHandle nsHandle = pair.NamespaceHandle;
TypeDefinitionHandle typeDef = pair.TypeDef;
ArrayBuilder builder;
if (namespaceHandles.TryGetValue(nsHandle, out builder))
{
builder.Add(typeDef);
}
else
{
namespaceHandles.Add(nsHandle, new ArrayBuilder { typeDef });
}
}
foreach (var kvp in namespaceHandles)
{
string @namespace = MetadataReader.GetString(kvp.Key);
ArrayBuilder builder;
if (namespaces.TryGetValue(@namespace, out builder))
{
builder.AddRange(kvp.Value);
}
else
{
namespaces.Add(@namespace, kvp.Value);
}
}
}
private class NamespaceHandleEqualityComparer : IEqualityComparer
{
public static readonly NamespaceHandleEqualityComparer Singleton = new NamespaceHandleEqualityComparer();
private NamespaceHandleEqualityComparer()
{
}
public bool Equals(NamespaceDefinitionHandle x, NamespaceDefinitionHandle y)
{
return x == y;
}
public int GetHashCode(NamespaceDefinitionHandle obj)
{
return obj.GetHashCode();
}
}
///
/// Supplements the namespace-to-RowIDs map with the namespaces of forwarded types.
/// These types will not have associated row IDs (represented as null, for efficiency).
/// These namespaces are important because we want lookups of missing forwarded types
/// to succeed far enough that we can actually find the type forwarder and provide
/// information about the target assembly.
///
/// For example, consider the following forwarded type:
///
/// .class extern forwarder Namespace.Type {}
///
/// If this type is referenced in source as "Namespace.Type", then dev10 reports
///
/// error CS1070: The type name 'Namespace.Name' could not be found. This type has been
/// forwarded to assembly 'pe2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
/// Consider adding a reference to that assembly.
///
/// If we did not include "Namespace" as a child of the global namespace of this module
/// (the forwarding module), then Roslyn would report that the type "Namespace" was not
/// found and say nothing about "Name" (because of the diagnostic already attached to
/// the qualifier).
///
/// An exception from metadata reader.
private void GetForwardedTypeNamespaceNamesOrThrow(Dictionary> namespaces)
{
EnsureForwardTypeToAssemblyMap();
foreach (var typeName in _lazyForwardedTypesToAssemblyIndexMap.Keys)
{
int index = typeName.LastIndexOf('.');
string namespaceName = index >= 0 ? typeName.Substring(0, index) : "";
if (!namespaces.ContainsKey(namespaceName))
{
namespaces.Add(namespaceName, null);
}
}
}
private IdentifierCollection ComputeTypeNameCollection()
{
try
{
var allTypeDefs = GetTypeDefsOrThrow(topLevelOnly: false);
var typeNames =
from typeDef in allTypeDefs
let metadataName = GetTypeDefNameOrThrow(typeDef.TypeDef)
let backtickIndex = metadataName.IndexOf('`')
select backtickIndex < 0 ? metadataName : metadataName.Substring(0, backtickIndex);
return new IdentifierCollection(typeNames);
}
catch (BadImageFormatException)
{
return new IdentifierCollection();
}
}
private IdentifierCollection ComputeNamespaceNameCollection()
{
try
{
var allTypeIds = GetTypeDefsOrThrow(topLevelOnly: true);
var fullNamespaceNames =
from id in allTypeIds
where !id.NamespaceHandle.IsNil
select MetadataReader.GetString(id.NamespaceHandle);
var namespaceNames =
from fullName in fullNamespaceNames.Distinct()
from name in fullName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries)
select name;
return new IdentifierCollection(namespaceNames);
}
catch (BadImageFormatException)
{
return new IdentifierCollection();
}
}
/// An exception from metadata reader.
internal ImmutableArray GetNestedTypeDefsOrThrow(TypeDefinitionHandle container)
{
return MetadataReader.GetTypeDefinition(container).GetNestedTypes();
}
/// An exception from metadata reader.
internal MethodImplementationHandleCollection GetMethodImplementationsOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).GetMethodImplementations();
}
///
/// Returns a collection of interfaces implemented by given type.
///
/// An exception from metadata reader.
internal InterfaceImplementationHandleCollection GetInterfaceImplementationsOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).GetInterfaceImplementations();
}
/// An exception from metadata reader.
internal MethodDefinitionHandleCollection GetMethodsOfTypeOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).GetMethods();
}
/// An exception from metadata reader.
internal PropertyDefinitionHandleCollection GetPropertiesOfTypeOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).GetProperties();
}
/// An exception from metadata reader.
internal EventDefinitionHandleCollection GetEventsOfTypeOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).GetEvents();
}
/// An exception from metadata reader.
internal FieldDefinitionHandleCollection GetFieldsOfTypeOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).GetFields();
}
/// An exception from metadata reader.
internal EntityHandle GetBaseTypeOfTypeOrThrow(TypeDefinitionHandle typeDef)
{
return MetadataReader.GetTypeDefinition(typeDef).BaseType;
}
internal TypeLayout GetTypeLayout(TypeDefinitionHandle typeDef)
{
try
{
// CLI Spec 22.8.3:
// The Class or ValueType indexed by Parent shall be SequentialLayout or ExplicitLayout.
// That is, AutoLayout types shall not own any rows in the ClassLayout table.
var def = MetadataReader.GetTypeDefinition(typeDef);
LayoutKind kind;
switch (def.Attributes & TypeAttributes.LayoutMask)
{
case TypeAttributes.SequentialLayout:
kind = LayoutKind.Sequential;
break;
case TypeAttributes.ExplicitLayout:
kind = LayoutKind.Explicit;
break;
case TypeAttributes.AutoLayout:
return default(TypeLayout);
default:
// TODO (tomat) report error:
return default(TypeLayout);
}
var layout = def.GetLayout();
int size = layout.Size;
int packingSize = layout.PackingSize;
if (packingSize > byte.MaxValue)
{
// TODO (tomat) report error:
packingSize = 0;
}
if (size < 0)
{
// TODO (tomat) report error:
size = 0;
}
return new TypeLayout(kind, size, (byte)packingSize);
}
catch (BadImageFormatException)
{
return default(TypeLayout);
}
}
internal bool IsNoPiaLocalType(TypeDefinitionHandle typeDef)
{
AttributeInfo attributeInfo;
return IsNoPiaLocalType(typeDef, out attributeInfo);
}
internal bool HasParamsAttribute(EntityHandle token)
{
return FindTargetAttribute(token, AttributeDescription.ParamArrayAttribute).HasValue;
}
internal bool HasExtensionAttribute(EntityHandle token, bool ignoreCase)
{
return FindTargetAttribute(token, ignoreCase ? AttributeDescription.CaseInsensitiveExtensionAttribute : AttributeDescription.CaseSensitiveExtensionAttribute).HasValue;
}
internal bool HasVisualBasicEmbeddedAttribute(EntityHandle token)
{
return FindTargetAttribute(token, AttributeDescription.VisualBasicEmbeddedAttribute).HasValue;
}
internal bool HasDefaultMemberAttribute(EntityHandle token, out string memberName)
{
return HasStringValuedAttribute(token, AttributeDescription.DefaultMemberAttribute, out memberName);
}
internal bool HasGuidAttribute(EntityHandle token, out string guidValue)
{
return HasStringValuedAttribute(token, AttributeDescription.GuidAttribute, out guidValue);
}
internal bool HasFixedBufferAttribute(EntityHandle token, out string elementTypeName, out int bufferSize)
{
return HasStringAndIntValuedAttribute(token, AttributeDescription.FixedBufferAttribute, out elementTypeName, out bufferSize);
}
internal bool HasAccessedThroughPropertyAttribute(EntityHandle token, out string propertyName)
{
return HasStringValuedAttribute(token, AttributeDescription.AccessedThroughPropertyAttribute, out propertyName);
}
internal bool HasRequiredAttributeAttribute(EntityHandle token)
{
return FindTargetAttribute(token, AttributeDescription.RequiredAttributeAttribute).HasValue;
}
internal bool HasAttribute(EntityHandle token, AttributeDescription description)
{
return FindTargetAttribute(token, description).HasValue;
}
internal CustomAttributeHandle GetAttributeHandle(EntityHandle token, AttributeDescription description)
{
return FindTargetAttribute(token, description).Handle;
}
private static readonly ImmutableArray s_simpleDynamicTransforms = ImmutableArray.Create(true);
internal bool HasDynamicAttribute(EntityHandle token, out ImmutableArray dynamicTransforms)
{
AttributeInfo info = FindTargetAttribute(token, AttributeDescription.DynamicAttribute);
Debug.Assert(!info.HasValue || info.SignatureIndex == 0 || info.SignatureIndex == 1);
if (!info.HasValue)
{
dynamicTransforms = default(ImmutableArray);
return false;
}
if (info.SignatureIndex == 0)
{
dynamicTransforms = s_simpleDynamicTransforms;
return true;
}
return TryExtractBoolArrayValueFromAttribute(info.Handle, out dynamicTransforms);
}
internal bool HasTupleElementNamesAttribute(EntityHandle token, out ImmutableArray tupleElementNames)
{
var info = FindTargetAttribute(token, AttributeDescription.TupleElementNamesAttribute);
Debug.Assert(!info.HasValue || info.SignatureIndex == 0 || info.SignatureIndex == 1);
if (!info.HasValue)
{
tupleElementNames = default(ImmutableArray);
return false;
}
return TryExtractStringArrayValueFromAttribute(info.Handle, out tupleElementNames);
}
internal bool HasDeprecatedOrObsoleteAttribute(EntityHandle token, out ObsoleteAttributeData obsoleteData)
{
AttributeInfo info;
info = FindTargetAttribute(token, AttributeDescription.DeprecatedAttribute);
if (info.HasValue)
{
return TryExtractDeprecatedDataFromAttribute(info, out obsoleteData);
}
info = FindTargetAttribute(token, AttributeDescription.ObsoleteAttribute);
if (info.HasValue)
{
return TryExtractObsoleteDataFromAttribute(info, out obsoleteData);
}
obsoleteData = null;
return false;
}
internal CustomAttributeHandle GetAttributeUsageAttributeHandle(EntityHandle token)
{
AttributeInfo info = FindTargetAttribute(token, AttributeDescription.AttributeUsageAttribute);
Debug.Assert(info.SignatureIndex == 0);
return info.Handle;
}
internal bool HasInterfaceTypeAttribute(EntityHandle token, out ComInterfaceType interfaceType)
{
AttributeInfo info = FindTargetAttribute(token, AttributeDescription.InterfaceTypeAttribute);
if (info.HasValue && TryExtractInterfaceTypeFromAttribute(info, out interfaceType))
{
return true;
}
interfaceType = default(ComInterfaceType);
return false;
}
internal bool HasTypeLibTypeAttribute(EntityHandle token, out Cci.TypeLibTypeFlags flags)
{
AttributeInfo info = FindTargetAttribute(token, AttributeDescription.TypeLibTypeAttribute);
if (info.HasValue && TryExtractTypeLibTypeFromAttribute(info, out flags))
{
return true;
}
flags = default(Cci.TypeLibTypeFlags);
return false;
}
internal bool HasDateTimeConstantAttribute(EntityHandle token, out ConstantValue defaultValue)
{
long value;
AttributeInfo info = FindLastTargetAttribute(token, AttributeDescription.DateTimeConstantAttribute);
if (info.HasValue && TryExtractLongValueFromAttribute(info.Handle, out value))
{
// if value is outside this range, DateTime would throw when constructed
if (value < DateTime.MinValue.Ticks || value > DateTime.MaxValue.Ticks)
{
defaultValue = ConstantValue.Bad;
}
else
{
defaultValue = ConstantValue.Create(new DateTime(value));
}
return true;
}
defaultValue = null;
return false;
}
internal bool HasDecimalConstantAttribute(EntityHandle token, out ConstantValue defaultValue)
{
decimal value;
AttributeInfo info = FindLastTargetAttribute(token, AttributeDescription.DecimalConstantAttribute);
if (info.HasValue && TryExtractDecimalValueFromDecimalConstantAttribute(info.Handle, out value))
{
defaultValue = ConstantValue.Create(value);
return true;
}
defaultValue = null;
return false;
}
internal ImmutableArray GetInternalsVisibleToAttributeValues(EntityHandle token)
{
List attrInfos = FindTargetAttributes(token, AttributeDescription.InternalsVisibleToAttribute);
ArrayBuilder result = ExtractStringValuesFromAttributes(attrInfos);
return result?.ToImmutableAndFree() ?? ImmutableArray.Empty;
}
internal ImmutableArray GetConditionalAttributeValues(EntityHandle token)
{
List attrInfos = FindTargetAttributes(token, AttributeDescription.ConditionalAttribute);
ArrayBuilder result = ExtractStringValuesFromAttributes(attrInfos);
return result?.ToImmutableAndFree() ?? ImmutableArray.Empty;
}
// This method extracts all the non-null string values from the given attributes.
private ArrayBuilder ExtractStringValuesFromAttributes(List attrInfos)
{
if (attrInfos == null)
{
return null;
}
var result = ArrayBuilder.GetInstance(attrInfos.Count);
foreach (var ai in attrInfos)
{
string extractedStr;
if (TryExtractStringValueFromAttribute(ai.Handle, out extractedStr) && extractedStr != null)
{
result.Add(extractedStr);
}
}
return result;
}
private bool TryExtractObsoleteDataFromAttribute(AttributeInfo attributeInfo, out ObsoleteAttributeData obsoleteData)
{
Debug.Assert(attributeInfo.HasValue);
switch (attributeInfo.SignatureIndex)
{
case 0:
// ObsoleteAttribute()
obsoleteData = new ObsoleteAttributeData(message: null, isError: false);
return true;
case 1:
// ObsoleteAttribute(string)
string message;
if (TryExtractStringValueFromAttribute(attributeInfo.Handle, out message))
{
obsoleteData = new ObsoleteAttributeData(message, isError: false);
return true;
}
obsoleteData = null;
return false;
case 2:
// ObsoleteAttribute(string, bool)
return TryExtractValueFromAttribute(attributeInfo.Handle, out obsoleteData, s_attributeObsoleteDataExtractor);
default:
throw ExceptionUtilities.UnexpectedValue(attributeInfo.SignatureIndex);
}
}
private bool TryExtractDeprecatedDataFromAttribute(AttributeInfo attributeInfo, out ObsoleteAttributeData obsoleteData)
{
Debug.Assert(attributeInfo.HasValue);
switch (attributeInfo.SignatureIndex)
{
case 0: // DeprecatedAttribute(String, DeprecationType, UInt32)
case 1: // DeprecatedAttribute(String, DeprecationType, UInt32, Platform)
case 2: // DeprecatedAttribute(String, DeprecationType, UInt32, Type)
case 3: // DeprecatedAttribute(String, DeprecationType, UInt32, String)
return TryExtractValueFromAttribute(attributeInfo.Handle, out obsoleteData, s_attributeDeprecatedDataExtractor);
default:
throw ExceptionUtilities.UnexpectedValue(attributeInfo.SignatureIndex);
}
}
private bool TryExtractInterfaceTypeFromAttribute(AttributeInfo attributeInfo, out ComInterfaceType interfaceType)
{
Debug.Assert(attributeInfo.HasValue);
switch (attributeInfo.SignatureIndex)
{
case 0:
// InterfaceTypeAttribute(Int16)
short shortValue;
if (TryExtractValueFromAttribute(attributeInfo.Handle, out shortValue, s_attributeShortValueExtractor) &&
IsValidComInterfaceType(shortValue))
{
interfaceType = (ComInterfaceType)shortValue;
return true;
}
break;
case 1:
// InterfaceTypeAttribute(ComInterfaceType)
int intValue;
if (TryExtractValueFromAttribute(attributeInfo.Handle, out intValue, s_attributeIntValueExtractor) &&
IsValidComInterfaceType(intValue))
{
interfaceType = (ComInterfaceType)intValue;
return true;
}
break;
default:
throw ExceptionUtilities.UnexpectedValue(attributeInfo.SignatureIndex);
}
interfaceType = default(ComInterfaceType);
return false;
}
private static bool IsValidComInterfaceType(int comInterfaceType)
{
switch (comInterfaceType)
{
case (int)Cci.Constants.ComInterfaceType_InterfaceIsDual:
case (int)Cci.Constants.ComInterfaceType_InterfaceIsIDispatch:
case (int)ComInterfaceType.InterfaceIsIInspectable:
case (int)ComInterfaceType.InterfaceIsIUnknown:
return true;
default:
return false;
}
}
private bool TryExtractTypeLibTypeFromAttribute(AttributeInfo info, out Cci.TypeLibTypeFlags flags)
{
Debug.Assert(info.HasValue);
switch (info.SignatureIndex)
{
case 0:
// TypeLibTypeAttribute(Int16)
short shortValue;
if (TryExtractValueFromAttribute(info.Handle, out shortValue, s_attributeShortValueExtractor))
{
flags = (Cci.TypeLibTypeFlags)shortValue;
return true;
}
break;
case 1:
// TypeLibTypeAttribute(TypeLibTypeFlags)
int intValue;
if (TryExtractValueFromAttribute(info.Handle, out intValue, s_attributeIntValueExtractor))
{
flags = (Cci.TypeLibTypeFlags)intValue;
return true;
}
break;
default:
throw ExceptionUtilities.UnexpectedValue(info.SignatureIndex);
}
flags = default(Cci.TypeLibTypeFlags);
return false;
}
internal bool TryExtractStringValueFromAttribute(CustomAttributeHandle handle, out string value)
{
return TryExtractValueFromAttribute(handle, out value, s_attributeStringValueExtractor);
}
internal bool TryExtractLongValueFromAttribute(CustomAttributeHandle handle, out long value)
{
return TryExtractValueFromAttribute(handle, out value, s_attributeLongValueExtractor);
}
// Note: not a general purpose helper
private bool TryExtractDecimalValueFromDecimalConstantAttribute(CustomAttributeHandle handle, out decimal value)
{
return TryExtractValueFromAttribute(handle, out value, s_decimalValueInDecimalConstantAttributeExtractor);
}
private struct StringAndInt
{
public string StringValue;
public int IntValue;
}
private bool TryExtractStringAndIntValueFromAttribute(CustomAttributeHandle handle, out string stringValue, out int intValue)
{
StringAndInt data;
var result = TryExtractValueFromAttribute(handle, out data, s_attributeStringAndIntValueExtractor);
stringValue = data.StringValue;
intValue = data.IntValue;
return result;
}
private bool TryExtractBoolArrayValueFromAttribute(CustomAttributeHandle handle, out ImmutableArray value)
{
return TryExtractValueFromAttribute(handle, out value, s_attributeBoolArrayValueExtractor);
}
private bool TryExtractStringArrayValueFromAttribute(CustomAttributeHandle handle, out ImmutableArray value)
{
return TryExtractValueFromAttribute(handle, out value, s_attributeStringArrayValueExtractor);
}
private bool TryExtractValueFromAttribute(CustomAttributeHandle handle, out T value, AttributeValueExtractor valueExtractor)
{
Debug.Assert(!handle.IsNil);
// extract the value
try
{
BlobHandle valueBlob = GetCustomAttributeValueOrThrow(handle);
if (!valueBlob.IsNil)
{
// TODO: error checking offset in range
BlobReader reader = MetadataReader.GetBlobReader(valueBlob);
if (reader.Length > 4)
{
// check prolog
if (reader.ReadByte() == 1 && reader.ReadByte() == 0)
{
return valueExtractor(out value, ref reader);
}
}
}
}
catch (BadImageFormatException)
{ }
value = default(T);
return false;
}
internal bool HasStringValuedAttribute(EntityHandle token, AttributeDescription description, out string value)
{
AttributeInfo info = FindTargetAttribute(token, description);
if (info.HasValue)
{
return TryExtractStringValueFromAttribute(info.Handle, out value);
}
value = null;
return false;
}
private bool HasStringAndIntValuedAttribute(EntityHandle token, AttributeDescription description, out string stringValue, out int intValue)
{
AttributeInfo info = FindTargetAttribute(token, description);
if (info.HasValue)
{
return TryExtractStringAndIntValueFromAttribute(info.Handle, out stringValue, out intValue);
}
stringValue = null;
intValue = 0;
return false;
}
internal bool IsNoPiaLocalType(
TypeDefinitionHandle typeDef,
out string interfaceGuid,
out string scope,
out string identifier)
{
AttributeInfo typeIdentifierInfo;
if (!IsNoPiaLocalType(typeDef, out typeIdentifierInfo))
{
interfaceGuid = null;
scope = null;
identifier = null;
return false;
}
interfaceGuid = null;
scope = null;
identifier = null;
try
{
if (GetTypeDefFlagsOrThrow(typeDef).IsInterface())
{
HasGuidAttribute(typeDef, out interfaceGuid);
}
if (typeIdentifierInfo.SignatureIndex == 1)
{
// extract the value
BlobHandle valueBlob = GetCustomAttributeValueOrThrow(typeIdentifierInfo.Handle);
if (!valueBlob.IsNil)
{
BlobReader reader = MetadataReader.GetBlobReader(valueBlob);
if (reader.Length > 4)
{
// check prolog
if (reader.ReadInt16() == 1)
{
if (!CrackStringInAttributeValue(out scope, ref reader) ||
!CrackStringInAttributeValue(out identifier, ref reader))
{
return false;
}
}
}
}
}
return true;
}
catch (BadImageFormatException)
{
return false;
}
}
internal static bool CrackObsoleteAttributeData(out ObsoleteAttributeData value, ref BlobReader sig)
{
string message;
if (CrackStringInAttributeValue(out message, ref sig) && sig.RemainingBytes >= 1)
{
bool isError = sig.ReadBoolean();
value = new ObsoleteAttributeData(message, isError);
return true;
}
value = null;
return false;
}
internal static bool CrackDeprecatedAttributeData(out ObsoleteAttributeData value, ref BlobReader sig)
{
StringAndInt args;
if (CrackStringAndIntInAttributeValue(out args, ref sig))
{
value = new ObsoleteAttributeData(args.StringValue, args.IntValue == 1);
return true;
}
value = null;
return false;
}
private static bool CrackStringAndIntInAttributeValue(out StringAndInt value, ref BlobReader sig)
{
value = default(StringAndInt);
return
CrackStringInAttributeValue(out value.StringValue, ref sig) &&
CrackIntInAttributeValue(out value.IntValue, ref sig);
}
internal static bool CrackStringInAttributeValue(out string value, ref BlobReader sig)
{
try
{
int strLen;
if (sig.TryReadCompressedInteger(out strLen) && sig.RemainingBytes >= strLen)
{
value = sig.ReadUTF8(strLen);
// Trim null characters at the end to mimic native compiler behavior.
// There are libraries that have them and leaving them in breaks tests.
value = value.TrimEnd('\0');
return true;
}
value = null;
// Strings are stored as UTF8, but 0xFF means NULL string.
return sig.RemainingBytes >= 1 && sig.ReadByte() == 0xFF;
}
catch (BadImageFormatException)
{
value = null;
return false;
}
}
internal static bool CrackStringArrayInAttributeValue(out ImmutableArray value, ref BlobReader sig)
{
if (sig.RemainingBytes >= 4)
{
uint arrayLen = sig.ReadUInt32();
var stringArray = new string[arrayLen];
for (int i = 0; i < arrayLen; i++)
{
if (!CrackStringInAttributeValue(out stringArray[i], ref sig))
{
value = stringArray.AsImmutableOrNull();
return false;
}
}
value = stringArray.AsImmutableOrNull();
return true;
}
value = default(ImmutableArray);
return false;
}
internal static bool CrackByteInAttributeValue(out byte value, ref BlobReader sig)
{
if (sig.RemainingBytes >= 1)
{
value = sig.ReadByte();
return true;
}
value = 0xff;
return false;
}
internal static bool CrackShortInAttributeValue(out short value, ref BlobReader sig)
{
if (sig.RemainingBytes >= 2)
{
value = sig.ReadInt16();
return true;
}
value = -1;
return false;
}
internal static bool CrackIntInAttributeValue(out int value, ref BlobReader sig)
{
if (sig.RemainingBytes >= 4)
{
value = sig.ReadInt32();
return true;
}
value = -1;
return false;
}
internal static bool CrackLongInAttributeValue(out long value, ref BlobReader sig)
{
if (sig.RemainingBytes >= 8)
{
value = sig.ReadInt64();
return true;
}
value = -1;
return false;
}
// Note: not a general purpose helper
internal static bool CrackDecimalInDecimalConstantAttribute(out decimal value, ref BlobReader sig)
{
byte scale;
byte sign;
int high;
int mid;
int low;
if (CrackByteInAttributeValue(out scale, ref sig) &&
CrackByteInAttributeValue(out sign, ref sig) &&
CrackIntInAttributeValue(out high, ref sig) &&
CrackIntInAttributeValue(out mid, ref sig) &&
CrackIntInAttributeValue(out low, ref sig))
{
value = new decimal(low, mid, high, sign != 0, scale);
return true;
}
value = -1;
return false;
}
internal static bool CrackBoolArrayInAttributeValue(out ImmutableArray value, ref BlobReader sig)
{
if (sig.RemainingBytes >= 4)
{
uint arrayLen = sig.ReadUInt32();
if (sig.RemainingBytes >= arrayLen)
{
var boolArray = new bool[arrayLen];
for (int i = 0; i < arrayLen; i++)
{
boolArray[i] = (sig.ReadByte() == 1);
}
value = boolArray.AsImmutableOrNull();
return true;
}
}
value = default(ImmutableArray);
return false;
}
internal struct AttributeInfo
{
public readonly CustomAttributeHandle Handle;
public readonly byte SignatureIndex;
public AttributeInfo(CustomAttributeHandle handle, int signatureIndex)
{
Debug.Assert(signatureIndex >= 0 && signatureIndex <= byte.MaxValue);
this.Handle = handle;
this.SignatureIndex = (byte)signatureIndex;
}
public bool HasValue
{
get { return !Handle.IsNil; }
}
}
internal List FindTargetAttributes(EntityHandle hasAttribute, AttributeDescription description)
{
List result = null;
try
{
foreach (var attributeHandle in MetadataReader.GetCustomAttributes(hasAttribute))
{
int signatureIndex = GetTargetAttributeSignatureIndex(attributeHandle, description);
if (signatureIndex != -1)
{
if (result == null)
{
result = new List();
}
// We found a match
result.Add(new AttributeInfo(attributeHandle, signatureIndex));
}
}
}
catch (BadImageFormatException)
{ }
return result;
}
private AttributeInfo FindTargetAttribute(EntityHandle hasAttribute, AttributeDescription description)
{
return FindTargetAttribute(MetadataReader, hasAttribute, description);
}
internal static AttributeInfo FindTargetAttribute(MetadataReader metadataReader, EntityHandle hasAttribute, AttributeDescription description)
{
try
{
foreach (var attributeHandle in metadataReader.GetCustomAttributes(hasAttribute))
{
int signatureIndex = GetTargetAttributeSignatureIndex(metadataReader, attributeHandle, description);
if (signatureIndex != -1)
{
// We found a match
return new AttributeInfo(attributeHandle, signatureIndex);
}
}
}
catch (BadImageFormatException)
{ }
return default(AttributeInfo);
}
internal AttributeInfo FindLastTargetAttribute(EntityHandle hasAttribute, AttributeDescription description)
{
try
{
AttributeInfo attrInfo = default(AttributeInfo);
foreach (var attributeHandle in MetadataReader.GetCustomAttributes(hasAttribute))
{
int signatureIndex = GetTargetAttributeSignatureIndex(attributeHandle, description);
if (signatureIndex != -1)
{
// We found a match
attrInfo = new AttributeInfo(attributeHandle, signatureIndex);
}
}
return attrInfo;
}
catch (BadImageFormatException)
{ }
return default(AttributeInfo);
}
/// An exception from metadata reader.
internal int GetParamArrayCountOrThrow(EntityHandle hasAttribute)
{
int count = 0;
foreach (var attributeHandle in MetadataReader.GetCustomAttributes(hasAttribute))
{
if (GetTargetAttributeSignatureIndex(attributeHandle,
AttributeDescription.ParamArrayAttribute) != -1)
{
count++;
}
}
return count;
}
private bool IsNoPiaLocalType(TypeDefinitionHandle typeDef, out AttributeInfo attributeInfo)
{
if (_lazyContainsNoPiaLocalTypes == ThreeState.False)
{
attributeInfo = default(AttributeInfo);
return false;
}
if (_lazyNoPiaLocalTypeCheckBitMap != null &&
_lazyTypeDefToTypeIdentifierMap != null)
{
int rid = MetadataReader.GetRowNumber(typeDef);
Debug.Assert(rid > 0);
int item = rid / 32;
int bit = 1 << (rid % 32);
if ((_lazyNoPiaLocalTypeCheckBitMap[item] & bit) != 0)
{
return _lazyTypeDefToTypeIdentifierMap.TryGetValue(typeDef, out attributeInfo);
}
}
try
{
foreach (var attributeHandle in MetadataReader.GetCustomAttributes(typeDef))
{
int signatureIndex = IsTypeIdentifierAttribute(attributeHandle);
if (signatureIndex != -1)
{
// We found a match
_lazyContainsNoPiaLocalTypes = ThreeState.True;
RegisterNoPiaLocalType(typeDef, attributeHandle, signatureIndex);
attributeInfo = new AttributeInfo(attributeHandle, signatureIndex);
return true;
}
}
}
catch (BadImageFormatException)
{ }
RecordNoPiaLocalTypeCheck(typeDef);
attributeInfo = default(AttributeInfo);
return false;
}
private void RegisterNoPiaLocalType(TypeDefinitionHandle typeDef, CustomAttributeHandle customAttribute, int signatureIndex)
{
if (_lazyNoPiaLocalTypeCheckBitMap == null)
{
Interlocked.CompareExchange(
ref _lazyNoPiaLocalTypeCheckBitMap,
new int[(MetadataReader.TypeDefinitions.Count + 32) / 32],
null);
}
if (_lazyTypeDefToTypeIdentifierMap == null)
{
Interlocked.CompareExchange(
ref _lazyTypeDefToTypeIdentifierMap,
new ConcurrentDictionary(),
null);
}
_lazyTypeDefToTypeIdentifierMap.TryAdd(typeDef, new AttributeInfo(customAttribute, signatureIndex));
RecordNoPiaLocalTypeCheck(typeDef);
}
private void RecordNoPiaLocalTypeCheck(TypeDefinitionHandle typeDef)
{
if (_lazyNoPiaLocalTypeCheckBitMap == null)
{
return;
}
int rid = MetadataTokens.GetRowNumber(typeDef);
Debug.Assert(rid > 0);
int item = rid / 32;
int bit = 1 << (rid % 32);
int oldValue;
do
{
oldValue = _lazyNoPiaLocalTypeCheckBitMap[item];
}
while (Interlocked.CompareExchange(
ref _lazyNoPiaLocalTypeCheckBitMap[item],
oldValue | bit,
oldValue) != oldValue);
}
///
/// Determine if custom attribute application is
/// NoPia TypeIdentifier.
///
///
/// An index of the target constructor signature in
/// signaturesOfTypeIdentifierAttribute array, -1 if
/// this is not NoPia TypeIdentifier.
///
private int IsTypeIdentifierAttribute(CustomAttributeHandle customAttribute)
{
const int No = -1;
try
{
if (MetadataReader.GetCustomAttribute(customAttribute).Parent.Kind != HandleKind.TypeDefinition)
{
// Ignore attributes attached to anything, but type definitions.
return No;
}
return GetTargetAttributeSignatureIndex(customAttribute, AttributeDescription.TypeIdentifierAttribute);
}
catch (BadImageFormatException)
{
return No;
}
}
///
/// Determines if a custom attribute matches a namespace and name.
///
/// Handle of the custom attribute.
/// The custom attribute's namespace in metadata format (case sensitive)
/// The custom attribute's type name in metadata format (case sensitive)
/// Constructor of the custom attribute.
/// Should case be ignored for name comparison?
/// true if match is found
internal bool IsTargetAttribute(
CustomAttributeHandle customAttribute,
string namespaceName,
string typeName,
out EntityHandle ctor,
bool ignoreCase = false)
{
return IsTargetAttribute(MetadataReader, customAttribute, namespaceName, typeName, out ctor, ignoreCase);
}
///
/// Determines if a custom attribute matches a namespace and name.
///
/// The metadata reader.
/// Handle of the custom attribute.
/// The custom attribute's namespace in metadata format (case sensitive)
/// The custom attribute's type name in metadata format (case sensitive)
/// Constructor of the custom attribute.
/// Should case be ignored for name comparison?
/// true if match is found
private static bool IsTargetAttribute(
MetadataReader metadataReader,
CustomAttributeHandle customAttribute,
string namespaceName,
string typeName,
out EntityHandle ctor,
bool ignoreCase)
{
Debug.Assert(namespaceName != null);
Debug.Assert(typeName != null);
EntityHandle ctorType;
StringHandle ctorTypeNamespace;
StringHandle ctorTypeName;
if (!GetTypeAndConstructor(metadataReader, customAttribute, out ctorType, out ctor))
{
return false;
}
if (!GetAttributeNamespaceAndName(metadataReader, ctorType, out ctorTypeNamespace, out ctorTypeName))
{
return false;
}
try
{
return StringEquals(metadataReader, ctorTypeName, typeName, ignoreCase)
&& StringEquals(metadataReader, ctorTypeNamespace, namespaceName, ignoreCase);
}
catch (BadImageFormatException)
{
return false;
}
}
///
/// Returns MetadataToken for assembly ref matching name
///
/// The assembly name in metadata format (case sensitive)
/// Matching assembly ref token or nil (0)
internal AssemblyReferenceHandle GetAssemblyRef(string assemblyName)
{
Debug.Assert(assemblyName != null);
try
{
// Iterate over assembly ref rows
foreach (var assemblyRef in MetadataReader.AssemblyReferences)
{
// Check whether matching name
if (MetadataReader.StringComparer.Equals(MetadataReader.GetAssemblyReference(assemblyRef).Name, assemblyName))
{
// Return assembly ref token
return assemblyRef;
}
}
}
catch (BadImageFormatException)
{ }
// Not found
return default(AssemblyReferenceHandle);
}
///
/// Returns MetadataToken for type ref matching resolution scope and name
///
/// The resolution scope token
/// The namespace name in metadata format (case sensitive)
/// The type name in metadata format (case sensitive)
/// Matching type ref token or nil (0)
internal EntityHandle GetTypeRef(
EntityHandle resolutionScope,
string namespaceName,
string typeName)
{
Debug.Assert(!resolutionScope.IsNil);
Debug.Assert(namespaceName != null);
Debug.Assert(typeName != null);
try
{
// Iterate over type ref rows
foreach (var handle in MetadataReader.TypeReferences)
{
var typeRef = MetadataReader.GetTypeReference(handle);
// Check whether matching resolution scope
if (typeRef.ResolutionScope != resolutionScope)
{
continue;
}
// Check whether matching name
if (!MetadataReader.StringComparer.Equals(typeRef.Name, typeName))
{
continue;
}
if (MetadataReader.StringComparer.Equals(typeRef.Namespace, namespaceName))
{
// Return type ref token
return handle;
}
}
}
catch (BadImageFormatException)
{ }
// Not found
return default(TypeReferenceHandle);
}
/// An exception from metadata reader.
public void GetTypeRefPropsOrThrow(
TypeReferenceHandle handle,
out string name,
out string @namespace,
out EntityHandle resolutionScope)
{
TypeReference typeRef = MetadataReader.GetTypeReference(handle);
resolutionScope = typeRef.ResolutionScope;
name = MetadataReader.GetString(typeRef.Name);
Debug.Assert(MetadataHelpers.IsValidMetadataIdentifier(name));
@namespace = MetadataReader.GetString(typeRef.Namespace);
}
///
/// Determine if custom attribute matches the target attribute.
///
///
/// Handle of the custom attribute.
///
/// The attribute to match.
///
/// An index of the target constructor signature in
/// signatures array, -1 if
/// this is not the target attribute.
///
internal int GetTargetAttributeSignatureIndex(CustomAttributeHandle customAttribute, AttributeDescription description)
{
return GetTargetAttributeSignatureIndex(MetadataReader, customAttribute, description);
}
///
/// Determine if custom attribute matches the target attribute.
///
///
/// The metadata reader.
///
///
/// Handle of the custom attribute.
///
/// The attribute to match.
///
/// An index of the target constructor signature in
/// signatures array, -1 if
/// this is not the target attribute.
///
private static int GetTargetAttributeSignatureIndex(MetadataReader metadataReader, CustomAttributeHandle customAttribute, AttributeDescription description)
{
const int No = -1;
EntityHandle ctor;
// Check namespace and type name and get signature if a match is found
if (!IsTargetAttribute(metadataReader, customAttribute, description.Namespace, description.Name, out ctor, description.MatchIgnoringCase))
{
return No;
}
try
{
// Check signatures
BlobReader sig = metadataReader.GetBlobReader(GetMethodSignatureOrThrow(metadataReader, ctor));
for (int i = 0; i < description.Signatures.Length; i++)
{
var targetSignature = description.Signatures[i];
Debug.Assert(targetSignature.Length >= 3);
sig.Reset();
// Make sure the headers match.
if (sig.RemainingBytes >= 3 &&
sig.ReadByte() == targetSignature[0] &&
sig.ReadByte() == targetSignature[1] &&
sig.ReadByte() == targetSignature[2])
{
int j = 3;
for (; j < targetSignature.Length; j++)
{
if (sig.RemainingBytes == 0)
{
// No more bytes in the signature
break;
}
SignatureTypeCode b = sig.ReadSignatureTypeCode();
if ((SignatureTypeCode)targetSignature[j] == b)
{
switch (b)
{
case SignatureTypeCode.TypeHandle:
EntityHandle token = sig.ReadTypeHandle();
HandleKind tokenType = token.Kind;
StringHandle name;
StringHandle ns;
if (tokenType == HandleKind.TypeDefinition)
{
TypeDefinitionHandle typeHandle = (TypeDefinitionHandle)token;
if (IsNestedTypeDefOrThrow(metadataReader, typeHandle))
{
// At the moment, none of the well-known attributes take nested types.
break; // Signature doesn't match.
}
TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeHandle);
name = typeDef.Name;
ns = typeDef.Namespace;
}
else if (tokenType == HandleKind.TypeReference)
{
TypeReference typeRef = metadataReader.GetTypeReference((TypeReferenceHandle)token);
if (typeRef.ResolutionScope.Kind == HandleKind.TypeReference)
{
// At the moment, none of the well-known attributes take nested types.
break; // Signature doesn't match.
}
name = typeRef.Name;
ns = typeRef.Namespace;
}
else
{
break; // Signature doesn't match.
}
AttributeDescription.TypeHandleTargetInfo targetInfo = AttributeDescription.TypeHandleTargets[targetSignature[j + 1]];
if (StringEquals(metadataReader, ns, targetInfo.Namespace, ignoreCase: false) &&
StringEquals(metadataReader, name, targetInfo.Name, ignoreCase: false))
{
j++;
continue;
}
break; // Signature doesn't match.
case SignatureTypeCode.SZArray:
// Verify array element type
continue;
default:
continue;
}
}
break; // Signature doesn't match.
}
if (sig.RemainingBytes == 0 && j == targetSignature.Length)
{
// We found a match
return i;
}
}
}
}
catch (BadImageFormatException)
{ }
return No;
}
///
/// Given a token for a constructor, return the token for the constructor's type and the blob containing the
/// constructor's signature.
///
/// True if the function successfully returns the type and signature.
internal bool GetTypeAndConstructor(
CustomAttributeHandle customAttribute,
out EntityHandle ctorType,
out EntityHandle attributeCtor)
{
return GetTypeAndConstructor(MetadataReader, customAttribute, out ctorType, out attributeCtor);
}
///
/// Given a token for a constructor, return the token for the constructor's type and the blob containing the
/// constructor's signature.
///
/// True if the function successfully returns the type and signature.
private static bool GetTypeAndConstructor(
MetadataReader metadataReader,
CustomAttributeHandle customAttribute,
out EntityHandle ctorType,
out EntityHandle attributeCtor)
{
try
{
ctorType = default(EntityHandle);
attributeCtor = metadataReader.GetCustomAttribute(customAttribute).Constructor;
if (attributeCtor.Kind == HandleKind.MemberReference)
{
MemberReference memberRef = metadataReader.GetMemberReference((MemberReferenceHandle)attributeCtor);
StringHandle ctorName = memberRef.Name;
if (!metadataReader.StringComparer.Equals(ctorName, WellKnownMemberNames.InstanceConstructorName))
{
// Not a constructor.
return false;
}
ctorType = memberRef.Parent;
}
else if (attributeCtor.Kind == HandleKind.MethodDefinition)
{
var methodDef = metadataReader.GetMethodDefinition((MethodDefinitionHandle)attributeCtor);
if (!metadataReader.StringComparer.Equals(methodDef.Name, WellKnownMemberNames.InstanceConstructorName))
{
// Not a constructor.
return false;
}
ctorType = methodDef.GetDeclaringType();
Debug.Assert(!ctorType.IsNil);
}
else
{
// invalid metadata
return false;
}
return true;
}
catch (BadImageFormatException)
{
ctorType = default(EntityHandle);
attributeCtor = default(EntityHandle);
return false;
}
}
///
/// Given a token for a type, return the type's name and namespace. Only works for top level types.
/// namespaceHandle will be NamespaceDefinitionHandle for defs and StringHandle for refs.
///
/// True if the function successfully returns the name and namespace.
internal bool GetAttributeNamespaceAndName(EntityHandle typeDefOrRef, out StringHandle namespaceHandle, out StringHandle nameHandle)
{
return GetAttributeNamespaceAndName(MetadataReader, typeDefOrRef, out namespaceHandle, out nameHandle);
}
///
/// Given a token for a type, return the type's name and namespace. Only works for top level types.
/// namespaceHandle will be NamespaceDefinitionHandle for defs and StringHandle for refs.
///
/// True if the function successfully returns the name and namespace.
private static bool GetAttributeNamespaceAndName(MetadataReader metadataReader, EntityHandle typeDefOrRef, out StringHandle namespaceHandle, out StringHandle nameHandle)
{
nameHandle = default(StringHandle);
namespaceHandle = default(StringHandle);
try
{
if (typeDefOrRef.Kind == HandleKind.TypeReference)
{
TypeReference typeRefRow = metadataReader.GetTypeReference((TypeReferenceHandle)typeDefOrRef);
HandleKind handleType = typeRefRow.ResolutionScope.Kind;
if (handleType == HandleKind.TypeReference || handleType == HandleKind.TypeDefinition)
{
// TODO - Support nested types.
return false;
}
nameHandle = typeRefRow.Name;
namespaceHandle = typeRefRow.Namespace;
}
else if (typeDefOrRef.Kind == HandleKind.TypeDefinition)
{
var def = metadataReader.GetTypeDefinition((TypeDefinitionHandle)typeDefOrRef);
if (IsNested(def.Attributes))
{
// TODO - Support nested types.
return false;
}
nameHandle = def.Name;
namespaceHandle = def.Namespace;
}
else
{
// unsupported metadata
return false;
}
return true;
}
catch (BadImageFormatException)
{
return false;
}
}
///
/// For testing purposes only!!!
///
internal void PretendThereArentNoPiaLocalTypes()
{
Debug.Assert(_lazyContainsNoPiaLocalTypes != ThreeState.True);
_lazyContainsNoPiaLocalTypes = ThreeState.False;
}
internal bool ContainsNoPiaLocalTypes()
{
if (_lazyContainsNoPiaLocalTypes == ThreeState.Unknown)
{
try
{
foreach (var attributeHandle in MetadataReader.CustomAttributes)
{
int signatureIndex = IsTypeIdentifierAttribute(attributeHandle);
if (signatureIndex != -1)
{
// We found a match
_lazyContainsNoPiaLocalTypes = ThreeState.True;
// We excluded attributes not applied on TypeDefs above:
var parent = (TypeDefinitionHandle)MetadataReader.GetCustomAttribute(attributeHandle).Parent;
RegisterNoPiaLocalType(parent, attributeHandle, signatureIndex);
return true;
}
}
}
catch (BadImageFormatException)
{ }
_lazyContainsNoPiaLocalTypes = ThreeState.False;
}
return _lazyContainsNoPiaLocalTypes == ThreeState.True;
}
#endregion
#region TypeSpec helpers
/// An exception from metadata reader.
internal BlobReader GetTypeSpecificationSignatureReaderOrThrow(TypeSpecificationHandle typeSpec)
{
// TODO: Check validity of the typeSpec handle.
BlobHandle signature = MetadataReader.GetTypeSpecification(typeSpec).Signature;
// TODO: error checking offset in range
return MetadataReader.GetBlobReader(signature);
}
#endregion
#region MethodSpec helpers
/// An exception from metadata reader.
internal void GetMethodSpecificationOrThrow(MethodSpecificationHandle handle, out EntityHandle method, out BlobHandle instantiation)
{
var methodSpec = MetadataReader.GetMethodSpecification(handle);
method = methodSpec.Method;
instantiation = methodSpec.Signature;
}
#endregion
#region GenericParam helpers
/// An exception from metadata reader.
internal void GetGenericParamPropsOrThrow(
GenericParameterHandle handle,
out string name,
out GenericParameterAttributes flags)
{
GenericParameter row = MetadataReader.GetGenericParameter(handle);
name = MetadataReader.GetString(row.Name);
flags = row.Attributes;
}
#endregion
#region MethodDef helpers
/// An exception from metadata reader.
internal string GetMethodDefNameOrThrow(MethodDefinitionHandle methodDef)
{
return MetadataReader.GetString(MetadataReader.GetMethodDefinition(methodDef).Name);
}
/// An exception from metadata reader.
internal BlobHandle GetMethodSignatureOrThrow(MethodDefinitionHandle methodDef)
{
return GetMethodSignatureOrThrow(MetadataReader, methodDef);
}
/// An exception from metadata reader.
private static BlobHandle GetMethodSignatureOrThrow(MetadataReader metadataReader, MethodDefinitionHandle methodDef)
{
return metadataReader.GetMethodDefinition(methodDef).Signature;
}
/// An exception from metadata reader.
internal BlobHandle GetMethodSignatureOrThrow(EntityHandle methodDefOrRef)
{
return GetMethodSignatureOrThrow(MetadataReader, methodDefOrRef);
}
/// An exception from metadata reader.
private static BlobHandle GetMethodSignatureOrThrow(MetadataReader metadataReader, EntityHandle methodDefOrRef)
{
switch (methodDefOrRef.Kind)
{
case HandleKind.MethodDefinition:
return GetMethodSignatureOrThrow(metadataReader, (MethodDefinitionHandle)methodDefOrRef);
case HandleKind.MemberReference:
return GetSignatureOrThrow(metadataReader, (MemberReferenceHandle)methodDefOrRef);
default:
throw ExceptionUtilities.UnexpectedValue(methodDefOrRef.Kind);
}
}
/// An exception from metadata reader.
public MethodAttributes GetMethodDefFlagsOrThrow(MethodDefinitionHandle methodDef)
{
return MetadataReader.GetMethodDefinition(methodDef).Attributes;
}
/// An exception from metadata reader.
internal TypeDefinitionHandle FindContainingTypeOrThrow(MethodDefinitionHandle methodDef)
{
return MetadataReader.GetMethodDefinition(methodDef).GetDeclaringType();
}
/// An exception from metadata reader.
internal TypeDefinitionHandle FindContainingTypeOrThrow(FieldDefinitionHandle fieldDef)
{
return MetadataReader.GetFieldDefinition(fieldDef).GetDeclaringType();
}
/// An exception from metadata reader.
internal EntityHandle GetContainingTypeOrThrow(MemberReferenceHandle memberRef)
{
return MetadataReader.GetMemberReference(memberRef).Parent;
}
/// An exception from metadata reader.
public void GetMethodDefPropsOrThrow(
MethodDefinitionHandle methodDef,
out string name,
out MethodImplAttributes implFlags,
out MethodAttributes flags,
out int rva)
{
MethodDefinition methodRow = MetadataReader.GetMethodDefinition(methodDef);
name = MetadataReader.GetString(methodRow.Name);
implFlags = methodRow.ImplAttributes;
flags = methodRow.Attributes;
rva = methodRow.RelativeVirtualAddress;
Debug.Assert(rva >= 0);
}
/// An exception from metadata reader.
internal void GetMethodImplPropsOrThrow(
MethodImplementationHandle methodImpl,
out EntityHandle body,
out EntityHandle declaration)
{
var impl = MetadataReader.GetMethodImplementation(methodImpl);
body = impl.MethodBody;
declaration = impl.MethodDeclaration;
}
/// An exception from metadata reader.
internal GenericParameterHandleCollection GetGenericParametersForMethodOrThrow(MethodDefinitionHandle methodDef)
{
return MetadataReader.GetMethodDefinition(methodDef).GetGenericParameters();
}
/// An exception from metadata reader.
internal ParameterHandleCollection GetParametersOfMethodOrThrow(MethodDefinitionHandle methodDef)
{
return MetadataReader.GetMethodDefinition(methodDef).GetParameters();
}
internal DllImportData GetDllImportData(MethodDefinitionHandle methodDef)
{
try
{
var methodImport = MetadataReader.GetMethodDefinition(methodDef).GetImport();
if (methodImport.Module.IsNil)
{
// TODO (tomat): report an error?
return null;
}
string moduleName = GetModuleRefNameOrThrow(methodImport.Module);
string entryPointName = MetadataReader.GetString(methodImport.Name);
MethodImportAttributes flags = (MethodImportAttributes)methodImport.Attributes;
return new DllImportData(moduleName, entryPointName, flags);
}
catch (BadImageFormatException)
{
return null;
}
}
#endregion
#region MemberRef helpers
/// An exception from metadata reader.
public string GetMemberRefNameOrThrow(MemberReferenceHandle memberRef)
{
return GetMemberRefNameOrThrow(MetadataReader, memberRef);
}
/// An exception from metadata reader.
private static string GetMemberRefNameOrThrow(MetadataReader metadataReader, MemberReferenceHandle memberRef)
{
return metadataReader.GetString(metadataReader.GetMemberReference(memberRef).Name);
}
/// An exception from metadata reader.
internal BlobHandle GetSignatureOrThrow(MemberReferenceHandle memberRef)
{
return GetSignatureOrThrow(MetadataReader, memberRef);
}
/// An exception from metadata reader.
private static BlobHandle GetSignatureOrThrow(MetadataReader metadataReader, MemberReferenceHandle memberRef)
{
return metadataReader.GetMemberReference(memberRef).Signature;
}
/// An exception from metadata reader.
public void GetMemberRefPropsOrThrow(
MemberReferenceHandle memberRef,
out EntityHandle @class,
out string name,
out byte[] signature)
{
MemberReference row = MetadataReader.GetMemberReference(memberRef);
@class = row.Parent;
name = MetadataReader.GetString(row.Name);
signature = MetadataReader.GetBlobBytes(row.Signature);
}
#endregion MemberRef helpers
#region ParamDef helpers
/// An exception from metadata reader.
internal void GetParamPropsOrThrow(
ParameterHandle parameterDef,
out string name,
out ParameterAttributes flags)
{
Parameter parameter = MetadataReader.GetParameter(parameterDef);
name = MetadataReader.GetString(parameter.Name);
flags = parameter.Attributes;
}
/// An exception from metadata reader.
internal string GetParamNameOrThrow(ParameterHandle parameterDef)
{
Parameter parameter = MetadataReader.GetParameter(parameterDef);
return MetadataReader.GetString(parameter.Name);
}
/// An exception from metadata reader.
internal int GetParameterSequenceNumberOrThrow(ParameterHandle param)
{
return MetadataReader.GetParameter(param).SequenceNumber;
}
#endregion
#region PropertyDef helpers
/// An exception from metadata reader.
internal string GetPropertyDefNameOrThrow(PropertyDefinitionHandle propertyDef)
{
return MetadataReader.GetString(MetadataReader.GetPropertyDefinition(propertyDef).Name);
}
/// An exception from metadata reader.
internal BlobHandle GetPropertySignatureOrThrow(PropertyDefinitionHandle propertyDef)
{
return MetadataReader.GetPropertyDefinition(propertyDef).Signature;
}
/// An exception from metadata reader.
internal void GetPropertyDefPropsOrThrow(
PropertyDefinitionHandle propertyDef,
out string name,
out PropertyAttributes flags)
{
PropertyDefinition property = MetadataReader.GetPropertyDefinition(propertyDef);
name = MetadataReader.GetString(property.Name);
flags = property.Attributes;
}
#endregion
#region EventDef helpers
/// An exception from metadata reader.
internal string GetEventDefNameOrThrow(EventDefinitionHandle eventDef)
{
return MetadataReader.GetString(MetadataReader.GetEventDefinition(eventDef).Name);
}
/// An exception from metadata reader.
internal void GetEventDefPropsOrThrow(
EventDefinitionHandle eventDef,
out string name,
out EventAttributes flags,
out EntityHandle type)
{
EventDefinition eventRow = MetadataReader.GetEventDefinition(eventDef);
name = MetadataReader.GetString(eventRow.Name);
flags = eventRow.Attributes;
type = eventRow.Type;
}
#endregion
#region FieldDef helpers
/// An exception from metadata reader.
public string GetFieldDefNameOrThrow(FieldDefinitionHandle fieldDef)
{
return MetadataReader.GetString(MetadataReader.GetFieldDefinition(fieldDef).Name);
}
/// An exception from metadata reader.
internal BlobHandle GetFieldSignatureOrThrow(FieldDefinitionHandle fieldDef)
{
return MetadataReader.GetFieldDefinition(fieldDef).Signature;
}
/// An exception from metadata reader.
public FieldAttributes GetFieldDefFlagsOrThrow(FieldDefinitionHandle fieldDef)
{
return MetadataReader.GetFieldDefinition(fieldDef).Attributes;
}
/// An exception from metadata reader.
public void GetFieldDefPropsOrThrow(
FieldDefinitionHandle fieldDef,
out string name,
out FieldAttributes flags)
{
FieldDefinition fieldRow = MetadataReader.GetFieldDefinition(fieldDef);
name = MetadataReader.GetString(fieldRow.Name);
flags = fieldRow.Attributes;
}
internal ConstantValue GetParamDefaultValue(ParameterHandle param)
{
Debug.Assert(!param.IsNil);
try
{
var constantHandle = MetadataReader.GetParameter(param).GetDefaultValue();
// TODO: Error checking: Throw an error if the table entry cannot be found
return constantHandle.IsNil ? ConstantValue.Bad : GetConstantValueOrThrow(constantHandle);
}
catch (BadImageFormatException)
{
return ConstantValue.Bad;
}
}
internal ConstantValue GetConstantFieldValue(FieldDefinitionHandle fieldDef)
{
Debug.Assert(!fieldDef.IsNil);
try
{
var constantHandle = MetadataReader.GetFieldDefinition(fieldDef).GetDefaultValue();
// TODO: Error checking: Throw an error if the table entry cannot be found
return constantHandle.IsNil ? ConstantValue.Bad : GetConstantValueOrThrow(constantHandle);
}
catch (BadImageFormatException)
{
return ConstantValue.Bad;
}
}
#endregion
#region Attribute Helpers
/// An exception from metadata reader.
public CustomAttributeHandleCollection GetCustomAttributesOrThrow(EntityHandle handle)
{
return MetadataReader.GetCustomAttributes(handle);
}
/// An exception from metadata reader.
public BlobHandle GetCustomAttributeValueOrThrow(CustomAttributeHandle handle)
{
return MetadataReader.GetCustomAttribute(handle).Value;
}
#endregion
/// An exception from metadata reader.
private BlobHandle GetMarshallingDescriptorHandleOrThrow(EntityHandle fieldOrParameterToken)
{
return fieldOrParameterToken.Kind == HandleKind.FieldDefinition ?
MetadataReader.GetFieldDefinition((FieldDefinitionHandle)fieldOrParameterToken).GetMarshallingDescriptor() :
MetadataReader.GetParameter((ParameterHandle)fieldOrParameterToken).GetMarshallingDescriptor();
}
internal UnmanagedType GetMarshallingType(EntityHandle fieldOrParameterToken)
{
try
{
var blob = GetMarshallingDescriptorHandleOrThrow(fieldOrParameterToken);
if (blob.IsNil)
{
// TODO (tomat): report error:
return 0;
}
byte firstByte = MetadataReader.GetBlobReader(blob).ReadByte();
// return only valid types, other values are not interesting for the compiler:
return firstByte <= 0x50 ? (UnmanagedType)firstByte : 0;
}
catch (BadImageFormatException)
{
return 0;
}
}
internal ImmutableArray GetMarshallingDescriptor(EntityHandle fieldOrParameterToken)
{
try
{
var blob = GetMarshallingDescriptorHandleOrThrow(fieldOrParameterToken);
if (blob.IsNil)
{
// TODO (tomat): report error:
return ImmutableArray.Empty;
}
return MetadataReader.GetBlobBytes(blob).AsImmutableOrNull();
}
catch (BadImageFormatException)
{
return ImmutableArray.Empty;
}
}
internal int? GetFieldOffset(FieldDefinitionHandle fieldDef)
{
try
{
int offset = MetadataReader.GetFieldDefinition(fieldDef).GetOffset();
if (offset == -1)
{
return null;
}
return offset;
}
catch (BadImageFormatException)
{
return null;
}
}
/// An exception from metadata reader.
private ConstantValue GetConstantValueOrThrow(ConstantHandle handle)
{
var constantRow = MetadataReader.GetConstant(handle);
BlobReader reader = MetadataReader.GetBlobReader(constantRow.Value);
switch (constantRow.TypeCode)
{
case ConstantTypeCode.Boolean:
return ConstantValue.Create(reader.ReadBoolean());
case ConstantTypeCode.Char:
return ConstantValue.Create(reader.ReadChar());
case ConstantTypeCode.SByte:
return ConstantValue.Create(reader.ReadSByte());
case ConstantTypeCode.Int16:
return ConstantValue.Create(reader.ReadInt16());
case ConstantTypeCode.Int32:
return ConstantValue.Create(reader.ReadInt32());
case ConstantTypeCode.Int64:
return ConstantValue.Create(reader.ReadInt64());
case ConstantTypeCode.Byte:
return ConstantValue.Create(reader.ReadByte());
case ConstantTypeCode.UInt16:
return ConstantValue.Create(reader.ReadUInt16());
case ConstantTypeCode.UInt32:
return ConstantValue.Create(reader.ReadUInt32());
case ConstantTypeCode.UInt64:
return ConstantValue.Create(reader.ReadUInt64());
case ConstantTypeCode.Single:
return ConstantValue.Create(reader.ReadSingle());
case ConstantTypeCode.Double:
return ConstantValue.Create(reader.ReadDouble());
case ConstantTypeCode.String:
return ConstantValue.Create(reader.ReadUTF16(reader.Length));
case ConstantTypeCode.NullReference:
// Partition II section 22.9:
// The encoding of Type for the nullref value is ELEMENT_TYPE_CLASS with a Value of a 4-byte zero.
// Unlike uses of ELEMENT_TYPE_CLASS in signatures, this one is not followed by a type token.
if (reader.ReadUInt32() == 0)
{
return ConstantValue.Null;
}
break;
}
return ConstantValue.Bad;
}
internal (int FirstIndex, int SecondIndex) GetAssemblyRefsForForwardedType(string fullName, bool ignoreCase, out string matchedName)
{
EnsureForwardTypeToAssemblyMap();
if (ignoreCase)
{
// This linear search is not the optimal way to use a hashmap, but we should only use
// this functionality when computing diagnostics. Note
// that we can't store the map case-insensitively, since real metadata name
// lookup has to remain case sensitive.
foreach (var pair in _lazyForwardedTypesToAssemblyIndexMap)
{
if (string.Equals(pair.Key, fullName, StringComparison.OrdinalIgnoreCase))
{
matchedName = pair.Key;
return pair.Value;
}
}
}
else
{
(int FirstIndex, int SecondIndex) assemblyIndices;
if (_lazyForwardedTypesToAssemblyIndexMap.TryGetValue(fullName, out assemblyIndices))
{
matchedName = fullName;
return assemblyIndices;
}
}
matchedName = null;
return (FirstIndex: -1, SecondIndex: - 1);
}
internal IEnumerable> GetForwardedTypes()
{
EnsureForwardTypeToAssemblyMap();
return _lazyForwardedTypesToAssemblyIndexMap;
}
private void EnsureForwardTypeToAssemblyMap()
{
if (_lazyForwardedTypesToAssemblyIndexMap == null)
{
var typesToAssemblyIndexMap = new Dictionary();
try
{
var forwarders = MetadataReader.ExportedTypes;
foreach (var handle in forwarders)
{
ExportedType exportedType = MetadataReader.GetExportedType(handle);
if (!exportedType.IsForwarder)
{
continue;
}
AssemblyReferenceHandle refHandle = (AssemblyReferenceHandle)exportedType.Implementation;
if (refHandle.IsNil)
{
continue;
}
int referencedAssemblyIndex;
try
{
referencedAssemblyIndex = this.GetAssemblyReferenceIndexOrThrow(refHandle);
}
catch (BadImageFormatException)
{
continue;
}
if (referencedAssemblyIndex < 0 || referencedAssemblyIndex >= this.ReferencedAssemblies.Length)
{
continue;
}
string name = MetadataReader.GetString(exportedType.Name);
StringHandle ns = exportedType.Namespace;
if (!ns.IsNil)
{
string namespaceString = MetadataReader.GetString(ns);
if (namespaceString.Length > 0)
{
name = namespaceString + "." + name;
}
}
(int FirstIndex, int SecondIndex) indices;
if (typesToAssemblyIndexMap.TryGetValue(name, out indices))
{
Debug.Assert(indices.FirstIndex >= 0, "Not allowed to store a negative (non-existent) index in typesToAssemblyIndexMap");
// Store it only if it was not a duplicate
if (indices.FirstIndex != referencedAssemblyIndex && indices.SecondIndex < 0)
{
indices.SecondIndex = referencedAssemblyIndex;
typesToAssemblyIndexMap[name] = indices;
}
}
else
{
typesToAssemblyIndexMap.Add(name, (FirstIndex: referencedAssemblyIndex, SecondIndex: -1));
}
}
}
catch (BadImageFormatException)
{ }
_lazyForwardedTypesToAssemblyIndexMap = typesToAssemblyIndexMap;
}
}
internal IdentifierCollection TypeNames
{
get
{
return _lazyTypeNameCollection.Value;
}
}
internal IdentifierCollection NamespaceNames
{
get
{
return _lazyNamespaceNameCollection.Value;
}
}
/// An exception from metadata reader.
internal PropertyAccessors GetPropertyMethodsOrThrow(PropertyDefinitionHandle propertyDef)
{
return MetadataReader.GetPropertyDefinition(propertyDef).GetAccessors();
}
/// An exception from metadata reader.
internal EventAccessors GetEventMethodsOrThrow(EventDefinitionHandle eventDef)
{
return MetadataReader.GetEventDefinition(eventDef).GetAccessors();
}
/// An exception from metadata reader.
internal int GetAssemblyReferenceIndexOrThrow(AssemblyReferenceHandle assemblyRef)
{
return MetadataReader.GetRowNumber(assemblyRef) - 1;
}
internal static bool IsNested(TypeAttributes flags)
{
return (flags & ((TypeAttributes)0x00000006)) != 0;
}
///
/// Returns true if method IL can be retrieved from the module.
///
internal bool HasIL
{
get { return IsEntireImageAvailable; }
}
///
/// Returns true if the full image of the module is available.
///
internal bool IsEntireImageAvailable
{
get { return _peReaderOpt != null && _peReaderOpt.IsEntireImageAvailable; }
}
/// Invalid metadata.
internal MethodBodyBlock GetMethodBodyOrThrow(MethodDefinitionHandle methodHandle)
{
// we shouldn't ask for method IL if we don't have PE image
Debug.Assert(_peReaderOpt != null);
MethodDefinition method = this.MetadataReader.GetMethodDefinition(methodHandle);
if ((method.ImplAttributes & MethodImplAttributes.CodeTypeMask) != MethodImplAttributes.IL ||
method.RelativeVirtualAddress == 0)
{
return null;
}
return _peReaderOpt.GetMethodBody(method.RelativeVirtualAddress);
}
// TODO: remove, API should be provided by MetadataReader
private static bool StringEquals(MetadataReader metadataReader, StringHandle nameHandle, string name, bool ignoreCase)
{
if (ignoreCase)
{
return string.Equals(metadataReader.GetString(nameHandle), name, StringComparison.OrdinalIgnoreCase);
}
return metadataReader.StringComparer.Equals(nameHandle, name);
}
// Provides a UTF8 decoder to the MetadataReader that reuses strings from the string table
// rather than allocating on each call to MetadataReader.GetString(handle).
private sealed class StringTableDecoder : MetadataStringDecoder
{
public static readonly StringTableDecoder Instance = new StringTableDecoder();
private StringTableDecoder() : base(System.Text.Encoding.UTF8) { }
public unsafe override string GetString(byte* bytes, int byteCount)
{
return StringTable.AddSharedUTF8(bytes, byteCount);
}
}
public ModuleMetadata GetNonDisposableMetadata() => _owner.Copy();
}
}