mirror of
https://github.com/StarBeat/JsonRpc.git
synced 2026-03-08 03:55:29 +08:00
init repo
This commit is contained in:
parent
2bfce66fb5
commit
5c8c6a82bb
4
.gitignore
vendored
4
.gitignore
vendored
@ -23,6 +23,10 @@ bld/
|
|||||||
[Ll]og/
|
[Ll]og/
|
||||||
[Ll]ogs/
|
[Ll]ogs/
|
||||||
|
|
||||||
|
.vs
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
|
||||||
# .NET Core
|
# .NET Core
|
||||||
project.lock.json
|
project.lock.json
|
||||||
project.fragment.lock.json
|
project.fragment.lock.json
|
||||||
|
|||||||
36
CodeGenerator/AttributeChecker.cs
Normal file
36
CodeGenerator/AttributeChecker.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using Mono.Cecil;
|
||||||
|
|
||||||
|
namespace CodeGenerator
|
||||||
|
{
|
||||||
|
public static class AttributeChecker
|
||||||
|
{
|
||||||
|
public static CustomAttribute? GetTargetttribute(this ICustomAttributeProvider definition)
|
||||||
|
{
|
||||||
|
var customAttributes = definition.CustomAttributes;
|
||||||
|
|
||||||
|
return customAttributes.FirstOrDefault(_ => _.AttributeType.Name == nameof(JsonRPCAttribute));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ContainsTargetAttribute(this ICustomAttributeProvider definition) =>
|
||||||
|
GetTargetttribute(definition) != null;
|
||||||
|
|
||||||
|
public static bool IsCompilerGenerated(this ICustomAttributeProvider definition)
|
||||||
|
{
|
||||||
|
var customAttributes = definition.CustomAttributes;
|
||||||
|
|
||||||
|
return customAttributes.Any(_ => _.AttributeType.Name == "CompilerGeneratedAttribute");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveTargetAttribute(this ICustomAttributeProvider definition)
|
||||||
|
{
|
||||||
|
var customAttributes = definition.CustomAttributes;
|
||||||
|
|
||||||
|
var targetAttribute = customAttributes.FirstOrDefault(_ => _.AttributeType.Name == nameof(JsonRPCAttribute));
|
||||||
|
|
||||||
|
if (targetAttribute != null)
|
||||||
|
{
|
||||||
|
customAttributes.Remove(targetAttribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
145
CodeGenerator/CecilExtensions.cs
Normal file
145
CodeGenerator/CecilExtensions.cs
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
using Mono.Collections.Generic;
|
||||||
|
|
||||||
|
namespace CodeGenerator
|
||||||
|
{
|
||||||
|
public static class CecilExtensions
|
||||||
|
{
|
||||||
|
public static bool IsBoxingRequired(this TypeReference typeReference, TypeReference expectedType)
|
||||||
|
{
|
||||||
|
if (expectedType.IsValueType && string.Equals(typeReference.FullName, expectedType.FullName))
|
||||||
|
{
|
||||||
|
// Boxing is never required if type is expected
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeReference.IsValueType ||
|
||||||
|
typeReference.IsGenericParameter)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<MethodDefinition> AbstractMethods(this TypeDefinition type) =>
|
||||||
|
type.Methods.Where(_ => _.IsAbstract).ToList();
|
||||||
|
|
||||||
|
public static IEnumerable<MethodDefinition> ConcreteMethods(this TypeDefinition type) =>
|
||||||
|
type.Methods.Where(x => !x.IsAbstract &&
|
||||||
|
x.HasBody &&
|
||||||
|
!IsEmptyConstructor(x)).ToList();
|
||||||
|
|
||||||
|
static bool IsEmptyConstructor(this MethodDefinition method) =>
|
||||||
|
method.Name == ".ctor" &&
|
||||||
|
method.Body.Instructions.Count(_ => _.OpCode != OpCodes.Nop) == 3;
|
||||||
|
|
||||||
|
public static bool IsInstanceConstructor(this MethodDefinition methodDefinition) =>
|
||||||
|
methodDefinition.IsConstructor && !methodDefinition.IsStatic;
|
||||||
|
|
||||||
|
public static void InsertBefore(this MethodBody body, Instruction target, Instruction instruction) =>
|
||||||
|
body.Instructions.InsertBefore(target, instruction);
|
||||||
|
|
||||||
|
public static void InsertBefore(this Collection<Instruction> instructions, Instruction target, Instruction instruction)
|
||||||
|
{
|
||||||
|
var index = instructions.IndexOf(target);
|
||||||
|
instructions.Insert(index, instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string MethodName(this MethodDefinition method)
|
||||||
|
{
|
||||||
|
if (method.IsConstructor)
|
||||||
|
{
|
||||||
|
return $"{method.DeclaringType.Name}{method.Name} ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{method.DeclaringType.Name}.{method.Name} ";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Insert(this MethodBody body, int index, IEnumerable<Instruction> instructions)
|
||||||
|
{
|
||||||
|
instructions = instructions.Reverse();
|
||||||
|
foreach (var instruction in instructions)
|
||||||
|
{
|
||||||
|
body.Instructions.Insert(index, instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Add(this MethodBody body, params Instruction[] instructions)
|
||||||
|
{
|
||||||
|
foreach (var instruction in instructions)
|
||||||
|
{
|
||||||
|
body.Instructions.Add(instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsYield(this MethodDefinition method)
|
||||||
|
{
|
||||||
|
if (method.ReturnType is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!method.ReturnType.Name.StartsWith("IEnumerable"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stateMachinePrefix = $"<{method.Name}>";
|
||||||
|
var nestedTypes = method.DeclaringType.NestedTypes;
|
||||||
|
return nestedTypes.Any(_ => _.Name.StartsWith(stateMachinePrefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CustomAttribute GetAsyncStateMachineAttribute(this MethodDefinition method) =>
|
||||||
|
method.CustomAttributes.FirstOrDefault(_ => _.AttributeType.Name == "AsyncStateMachineAttribute");
|
||||||
|
|
||||||
|
public static bool IsAsync(this MethodDefinition method) =>
|
||||||
|
GetAsyncStateMachineAttribute(method) != null;
|
||||||
|
|
||||||
|
public static bool IsLeaveInstruction(this Instruction instruction) =>
|
||||||
|
instruction.OpCode == OpCodes.Leave ||
|
||||||
|
instruction.OpCode == OpCodes.Leave_S;
|
||||||
|
|
||||||
|
public static MethodDefinition Method(this TypeDefinition type, string name)
|
||||||
|
{
|
||||||
|
var method = type.Methods.FirstOrDefault(_ => _.Name == name);
|
||||||
|
|
||||||
|
if (method is null)
|
||||||
|
{
|
||||||
|
throw new($"Could not find method '{name}' on type {type.FullName}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodDefinition Method(this TypeDefinition type, string name, params string[] parameters)
|
||||||
|
{
|
||||||
|
var method = type.Methods
|
||||||
|
.FirstOrDefault(x =>
|
||||||
|
{
|
||||||
|
return x.Name == name &&
|
||||||
|
parameters.Length == x.Parameters.Count &&
|
||||||
|
x.Parameters.Select(y => y.ParameterType.Name).SequenceEqual(parameters);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (method is null)
|
||||||
|
{
|
||||||
|
throw new($"Could not find method '{name}' on type {type.FullName}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TypeDefinition Type(this List<TypeDefinition> types, string name)
|
||||||
|
{
|
||||||
|
var type = types.FirstOrDefault(_ => _.Name == name);
|
||||||
|
if (type is null)
|
||||||
|
{
|
||||||
|
throw new($"Could not find type '{name}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
CodeGenerator/CodeGenerator.csproj
Normal file
18
CodeGenerator/CodeGenerator.csproj
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
|
||||||
|
<PackageReference Include="Mono.Cecil" Version="0.11.6" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\JsonRPC\JsonRPC.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
8
CodeGenerator/JsonRPCAttribute.cs
Normal file
8
CodeGenerator/JsonRPCAttribute.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace CodeGenerator
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
|
||||||
|
public class JsonRPCAttribute : Attribute
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
506
CodeGenerator/JsonRPCCodeGenerator.cs
Normal file
506
CodeGenerator/JsonRPCCodeGenerator.cs
Normal file
@ -0,0 +1,506 @@
|
|||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.Emit;
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
using Mono.Cecil.Rocks;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CodeGenerator
|
||||||
|
{
|
||||||
|
public class JsonRPCCodeGenerator
|
||||||
|
{
|
||||||
|
private readonly Action<string> logger;
|
||||||
|
private ModuleDefinition moduleDefinition = null!;
|
||||||
|
private List<TypeDefinition> types = null!;
|
||||||
|
private string filePath = null!;
|
||||||
|
private MethodDefinition moduleeStaticConstructor = null;
|
||||||
|
public JsonRPCCodeGenerator(Action<string> logFn)
|
||||||
|
{
|
||||||
|
this.logger = logFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadAssembly(string filePath)
|
||||||
|
{
|
||||||
|
this.filePath = filePath;
|
||||||
|
var readerParameters = new ReaderParameters
|
||||||
|
{
|
||||||
|
InMemory = true
|
||||||
|
};
|
||||||
|
|
||||||
|
moduleDefinition = ModuleDefinition.ReadModule(filePath, readerParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteModule()
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(filePath, $"WriteModule call but {filePath} is null.");
|
||||||
|
|
||||||
|
moduleDefinition.Write(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddModuleStaticConstructor(TypeDefinition type)
|
||||||
|
{
|
||||||
|
var sctor = type.GetStaticConstructor();
|
||||||
|
if (sctor == null)
|
||||||
|
{
|
||||||
|
sctor = new MethodDefinition(".cctor", MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig, moduleDefinition.ImportReference(typeof(void)));
|
||||||
|
sctor.IsRuntimeSpecialName = true;
|
||||||
|
sctor.IsSpecialName = true;
|
||||||
|
|
||||||
|
//var cw = moduleDefinition.ImportReference(typeof(Console).GetMethods().Where(m => m.GetParameters().Length == 1 && m.Name == "WriteLine" && m.GetParameters()[0].ParameterType == typeof(string)).First());
|
||||||
|
|
||||||
|
// sctor.Body.Instructions.Add(Instruction.Create(OpCodes.Nop));
|
||||||
|
// sctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ldstr, "AddModuleStaticConstructor"));
|
||||||
|
// sctor.Body.Instructions.Add(Instruction.Create(OpCodes.Call, cw));
|
||||||
|
|
||||||
|
sctor.Body.Instructions.Add(Instruction.Create(OpCodes.Nop));
|
||||||
|
sctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||||
|
type.Methods.Add(sctor);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleeStaticConstructor = sctor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddModuleInitializerMethod(MethodReference methodReference)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(moduleeStaticConstructor, $"moduleeStaticConstructor is null.");
|
||||||
|
if (moduleeStaticConstructor.Body.Instructions[0].OpCode.Code == Code.Nop)
|
||||||
|
{
|
||||||
|
moduleeStaticConstructor.Body.Instructions.Insert(1, Instruction.Create(OpCodes.Call, methodReference));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
moduleeStaticConstructor.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, methodReference));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ProcessAssembly()
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(moduleDefinition, $"moduleDefinition is null.");
|
||||||
|
|
||||||
|
types = moduleDefinition.GetTypes().ToList();
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
if (type.Name.Equals("<Module>"))
|
||||||
|
{
|
||||||
|
AddModuleStaticConstructor(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moduleDefinition.Assembly.ContainsTargetAttribute())
|
||||||
|
{
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
if (type.IsCompilerGenerated())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var method in type.ConcreteMethods())
|
||||||
|
{
|
||||||
|
ProcessMethod(type, method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
if (type.IsCompilerGenerated())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.ContainsTargetAttribute())
|
||||||
|
{
|
||||||
|
foreach (var method in type.ConcreteMethods())
|
||||||
|
{
|
||||||
|
ProcessMethod(type, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var method in type.ConcreteMethods().Where(_ => _.ContainsTargetAttribute()))
|
||||||
|
{
|
||||||
|
ProcessMethod(type, method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodDefinition CompileFunc(MemoryStream ms, string funcBody, string returnType)
|
||||||
|
{
|
||||||
|
returnType = returnType.EndsWith("Void") ? "void" : returnType;
|
||||||
|
string code = @$"
|
||||||
|
using System;
|
||||||
|
public class DynamicClass
|
||||||
|
{{
|
||||||
|
public static {returnType} HelloWorld {funcBody}
|
||||||
|
}}";
|
||||||
|
|
||||||
|
// 创建语法树
|
||||||
|
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
|
||||||
|
|
||||||
|
// 引用必要的程序集
|
||||||
|
var references = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
|
.Where(a => !a.IsDynamic)
|
||||||
|
.Select(a => MetadataReference.CreateFromFile(a.Location))
|
||||||
|
.Cast<MetadataReference>()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 创建编译选项
|
||||||
|
CSharpCompilationOptions options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true);
|
||||||
|
|
||||||
|
// 创建编译对象
|
||||||
|
CSharpCompilation compilation = CSharpCompilation.Create(
|
||||||
|
"DynamicAssembly",
|
||||||
|
new[] { syntaxTree },
|
||||||
|
references,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
// 编译代码
|
||||||
|
EmitResult result = compilation.Emit(ms);
|
||||||
|
|
||||||
|
// 检查编译错误
|
||||||
|
if (!result.Success)
|
||||||
|
{
|
||||||
|
foreach (var diagnostic in result.Diagnostics)
|
||||||
|
{
|
||||||
|
Console.WriteLine(diagnostic.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载编译后的程序集
|
||||||
|
ms.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var md = ModuleDefinition.ReadModule(ms);
|
||||||
|
var m = md.GetType("DynamicClass").Method("HelloWorld");
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetMethodSignature(MethodDefinition method)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.Append(method.DeclaringType.FullName.Replace('.', '_'));
|
||||||
|
sb.Append("__");
|
||||||
|
sb.Append(method.Name);
|
||||||
|
sb.Append('_');
|
||||||
|
for (int i = 0; i < method.Parameters.Count; i++)
|
||||||
|
{
|
||||||
|
var param = method.Parameters[i];
|
||||||
|
sb.Append('_');
|
||||||
|
sb.Append(param.ParameterType.Name);
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodDefinition AddRPCRealMethod(TypeDefinition type, MethodDefinition method)
|
||||||
|
{
|
||||||
|
var methodSignature = GetMethodSignature(method);
|
||||||
|
MethodBody body = method.Body;
|
||||||
|
|
||||||
|
MethodDefinition realMethod = new MethodDefinition(methodSignature, MethodAttributes.Public | MethodAttributes.Static, method.ReturnType);
|
||||||
|
realMethod.Body.InitLocals = body.InitLocals;
|
||||||
|
realMethod.Body.Instructions.Clear();
|
||||||
|
realMethod.Body.Variables.Clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < body.Instructions.Count; i++)
|
||||||
|
{
|
||||||
|
var ins = body.Instructions[i];
|
||||||
|
realMethod.Body.Instructions.Add(ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < body.Variables.Count; i++)
|
||||||
|
{
|
||||||
|
realMethod.Body.Variables.Add(body.Variables[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < method.Parameters.Count; i++)
|
||||||
|
{
|
||||||
|
var param = method.Parameters[i];
|
||||||
|
realMethod.Parameters.Add(param);
|
||||||
|
}
|
||||||
|
type.Methods.Add(realMethod);
|
||||||
|
|
||||||
|
|
||||||
|
return realMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRPCHandleMethod(TypeDefinition type, MethodDefinition method)
|
||||||
|
{
|
||||||
|
var name = method.Name +"_Handle";
|
||||||
|
var handleMethod = new MethodDefinition(name, MethodAttributes.Static | MethodAttributes.Public, moduleDefinition.ImportReference(typeof(JsonRPC.Protocol.JsonRPCResponse)));
|
||||||
|
handleMethod.Parameters.Add(new("req", ParameterAttributes.In, moduleDefinition.ImportReference(typeof(JsonRPC.Protocol.JsonRPCRequest))));
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
StringBuilder bodyBuilder = new();
|
||||||
|
StringBuilder parameterBuilder = new();
|
||||||
|
type.Methods.Add(handleMethod);
|
||||||
|
bodyBuilder.AppendLine(@$"
|
||||||
|
(JsonRPC.Protocol.JsonRPCRequest req)
|
||||||
|
{{
|
||||||
|
|
||||||
|
if(req.Params != null && req.Params.Count != {method.Parameters.Count})
|
||||||
|
{{
|
||||||
|
return new JsonRPC.Protocol.JsonRPCResponse()
|
||||||
|
{{
|
||||||
|
Id = req.Id,
|
||||||
|
Error = new JsonRPC.Protocol.JsonRPCError()
|
||||||
|
{{
|
||||||
|
Code = (int)JsonRPC.Protocol.EErrorCode.InvalidParam,
|
||||||
|
Message = ""req Parameters {method.Parameters.Count}"",
|
||||||
|
}}
|
||||||
|
}};
|
||||||
|
}}
|
||||||
|
else
|
||||||
|
{{
|
||||||
|
|
||||||
|
");
|
||||||
|
|
||||||
|
bool returnVoid = method.ReturnType.Name.EndsWith("Void");
|
||||||
|
if (!returnVoid)
|
||||||
|
{
|
||||||
|
bodyBuilder.Append("var ret =");
|
||||||
|
}
|
||||||
|
bodyBuilder.Append($"{method.Name}(");
|
||||||
|
for (int i = 0; i < method.Parameters.Count; i++)
|
||||||
|
{
|
||||||
|
var param = method.Parameters[i];
|
||||||
|
bodyBuilder.Append($"({param.ParameterType.FullName})req.Params[{i}]");
|
||||||
|
parameterBuilder.Append(param.ParameterType.FullName);
|
||||||
|
parameterBuilder.Append(' ');
|
||||||
|
parameterBuilder.Append(param.Name);
|
||||||
|
if (param != method.Parameters[^1])
|
||||||
|
{
|
||||||
|
parameterBuilder.Append(',');
|
||||||
|
bodyBuilder.Append(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bodyBuilder.Append(");");
|
||||||
|
if (returnVoid)
|
||||||
|
{
|
||||||
|
bodyBuilder.AppendLine(@"
|
||||||
|
return new JsonRPC.Protocol.JsonRPCResponse()
|
||||||
|
{
|
||||||
|
Id = req.Id,
|
||||||
|
};
|
||||||
|
");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bodyBuilder.AppendLine(@$"
|
||||||
|
return new JsonRPC.Protocol.JsonRPCResponse<{method.ReturnType.FullName}>()
|
||||||
|
{{
|
||||||
|
Id = req.Id,
|
||||||
|
Result = ret,
|
||||||
|
}};
|
||||||
|
");
|
||||||
|
}
|
||||||
|
bodyBuilder.AppendLine($@"
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
public static {(returnVoid ? "void" : method.ReturnType.FullName)} {method.Name}({parameterBuilder.ToString()}){{ return {(returnVoid ? "" : "default")};}}
|
||||||
|
");
|
||||||
|
|
||||||
|
|
||||||
|
var tempFunc = CompileFunc(ms, bodyBuilder.ToString(), "JsonRPC.Protocol.JsonRPCResponse");
|
||||||
|
ReplaceMethodBody(tempFunc, handleMethod);
|
||||||
|
for (int i = 0; i < handleMethod.Body.Instructions.Count; i++)
|
||||||
|
{
|
||||||
|
var ins = handleMethod.Body.Instructions[i];
|
||||||
|
if (ins.OpCode.Code == Code.Call && ins.Operand is MethodReference mr)
|
||||||
|
{
|
||||||
|
if (mr.Name.Equals(method.Name))
|
||||||
|
{
|
||||||
|
ins.Operand = moduleDefinition.ImportReference(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddRPCRegMethod(type, handleMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRPCRegMethod(TypeDefinition type, MethodDefinition method)
|
||||||
|
{
|
||||||
|
var name = method.Name +"_Reg";
|
||||||
|
|
||||||
|
var regMethod = new MethodDefinition(name, MethodAttributes.Static | MethodAttributes.Public, moduleDefinition.ImportReference(typeof(void)));
|
||||||
|
type.Methods.Add(regMethod);
|
||||||
|
|
||||||
|
var body = regMethod.Body;
|
||||||
|
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
StringBuilder bodyBuilder = new();
|
||||||
|
|
||||||
|
bodyBuilder.AppendLine(@$"
|
||||||
|
()
|
||||||
|
{{
|
||||||
|
var fn = new Func<JsonRPC.Protocol.JsonRPCRequest, JsonRPC.Protocol.JsonRPCResponse>({method.Name});
|
||||||
|
JsonRPC.RPC.Receiver.Instance.RegHandler(""{method.Name.Replace("_Handle", "")}"", fn);
|
||||||
|
}}
|
||||||
|
public static JsonRPC.Protocol.JsonRPCResponse {method.Name}(JsonRPC.Protocol.JsonRPCRequest req){{ return null;}}
|
||||||
|
");
|
||||||
|
|
||||||
|
var tempFunc = CompileFunc(ms, bodyBuilder.ToString(), "Void");
|
||||||
|
ReplaceMethodBody(tempFunc, regMethod);
|
||||||
|
for (int i = 0; i < regMethod.Body.Instructions.Count; i++)
|
||||||
|
{
|
||||||
|
var ins = regMethod.Body.Instructions[i];
|
||||||
|
if (ins.OpCode.Code == Code.Ldftn && ins.Operand is MethodReference mr)
|
||||||
|
{
|
||||||
|
if (mr.Name.Equals(method.Name))
|
||||||
|
{
|
||||||
|
ins.Operand = moduleDefinition.ImportReference(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddModuleInitializerMethod(regMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReplaceMethodBody(MethodDefinition srcMethod, MethodDefinition dstMethod)
|
||||||
|
{
|
||||||
|
var body = dstMethod.Body;
|
||||||
|
body.Instructions.Clear();
|
||||||
|
body.Variables.Clear();
|
||||||
|
|
||||||
|
srcMethod.Body.SimplifyMacros();
|
||||||
|
|
||||||
|
body.MaxStackSize = srcMethod.Body.MaxStackSize;
|
||||||
|
body.InitLocals = srcMethod.Body.InitLocals;
|
||||||
|
|
||||||
|
body.LocalVarToken = srcMethod.Body.LocalVarToken;
|
||||||
|
|
||||||
|
foreach (var item in srcMethod.Body.Instructions)
|
||||||
|
{
|
||||||
|
if (item.OpCode.Code == Code.Castclass && item.Operand is TypeReference tr)
|
||||||
|
{
|
||||||
|
var instruction = Instruction.Create(item.OpCode, moduleDefinition.ImportReference(tr));
|
||||||
|
instruction.Offset = item.Offset;
|
||||||
|
body.Instructions.Add(instruction);
|
||||||
|
}
|
||||||
|
else if (item.Operand is MethodReference mr)
|
||||||
|
{
|
||||||
|
var newMr = moduleDefinition.ImportReference(mr);
|
||||||
|
var instruction = Instruction.Create(item.OpCode, newMr);
|
||||||
|
instruction.Offset = item.Offset;
|
||||||
|
body.Instructions.Add(instruction);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
body.Instructions.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < srcMethod.Body.Variables.Count; i++)
|
||||||
|
{
|
||||||
|
var localVar = srcMethod.Body.Variables[i];
|
||||||
|
localVar.VariableType = moduleDefinition.ImportReference(localVar.VariableType);
|
||||||
|
body.Variables.Add(localVar);
|
||||||
|
}
|
||||||
|
body.Optimize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessMethod(TypeDefinition type, MethodDefinition method)
|
||||||
|
{
|
||||||
|
bool hasTargetAttribute = method.ContainsTargetAttribute();
|
||||||
|
|
||||||
|
if (method.IsYield())
|
||||||
|
{
|
||||||
|
if (hasTargetAttribute)
|
||||||
|
{
|
||||||
|
logger.Invoke("Could not process '" + method.FullName + "' since methods that yield are currently not supported. Please remove the [Time] attribute from that method.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Invoke("Skipping '" + method.FullName + "' since methods that yield are not supported.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!method.IsStatic)
|
||||||
|
{
|
||||||
|
logger.Invoke("Skipping '" + method.FullName + "' non static methods are not supported.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method.IsAsync() && hasTargetAttribute)
|
||||||
|
{
|
||||||
|
logger.Invoke("Could not process '" + method.FullName + "' async methods are not supported.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var realMethod = AddRPCRealMethod(type, method);
|
||||||
|
AddRPCHandleMethod(type, realMethod);
|
||||||
|
StringBuilder parameterBuilder = new();
|
||||||
|
StringBuilder bodyBuilder = new();
|
||||||
|
bool returnVoid = method.ReturnType.Name.EndsWith("Void");
|
||||||
|
|
||||||
|
parameterBuilder.Append('(');
|
||||||
|
bodyBuilder.AppendLine("{");
|
||||||
|
bodyBuilder.AppendLine($"JsonRPC.Protocol.JsonRPCRequest req = new(\"{realMethod.Name}\", JsonRPC.RPC.Sender.Instance.GetId());");
|
||||||
|
foreach (var item in method.Parameters)
|
||||||
|
{
|
||||||
|
parameterBuilder.Append(item.ParameterType.FullName);
|
||||||
|
parameterBuilder.Append(' ');
|
||||||
|
parameterBuilder.Append(item.Name);
|
||||||
|
if (item != method.Parameters[^1])
|
||||||
|
{
|
||||||
|
parameterBuilder.Append(",");
|
||||||
|
}
|
||||||
|
bodyBuilder.AppendLine($"req.AddParam({item.Name});");
|
||||||
|
}
|
||||||
|
parameterBuilder.Append(')');
|
||||||
|
if(returnVoid)
|
||||||
|
{
|
||||||
|
bodyBuilder.AppendLine(@$"
|
||||||
|
var json = JsonRPC.RPC.Sender.Instance.Send(req);
|
||||||
|
var res = JsonRPC.RPC.Sender.Instance.JsonDeserialize<JsonRPC.Protocol.JsonRPCResponse>(json);
|
||||||
|
if(!JsonRPC.RPC.Sender.Instance.HnadleResponseError(res))
|
||||||
|
{{
|
||||||
|
}}
|
||||||
|
return;
|
||||||
|
}}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bodyBuilder.AppendLine(@$"
|
||||||
|
var json = JsonRPC.RPC.Sender.Instance.Send(req);
|
||||||
|
var res = JsonRPC.RPC.Sender.Instance.JsonDeserialize<JsonRPC.Protocol.JsonRPCResponse<{method.ReturnType.FullName}>>(json);
|
||||||
|
if(!JsonRPC.RPC.Sender.Instance.HnadleResponseError(res))
|
||||||
|
{{
|
||||||
|
return default;
|
||||||
|
}}
|
||||||
|
|
||||||
|
return res.Result;
|
||||||
|
}}");
|
||||||
|
}
|
||||||
|
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
var sigFunc = CompileFunc(ms, parameterBuilder.AppendLine(bodyBuilder.ToString()).ToString(), method.ReturnType.FullName);
|
||||||
|
ReplaceMethodBody(sigFunc, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveAttributes()
|
||||||
|
{
|
||||||
|
moduleDefinition.Assembly.RemoveTargetAttribute();
|
||||||
|
foreach (var typeDefinition in types)
|
||||||
|
{
|
||||||
|
typeDefinition.RemoveTargetAttribute();
|
||||||
|
foreach (var method in typeDefinition.Methods)
|
||||||
|
{
|
||||||
|
method.RemoveTargetAttribute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
43
JsonRPC.sln
Normal file
43
JsonRPC.sln
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.14.36518.9
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGenerator", "CodeGenerator\CodeGenerator.csproj", "{35FC52B4-AB3D-4879-ADAA-F90A63A78F8D}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{FA0F37D3-770B-42F4-A53D-36CB8161A6F4}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestInterceptor", "TestInterceptor\TestInterceptor.csproj", "{0B65E76A-4435-4D6B-864A-097475739B42}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonRPC", "JsonRPC\JsonRPC.csproj", "{55CEBB38-910A-413E-80F3-35774E12A396}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{35FC52B4-AB3D-4879-ADAA-F90A63A78F8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{35FC52B4-AB3D-4879-ADAA-F90A63A78F8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{35FC52B4-AB3D-4879-ADAA-F90A63A78F8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{35FC52B4-AB3D-4879-ADAA-F90A63A78F8D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{FA0F37D3-770B-42F4-A53D-36CB8161A6F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FA0F37D3-770B-42F4-A53D-36CB8161A6F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FA0F37D3-770B-42F4-A53D-36CB8161A6F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FA0F37D3-770B-42F4-A53D-36CB8161A6F4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0B65E76A-4435-4D6B-864A-097475739B42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0B65E76A-4435-4D6B-864A-097475739B42}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0B65E76A-4435-4D6B-864A-097475739B42}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0B65E76A-4435-4D6B-864A-097475739B42}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{55CEBB38-910A-413E-80F3-35774E12A396}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{55CEBB38-910A-413E-80F3-35774E12A396}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{55CEBB38-910A-413E-80F3-35774E12A396}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{55CEBB38-910A-413E-80F3-35774E12A396}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {D497A5D8-3095-4DF8-A63B-C381551EF746}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
18
JsonRPC/JsonRPC.csproj
Normal file
18
JsonRPC/JsonRPC.csproj
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<BaseOutputPath></BaseOutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Http\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
15
JsonRPC/Protocol/EJsonRPCVersion.cs
Normal file
15
JsonRPC/Protocol/EJsonRPCVersion.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
|
||||||
|
namespace JsonRPC.Protocol
|
||||||
|
{
|
||||||
|
public enum EJsonRPCVersion
|
||||||
|
{
|
||||||
|
[EnumMember(Value = "1.0")]
|
||||||
|
Version1 = 1,
|
||||||
|
|
||||||
|
[EnumMember(Value = "2.0")]
|
||||||
|
Version2 = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
26
JsonRPC/Protocol/JsonRPCError.cs
Normal file
26
JsonRPC/Protocol/JsonRPCError.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace JsonRPC.Protocol
|
||||||
|
{
|
||||||
|
public enum EErrorCode
|
||||||
|
{
|
||||||
|
ParseError = -32700,// 解析错误,服务器收到无效的 JSON。
|
||||||
|
InvalidRequest = -32600, // 无效请求,发送的 JSON 不是有效的请求对象。
|
||||||
|
MethodNotFound = -32601, // 方法未找到,方法不存在或无效。
|
||||||
|
InvalidParam = -32602, // 无效参数,提供的参数无效。
|
||||||
|
InternalError = -32603, // 内部错误,JSON-RPC 内部错误。
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class JsonRPCError
|
||||||
|
{
|
||||||
|
[JsonPropertyName("data")]
|
||||||
|
public string? Data { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("code")]
|
||||||
|
public int Code { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("message")]
|
||||||
|
public string Message { get; set; } = null!;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
JsonRPC/Protocol/JsonRPCRequest.cs
Normal file
38
JsonRPC/Protocol/JsonRPCRequest.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace JsonRPC.Protocol
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public class JsonRPCRequest
|
||||||
|
{
|
||||||
|
[JsonPropertyName("method")]
|
||||||
|
public string Method { get; private set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("params")]
|
||||||
|
public List<object> Params { get; private set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public int Id { get; private set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("jsonrpc")]
|
||||||
|
public EJsonRPCVersion Version => EJsonRPCVersion.Version2;
|
||||||
|
|
||||||
|
public JsonRPCRequest(string method, int id)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(method))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Method = method;
|
||||||
|
this.Id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonRPCRequest AddParam(object param)
|
||||||
|
{
|
||||||
|
this.Params.Add(param);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
24
JsonRPC/Protocol/JsonRPCResponse.cs
Normal file
24
JsonRPC/Protocol/JsonRPCResponse.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace JsonRPC.Protocol
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public class JsonRPCResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("jsonrpc")]
|
||||||
|
public EJsonRPCVersion Version { get; set; } = EJsonRPCVersion.Version2;
|
||||||
|
|
||||||
|
[JsonPropertyName("error")]
|
||||||
|
public JsonRPCError? Error { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class JsonRPCResponse<T> : JsonRPCResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("result")]
|
||||||
|
public T? Result { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
19
JsonRPC/RPC/AbsSingle.cs
Normal file
19
JsonRPC/RPC/AbsSingle.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
public abstract class AbsSingle<T> where T : class, new()
|
||||||
|
{
|
||||||
|
static public T Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return SingleHolder.instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingleHolder
|
||||||
|
{
|
||||||
|
public static T instance;
|
||||||
|
static SingleHolder()
|
||||||
|
{
|
||||||
|
instance = new T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
JsonRPC/RPC/Receiver.cs
Normal file
71
JsonRPC/RPC/Receiver.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
using JsonRPC.Protocol;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace JsonRPC.RPC
|
||||||
|
{
|
||||||
|
public class Receiver : AbsSingle<Receiver>
|
||||||
|
{
|
||||||
|
Dictionary<string, Func<JsonRPCRequest, JsonRPCResponse>> handlers = new();
|
||||||
|
|
||||||
|
public void RegHandler(string methodSig, Func<JsonRPCRequest, JsonRPCResponse> func)
|
||||||
|
{
|
||||||
|
handlers.Add(methodSig, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OnReceive(string msg)
|
||||||
|
{
|
||||||
|
JsonRPCResponse response;
|
||||||
|
var req = JsonConvert.DeserializeObject<JsonRPCRequest>(msg);
|
||||||
|
if(req == null)
|
||||||
|
{
|
||||||
|
response = new JsonRPCResponse()
|
||||||
|
{
|
||||||
|
Error = new JsonRPCError()
|
||||||
|
{
|
||||||
|
Code = (int)EErrorCode.ParseError,
|
||||||
|
Message = msg,
|
||||||
|
Data = "invalid json."
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handlers.TryGetValue(req.Method, out var handler))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response = handler!(req);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
response = new JsonRPCResponse()
|
||||||
|
{
|
||||||
|
Id = req.Id,
|
||||||
|
Error = new JsonRPCError()
|
||||||
|
{
|
||||||
|
Code = (int)EErrorCode.InternalError,
|
||||||
|
Message = msg,
|
||||||
|
Data = e.ToString()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response = new JsonRPCResponse()
|
||||||
|
{
|
||||||
|
Id = req.Id,
|
||||||
|
Error = new JsonRPCError()
|
||||||
|
{
|
||||||
|
Code = (int)EErrorCode.MethodNotFound,
|
||||||
|
Message = msg,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
45
JsonRPC/RPC/Sender.cs
Normal file
45
JsonRPC/RPC/Sender.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using JsonRPC.Protocol;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace JsonRPC.RPC
|
||||||
|
{
|
||||||
|
public class Sender : AbsSingle<Sender>
|
||||||
|
{
|
||||||
|
Func<string, string> sendFunc = null!;
|
||||||
|
Action<JsonRPCError> errorFunc = null!;
|
||||||
|
int id;
|
||||||
|
|
||||||
|
public int GetId()
|
||||||
|
{
|
||||||
|
return Interlocked.Increment(ref id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSendFunction(Func<string, string> send)
|
||||||
|
{
|
||||||
|
sendFunc = send;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HnadleResponseError(JsonRPCResponse response)
|
||||||
|
{
|
||||||
|
if(response.Error != null)
|
||||||
|
{
|
||||||
|
errorFunc?.Invoke(response.Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Send(JsonRPCRequest request)
|
||||||
|
{
|
||||||
|
var reqJs = JsonConvert.SerializeObject(request);
|
||||||
|
|
||||||
|
return sendFunc.Invoke(reqJs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T JsonDeserialize<T>(string json) where T : JsonRPCResponse
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<T>(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Test/Program.cs
Normal file
17
Test/Program.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// See https://aka.ms/new-console-template for more information
|
||||||
|
using CodeGenerator;
|
||||||
|
using JsonRPC.Protocol;
|
||||||
|
Console.WriteLine("Hello, World!");
|
||||||
|
|
||||||
|
JsonRPCCodeGenerator jsonRPCCodeGenerator = new JsonRPCCodeGenerator((s) => Console.WriteLine(s));
|
||||||
|
jsonRPCCodeGenerator.LoadAssembly("G:\\Misc\\JsonRPC\\TestInterceptor\\bin\\Debug\\net8.0\\TestInterceptor.dll");
|
||||||
|
jsonRPCCodeGenerator.ProcessAssembly();
|
||||||
|
jsonRPCCodeGenerator.WriteModule();
|
||||||
|
JsonRPC.RPC.Sender.Instance.SetSendFunction((s) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine(s);
|
||||||
|
return JsonRPC.RPC.Receiver.Instance.OnReceive(s);
|
||||||
|
});
|
||||||
|
TestInterceptor.Class1.Echo("ss");
|
||||||
|
Console.WriteLine(TestInterceptor.Class1.Test1());
|
||||||
|
//typeof(TestInterceptor.Class1).GetMethod("TestInterceptor_Class1__Test_", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)?.Invoke(null, null);
|
||||||
16
Test/Test.csproj
Normal file
16
Test/Test.csproj
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\CodeGenerator\CodeGenerator.csproj" />
|
||||||
|
<ProjectReference Include="..\JsonRPC\JsonRPC.csproj" />
|
||||||
|
<ProjectReference Include="..\TestInterceptor\TestInterceptor.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
36
TestInterceptor/Class1.cs
Normal file
36
TestInterceptor/Class1.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using CodeGenerator;
|
||||||
|
using JsonRPC.Protocol;
|
||||||
|
using JsonRPC.RPC;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace TestInterceptor
|
||||||
|
{
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
static Class1() { Console.WriteLine("Class1 Class1"); }
|
||||||
|
|
||||||
|
[JsonRPC]
|
||||||
|
public static void Test()
|
||||||
|
{
|
||||||
|
Console.WriteLine("static TestFunc");
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonRPC]
|
||||||
|
public static void Echo(string msg)
|
||||||
|
{
|
||||||
|
Console.WriteLine(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonRPC]
|
||||||
|
public static string Test1()
|
||||||
|
{
|
||||||
|
return "aaaaaaa";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static string Test11()
|
||||||
|
{
|
||||||
|
return "aaaaaaa";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
TestInterceptor/TestInterceptor.csproj
Normal file
14
TestInterceptor/TestInterceptor.csproj
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\CodeGenerator\CodeGenerator.csproj" />
|
||||||
|
<ProjectReference Include="..\JsonRPC\JsonRPC.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Loading…
x
Reference in New Issue
Block a user