66 lines
2.6 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|