itfx/src/InterfaceForward.Application/ForwardCore/NatashaScriptCompiler.cs
2026-06-01 17:09:54 +08:00

84 lines
2.8 KiB
C#

using System.Reflection;
using InterfaceForward.Application.Contracts.ForwardCore;
using Microsoft.CodeAnalysis;
namespace InterfaceForward.Application.ForwardCore;
internal static class NatashaScriptCompiler
{
public static Action<ForwardCoreContext> CompileAction(string script)
{
var method = Compile(script, "void");
return method.CreateDelegate<Action<ForwardCoreContext>>();
}
public static Func<ForwardCoreContext, Task> CompileAsyncFunc(string script)
{
var method = Compile(script, "async System.Threading.Tasks.Task");
return method.CreateDelegate<Func<ForwardCoreContext, Task>>();
}
static MethodInfo Compile(string script, string returnType)
{
var typeName = "Script_" + Guid.NewGuid().ToString("N");
var code = $$"""
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using InterfaceForward.Application.Contracts.ForwardCore;
using InterfaceForward.Domain.Shared;
using InterfaceForward.Domain.Shared.Dtos;
using InterfaceForward.Domain.Shared.Enum;
using Newtonsoft.Json.Linq;
public static class {{typeName}}
{
public static {{returnType}} Run(ForwardCoreContext obj)
{
var context = obj;
{{script}}
}
}
""";
var assembly = new AssemblyCSharpBuilder()
.UseRandomDomain()
.WithSpecifiedReferences(GetMetadataReferences())
.Add(code)
.GetAssembly();
return assembly
.GetType(typeName, throwOnError: true)!
.GetMethod("Run", BindingFlags.Public | BindingFlags.Static)!;
}
static IEnumerable<MetadataReference> GetMetadataReferences()
{
var paths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") is string trustedPlatformAssemblies)
{
foreach (var path in trustedPlatformAssemblies.Split(Path.PathSeparator))
{
AddPath(path);
}
}
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
AddPath(assembly.Location);
}
return paths.Select(path => MetadataReference.CreateFromFile(path));
void AddPath(string? path)
{
if (!string.IsNullOrWhiteSpace(path) && File.Exists(path))
{
paths.Add(path);
}
}
}
}