227 lines
10 KiB
C#
227 lines
10 KiB
C#
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.Scripting;
|
|
using RobotNet.Script.Shares;
|
|
using System.Collections.Immutable;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
|
|
namespace RobotNet.ScriptManager.Helpers;
|
|
|
|
public static class ScriptConfiguration
|
|
{
|
|
public static readonly string ScriptStorePath;
|
|
public static readonly string DllsPath;
|
|
public static readonly ScriptOptions ScriptOptions;
|
|
public static readonly ImmutableArray<MetadataReference> MetadataReferences;
|
|
public static readonly ImmutableArray<string> UsingNamespaces;
|
|
public static readonly string UsingNamespacesScript;
|
|
public static readonly string DeveloptGlobalsScript;
|
|
public static readonly string RuntimeGlobalsScript;
|
|
|
|
static ScriptConfiguration()
|
|
{
|
|
ScriptStorePath = "scripts";
|
|
DllsPath = "dlls";
|
|
UsingNamespaces = ["System", "System.Collections.Generic", "System.Linq.Expressions", "System.Threading", "System.Threading.Tasks", "System.Runtime.CompilerServices", "RobotNet.Script"];
|
|
UsingNamespacesScript = string.Join(Environment.NewLine, UsingNamespaces.Select(ns => $"using {ns};"));
|
|
|
|
List<MetadataReference> metadataRefs = [];
|
|
var currentDirectory = Directory.GetCurrentDirectory();
|
|
if (Directory.Exists(DllsPath))
|
|
{
|
|
foreach (var dll in Directory.GetFiles(DllsPath, "*.dll"))
|
|
{
|
|
metadataRefs.Add(MetadataReference.CreateFromFile(Path.Combine(currentDirectory, dll), properties: MetadataReferenceProperties.Assembly));
|
|
}
|
|
}
|
|
|
|
MetadataReferences = [.. metadataRefs];
|
|
var options = ScriptOptions.Default;
|
|
options.MetadataReferences.Clear();
|
|
ScriptOptions = options.AddReferences(MetadataReferences).AddImports(UsingNamespaces).WithEmitDebugInformation(false);
|
|
|
|
DeveloptGlobalsScript = BuildDeveloptGlobalsScript();
|
|
RuntimeGlobalsScript = BuildRuntimeGlobalsScript();
|
|
}
|
|
|
|
public static string GetScriptCode() => ReadAllTextFromFolder(ScriptStorePath);
|
|
|
|
private static string ReadAllTextFromFolder(string path)
|
|
{
|
|
var dirInfo = new DirectoryInfo(path);
|
|
|
|
var code = string.Join(Environment.NewLine, [.. dirInfo.GetFiles("*.cs").Select(f => File.ReadAllText(f.FullName))]);
|
|
var after = string.Join(Environment.NewLine, dirInfo.GetDirectories().Select(dir => ReadAllTextFromFolder(dir.FullName)).ToArray());
|
|
return string.Join(Environment.NewLine, code, after);
|
|
}
|
|
|
|
private static string BuildRuntimeGlobalsScript()
|
|
{
|
|
var type = typeof(IRobotNetGlobals);
|
|
var sb = new StringBuilder();
|
|
|
|
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
|
|
{
|
|
sb.AppendLine($@"{CSharpSyntaxHelper.ToTypeString(field.FieldType)} {field.Name}
|
|
{{
|
|
get => ({CSharpSyntaxHelper.ToTypeString(field.FieldType)})robotnet[""{field.Name}""];
|
|
set => robotnet[""{field.Name}""] = value;
|
|
}}");
|
|
}
|
|
|
|
foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
|
{
|
|
if (prop.GetIndexParameters().Length == 0)
|
|
{
|
|
var hasGetter = prop.GetGetMethod() != null;
|
|
var hasSetter = prop.GetSetMethod() != null;
|
|
if (hasGetter || hasSetter)
|
|
{
|
|
var propBuilder = new StringBuilder();
|
|
propBuilder.AppendLine($"{CSharpSyntaxHelper.ToTypeString(prop.PropertyType)} {prop.Name}");
|
|
propBuilder.AppendLine("{");
|
|
if (hasGetter)
|
|
propBuilder.AppendLine($@" get => ((Func<{CSharpSyntaxHelper.ToTypeString(prop.PropertyType)}>)robotnet[""get_{prop.Name}""])();");
|
|
|
|
if (hasSetter)
|
|
propBuilder.AppendLine($@" set => ((Action<{CSharpSyntaxHelper.ToTypeString(prop.PropertyType)}>)robotnet[""set_{prop.Name}""])(value);");
|
|
propBuilder.AppendLine("}");
|
|
sb.AppendLine(propBuilder.ToString());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Handle indexers (properties with parameters)
|
|
var indexParams = prop.GetIndexParameters();
|
|
var paramDecl = string.Join(", ", indexParams.Select(p => $"{CSharpSyntaxHelper.ToTypeString(p.ParameterType)} {p.Name}"));
|
|
var paramNames = string.Join(", ", indexParams.Select(p => p.Name));
|
|
var getterDelegateType = $"Func<{string.Join(", ", indexParams.Select(p => CSharpSyntaxHelper.ToTypeString(p.ParameterType)).Concat([CSharpSyntaxHelper.ToTypeString(prop.PropertyType)]))}>";
|
|
var setterDelegateType = $"Action<{string.Join(", ", indexParams.Select(p => CSharpSyntaxHelper.ToTypeString(p.ParameterType)).Concat([CSharpSyntaxHelper.ToTypeString(prop.PropertyType)]))}>";
|
|
|
|
var propBuilder = new StringBuilder();
|
|
propBuilder.AppendLine($"{CSharpSyntaxHelper.ToTypeString(prop.PropertyType)} this[{paramDecl}]");
|
|
propBuilder.AppendLine("{");
|
|
if (prop.GetGetMethod() != null)
|
|
propBuilder.AppendLine($@" get => (({getterDelegateType})robotnet[""get_{prop.Name}_indexer""])({paramNames});");
|
|
if (prop.GetSetMethod() != null)
|
|
propBuilder.AppendLine($@" set => (({setterDelegateType})robotnet[""set_{prop.Name}_indexer""])({(string.IsNullOrEmpty(paramNames) ? "value" : paramNames + ", value")});");
|
|
propBuilder.AppendLine("}");
|
|
sb.AppendLine(propBuilder.ToString());
|
|
}
|
|
}
|
|
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
|
|
{
|
|
if (!method.IsSpecialName)
|
|
{
|
|
var parameters = method.GetParameters();
|
|
var parametersStr = string.Join(", ", parameters.Select(ToParameterString));
|
|
var args = string.Join(", ", parameters.Select(p => p.Name));
|
|
var returnType = CSharpSyntaxHelper.ToTypeString(method.ReturnType);
|
|
|
|
|
|
var paramTypes = string.Join(",", parameters.Select(p => p.ParameterType.FullName));
|
|
var methodKey = $"{method.Name}({paramTypes})";
|
|
|
|
var methodType = method.ReturnType == typeof(void)
|
|
? (parameters.Length == 0 ? "Action" : $"Action<{string.Join(", ", parameters.Select(p => CSharpSyntaxHelper.ToTypeString(p.ParameterType)))}>")
|
|
: $"Func<{string.Join(", ", parameters.Select(p => CSharpSyntaxHelper.ToTypeString(p.ParameterType)).Concat([CSharpSyntaxHelper.ToTypeString(method.ReturnType)]))}>";
|
|
|
|
sb.AppendLine($@"{returnType} {method.Name}({parametersStr}) => (({methodType})robotnet[""{methodKey}""]){(string.IsNullOrEmpty(args) ? "()" : $"({args})")};");
|
|
}
|
|
}
|
|
|
|
var code = sb.ToString();
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string BuildDeveloptGlobalsScript()
|
|
{
|
|
var sb = new StringBuilder();
|
|
var glovalType = typeof(IRobotNetGlobals);
|
|
|
|
var properties = glovalType.GetProperties();
|
|
foreach (var property in properties)
|
|
{
|
|
var propStr = "";
|
|
if (property.CanRead)
|
|
{
|
|
propStr = $"{CSharpSyntaxHelper.ToTypeString(property.PropertyType)} {property.Name} {{ get; {(property.CanWrite ? "set; " : "")}}}";
|
|
}
|
|
else if (property.CanWrite)
|
|
{
|
|
propStr = $"{CSharpSyntaxHelper.ToTypeString(property.PropertyType)} {property.Name} {{ set => throw new System.NotImplementedException(); }}";
|
|
}
|
|
if(string.IsNullOrEmpty(propStr)) continue;
|
|
sb.AppendLine(propStr);
|
|
}
|
|
|
|
var fields = glovalType.GetFields();
|
|
foreach (var field in fields)
|
|
{
|
|
sb.AppendLine($"{CSharpSyntaxHelper.ToTypeString(field.FieldType)} {field.Name};");
|
|
}
|
|
|
|
var methods = glovalType.GetMethods();
|
|
foreach (var method in methods)
|
|
{
|
|
if (method.Name.StartsWith("get_") || method.Name.StartsWith("set_")) continue;
|
|
|
|
var notImplementedMethod = $"{CSharpSyntaxHelper.ToTypeString(method.ReturnType)} {method.Name}({string.Join(',', method.GetParameters().Select(ToParameterString))}) => throw new System.NotImplementedException();";
|
|
sb.AppendLine(notImplementedMethod);
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string ToParameterString(ParameterInfo parameter)
|
|
{
|
|
var modifier = "";
|
|
if (parameter.IsDefined(typeof(ParamArrayAttribute), false))
|
|
modifier = "params ";
|
|
else if (parameter.IsIn && parameter.ParameterType.IsByRef && !parameter.IsOut)
|
|
modifier = "in ";
|
|
else if (parameter.IsOut)
|
|
modifier = "out ";
|
|
else if (parameter.ParameterType.IsByRef)
|
|
modifier = "ref ";
|
|
|
|
var typeString = CSharpSyntaxHelper.ToTypeString(
|
|
parameter.ParameterType.IsByRef
|
|
? parameter.ParameterType.GetElementType()!
|
|
: parameter.ParameterType
|
|
);
|
|
|
|
var defaultValue = "";
|
|
if (parameter.HasDefaultValue)
|
|
{
|
|
if (parameter.DefaultValue != null)
|
|
{
|
|
if (parameter.ParameterType.IsEnum)
|
|
{
|
|
defaultValue = $" = {CSharpSyntaxHelper.ToTypeString(parameter.ParameterType)}.{parameter.DefaultValue}";
|
|
}
|
|
else if (parameter.DefaultValue is string)
|
|
{
|
|
defaultValue = $" = \"{parameter.DefaultValue}\"";
|
|
}
|
|
else if (parameter.DefaultValue is bool b)
|
|
{
|
|
defaultValue = $" = {b.ToString().ToLower()}";
|
|
}
|
|
else
|
|
{
|
|
defaultValue = $" = {parameter.DefaultValue}";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
defaultValue = " = null";
|
|
}
|
|
}
|
|
|
|
return $"{modifier}{typeString} {parameter.Name}{defaultValue}";
|
|
}
|
|
|
|
}
|