// 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; private Dictionary> _lazyForwardedTypesToAssemblyMap; 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 _lazyForwardedTypesToAssemblyMap.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 HashSet 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 _lazyForwardedTypesToAssemblyMap) { if (string.Equals(pair.Key, fullName, StringComparison.OrdinalIgnoreCase)) { matchedName = pair.Key; return pair.Value; } } } else { HashSet assemblyRefs; if (_lazyForwardedTypesToAssemblyMap.TryGetValue(fullName, out assemblyRefs)) { matchedName = fullName; return assemblyRefs; } } matchedName = null; return default(HashSet); } internal IEnumerable>> GetForwardedTypes() { EnsureForwardTypeToAssemblyMap(); return _lazyForwardedTypesToAssemblyMap; } private void EnsureForwardTypeToAssemblyMap() { if (_lazyForwardedTypesToAssemblyMap == null) { var typesToAssemblyMap = new Dictionary>(); try { var forwarders = MetadataReader.ExportedTypes; foreach (var handle in forwarders) { ExportedType exportedType = MetadataReader.GetExportedType(handle); if (!exportedType.IsForwarder) { continue; } AssemblyReferenceHandle newReference = (AssemblyReferenceHandle)exportedType.Implementation; if (newReference.IsNil) { continue; } string name = MetadataReader.GetString(exportedType.Name); if (!exportedType.Namespace.IsNil) { string namespaceString = MetadataReader.GetString(exportedType.Namespace); if (namespaceString.Length > 0) { name = namespaceString + "." + name; } } HashSet references; if (typesToAssemblyMap.TryGetValue(name, out references)) { references.Add(newReference); } else { references = new HashSet(); references.Add(newReference); typesToAssemblyMap.Add(name, references); } } } catch (BadImageFormatException) { } _lazyForwardedTypesToAssemblyMap = typesToAssemblyMap; } } 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(); } }