File: AssemblyUtilities.cs
Web Access
Project: ..\..\..\src\Framework\Microsoft.Build.Framework.csproj (Microsoft.Build.Framework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Globalization;
using System.Reflection;
 
#if !FEATURE_CULTUREINFO_GETCULTURES
using System.Linq;
using Microsoft.Build.Framework;
#endif
 
 
// Declare this to get init properties. See https://github.com/dotnet/roslyn/issues/45510#issuecomment-694977239
#nullable disable
 
namespace System.Runtime.CompilerServices
{
    internal static class IsExternalInit { }
}
 
namespace Microsoft.Build.Shared
{
    /// <summary>
    /// This class contains common reflection tasks
    /// </summary>
    internal static class AssemblyUtilities
    {
#if !FEATURE_CULTUREINFO_GETCULTURES
        // True when the cached method info objects have been set.
        private static bool s_initialized;
 
        // Cached method info
        private static PropertyInfo s_assemblylocationProperty;
        private static MethodInfo s_cultureInfoGetCultureMethod;
 
        private static Lazy<CultureInfo[]> s_validCultures = new Lazy<CultureInfo[]>(() => GetValidCultures(), true);
#endif
 
#if !CLR2COMPATIBILITY
        private static Lazy<Assembly> s_entryAssembly = new Lazy<Assembly>(() => GetEntryAssembly());
        public static Assembly EntryAssembly => s_entryAssembly.Value;
#else
        public static Assembly EntryAssembly = GetEntryAssembly();
#endif
 
        public static string GetAssemblyLocation(Assembly assembly)
        {
#if FEATURE_ASSEMBLY_LOCATION
            return assembly.Location;
#else
            // Assembly.Location is only available in .netstandard1.5, but MSBuild needs to target 1.3.
            // use reflection to access the property
            Initialize();
 
            if (s_assemblylocationProperty == null)
            {
                throw new NotSupportedException("Type Assembly does not have the Location property");
            }
 
            return (string)s_assemblylocationProperty.GetValue(assembly);
#endif
        }
 
#if CLR2COMPATIBILITY
        /// <summary>
        /// Shim for the lack of <see cref="System.Reflection.IntrospectionExtensions.GetTypeInfo"/> in .NET 3.5.
        /// </summary>
        public static Type GetTypeInfo(this Type t)
        {
            return t;
        }
#endif
 
        public static AssemblyName CloneIfPossible(this AssemblyName assemblyNameToClone)
        {
#if CLR2COMPATIBILITY
            return (AssemblyName)assemblyNameToClone.Clone();
#else
 
            // NOTE: In large projects, this is called a lot. Avoid calling AssemblyName.Clone
            // because it clones the Version property (which is immutable) and the PublicKey property
            // and the PublicKeyToken property.
            //
            // While the array themselves are mutable - throughout MSBuild they are only ever
            // read from.
            AssemblyName name = new AssemblyName();
            name.Name = assemblyNameToClone.Name;
            name.SetPublicKey(assemblyNameToClone.GetPublicKey());
            name.SetPublicKeyToken(assemblyNameToClone.GetPublicKeyToken());
            name.Version = assemblyNameToClone.Version;
            name.Flags = assemblyNameToClone.Flags;
            name.ProcessorArchitecture = assemblyNameToClone.ProcessorArchitecture;
 
#if !RUNTIME_TYPE_NETCORE
            name.CultureInfo = assemblyNameToClone.CultureInfo;
            name.HashAlgorithm = assemblyNameToClone.HashAlgorithm;
            name.VersionCompatibility = assemblyNameToClone.VersionCompatibility;
            name.CodeBase = assemblyNameToClone.CodeBase;
            name.KeyPair = assemblyNameToClone.KeyPair;
            name.VersionCompatibility = assemblyNameToClone.VersionCompatibility;
#else
            // Setting the culture name creates a new CultureInfo, leading to many allocations. Only set CultureName when the CultureInfo member is not available.
            name.CultureName = assemblyNameToClone.CultureName;
#endif
 
            return name;
#endif
 
        }
 
#if !FEATURE_CULTUREINFO_GETCULTURES
        public static bool CultureInfoHasGetCultures()
        {
            return s_cultureInfoGetCultureMethod != null;
        }
#endif // !FEATURE_CULTUREINFO_GETCULTURES
 
        public static CultureInfo[] GetAllCultures()
        {
#if FEATURE_CULTUREINFO_GETCULTURES
            return CultureInfo.GetCultures(CultureTypes.AllCultures);
#else
            Initialize();
 
            if (!CultureInfoHasGetCultures())
            {
                throw new NotSupportedException("CultureInfo does not have the method GetCultures");
            }
 
            return s_validCultures.Value;
#endif
        }
 
#if !FEATURE_CULTUREINFO_GETCULTURES
        /// <summary>
        /// Initialize static fields. Doesn't need to be thread safe.
        /// </summary>
        private static void Initialize()
        {
            if (s_initialized)
            {
                return;
            }
 
            s_assemblylocationProperty = typeof(Assembly).GetProperty("Location", typeof(string));
            s_cultureInfoGetCultureMethod = typeof(CultureInfo).GetMethod("GetCultures");
 
            s_initialized = true;
        }
#endif // !FEATURE_CULTUREINFO_GETCULTURES
 
        private static Assembly GetEntryAssembly()
        {
            return System.Reflection.Assembly.GetEntryAssembly();
        }
 
#if !FEATURE_CULTUREINFO_GETCULTURES
        private static CultureInfo[] GetValidCultures()
        {
            var cultureTypesType = s_cultureInfoGetCultureMethod?.GetParameters().FirstOrDefault()?.ParameterType;
 
            FrameworkErrorUtilities.VerifyThrow(cultureTypesType?.Name == "CultureTypes" &&
                                       Enum.IsDefined(cultureTypesType, "AllCultures"),
                                       "GetCulture is expected to accept CultureTypes.AllCultures");
 
            var allCulturesEnumValue = Enum.Parse(cultureTypesType, "AllCultures", true);
 
            var cultures = s_cultureInfoGetCultureMethod.Invoke(null, [allCulturesEnumValue]) as CultureInfo[];
 
            // CultureInfo.GetCultures should work if all reflection checks pass
            FrameworkErrorUtilities.VerifyThrowInternalNull(cultures);
 
            return cultures;
        }
#endif
    }
}