File: Metadata\IlasmUtilities.cs
Web Access
Project: src\src\Compilers\Test\Core\Microsoft.CodeAnalysis.Test.Utilities.csproj (Microsoft.CodeAnalysis.Test.Utilities)
// 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.IO;
using System.Runtime.InteropServices;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Test.Utilities
{
    public static class IlasmUtilities
    {
        public static DisposableFile CreateTempAssembly(string declarations, bool prependDefaultHeader = true)
        {
            IlasmTempAssembly(declarations, prependDefaultHeader, includePdb: false, autoInherit: true, assemblyPath: out var assemblyPath, pdbPath: out var pdbPath);
            Assert.NotNull(assemblyPath);
            Assert.Null(pdbPath);
            return new DisposableFile(assemblyPath);
        }
 
        private static string GetArchitecture()
        {
#if NET8_0_OR_GREATER
            return RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant();
#else
            return "x64";
#endif
        }
 
        public static readonly string Architecture = GetArchitecture();
 
        private static string GetIlasmPath()
        {
            if (ExecutionConditionUtil.IsWindowsDesktop)
            {
                // The desktop ilasm is still necessary because a number of our tests depend on being able to 
                // emit PDB files for net modules. That feature is not available on coreclr ilasm.
                return Path.Combine(
                    Path.GetDirectoryName(RuntimeUtilities.GetAssemblyLocation(typeof(object))),
                    "ilasm.exe");
            }
 
            var ilasmExeName = PlatformInformation.IsWindows ? "ilasm.exe" : "ilasm";
            var directory = Path.GetDirectoryName(RuntimeUtilities.GetAssemblyLocation(typeof(RuntimeUtilities)));
            string ridName;
            if (ExecutionConditionUtil.IsWindows)
            {
                ridName = "win-x64";
            }
            else if (ExecutionConditionUtil.IsMacOS)
            {
                ridName = "osx-x64";
            }
            else if (ExecutionConditionUtil.IsLinux)
            {
                ridName = $"linux-{GetArchitecture()}";
            }
            else
            {
                throw new PlatformNotSupportedException("Runtime platform not supported for testing");
            }
 
            return Path.Combine(directory, "runtimes", ridName, "native", ilasmExeName);
        }
 
        private static readonly string IlasmPath = GetIlasmPath();
 
        public static void IlasmTempAssembly(string declarations, bool appendDefaultHeader, bool includePdb, bool autoInherit, out string assemblyPath, out string pdbPath)
        {
            if (declarations == null)
                throw new ArgumentNullException(nameof(declarations));
 
            using (var sourceFile = new DisposableFile(extension: ".il"))
            {
                string sourceFileName = Path.GetFileNameWithoutExtension(sourceFile.Path);
 
                assemblyPath = Path.Combine(
                    TempRoot.Root,
                    Path.ChangeExtension(Path.GetFileName(sourceFile.Path), "dll"));
 
                string completeIL;
                if (appendDefaultHeader)
                {
                    const string corLibName = "mscorlib";
                    const string corLibVersion = "4:0:0:0";
                    const string corLibKey = "B7 7A 5C 56 19 34 E0 89";
 
                    completeIL =
$@".assembly '{sourceFileName}' {{}} 
 
.assembly extern {corLibName} 
{{
  .publickeytoken = ({corLibKey})
  .ver {corLibVersion}
}} 
 
{declarations}";
                }
                else
                {
                    completeIL = declarations.Replace("<<GeneratedFileName>>", sourceFileName);
                }
 
                sourceFile.WriteAllText(completeIL);
 
                var arguments = $"\"{sourceFile.Path}\" -DLL {(autoInherit ? "" : "-noautoinherit")} -out=\"{assemblyPath}\"";
 
                if (includePdb && !MonoHelpers.IsRunningOnMono())
                {
                    pdbPath = Path.ChangeExtension(assemblyPath, "pdb");
                    arguments += string.Format(" -PDB=\"{0}\"", pdbPath);
                }
                else
                {
                    pdbPath = null;
                }
 
                var result = ProcessUtilities.Run(IlasmPath, arguments);
 
                if (result.ContainsErrors)
                {
                    throw new ArgumentException(
                        "The provided IL cannot be compiled." + Environment.NewLine +
                        IlasmPath + " " + arguments + Environment.NewLine +
                        result,
                        nameof(declarations));
                }
            }
        }
    }
}