using Serein.Library.Api;
using Serein.Script.Node;
using Serein.Script.Node.FlowControl;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
namespace Serein.Script
{
/*
暂未想到如何编译出具备 await / async 功能的IL代码,暂时放弃
*/
///
/// IL 代码生成结果
///
internal record ILResult
{
///
/// 返回类型
///
public Type? ReturnType { get; set; }
///
/// 临时变量,可用于缓存值,避免重复计算
///
public LocalBuilder? TempVar { get; set; } // 可用于缓存
///
/// 发射 IL 代码的委托,接受 ILGenerator 参数
///
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。
public Action Emit { get; set; } // 用于推入值的代码
#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。
}
///
/// 脚本编译IL代码
///
internal class SereinScriptILCompiler
{
///
/// 符号表
///
private readonly Dictionary symbolInfos;
///
/// 发射IL
///
//private ILGenerator _il;
///
/// 创建的对应方法
///
private DynamicMethod _method;
///
/// 临时变量绑定
///
private readonly Dictionary _locals = new();
///
/// 节点对应的委托缓存,避免重复编译同一节点
///
private readonly Dictionary _nodeCache = new();
///
/// IL映射
///
Dictionary _ilResults = new Dictionary();
private Label _methodExit;
public SereinScriptILCompiler(Dictionary symbolInfos)
{
this.symbolInfos = symbolInfos;
}
///
/// 是否调用了异步方法
///
//private bool _isUseAwait = false;
private Dictionary _parameterIndexes = new Dictionary();
public Delegate Compiler(string compilerMethodName, ProgramNode programNode, Dictionary? argTypes = null)
{
argTypes ??= new Dictionary();
var parameterTypes = argTypes.Values.ToArray();
var parameterNames = argTypes.Keys.ToArray();
// 构建参数索引映射,方便 EmitNode 访问参数
_parameterIndexes.Clear();
for (int i = 0; i < parameterNames.Length; i++)
{
_parameterIndexes[parameterNames[i]] = i;
}
_method = new DynamicMethod(
compilerMethodName,
typeof(object),
parameterTypes,
typeof(SereinScriptILCompiler).Module,
skipVisibility: true);
var il = _method.GetILGenerator();
_ilResults.Clear();
var resultType = symbolInfos[programNode];
_methodExit = il.DefineLabel();
foreach (var node in programNode.Statements)
{
EmitNode(il, node);
}
il.MarkLabel(_methodExit);
if (resultType == typeof(void))
{
il.Emit(OpCodes.Ldnull);
}
else if (resultType.IsValueType)
{
il.Emit(OpCodes.Box, resultType);
}
il.Emit(OpCodes.Ret);
Type delegateType;
if (parameterTypes.Length == 0)
{
delegateType = typeof(Func