// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // namespace MUnique.OpenMU.PlugIns; using System.Collections.Immutable; using System.IO; using System.Reflection; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; /// /// Extensions for s. /// public static class SyntaxTreeExtensions { private static IList? _assemblyReferences; private static IList AssemblyReferences { get { if (_assemblyReferences is { }) { return _assemblyReferences; } // Force Nito assemblies to be loaded, so they are part of the trusted platform assemblies. _ = Directory.EnumerateFiles(new FileInfo(typeof(SyntaxTreeExtensions).Assembly.Location).DirectoryName!, "Nito.*.dll") .Select(Assembly.LoadFrom) .ToList(); var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies() .Where(a => !a.IsDynamic) .Select(a => a.Location) .ToImmutableHashSet(); var separator = (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix) ? ':' : ';'; _assemblyReferences = (AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") as string) ?.Split(separator) .Select(path => MetadataReference.CreateFromFile(path)) .Where(metaData => metaData.FilePath is not null && loadedAssemblies.Contains(metaData.FilePath)) .ToList() ?? new List(); return _assemblyReferences; } } /// /// Compiles the and load its assembly into memory. /// /// The syntax tree. /// Name of the assembly. /// The compiled assembly. public static Assembly CompileAndLoad(this SyntaxTree syntaxTree, string assemblyName) { var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) .WithOverflowChecks(false) .WithOptimizationLevel(OptimizationLevel.Release) .WithUsings("System", "System.Collections.Generic", "System.Threading", "Nito.AsyncEx"); var compilation = CSharpCompilation.Create(assemblyName, new[] { syntaxTree }, AssemblyReferences, options); using var stream = new MemoryStream(); var result = compilation.Emit(stream); if (!result.Success) { var stringBuilder = new StringBuilder(); result.Diagnostics .Where(m => m.Severity == DiagnosticSeverity.Error) .Select(d => $"{d.GetMessage()} @ {d.Location}") .ToList() .ForEach(message => stringBuilder.AppendLine(message)); throw new ArgumentException(stringBuilder.ToString()); } return Assembly.Load(stream.ToArray()); } }