修改了http服务器无法正确处理post请求入参;添加了modbus tcp客户端支持。

This commit is contained in:
fengjiayi
2025-07-23 15:57:57 +08:00
parent acf0b87ad0
commit 4e20e816ae
24 changed files with 2466 additions and 189 deletions

View File

@@ -130,7 +130,7 @@ namespace Serein.Library.Network
}
else if (jsonObject != null && PostArgTypes[i] == PostArgType.IsBobyData)
{
args[i] = jsonObject;
args[i] = jsonObject.ToObject(type);
}
else if (jsonObject != null)
{

View File

@@ -36,10 +36,10 @@ namespace Serein.Library.Web
/// <summary>
/// 标记该类为 Web Api 处理类
/// </summary>
public class AutoHostingAttribute : Attribute
public class WebApiControllerAttribute : Attribute
{
public string Url { get; }
public AutoHostingAttribute(string url = "")
public WebApiControllerAttribute(string url = "")
{
this.Url = url;
}

View File

@@ -92,7 +92,7 @@ namespace Serein.Library.Web
{
if (!controllerType.IsClass || controllerType.IsAbstract) return; // 如果不是类或者是抽象类,则直接返回
var autoHostingAttribute = controllerType.GetCustomAttribute<AutoHostingAttribute>();
var autoHostingAttribute = controllerType.GetCustomAttribute<WebApiControllerAttribute>();
var methods = controllerType.GetMethods().Where(m => m.GetCustomAttribute<WebApiAttribute>() != null).ToArray();
@@ -264,7 +264,7 @@ namespace Serein.Library.Web
/// <param name="controllerType">控制器类型</param>
/// <param name="method">方法信息</param>
/// <returns>方法对应的urk</returns>
private string AddRoutesUrl(AutoHostingAttribute autoHostingAttribute, WebApiAttribute webAttribute, Type controllerType, MethodInfo method)
private string AddRoutesUrl(WebApiControllerAttribute autoHostingAttribute, WebApiAttribute webAttribute, Type controllerType, MethodInfo method)
{
string controllerName;
if (string.IsNullOrWhiteSpace(autoHostingAttribute.Url))

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Threading.Tasks;
@@ -47,8 +48,15 @@ namespace Serein.Library.Web
{
while (listener.IsListening)
{
var context = await listener.GetContextAsync(); // 获取请求上下文
ProcessRequestAsync(context); // 处理请求
try
{
var context = await listener.GetContextAsync(); // 获取请求上下文
await ProcessRequestAsync(context); // 处理请求
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
});
}
@@ -84,7 +92,7 @@ namespace Serein.Library.Web
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private async void ProcessRequestAsync(HttpListenerContext context)
private async Task ProcessRequestAsync(HttpListenerContext context)
{
// 添加CORS头部
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Library.Network.Modbus
{
/// <summary>
/// Modbus 功能码枚举
/// </summary>
public enum ModbusFunctionCode
{
/// <summary>
/// 读线圈
/// </summary>
ReadCoils = 0x01,
/// <summary>
/// 读离散输入
/// </summary>
ReadDiscreteInputs = 0x02,
/// <summary>
/// 读保持寄存器
/// </summary>
ReadHoldingRegisters = 0x03,
/// <summary>
/// 读输入寄存器
/// </summary>
ReadInputRegisters = 0x04,
/// <summary>
/// 写单个线圈
/// </summary>
WriteSingleCoil = 0x05,
/// <summary>
/// 写单个寄存器
/// </summary>
WriteSingleRegister = 0x06,
/// <summary>
/// 写多个线圈
/// </summary>
WriteMultipleCoils = 0x0F,
/// <summary>
/// 写多个寄存器
/// </summary>
WriteMultipleRegister = 0x10,
}
}

View File

@@ -0,0 +1,403 @@
using System;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Channels;
using System.Threading;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
namespace Serein.Library.Network.Modbus
{
/// <summary>
/// Modbus TCP 客户端
/// </summary>
public class ModbusTcpClient : IDisposable
{
/// <summary>
/// 消息通道
/// </summary>
private readonly Channel<ModbusTcpRequest> _channel = Channel.CreateUnbounded<ModbusTcpRequest>();
/// <summary>
/// TCP客户端
/// </summary>
private readonly TcpClient _tcpClient;
/// <summary>
/// TCP客户端的网络流用于发送和接收数据
/// </summary>
private readonly NetworkStream _stream;
/// <summary>
/// 存储未完成请求的字典键为事务ID值为任务完成源
/// </summary>
private readonly ConcurrentDictionary<ushort, TaskCompletionSource<byte[]>> _pendingRequests = new();
/// <summary>
/// 事务ID计数器用于生成唯一的事务ID
/// </summary>
private int _transactionId = 0;
public ModbusTcpClient(string host, int port = 502)
{
_tcpClient = new TcpClient();
_tcpClient.Connect(host, port);
_stream = _tcpClient.GetStream();
_ = ProcessQueueAsync(); // 启动后台消费者
_ = ReceiveLoopAsync(); // 启动接收响应线程
}
#region
/// <summary>
/// 读取线圈状态
/// </summary>
/// <param name="startAddress"></param>
/// <param name="quantity"></param>
/// <returns></returns>
public async Task<bool[]> ReadCoils(ushort startAddress, ushort quantity)
{
var pdu = BuildReadPdu(startAddress, quantity);
var responsePdu = await SendAsync(ModbusFunctionCode.ReadCoils, pdu);
return ParseDiscreteBits(responsePdu, quantity);
}
/// <summary>
/// 读取离散输入状态
/// </summary>
/// <param name="startAddress"></param>
/// <param name="quantity"></param>
/// <returns></returns>
public async Task<bool[]> ReadDiscreteInputs(ushort startAddress, ushort quantity)
{
var pdu = BuildReadPdu(startAddress, quantity);
var responsePdu = await SendAsync(ModbusFunctionCode.ReadDiscreteInputs, pdu);
return ParseDiscreteBits(responsePdu, quantity);
}
/// <summary>
/// 读取保持寄存器
/// </summary>
/// <param name="startAddress"></param>
/// <param name="quantity"></param>
/// <returns></returns>
public async Task<ushort[]> ReadHoldingRegisters(ushort startAddress, ushort quantity)
{
var pdu = BuildReadPdu(startAddress, quantity);
var responsePdu = await SendAsync(ModbusFunctionCode.ReadHoldingRegisters, pdu);
return ParseRegisters(responsePdu, quantity);
}
/// <summary>
/// 读取输入寄存器
/// </summary>
/// <param name="startAddress"></param>
/// <param name="quantity"></param>
/// <returns></returns>
public async Task<ushort[]> ReadInputRegisters(ushort startAddress, ushort quantity)
{
var pdu = BuildReadPdu(startAddress, quantity);
var responsePdu = await SendAsync(ModbusFunctionCode.ReadInputRegisters, pdu);
return ParseRegisters(responsePdu, quantity);
}
/// <summary>
/// 写单个线圈
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
public async Task WriteSingleCoil(ushort address, bool value)
{
var pdu = new byte[]
{
(byte)(address >> 8), // 地址高字节
(byte)(address & 0xFF), // 地址低字节
value ? (byte)0xFF : (byte)0x00, // 线圈值高字节高电平为0xFF低电平为00
0x00 // 线圈值低字节
};
await SendAsync(ModbusFunctionCode.WriteSingleCoil, pdu);
}
/// <summary>
/// 写单个寄存器
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
public async Task WriteSingleRegister(ushort address, ushort value)
{
var pdu = new byte[]
{
(byte)(address >> 8), // 地址高字节
(byte)(address & 0xFF), // 地址低字节
(byte)(value >> 8), // 寄存器值高字节
(byte)(value & 0xFF) // 寄存器值低字节
};
await SendAsync(ModbusFunctionCode.WriteSingleRegister, pdu);
}
/// <summary>
/// 写多个线圈
/// </summary>
/// <param name="startAddress"></param>
/// <param name="values"></param>
/// <returns></returns>
public async Task WriteMultipleCoils(ushort startAddress, bool[] values)
{
int byteCount = (values.Length + 7) / 8; // 计算需要的字节数
byte[] data = new byte[byteCount];
for (int i = 0; i < values.Length; i++)
{
if (values[i])
data[i / 8] |= (byte)(1 << (i % 8)); // 设置对应位
}
var pdu = new List<byte>
{
(byte)(startAddress >> 8), // 地址高字节
(byte)(startAddress & 0xFF), // 地址低字节
(byte)(values.Length >> 8), // 数量高字节
(byte)(values.Length & 0xFF), // 数量低字节
(byte)data.Length // 字节数
};
pdu.AddRange(data);
await SendAsync(ModbusFunctionCode.WriteMultipleCoils, pdu.ToArray());
}
/// <summary>
/// 写多个寄存器
/// </summary>
/// <param name="startAddress"></param>
/// <param name="values"></param>
/// <returns></returns>
public async Task WriteMultipleRegisters(ushort startAddress, ushort[] values)
{
var pdu = new List<byte>
{
(byte)(startAddress >> 8), // 地址高字节
(byte)(startAddress & 0xFF), // 地址低字节
(byte)(values.Length >> 8), // 数量高字节
(byte)(values.Length & 0xFF), // 数量低字节
(byte)(values.Length * 2) // 字节数
};
foreach (var val in values)
{
pdu.Add((byte)(val >> 8)); // 寄存器值高字节
pdu.Add((byte)(val & 0xFF)); // 寄存器值低字节
}
await SendAsync(ModbusFunctionCode.WriteMultipleRegister, pdu.ToArray());
}
/// <summary>
/// 构建读取PDU数据
/// </summary>
/// <param name="startAddress"></param>
/// <param name="quantity"></param>
/// <returns></returns>
private byte[] BuildReadPdu(ushort startAddress, ushort quantity)
{
byte[] buffer = new byte[4];
BinaryPrimitives.WriteUInt16BigEndian(buffer.AsSpan(0, 2), startAddress); // 起始地址高低字节
BinaryPrimitives.WriteUInt16BigEndian(buffer.AsSpan(2, 2), quantity); // 读取数量高低字节
return buffer;
}
/// <summary>
/// 解析离散位数据
/// </summary>
/// <param name="pdu"></param>
/// <param name="count"></param>
/// <returns></returns>
private bool[] ParseDiscreteBits(byte[] pdu, ushort count)
{
int byteCount = pdu[2]; // 第2字节是后续的字节数量
int dataIndex = 3; // 数据从第3字节开始0-based
var result = new bool[count];
for (int i = 0, bytePos = 0, bitPos = 0; i < count; i++, bitPos++)
{
if (bitPos == 8)
{
bitPos = 0;
bytePos++;
}
result[i] = ((pdu[dataIndex + bytePos] >> bitPos) & 0x01) != 0;
}
return result;
}
/// <summary>
/// 解析寄存器数据
/// </summary>
/// <param name="pdu"></param>
/// <param name="count"></param>
/// <returns></returns>
private ushort[] ParseRegisters(byte[] pdu, ushort count)
{
var result = new ushort[count];
int dataStart = 3; // 数据从第3字节开始
for (int i = 0; i < count; i++)
{
int offset = dataStart + i * 2;
result[i] = (ushort)((pdu[offset] << 8) | pdu[offset + 1]);
}
return result;
}
#endregion
/// <summary>
/// 处理消息队列,发送请求到服务器
/// </summary>
/// <returns></returns>
private async Task ProcessQueueAsync()
{
var request = await _channel.Reader.ReadAsync();
byte[] packet = BuildPacket(request.TransactionId, 0x01, (byte)request.FunctionCode, request.PDU);
await _stream.WriteAsync(packet, 0, packet.Length);
await _stream.FlushAsync();
}
/// <summary>
/// 发送请求并等待响应
/// </summary>
/// <param name="functionCode">功能码</param>
/// <param name="pdu">内容</param>
/// <returns></returns>
/// <exception cref="TimeoutException"></exception>
public Task<byte[]> SendAsync(ModbusFunctionCode functionCode, byte[] pdu)
{
int id = Interlocked.Increment(ref _transactionId);
var transactionId = (ushort)(id % ushort.MaxValue); // 0~65535 循环
var tcs = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);
var request = new ModbusTcpRequest
{
TransactionId = transactionId,
FunctionCode = functionCode,
PDU = pdu,
Completion = tcs
};
_pendingRequests[transactionId] = tcs;
_channel.Writer.TryWrite(request);
return tcs.Task;
}
/// <summary>
/// 接收数据循环
/// </summary>
/// <returns></returns>
private async Task ReceiveLoopAsync()
{
var buffer = new byte[1024];
while (true)
{
#if NET462
int len = await _stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
#else
int len = await _stream.ReadAsync(buffer.AsMemory(0, buffer.Length)).ConfigureAwait(false);
#endif
if (len == 0) return; // 连接关闭
if (len < 6)
{
Console.WriteLine("接收到的数据长度不足");
return;
}
ushort protocolId = BinaryPrimitives.ReadUInt16BigEndian(buffer.AsSpan(2, 2));
if (protocolId != 0x0000)
{
Console.WriteLine($"协议不匹配: {protocolId:X4}");
return;
}
ushort dataLength = BinaryPrimitives.ReadUInt16BigEndian(buffer.AsSpan(4, 2));
// 检查数据长度是否合法
if (dataLength > 253 || len < 6 + dataLength)
{
Console.WriteLine($"数据长度异常: dataLength={dataLength}, 实际接收={len}");
return;
}
ushort transactionId = BinaryPrimitives.ReadUInt16BigEndian(buffer.AsSpan(0, 2));
if (_pendingRequests.TryRemove(transactionId, out var tcs))
{
var responsePdu = new ReadOnlySpan<byte>(buffer, 6, dataLength).ToArray();
tcs.SetResult(responsePdu); // 如需 byte[] 则 ToArray
}
else
{
Console.WriteLine($"未匹配到 TransactionId={transactionId} 的请求");
}
}
}
/// <summary>
/// 构造 Modbus Tcp 报文
/// </summary>
/// <param name="transactionId"></param>
/// <param name="unitId"></param>
/// <param name="functionCode"></param>
/// <param name="pduData"></param>
/// <returns></returns>
private byte[] BuildPacket(ushort transactionId, byte unitId, byte functionCode, byte[] pduData)
{
int pduLength = 1 + pduData.Length; // PDU 长度 = 功能码1字节 + 数据长度
int totalLength = 7 + pduLength; // MBAP头长度 = 7字节 + PDU长度
Span<byte> packet = totalLength <= 256 ? stackalloc byte[totalLength] : new byte[totalLength];
// 写入事务ID大端序
packet[0] = (byte)(transactionId >> 8);
packet[1] = (byte)(transactionId);
// 协议ID固定0x0000
packet[2] = 0;
packet[3] = 0;
// 长度PDU长度 + 1字节UnitID
ushort length = (ushort)(pduLength + 1);
packet[4] = (byte)(length >> 8);
packet[5] = (byte)(length);
// UnitID & 功能码
packet[6] = unitId;
packet[7] = functionCode;
// 复制PDU数据
pduData.AsSpan().CopyTo(packet.Slice(8));
return packet.ToArray();
}
public void Dispose()
{
var tcs = _pendingRequests.Values.ToArray();
foreach (var pending in tcs)
{
pending.TrySetCanceled();
}
_stream?.Close();
_tcpClient?.Close();
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Library.Network.Modbus
{
/// <summary>
/// Modbus TCP 请求实体
/// </summary>
public class ModbusTcpRequest
{
/// <summary>
/// 事务ID
/// </summary>
public ushort TransactionId { get; set; }
/// <summary>
/// 功能码
/// </summary>
public ModbusFunctionCode FunctionCode { get; set; }
/// <summary>
/// PDU 数据
/// </summary>
public byte[] PDU { get; set; }
/// <summary>
/// 请求的完成源,用于异步等待响应
/// </summary>
public TaskCompletionSource<byte[]> Completion { get; set; }
}
}

110
Library/ScriptBaseFunc.cs Normal file
View File

@@ -0,0 +1,110 @@
using Serein.Library;
using Serein.Library.Utils;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Serein.Library
{
public static class ScriptBaseFunc
{
public static DateTime now() => DateTime.Now;
#region
public static bool @bool(object value)
{
return ConvertHelper.ValueParse<bool>(value);
}
public static byte @byte(object value)
{
return ConvertHelper.ValueParse<byte>(value);
}
public static decimal @decimal(object value)
{
return ConvertHelper.ValueParse<decimal>(value);
}
public static float @float(object value)
{
return ConvertHelper.ValueParse<float>(value);
}
public static double @double(object value)
{
return ConvertHelper.ValueParse<double>(value);
}
public static int @int(object value)
{
return ConvertHelper.ValueParse<int>(value);
}
public static int @long(object value)
{
return ConvertHelper.ValueParse<int>(value);
}
#endregion
public static int len(object target)
{
// 获取数组或集合对象
// 访问数组或集合中的指定索引
if (target is Array array)
{
return array.Length;
}
else if (target is string chars)
{
return chars.Length;
}
else if (target is IDictionary<object, object> dict)
{
return dict.Count;
}
else if (target is IList<object> list)
{
return list.Count;
}
else
{
throw new ArgumentException($"并非有效集合");
}
}
public static string str(object obj)
{
return obj?.ToString() ?? string.Empty;
}
public static object global(string name)
{
return SereinEnv.GetFlowGlobalData(name);
}
public static Type type(object type)
{
return type.GetType();
}
public static void log(object value)
{
SereinEnv.WriteLine(InfoType.INFO, value?.ToString());
}
public static async Task sleep(object value)
{
if (value is int @int)
{
Console.WriteLine($"等待{@int}ms");
await Task.Delay(@int);
}
else if (value is TimeSpan timeSpan)
{
Console.WriteLine($"等待{timeSpan}");
await Task.Delay(timeSpan);
}
}
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.2.0</Version>
<Version>1.2.1.1</Version>
<TargetFrameworks>net8.0;net462</TargetFrameworks>
<BaseOutputPath>..\.\.Output</BaseOutputPath>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>

View File

@@ -1,23 +0,0 @@
using Newtonsoft.Json.Linq;
using Serein.Library.Api;
using Serein.Library.Utils;
using System;
using System.Collections.Concurrent;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Transactions;
namespace Serein.Library.Utils
{
}

View File

@@ -1,13 +1,12 @@
using Net462DllTest.View;
using Serein.Library;
using Serein.Library;
namespace Net462DllTest.Signal
{
public enum FromValue
/*public enum FromValue
{
[BindValue(typeof(FromWorkBenchView))]
FromWorkBenchView,
[BindValue(typeof(TestFormView))]
TestFormView,
}
}*/
}

View File

@@ -9,7 +9,7 @@ using System;
namespace Net462DllTest.Web
{
[AutoHosting]
[WebApiController]
public class FlowController : ControllerBase
{
private readonly SiemensPlcDevice plcDevice;

View File

@@ -1,10 +1,10 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.Script;
using Serein.Script.Node.FlowControl;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
@@ -52,7 +52,8 @@ namespace Serein.NodeFlow.Model
static SingleScriptNode()
{
// 挂载静态方法
var tempMethods = typeof(ScriptBaseFunc).GetMethods().Where(method =>
var tempMethods = typeof(Serein.Library.ScriptBaseFunc).GetMethods().Where(method =>
method.IsStatic &&
!(method.Name.Equals("GetHashCode")
|| method.Name.Equals("Equals")
|| method.Name.Equals("ToString")
@@ -86,7 +87,7 @@ namespace Serein.NodeFlow.Model
pd[0] = new ParameterDetails
{
Index = 0,
Name = "string",
Name = "arg",
IsExplicitData = true,
DataValue = string.Empty,
DataType = typeof(string),
@@ -156,25 +157,35 @@ namespace Serein.NodeFlow.Model
var argTypes = MethodDetails.ParameterDetailss
.Select(pd =>
{
if(pd.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
{
var Type = pd.DataType;
return (pd.Name, Type);
}
if (Env.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var node) &&
node.MethodDetails?.ReturnType is not null)
{
pd.DataType = node.MethodDetails.ReturnType;
return (pd.Name, node.MethodDetails.ReturnType);
var Type = node.MethodDetails.ReturnType;
return (pd.Name, Type);
}
return default;
})
.Where(x => x != default)
.ToDictionary(x => x.Name, x => x.ReturnType); // 准备预定义类型
.ToDictionary(x => x.Name, x => x.Type); // 准备预定义类型
var returnType = sereinScript.ParserScript(Script, argTypes); // 开始解析获取程序主节点
MethodDetails.ReturnType = returnType;
var code = sereinScript.ConvertCSharpCode("Test", argTypes);
Debug.WriteLine(code);
return true;
}
catch (Exception ex)
{
SereinEnv.WriteLine(ex);
SereinEnv.WriteLine(InfoType.WARN, ex.Message);
return false; // 解析失败
}
}
@@ -244,116 +255,5 @@ namespace Serein.NodeFlow.Model
return new FlowResult(this.Guid, context, result);
}
#region
/*public IScriptFlowApi GetFlowApi()
{
return ScriptFlowApi;
}*/
private static class ScriptBaseFunc
{
public static DateTime now() => DateTime.Now;
#region
public static bool @bool(object value)
{
return ConvertHelper.ValueParse<bool>(value);
}
public static decimal @decimal(object value)
{
return ConvertHelper.ValueParse<decimal>(value);
}
public static float @float(object value)
{
return ConvertHelper.ValueParse<float>(value);
}
public static double @double(object value)
{
return ConvertHelper.ValueParse<double>(value);
}
public static int @int(object value)
{
return ConvertHelper.ValueParse<int>(value);
}
public static int @long(object value)
{
return ConvertHelper.ValueParse<int>(value);
}
#endregion
public static int len(object target)
{
// 获取数组或集合对象
// 访问数组或集合中的指定索引
if (target is Array array)
{
return array.Length;
}
else if (target is string chars)
{
return chars.Length;
}
else if (target is IDictionary<object, object> dict)
{
return dict.Count;
}
else if (target is IList<object> list)
{
return list.Count;
}
else
{
throw new ArgumentException($"并非有效集合");
}
}
public static void regType(Type type, string name = "")
{
SereinScript.AddClassType(type, name);
}
public static string str(object obj)
{
return obj?.ToString() ?? string.Empty;
}
public static object obj(Type type)
{
return Activator.CreateInstance(type);
}
public static object global(string name)
{
return SereinEnv.GetFlowGlobalData(name);
}
public static Type type(object type)
{
return type.GetType();
}
public static void log(object value)
{
SereinEnv.WriteLine(InfoType.INFO, value?.ToString());
}
public static async Task sleep(object value)
{
if (value is int @int)
{
Console.WriteLine($"等待{@int}ms");
await Task.Delay(@int);
}
else if (value is TimeSpan timeSpan)
{
Console.WriteLine($"等待{timeSpan}");
await Task.Delay(timeSpan);
}
}
}
#endregion
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.2.0</Version>
<Version>1.2.1</Version>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

View File

@@ -651,6 +651,10 @@ namespace Serein.NodeFlow.Services
private Dictionary<SingleFlowCallNode, FlowApiMethodInfo> flowApiMethodInfos = [];
/// <summary>
/// 生成流程接口方法信息
/// </summary>
/// <param name="flowCallNodes"></param>
private void GenerateFlowApi_InitFlowApiMethodInfos(SingleFlowCallNode[] flowCallNodes)
{
@@ -667,6 +671,11 @@ namespace Serein.NodeFlow.Services
}
}
}
/// <summary>
/// 生成流程接口模板类
/// </summary>
/// <param name="sb"></param>
private void GenerateFlowApi_InterfaceAndImpleClass(StringBuilder sb)
{
/*
@@ -703,6 +712,10 @@ namespace Serein.NodeFlow.Services
}
/// <summary>
/// 生成流程接口参数
/// </summary>
/// <param name="sb"></param>
private void GenerateFlowApi_ApiParamClass(StringBuilder sb)
{
var infos = flowApiMethodInfos.Values.ToArray();

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>1.2.0</Version>
<Version>1.2.1</Version>
<IsRoslynComponent>true</IsRoslynComponent>
<BaseOutputPath>..\.\.Output</BaseOutputPath>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>

View File

@@ -39,7 +39,7 @@ namespace Serein.Script.Node
public CollectionIndexNode Collection { get; }
/// <summary>
/// 索引来源
/// 赋值值来源
/// </summary>
public ASTNode Value { get; }

View File

@@ -19,6 +19,10 @@
<None Remove="Tool\**" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Node\ExpressionNode.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Library\Serein.Library.csproj" />
</ItemGroup>

View File

@@ -1,4 +1,5 @@
using Serein.Library;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Serein.Library;
using Serein.Library.Api;
using Serein.Script.Node;
using Serein.Script.Node.FlowControl;
@@ -22,7 +23,7 @@ namespace Serein.Script
/// <summary>
/// 类型分析
/// </summary>
public SereinScriptTypeAnalysis TypeAnalysis { get; set; } = new SereinScriptTypeAnalysis();
public SereinScriptTypeAnalysis TypeAnalysis { get; } = new SereinScriptTypeAnalysis();
@@ -34,7 +35,7 @@ namespace Serein.Script
SereinScriptParser parser = new SereinScriptParser();
SereinScriptTypeAnalysis analysis = new SereinScriptTypeAnalysis();
var programNode = parser.Parse(script);
analysis.NodeSymbolInfos.Clear(); // 清空符号表
analysis.Reset();
if (argTypes is not null) analysis.LoadSymbol(argTypes); // 提前加载脚本节点定义的符号
analysis.Analysis(programNode); // 分析节点类型
SereinScriptInterpreter Interpreter = new SereinScriptInterpreter(analysis.NodeSymbolInfos);
@@ -61,6 +62,14 @@ namespace Serein.Script
return returnType; // 脚本返回类型
}
/// <summary>
/// 执行脚本
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public async Task<object?> InterpreterAsync(IScriptInvokeContext context)
{
if(programNode is null)
@@ -73,6 +82,35 @@ namespace Serein.Script
}
/// <summary>
/// 转换为c#代码
/// </summary>
/// <param name="script">脚本</param>
/// <param name="argTypes">挂载的变量</param>
/// <returns></returns>
public string ConvertCSharpCode(string mehtodName, Dictionary<string, Type>? argTypes = null)
{
if (string.IsNullOrWhiteSpace(mehtodName)) return string.Empty;
if (programNode is null) return string.Empty;
SereinScriptToCsharpScript tool = new SereinScriptToCsharpScript(TypeAnalysis);
return tool.CompileToCSharp(mehtodName, programNode, argTypes);
}
/// <summary>
/// 编译为 IL 代码
/// </summary>
/// <param name="script">脚本</param>
/// <param name="argTypes">挂载的变量</param>
/// <returns></returns>
[Obsolete("因为暂未想到如何支持异步方法,所以暂时废弃生成", true)]
private Delegate CompilerIL(string dynamicMethodName, ProgramNode programNode, Dictionary<string, Type>? argTypes = null)
{
SereinScriptILCompiler compiler = new SereinScriptILCompiler(TypeAnalysis.NodeSymbolInfos);
var @delegate = compiler.Compiler(dynamicMethodName, programNode, argTypes); // 编译脚本
return @delegate;
}
@@ -96,10 +134,10 @@ namespace Serein.Script
/// <summary>
/// 挂载的函数调用的对象(用于解决函数需要实例才能调用的场景)
/// </summary>
public static Dictionary<string, Func<object>> DelegateInstances = new Dictionary<string, Func<object>>();
// public static Dictionary<string, Func<object>> DelegateInstances = new Dictionary<string, Func<object>>();
/// <summary>
/// 挂载静态函数
/// 挂载函数
/// </summary>
/// <param name="functionName"></param>
/// <param name="methodInfo"></param>
@@ -115,7 +153,7 @@ namespace Serein.Script
/// </summary>
/// <param name="functionName">函数名称</param>
/// <param name="methodInfo">方法信息</param>
public static void AddFunction(string functionName, MethodInfo methodInfo, Func<object>? callObj = null)
/*public static void AddFunction(string functionName, MethodInfo methodInfo, Func<object>? callObj = null)
{
if (!methodInfo.IsStatic && callObj is null)
{
@@ -132,7 +170,7 @@ namespace Serein.Script
{
FunctionDelegates[functionName] = new DelegateDetails(methodInfo);
}
}
}*/
/// <summary>
/// 挂载类型

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Script
{
internal static class SereinScriptExtension
{ /// <summary>
/// 添加代码
/// </summary>
/// <param name="sb">字符串构建器</param>
/// <param name="retractCount">缩进次数4个空格</param>
/// <param name="code">要添加的代码</param>
/// <returns>字符串构建器本身</returns>
public static StringBuilder AppendCode(this StringBuilder sb,
int retractCount = 0,
string code = null,
bool isWrapping = true)
{
if (!string.IsNullOrWhiteSpace(code))
{
string retract = new string(' ', retractCount * 4);
sb.Append(retract);
if (isWrapping)
{
sb.AppendLine(code);
}
else
{
sb.Append(code);
}
}
return sb;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -42,13 +42,6 @@ namespace Serein.Script
return result; // 返回最后一个节点的结果
}
/*
async Task<object?> InterpreterAsync(IScriptInvokeContext context, ProgramNode node)
{
return null;
}
return await InterpreterAsync(context, node);
*/
private async Task<object?> InterpretAsync(IScriptInvokeContext context, ASTNode node)
{
switch (node)
@@ -243,8 +236,8 @@ namespace Serein.Script
return await InterpreterObjectInstantiationNodeAsync(context, objectInstantiationNode);
case CtorAssignmentNode ctorAssignmentNode:
return default;
case ExpressionNode expressionNode: // 类型表达式(链式调用)
return await InterpretAsync(context, expressionNode.Value); // 直接计算表达式的值
/*case ExpressionNode expressionNode: // 类型表达式(链式调用)
return await InterpretAsync(context, expressionNode.Value); // 直接计算表达式的值*/
case MemberAccessNode memberAccessNode: // 对象成员访问
async Task<object?> InterpreterMemberAccessNodeAsync(IScriptInvokeContext context, MemberAccessNode memberAccessNode)
{
@@ -319,7 +312,7 @@ namespace Serein.Script
if (!SereinScript.FunctionDelegates.TryGetValue(funcName, out DelegateDetails? function))
throw new SereinSciptException(functionCallNode, $"没有挂载方法\"{functionCallNode.FunctionName}\"");
if (!function.EmitMethodInfo.IsStatic)
/* if (!function.EmitMethodInfo.IsStatic)
{
if (!SereinScript.DelegateInstances.TryGetValue(funcName, out var action))
{
@@ -331,7 +324,7 @@ namespace Serein.Script
{
throw new SereinSciptException(functionCallNode, $"函数 {funcName} 尝试获取实例时返回了 null ");
}
}
}*/
var result = await function.InvokeAsync(instance, arguments);
return result;

View File

@@ -0,0 +1,464 @@
using Newtonsoft.Json.Linq;
using Serein.Library;
using Serein.Script.Node;
using Serein.Script.Node.FlowControl;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Net.Security;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Serein.Script
{
/// <summary>
/// 将 Serein 脚本转换为 C# 脚本的类
/// </summary>
internal class SereinScriptToCsharpScript
{
/// <summary>
/// 符号表
/// </summary>
private readonly Dictionary<ASTNode, Type> _symbolInfos;
/// <summary>
/// 记录方法节点是否需要异步调用
/// </summary>
private readonly Dictionary<ASTNode, bool> _asyncMethods;
/// <summary>
/// 临时变量表
/// </summary>
private readonly Dictionary<string, Type> _local = [];
private bool _isTaskMain = false;
public SereinScriptToCsharpScript(SereinScriptTypeAnalysis analysis)
{
_symbolInfos = analysis.NodeSymbolInfos;
_asyncMethods = analysis.AsyncMethods;
_isTaskMain = _asyncMethods.Any(kvp => kvp.Value);
}
private readonly StringBuilder _codeBuilder = new StringBuilder();
private int _indentLevel = 0;
private void AppendLine(string code)
{
_codeBuilder.AppendLine(new string(' ', _indentLevel * 4) + code);
}
private void Append(string code)
{
_codeBuilder.Append(code);
}
private void Indent() => _indentLevel++;
private void Unindent() => _indentLevel--;
private List<Action<StringBuilder>> _classDefinitions = new List<Action<StringBuilder>>();
public string CompileToCSharp(string mehtodName, ProgramNode programNode, Dictionary<string, Type>? param)
{
_codeBuilder.Clear();
var sb = _codeBuilder;
var methodResultType = _symbolInfos[programNode];
if(methodResultType == typeof(void))
{
methodResultType = typeof(object);
}
var taskFullName = typeof(Task).FullName;
var returnContent = _isTaskMain ? $"global::{taskFullName}<global::{methodResultType.FullName}>" : $"global::{methodResultType.FullName}";
AppendLine("public class SereinScriptToCsharp");
AppendLine( "{");
Indent();
if(param is null || param.Count == 0)
{
AppendLine($"public static {returnContent} {mehtodName}()");
}
else
{
AppendLine($"public static {returnContent} {mehtodName}({GetMethodParamster(param)})");
}
AppendLine( "{");
Indent();
foreach (var stmt in programNode.Statements)
{
ConvertCode(stmt); // 递归遍历
Append(";");
}
if(!_symbolInfos.Keys.Any(node => node is ReturnNode))
{
AppendLine("return null;");
}
Unindent();
AppendLine("}");
Unindent();
AppendLine("}");
foreach (var cd in _classDefinitions)
{
cd.Invoke(sb);
}
return sb.ToString();
}
private string GetMethodParamster(Dictionary<string, Type> param)
{
var values = param.Select(kvp =>
{
_local[kvp.Key] = kvp.Value;
return $"global::{kvp.Value.FullName} {kvp.Key}";
});
return string.Join(',', values);
}
private void ConvertCode( ASTNode node)
{
switch (node)
{
case ProgramNode programNode: // 程序开始节点
break;
case ReturnNode returnNode: // 程序退出节点
void ConvertCodeOfReturnNode(ReturnNode returnNode)
{
if (returnNode.Value is not null)
{
Append($"return ");
ConvertCode(returnNode.Value);
Append(";");
AppendLine(string.Empty);
}
else
{
AppendLine("return defult;");
}
}
ConvertCodeOfReturnNode(returnNode);
break;
#region
case NullNode nullNode: // null
Append("null");
break;
case CharNode charNode: // char字面量
Append($"'{charNode.Value}'");
break;
case StringNode stringNode: // 字符串字面量
Append($"\"{stringNode.Value}\"");
break;
case BooleanNode booleanNode: // 布尔值字面量
Append($"{(booleanNode.Value ? "true" : "false")}");
break;
case NumberIntNode numberIntNode: // int整型数值字面量
Append($"{numberIntNode.Value}");
break;
case NumberLongNode numberLongNode: // long整型数值字面量
Append($"{numberLongNode.Value}");
break;
case NumberFloatNode numberFloatNode: // float浮点数值字面量
Append($"{numberFloatNode.Value}f");
break;
case NumberDoubleNode numberDoubleNode: // double浮点数值字面量
Append($"{numberDoubleNode.Value}d");
break;
#endregion
case IdentifierNode identifierNode: // 变量定义
void ConvertCodeOfIdentifierNode(IdentifierNode identifierNode)
{
var varName = identifierNode.Name;
if(_local.TryGetValue(varName, out var type))
{
// 定义过,需要使用变量
Append(varName);
}
else
{
if (_symbolInfos.TryGetValue(identifierNode, out type))
{
// 不存在,定义变量
Append($"global::{type.FullName} {varName}");
_local[varName] = type;
//Append(varName);
}
else
{
throw new Exception($"加载符号表时,无法匹配 IdentifierNode 节点的类型。 name {varName}");
}
}
}
ConvertCodeOfIdentifierNode(identifierNode);
break;
case IfNode ifNode: // if语句结构
void ConvertCodeOfIfNode(IfNode ifNOde)
{
AppendLine("");
Append($"if(");
ConvertCode(ifNOde.Condition); // 解析条件
Append($" == true)");
AppendLine("{");
Indent();
foreach(var item in ifNOde.TrueBranch)
{
ConvertCode(item);
//Append(";");
AppendLine(string.Empty);
}
Unindent();
AppendLine("}");
AppendLine("else");
AppendLine("{");
Indent();
foreach (var item in ifNOde.TrueBranch)
{
ConvertCode(item);
//Append(";");
AppendLine(string.Empty);
}
Unindent();
AppendLine("}");
}
ConvertCodeOfIfNode(ifNode);
break;
case WhileNode whileNode: // while语句结构
void ConvertCodeOfWhileNode(WhileNode whileNode)
{
AppendLine("");
Append($"while(");
ConvertCode(whileNode.Condition); // 解析条件
Append($" == {true})");
AppendLine("{");
Indent();
foreach (var item in whileNode.Body)
{
ConvertCode(item);
Append(";");
AppendLine(string.Empty);
}
Unindent();
AppendLine("}");
}
ConvertCodeOfWhileNode(whileNode);
break;
case AssignmentNode assignmentNode: // 变量赋值语句
void ConvertCodeOfAssignmentNode(AssignmentNode assignmentNode)
{
ConvertCode(assignmentNode.Target);
Append(" = ");
if (assignmentNode.Value is null)
{
Append("null");
}
else
{
ConvertCode(assignmentNode.Value);
}
Append(";");
AppendLine(string.Empty);
}
ConvertCodeOfAssignmentNode(assignmentNode);
break;
case BinaryOperationNode binaryOperationNode: // 二元运算操作
void ConvertCodeOfBinaryOperationNode(BinaryOperationNode binaryOperationNode)
{
Append("(");
ConvertCode(binaryOperationNode.Left); // 左操作数
Append(binaryOperationNode.Operator); // 操作符
ConvertCode(binaryOperationNode.Right); // 左操作数
Append(")");
}
ConvertCodeOfBinaryOperationNode(binaryOperationNode);
break;
case CollectionAssignmentNode collectionAssignmentNode:
void ConvertCodeOfCollectionAssignmentNode(CollectionAssignmentNode collectionAssignmentNode)
{
ConvertCode(collectionAssignmentNode.Collection);
Append(" = ");
ConvertCode(collectionAssignmentNode.Value);
Append(";");
AppendLine(string.Empty);
}
ConvertCodeOfCollectionAssignmentNode(collectionAssignmentNode);
break;
case CollectionIndexNode collectionIndexNode: // 集合类型操作
void ConvertCodeOfCollectionIndexNode(CollectionIndexNode collectionIndexNode)
{
ConvertCode(collectionIndexNode.Collection);
Append("[");
ConvertCode(collectionIndexNode.Index);
Append("]");
}
ConvertCodeOfCollectionIndexNode(collectionIndexNode);
break;
case ClassTypeDefinitionNode classTypeDefinitionNode: // 类型定义
void ConvertCodeOfClassTypeDefinitionNode(ClassTypeDefinitionNode classTypeDefinitionNode)
{
_classDefinitions.Add((sb) =>
{
var type = _symbolInfos[classTypeDefinitionNode.ClassType];
AppendLine($"public class {type.FullName}");
AppendLine("{");
Indent();
foreach (var property in classTypeDefinitionNode.Propertys)
{
var propertyName = property.Key;
var propertyType = _symbolInfos[property.Value];
AppendLine($"public global::{propertyType.FullName} {propertyName} {{ get; set; }}");
}
Unindent();
AppendLine("}");
});
}
ConvertCodeOfClassTypeDefinitionNode(classTypeDefinitionNode);
break;
case TypeNode typeNode: // 类型
break;
case ObjectInstantiationNode objectInstantiationNode: // 类型实例化
void ConvertCodeOfObjectInstantiationNode(ObjectInstantiationNode objectInstantiationNode)
{
var type = _symbolInfos[objectInstantiationNode.Type];
Append($"new global::{type}");
if (objectInstantiationNode.Arguments.Count > 0)
{
Append("(");
for (int i = 0; i < objectInstantiationNode.Arguments.Count; i++)
{
ConvertCode(objectInstantiationNode.Arguments[i]);
if (i < objectInstantiationNode.Arguments.Count - 1)
{
Append(", ");
}
}
Append(")");
}
else
{
Append("()");
}
if (objectInstantiationNode.CtorAssignments.Count > 0)
{
AppendLine("{");
Indent();
foreach (var assignment in objectInstantiationNode.CtorAssignments)
{
ConvertCode(assignment);
}
Unindent();
AppendLine("}");
}
}
ConvertCodeOfObjectInstantiationNode(objectInstantiationNode);
break;
case CtorAssignmentNode ctorAssignmentNode: // 构造器赋值
void ConvertCodeOfCtorAssignmentNode(CtorAssignmentNode ctorAssignmentNode)
{
var propertyName = ctorAssignmentNode.MemberName;
var value = ctorAssignmentNode.Value;
Append($"{propertyName} = ");
ConvertCode(value);
AppendLine(",");
}
ConvertCodeOfCtorAssignmentNode(ctorAssignmentNode);
break;
case MemberAccessNode memberAccessNode: // 对象成员访问
void ConvertCodeOfMemberAccessNode(MemberAccessNode memberAccessNode)
{
ConvertCode(memberAccessNode.Object);
Append($".{memberAccessNode.MemberName}");
}
ConvertCodeOfMemberAccessNode(memberAccessNode);
break;
case MemberAssignmentNode memberAssignmentNode: // 对象成员赋值
void ConvertCodeOfMemberAssignmentNode(MemberAssignmentNode memberAssignmentNode)
{
ConvertCode(memberAssignmentNode.Object);
Append($".{memberAssignmentNode.MemberName} = ");
ConvertCode(memberAssignmentNode.Value);
Append($";");
AppendLine(string.Empty);
}
ConvertCodeOfMemberAssignmentNode(memberAssignmentNode);
break;
case MemberFunctionCallNode memberFunctionCallNode: // 对象方法调用
void ConvertCodeOfMemberFunctionCallNode(MemberFunctionCallNode memberFunctionCallNode)
{
var isAsync = _asyncMethods.TryGetValue(memberFunctionCallNode, out var isAsyncValue) && isAsyncValue;
if (isAsync)
{
Append($"(await ");
}
ConvertCode(memberFunctionCallNode.Object);
Append($".{memberFunctionCallNode.FunctionName}(");
for (int i = 0; i < memberFunctionCallNode.Arguments.Count; i++)
{
ASTNode? argNode = memberFunctionCallNode.Arguments[i];
ConvertCode(argNode);
if (i < memberFunctionCallNode.Arguments.Count - 1)
{
Append(", ");
}
}
Append($")");
if (isAsync)
{
Append($")");
}
}
ConvertCodeOfMemberFunctionCallNode(memberFunctionCallNode);
break;
case FunctionCallNode functionCallNode: // 外部挂载的函数调用
void ConvertCodeOfFunctionCallNode(FunctionCallNode functionCallNode)
{
var isAsync = _asyncMethods.TryGetValue(functionCallNode, out var isAsyncValue) && isAsyncValue;
if (isAsync)
{
Append($"(await ");
}
var funcName = functionCallNode.FunctionName switch
{
"int" => "@int" ,
"bool" => "@bool" ,
"double" => "@double" ,
"long" => "@long" ,
"decimal" => "@decimal" ,
"float" => "@float" ,
"byte" => "@byte" ,
_ => functionCallNode.FunctionName,
};
Append($"global::Serein.Library.ScriptBaseFunc.{funcName}(");
for (int i = 0; i < functionCallNode.Arguments.Count; i++)
{
ASTNode? argNode = functionCallNode.Arguments[i];
ConvertCode(argNode);
if (i < functionCallNode.Arguments.Count - 1)
{
Append(", ");
}
}
Append($")");
if (isAsync)
{
Append($")");
}
}
ConvertCodeOfFunctionCallNode(functionCallNode);
break;
default: // 未定义的节点类型
break;
}
}
}
}

View File

@@ -5,6 +5,7 @@ using Serein.Script.Node.FlowControl;
using Serein.Script.Symbol;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reactive;
using System.Reflection;
@@ -27,6 +28,17 @@ namespace Serein.Script
/// </summary>
public Dictionary<ASTNode, Type> NodeSymbolInfos { get; } = new Dictionary<ASTNode, Type>();
/// <summary>
/// 记录方法节点是否需要进行异步调用
/// </summary>
public Dictionary<ASTNode, bool> AsyncMethods { get; } = new Dictionary<ASTNode, bool>();
public void Reset()
{
NodeSymbolInfos.Clear(); // 清空符号表
AsyncMethods.Clear();
}
public void LoadSymbol(Dictionary<string,Type> identifierNodes)
{
foreach(var kvp in identifierNodes)
@@ -74,6 +86,7 @@ namespace Serein.Script
}
}
/// <summary>
/// 类型获取
@@ -293,7 +306,7 @@ namespace Serein.Script
return valueType;
}
return AnalysisCtorAssignmentNode(ctorAssignmentNode);
case ExpressionNode expressionNode: // 类型表达式(链式调用)
/*case ExpressionNode expressionNode: // 类型表达式(链式调用)
Type AnalysisObjectMemberExpressionNode(ExpressionNode expressionNode)
{
// 1. 对象成员获取 MemberAccessNode
@@ -304,7 +317,7 @@ namespace Serein.Script
NodeSymbolInfos[expressionNode] = resultType;
return resultType;
}
return AnalysisObjectMemberExpressionNode(expressionNode);
return AnalysisObjectMemberExpressionNode(expressionNode);*/
case MemberAccessNode memberAccessNode: // 对象成员访问
Type AnalysisMemberAccessNode(MemberAccessNode memberAccessNode)
{
@@ -365,9 +378,12 @@ namespace Serein.Script
Type argType = types[index];
NodeSymbolInfos[argNode] = argType;
}
var isAsync = IsGenericTask(methodInfo.ReturnType, out var taskResult);
var methodReturnType = isAsync ? taskResult : methodInfo.ReturnType;
AsyncMethods[memberFunctionCallNode] = isAsync;
NodeSymbolInfos[memberFunctionCallNode.Object] = objectType;
NodeSymbolInfos[memberFunctionCallNode] = methodInfo.ReturnType;
return methodInfo.ReturnType;
NodeSymbolInfos[memberFunctionCallNode] = methodReturnType;
return methodReturnType;
}
@@ -386,8 +402,11 @@ namespace Serein.Script
Type argType = types[index];
NodeSymbolInfos[argNode] = argType;
}
NodeSymbolInfos[functionCallNode] = methodInfo.ReturnType;
return methodInfo.ReturnType;
var isAsync = IsGenericTask(methodInfo.ReturnType, out var taskResult);
var methodReturnType = isAsync ? taskResult : methodInfo.ReturnType;
AsyncMethods[functionCallNode] = isAsync;
NodeSymbolInfos[functionCallNode] = methodReturnType;
return methodReturnType;
}
return AnalysisFunctionCallNode(functionCallNode);
default: // 未定义的节点类型
@@ -398,7 +417,6 @@ namespace Serein.Script
/// <summary>
/// 类型分析
/// </summary>
@@ -472,9 +490,9 @@ namespace Serein.Script
case ObjectInstantiationNode objectInstantiationNode: // 类型实例化
Analysis(objectInstantiationNode);
break;
case ExpressionNode expressionNode: // 类型表达式(链式调用)
/* case ExpressionNode expressionNode: // 类型表达式(链式调用)
Analysis(expressionNode.Value);
break;
break;*/
case MemberAccessNode memberAccessNode: // 对象成员访问
Analysis(memberAccessNode);
break;
@@ -497,7 +515,7 @@ namespace Serein.Script
}
private void Analysis2(ASTNode node)
private void ToILCompiler(ASTNode node)
{
switch (node)
{
@@ -541,9 +559,7 @@ namespace Serein.Script
break;
case ObjectInstantiationNode objectInstantiationNode: // 类型实例化
break;
case CtorAssignmentNode ctorAssignmentNode:
break;
case ExpressionNode expressionNode: // 类型表达式(链式调用)
case CtorAssignmentNode ctorAssignmentNode: // 构造器赋值
break;
case MemberAccessNode memberAccessNode: // 对象成员访问
break;
@@ -582,8 +598,28 @@ namespace Serein.Script
}
}
private static bool IsGenericTask(Type returnType, out Type taskResult)
{
// 判断是否为 Task 类型或泛型 Task<T>
if (returnType == typeof(Task))
{
taskResult = typeof(void);
return true;
}
else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
{
// 获取泛型参数类型
Type genericArgument = returnType.GetGenericArguments()[0];
taskResult = genericArgument;
return true;
}
else
{
taskResult = null;
return false;
}
}
/// <summary>
/// 获取某个集合类型支持的索引参数类型