using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using TechnologyAssembler; namespace HashCalculator.GUI { internal class ScriptCompiler { private static readonly MetadataReference[] _references = new[] { typeof(object), // core typeof(Trace), // make sure trace is availaible typeof(Enumerable), // linq typeof(ScriptCompiler), // this typeof(TechnologyAssemblerCoreModule) // wed }.Select(t => MetadataReference.CreateFromFile(t.Assembly.Location)).ToArray(); public static IEnumerable<(string Id, string Message)> CheckSyntax(string code) { var syntaxTree = CSharpSyntaxTree.ParseText(code); return syntaxTree.GetDiagnostics().Select(diagnostic => (diagnostic.Id, diagnostic.GetMessage())); } public static bool Compile(string code, object[] arguments, out IEnumerable<(string Id, string Message)> messages) { // define source code, then parse it (to the type used for compilation) var syntaxTree = CSharpSyntaxTree.ParseText(code); // define other necessary objects for compilation var assemblyName = $"Gen{Guid.NewGuid()}.dll"; // analyse and generate IL code from syntax tree var compilation = CSharpCompilation.Create(assemblyName, syntaxTrees: new[] { syntaxTree }, references: _references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using var ms = new MemoryStream(); // write IL code into memory var result = compilation.Emit(ms); messages = result.Diagnostics.Select(diagnostic => (diagnostic.Id, diagnostic.GetMessage())); if (!result.Success) { return false; } // load this 'virtual' DLL so that we can use ms.Seek(0, SeekOrigin.Begin); var domain = AppDomain.CreateDomain($"runtime code {assemblyName}"); var assembly = domain.Load(ms.ToArray()); // create instance of the desired class and call the desired function var entryPoint = assembly.EntryPoint ?? throw new InvalidOperationException("Compiled assembly does not have an entry point"); entryPoint.Invoke(null, arguments); return true; } } }