Skip to content

Instantly share code, notes, and snippets.

@Undermove
Created February 22, 2022 12:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Undermove/719cef1796bc0e6a5aab911e3f5bdab7 to your computer and use it in GitHub Desktop.
Save Undermove/719cef1796bc0e6a5aab911e3f5bdab7 to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
namespace TestCompile.Controllers
{
[Route("api")]
[ApiController]
public class CheckCodeController : Controller
{
[HttpPost]
[Route("CheckCode")]
public ActionResult<CheckCodeResponse> CheckCode([FromBody] CheckCodeRequest request)
{
//https://www.damirscorner.com/blog/posts/20190802-CompilingAndExecutingCodeInACsApp.html
var syntaxTree = SyntaxFactory.ParseSyntaxTree(SourceText.From(request.CodeSample));
var assemblyPath = Path.ChangeExtension(Path.GetTempFileName(), "exe");
System.IO.File.WriteAllText(
Path.ChangeExtension(assemblyPath, "runtimeconfig.json"),
GenerateRuntimeConfig()
);
var dotNetCoreDir = Path.GetDirectoryName(typeof(object).GetTypeInfo().Assembly.Location);
//https://laurentkempe.com/2019/02/18/dynamically-compile-and-run-code-using-dotNET-Core-3.0/
var compilation = CSharpCompilation.Create(Path.GetFileName(assemblyPath))
.WithOptions(new CSharpCompilationOptions(OutputKind.ConsoleApplication))
.AddReferences(
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(Path.Combine(dotNetCoreDir, "System.Runtime.dll")),
MetadataReference.CreateFromFile(typeof(System.Runtime.AssemblyTargetedPatchBandAttribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo).Assembly.Location)
)
.AddSyntaxTrees(syntaxTree);
var result = compilation.Emit(assemblyPath);
string outputData = "No Data";
if (result.Success)
{
//https://stackoverflow.com/questions/4291912/process-start-how-to-get-the-output
var startInfo = new ProcessStartInfo("dotnet", assemblyPath)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
var process = new Process() { StartInfo = startInfo };
process.Start();
StreamReader reader = process.StandardOutput;
var errorOutput = process.StandardError.ReadToEnd();
//https://github.com/dotnet/roslyn/issues/33090
string output = reader.ReadToEnd();
outputData = string.IsNullOrEmpty(errorOutput) ? output : errorOutput;
}
else
{
Debug.Write(string.Join(
Environment.NewLine,
result.Diagnostics.Select(diagnostic => diagnostic.ToString())
));
}
return new CheckCodeResponse()
{
Result = result.Success ? outputData : string.Join(
Environment.NewLine,
result.Diagnostics.Select(diagnostic => diagnostic.ToString())
)};
}
private string GenerateRuntimeConfig()
{
using (var stream = new MemoryStream())
{
using (var writer = new Utf8JsonWriter(
stream,
new JsonWriterOptions() { Indented = true }
))
{
writer.WriteStartObject();
writer.WriteStartObject("runtimeOptions");
writer.WriteStartObject("framework");
writer.WriteString("name", "Microsoft.NETCore.App");
writer.WriteString(
"version",
RuntimeInformation.FrameworkDescription.Replace(".NET ", "")
);
writer.WriteEndObject();
writer.WriteEndObject();
writer.WriteEndObject();
}
return Encoding.UTF8.GetString(stream.ToArray());
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment