HashCalculator.GUI/ScriptCompiler.cs
2021-09-06 23:14:21 +02:00

66 lines
2.6 KiB
C#

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;
}
}
}