1
This commit is contained in:
@@ -2,31 +2,216 @@ using System;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BITKit.Mod;
|
||||
using Microsoft.CodeAnalysis.CSharp.Scripting;
|
||||
using Microsoft.CodeAnalysis.Emit;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using static BITKit.Mod.DotNetSdkRoslynService;
|
||||
#endif
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
public class BITSharp
|
||||
public static class BITSharp
|
||||
{
|
||||
public static ICodeGenerator SharedCodeGenerator = new CodeGenerator();
|
||||
public interface ICodeGenerator
|
||||
{
|
||||
public string Generate(Type type,string className=null);
|
||||
IReadOnlyList<string> GenerateNamespaces(Type type);
|
||||
string BeforeGenerate(Type type);
|
||||
string AfterGenerate(Type type);
|
||||
string GenerateMethod(MethodInfo methodInfo);
|
||||
string GenerateMethodContext(MethodInfo methodInfo);
|
||||
string GenerateField(FieldInfo fieldInfo);
|
||||
string GenerateProperty(PropertyInfo propertyInfo);
|
||||
|
||||
string GenerateEvent(EventInfo eventInfo);
|
||||
}
|
||||
public class CodeGenerator:ICodeGenerator
|
||||
{
|
||||
public virtual string Generate(Type type, string className = null)
|
||||
{
|
||||
if (type.IsInterface is false) throw new InvalidDataException("The type have to be interface");
|
||||
|
||||
var codeBuilder = new StringBuilder();
|
||||
|
||||
var nameSpaces = new HashSet<string>();
|
||||
|
||||
foreach (var propertyInfo in type.GetProperties())
|
||||
{
|
||||
nameSpaces.Add(propertyInfo.PropertyType.Namespace);
|
||||
}
|
||||
|
||||
foreach (var fieldInfo in type.GetFields())
|
||||
{
|
||||
nameSpaces.Add(fieldInfo.FieldType.Namespace);
|
||||
}
|
||||
|
||||
foreach (var method in type.GetMethods())
|
||||
{
|
||||
if (method.DeclaringType is not null)
|
||||
{
|
||||
nameSpaces.Add(method.DeclaringType.Namespace);
|
||||
}
|
||||
nameSpaces.Add(method.ReturnType.Namespace);
|
||||
foreach (var argument in method.GetGenericArguments())
|
||||
{
|
||||
nameSpaces.Add(argument.Namespace);
|
||||
}
|
||||
|
||||
foreach (var parameterInfo in method.GetParameters())
|
||||
{
|
||||
nameSpaces.Add(parameterInfo.ParameterType.Namespace);
|
||||
}
|
||||
}
|
||||
|
||||
nameSpaces.Add("System");
|
||||
nameSpaces.Add("System.Collections.Concurrent");
|
||||
nameSpaces.Add("System.Collections.Generic");
|
||||
|
||||
nameSpaces.Add(type.Namespace);
|
||||
|
||||
foreach (var name in GenerateNamespaces(type))
|
||||
{
|
||||
nameSpaces.Add(name);
|
||||
}
|
||||
|
||||
codeBuilder.AppendLine(string.Join('\n', nameSpaces.Where(x=>string.IsNullOrEmpty(x) is false).Select(x=>$"using {x};")));
|
||||
|
||||
|
||||
//codeBuilder.AppendLine("namespace BITSharpGen");
|
||||
//codeBuilder.AppendLine("{");
|
||||
|
||||
codeBuilder.AppendLine($"public class @{type.Name}Gen : {type.Name}");
|
||||
codeBuilder.AppendLine("{");
|
||||
|
||||
codeBuilder.AppendLine(BeforeGenerate(type));
|
||||
|
||||
foreach (var fieldInfo in type.GetFields())
|
||||
{
|
||||
codeBuilder.AppendLine(GenerateField(fieldInfo));
|
||||
}
|
||||
|
||||
foreach (var propertyInfo in type.GetProperties())
|
||||
{
|
||||
codeBuilder.AppendLine(GenerateProperty(propertyInfo));
|
||||
}
|
||||
|
||||
// 遍历接口的所有成员,生成方法的默认实现
|
||||
foreach (var method in type.GetMethods())
|
||||
{
|
||||
codeBuilder.AppendLine(GenerateMethod(method));
|
||||
}
|
||||
|
||||
codeBuilder.AppendLine(AfterGenerate(type));
|
||||
|
||||
codeBuilder.AppendLine("}");
|
||||
|
||||
return codeBuilder.ToString();
|
||||
}
|
||||
|
||||
public virtual IReadOnlyList<string> GenerateNamespaces(Type type)
|
||||
{
|
||||
return ArraySegment<string>.Empty;
|
||||
}
|
||||
|
||||
public virtual string BeforeGenerate(Type type)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public virtual string AfterGenerate(Type type)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public virtual string GenerateMethod(MethodInfo methodInfo)
|
||||
{
|
||||
var codeBuilder = new StringBuilder();
|
||||
HashSet<MethodInfo> propertyMethods = new();
|
||||
|
||||
foreach (var propertyInfo in methodInfo.DeclaringType!.GetProperties())
|
||||
{
|
||||
if (propertyInfo.GetMethod is not null)
|
||||
{
|
||||
propertyMethods.Add(propertyInfo.GetMethod);
|
||||
}
|
||||
|
||||
if (propertyInfo.SetMethod is not null)
|
||||
{
|
||||
propertyMethods.Add(propertyInfo.SetMethod);
|
||||
}
|
||||
}
|
||||
|
||||
if(propertyMethods.Contains(methodInfo))return string.Empty;
|
||||
string methodName = methodInfo.Name;
|
||||
string parameters = string.Join(", ", methodInfo.GetParameters()
|
||||
.Select(p => $"{CSharpName(p.ParameterType)} {p.Name}"));
|
||||
|
||||
var isAwaitable = methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
|
||||
|
||||
// 检查是否为泛型方法
|
||||
if (methodInfo.IsGenericMethod)
|
||||
{
|
||||
// 处理泛型方法
|
||||
var genericArgs = string.Join(", ", methodInfo.GetGenericArguments()
|
||||
.Select(CSharpName));
|
||||
|
||||
if (methodInfo.ReturnType == typeof(void))
|
||||
{
|
||||
codeBuilder.AppendLine($" public void {methodName}<{genericArgs}>({parameters}) {{ \n{GenerateMethodContext(methodInfo)}\n }}");
|
||||
}
|
||||
else
|
||||
{
|
||||
codeBuilder.AppendLine($" public {(isAwaitable?"async":string.Empty)} {CSharpName(methodInfo.ReturnType)} {methodName}<{genericArgs}>({parameters}) {{ {GenerateMethodContext(methodInfo)} }}");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (methodInfo.ReturnType == typeof(void))
|
||||
{
|
||||
codeBuilder.AppendLine($" public void {methodName}({parameters}) {{ {GenerateMethodContext(methodInfo)}}}"); // Void 方法默认空实现
|
||||
}
|
||||
else
|
||||
{
|
||||
// 返回默认值
|
||||
codeBuilder.AppendLine($" public {(isAwaitable?"async":string.Empty)} {CSharpName(methodInfo.ReturnType)} {methodName}({parameters}) {{ {GenerateMethodContext(methodInfo)} }}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return codeBuilder.ToString();
|
||||
}
|
||||
|
||||
public virtual string GenerateMethodContext(MethodInfo methodInfo)
|
||||
{
|
||||
return methodInfo.ReturnType == typeof(void) ? string.Empty : $"return default;";
|
||||
}
|
||||
|
||||
public virtual string GenerateField(FieldInfo fieldInfo)
|
||||
{
|
||||
return $"public {CSharpName(fieldInfo.FieldType)} {fieldInfo.Name};";
|
||||
}
|
||||
|
||||
public virtual string GenerateProperty(PropertyInfo propertyInfo)
|
||||
{
|
||||
return $"public {CSharpName(propertyInfo.PropertyType)} {propertyInfo.Name}" + "{ get;set; }";
|
||||
}
|
||||
|
||||
public virtual string GenerateEvent(EventInfo eventInfo)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
#if UNITY_5_3_OR_NEWER
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
private static void Reload()
|
||||
@@ -65,6 +250,9 @@ namespace BITKit
|
||||
public static IReadOnlyCollection<Assembly> GetReferencedAssemblies(Type type)
|
||||
{
|
||||
var result = new HashSet<Assembly>();
|
||||
|
||||
result.Add(type.Assembly);
|
||||
|
||||
foreach (var fieldInfo in type.GetFields())
|
||||
{
|
||||
result.Add(fieldInfo.FieldType.Assembly);
|
||||
@@ -84,114 +272,7 @@ namespace BITKit
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string GenerateCodeFromInterface(Type type,string namespaceName = null)
|
||||
{
|
||||
if (type.IsInterface is false) throw new InvalidDataException("The type have to be interface");
|
||||
|
||||
var codeBuilder = new StringBuilder();
|
||||
|
||||
var nameSpaces = new HashSet<string>();
|
||||
|
||||
HashSet<Type> allTypes = new();
|
||||
HashSet<MethodInfo> propertyMethods = new();
|
||||
|
||||
foreach (var propertyInfo in type.GetProperties())
|
||||
{
|
||||
if (propertyInfo.GetMethod is not null)
|
||||
{
|
||||
propertyMethods.Add(propertyInfo.GetMethod);
|
||||
}
|
||||
|
||||
if (propertyInfo.SetMethod is not null)
|
||||
{
|
||||
propertyMethods.Add(propertyInfo.SetMethod);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var method in type.GetMethods())
|
||||
{
|
||||
if (method.DeclaringType is not null)
|
||||
{
|
||||
nameSpaces.Add(method.DeclaringType.Namespace);
|
||||
}
|
||||
nameSpaces.Add(method.ReturnType.Namespace);
|
||||
foreach (var argument in method.GetGenericArguments())
|
||||
{
|
||||
nameSpaces.Add(argument.Namespace);
|
||||
}
|
||||
}
|
||||
|
||||
nameSpaces.Add("System");
|
||||
nameSpaces.Add("System.Collections.Concurrent");
|
||||
nameSpaces.Add("System.Collections.Generic");
|
||||
|
||||
|
||||
|
||||
codeBuilder.AppendLine(string.Join('\n', nameSpaces.Select(x=>$"using {x};")));
|
||||
|
||||
|
||||
//codeBuilder.AppendLine("namespace BITSharpGen");
|
||||
//codeBuilder.AppendLine("{");
|
||||
|
||||
codeBuilder.AppendLine($"public class @{type.Name}Gen : {type.Name}");
|
||||
codeBuilder.AppendLine("{");
|
||||
|
||||
|
||||
foreach (var propertyInfo in type.GetProperties())
|
||||
{
|
||||
codeBuilder.AppendLine($"public {CSharpName(propertyInfo.PropertyType)} {propertyInfo.Name}" + "{ get;set; }");
|
||||
}
|
||||
|
||||
// 遍历接口的所有成员,生成方法的默认实现
|
||||
foreach (var method in type.GetMethods())
|
||||
{
|
||||
if(propertyMethods.Contains(method))continue;
|
||||
string methodName = method.Name;
|
||||
string parameters = string.Join(", ", method.GetParameters()
|
||||
.Select(p => $"{CSharpName(p.ParameterType)} {p.Name}"));
|
||||
|
||||
// 检查是否为泛型方法
|
||||
if (method.IsGenericMethod)
|
||||
{
|
||||
// 处理泛型方法
|
||||
var genericArgs = string.Join(", ", method.GetGenericArguments()
|
||||
.Select(arg => CSharpName(arg)));
|
||||
|
||||
if (method.ReturnType == typeof(void))
|
||||
{
|
||||
codeBuilder.AppendLine($" public void {methodName}<{genericArgs}>({parameters}) {{ }}");
|
||||
}
|
||||
else
|
||||
{
|
||||
codeBuilder.AppendLine($" public {CSharpName(method.ReturnType)} {methodName}<{genericArgs}>({parameters}) {{ return default({CSharpName(method.ReturnType)}); }}");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (method.ReturnType == typeof(void))
|
||||
{
|
||||
codeBuilder.AppendLine($" public void {methodName}({parameters}) {{ }}"); // Void 方法默认空实现
|
||||
}
|
||||
else
|
||||
{
|
||||
// 返回默认值
|
||||
codeBuilder.AppendLine($" public {CSharpName(method.ReturnType)} {methodName}({parameters}) {{ return default({CSharpName(method.ReturnType)}); }}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//codeBuilder.AppendLine("\t}");
|
||||
|
||||
codeBuilder.AppendLine("}");
|
||||
|
||||
return codeBuilder.ToString();
|
||||
}
|
||||
|
||||
public static string CSharpName(Type type)
|
||||
public static string CSharpName(this Type type)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var name = type.Name;
|
||||
@@ -204,19 +285,22 @@ namespace BITKit
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static Assembly Compile(string code,ScriptOptions scriptOptions=null)
|
||||
public static Assembly Compile(string code, ScriptOptions scriptOptions = null)
|
||||
{
|
||||
scriptOptions ??= ScriptOptions.Default;
|
||||
|
||||
var script = CSharpScript.Create(code,scriptOptions);
|
||||
|
||||
var script = CSharpScript.Create(code, scriptOptions);
|
||||
script.Compile();
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
var result = script.GetCompilation().Emit(ms);
|
||||
|
||||
if (!result.Success) throw new Exception(string.Join("\n",result.Diagnostics));
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
var assembly = Assembly.Load(ms.ToArray()); // 加载程序集
|
||||
|
||||
return assembly;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user