File: MetadataReader\MetadataReaderExtensions.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Immutable;
using System.Globalization;
using System.Reflection;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.PooledObjects;
 
namespace Microsoft.CodeAnalysis
{
    internal static class MetadataReaderExtensions
    {
        internal static bool GetWinMdVersion(this MetadataReader reader, out int majorVersion, out int minorVersion)
        {
            if (reader.MetadataKind == MetadataKind.WindowsMetadata)
            {
                // Name should be of the form "WindowsRuntime {major}.{minor}".
                const string prefix = "WindowsRuntime ";
                string version = reader.MetadataVersion;
                if (version.StartsWith(prefix, StringComparison.Ordinal))
                {
                    var parts = version.Substring(prefix.Length).Split('.');
                    if ((parts.Length == 2) &&
                        int.TryParse(parts[0], NumberStyles.None, CultureInfo.InvariantCulture, out majorVersion) &&
                        int.TryParse(parts[1], NumberStyles.None, CultureInfo.InvariantCulture, out minorVersion))
                    {
                        return true;
                    }
                }
            }
 
            majorVersion = 0;
            minorVersion = 0;
            return false;
        }
 
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        internal static AssemblyIdentity ReadAssemblyIdentityOrThrow(this MetadataReader reader)
        {
            if (!reader.IsAssembly)
            {
                return null;
            }
 
            var assemblyDef = reader.GetAssemblyDefinition();
 
            return reader.CreateAssemblyIdentityOrThrow(
                assemblyDef.Version,
                assemblyDef.Flags,
                assemblyDef.PublicKey,
                assemblyDef.Name,
                assemblyDef.Culture,
                isReference: false);
        }
 
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        internal static ImmutableArray<AssemblyIdentity> GetReferencedAssembliesOrThrow(this MetadataReader reader)
        {
            var result = ArrayBuilder<AssemblyIdentity>.GetInstance(reader.AssemblyReferences.Count);
            try
            {
                foreach (var assemblyRef in reader.AssemblyReferences)
                {
                    AssemblyReference reference = reader.GetAssemblyReference(assemblyRef);
                    result.Add(reader.CreateAssemblyIdentityOrThrow(
                        reference.Version,
                        reference.Flags,
                        reference.PublicKeyOrToken,
                        reference.Name,
                        reference.Culture,
                        isReference: true));
                }
 
                return result.ToImmutable();
            }
            finally
            {
                result.Free();
            }
        }
 
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        internal static Guid GetModuleVersionIdOrThrow(this MetadataReader reader)
        {
            return reader.GetGuid(reader.GetModuleDefinition().Mvid);
        }
 
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        private static AssemblyIdentity CreateAssemblyIdentityOrThrow(
            this MetadataReader reader,
            Version version,
            AssemblyFlags flags,
            BlobHandle publicKey,
            StringHandle name,
            StringHandle culture,
            bool isReference)
        {
            string nameStr = reader.GetString(name);
            if (!MetadataHelpers.IsValidMetadataIdentifier(nameStr))
            {
                throw new BadImageFormatException(string.Format(CodeAnalysisResources.InvalidAssemblyName, nameStr));
            }
 
            string cultureName = culture.IsNil ? null : reader.GetString(culture);
            if (cultureName != null && !MetadataHelpers.IsValidMetadataIdentifier(cultureName))
            {
                throw new BadImageFormatException(string.Format(CodeAnalysisResources.InvalidCultureName, cultureName));
            }
 
            ImmutableArray<byte> publicKeyOrToken = reader.GetBlobContent(publicKey);
            bool hasPublicKey;
 
            if (isReference)
            {
                hasPublicKey = (flags & AssemblyFlags.PublicKey) != 0;
                if (hasPublicKey)
                {
                    if (!MetadataHelpers.IsValidPublicKey(publicKeyOrToken))
                    {
                        throw new BadImageFormatException(CodeAnalysisResources.InvalidPublicKey);
                    }
                }
                else
                {
                    if (!publicKeyOrToken.IsEmpty &&
                        publicKeyOrToken.Length != AssemblyIdentity.PublicKeyTokenSize)
                    {
                        throw new BadImageFormatException(CodeAnalysisResources.InvalidPublicKeyToken);
                    }
                }
            }
            else
            {
                // Assembly definitions never contain a public key token, they only can have a full key or nothing,
                // so the flag AssemblyFlags.PublicKey does not make sense for them and is ignored.
                // See Ecma-335, Partition II Metadata, 22.2 "Assembly : 0x20".
                // This also corresponds to the behavior of the native C# compiler and sn.exe tool.
                hasPublicKey = !publicKeyOrToken.IsEmpty;
                if (hasPublicKey && !MetadataHelpers.IsValidPublicKey(publicKeyOrToken))
                {
                    throw new BadImageFormatException(CodeAnalysisResources.InvalidPublicKey);
                }
            }
 
            if (publicKeyOrToken.IsEmpty)
            {
                publicKeyOrToken = default(ImmutableArray<byte>);
            }
 
            return new AssemblyIdentity(
                name: nameStr,
                version: version,
                cultureName: cultureName,
                publicKeyOrToken: publicKeyOrToken,
                hasPublicKey: hasPublicKey,
                isRetargetable: (flags & AssemblyFlags.Retargetable) != 0,
                contentType: (AssemblyContentType)((int)(flags & AssemblyFlags.ContentTypeMask) >> 9),
                noThrow: true);
        }
 
        internal static bool DeclaresTheObjectClass(this MetadataReader reader)
        {
            return reader.DeclaresType(IsTheObjectClass);
        }
 
        private static bool IsTheObjectClass(this MetadataReader reader, TypeDefinition typeDef)
        {
            return typeDef.BaseType.IsNil &&
                reader.IsPublicNonInterfaceType(typeDef, "System", "Object");
        }
 
        internal static bool DeclaresType(this MetadataReader reader, Func<MetadataReader, TypeDefinition, bool> predicate)
        {
            foreach (TypeDefinitionHandle handle in reader.TypeDefinitions)
            {
                try
                {
                    var typeDef = reader.GetTypeDefinition(handle);
                    if (predicate(reader, typeDef))
                    {
                        return true;
                    }
                }
                catch (BadImageFormatException)
                {
                }
            }
 
            return false;
        }
 
        /// <exception cref="BadImageFormatException">An exception from metadata reader.</exception>
        internal static bool IsPublicNonInterfaceType(this MetadataReader reader, TypeDefinition typeDef, string namespaceName, string typeName)
        {
            return (typeDef.Attributes & (TypeAttributes.Public | TypeAttributes.Interface)) == TypeAttributes.Public &&
                reader.StringComparer.Equals(typeDef.Name, typeName) &&
                reader.StringComparer.Equals(typeDef.Namespace, namespaceName);
        }
    }
}