miduo_client/Assets/ToLua/Injection/Editor/ToLuaInjection.cs

1293 lines
46 KiB
C#
Raw Normal View History

2020-05-09 12:51:21 +08:00
#if ENABLE_LUA_INJECTION
using System;
using System.IO;
using System.Xml;
using System.Text;
using System.Linq;
using UnityEngine;
using UnityEditor;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Unity.CecilTools;
using Unity.CecilTools.Extensions;
using CustomCecilRocks;
using System.Reflection;
using LuaInterface;
using UnityEditor.Callbacks;
using System.Collections.Generic;
using MethodBody = Mono.Cecil.Cil.MethodBody;
class InjectedMethodInfo
{
public string methodFullSignature;
public string methodOverloadSignature;
public string methodPublishedName;
public string methodName;
public int methodIndex;
}
[InitializeOnLoad]
public static class ToLuaInjection
{
static int offset = 0;
static int methodCounter = 0;
static bool EnableSymbols = true;
static Instruction cursor;
static VariableDefinition flagDef;
static VariableDefinition funcDef;
static TypeReference intTypeRef;
static TypeReference injectFlagTypeRef;
static TypeReference noToLuaAttrTypeRef;
static TypeDefinition injectStationTypeDef;
static TypeDefinition luaFunctionTypeDef;
static TypeDefinition luaTableTypeDef;
static MethodReference injectFlagGetter;
static MethodReference injectedFuncGetter;
static HashSet<string> dropTypeGroup = new HashSet<string>();
static HashSet<string> injectableTypeGroup = new HashSet<string>();
static Dictionary<MethodDefinition, VariableDefinition> resultTableGroup = new Dictionary<MethodDefinition, VariableDefinition>();
static SortedDictionary<string, List<InjectedMethodInfo>> bridgeInfo = new SortedDictionary<string, List<InjectedMethodInfo>>();
static OpCode[] ldargs = new OpCode[] { OpCodes.Ldarg_0, OpCodes.Ldarg_1, OpCodes.Ldarg_2, OpCodes.Ldarg_3 };
static OpCode[] ldcI4s = new OpCode[] { OpCodes.Ldc_I4_1, OpCodes.Ldc_I4_2, OpCodes.Ldc_I4_4, OpCodes.Ldc_I4_8 };
const string assemblyPath = "./Library/ScriptAssemblies/Assembly-CSharp.dll";
const InjectType injectType = InjectType.After | InjectType.Before | InjectType.Replace | InjectType.ReplaceWithPreInvokeBase | InjectType.ReplaceWithPostInvokeBase;
const InjectFilter injectIgnoring = InjectFilter.IgnoreGeneric | InjectFilter.IgnoreConstructor;// | InjectFilter.IgnoreNoToLuaAttr | InjectFilter.IgnoreProperty;
static HashSet<string> dropGenericNameGroup = new HashSet<string>
{
};
static HashSet<string> dropNamespaceGroup = new HashSet<string>
{
"LuaInterface",
};
static HashSet<string> forceInjectTypeGroup = new HashSet<string>
{
};
static ToLuaInjection()
{
LoadAndCheckAssembly(true);
InjectAll();
}
static void InjectAll()
{
var injectionStatus = EditorPrefs.GetInt(Application.dataPath + "WaitForInjection", 0);
if (Application.isPlaying || EditorApplication.isCompiling || injectionStatus == 0)
{
return;
}
bool bInjectInterupted = !LoadBlackList() || ToLuaMenu.UpdateMonoCecil(ref EnableSymbols) != 0 || !LoadBridgeEditorInfo();
if (!bInjectInterupted)
{
CacheInjectableTypeGroup();
Inject();
AssetDatabase.Refresh();
}
}
[PostProcessBuildAttribute()]
static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
{
var injectionStatus = EditorPrefs.GetInt(Application.dataPath + "WaitForInjection", 0);
if (injectionStatus == 0)
{
Debug.LogError("Inject Failed!!!");
}
EditorPrefs.SetInt(Application.dataPath + "WaitForInjection", 0);
}
[PostProcessScene]
static void OnPostProcessScene()
{
if (BuildPipeline.isBuildingPlayer)
{
EditorPrefs.SetInt(Application.dataPath + "WaitForInjection", 1);
}
InjectAll();
}
[DidReloadScripts]
static void OnDidReloadScripts()
{
if (!BuildPipeline.isBuildingPlayer)
{
EditorPrefs.SetInt(Application.dataPath + "WaitForInjection", 0);
}
}
[MenuItem("Lua/Inject All &i", false, 5)]
static void InjectByMenu()
{
if (Application.isPlaying)
{
EditorUtility.DisplayDialog("警告", "游戏运行过程中无法操作", "确定");
return;
}
EditorPrefs.SetInt(Application.dataPath + "WaitForInjection", 1);
if (EditorApplication.isCompiling)
{
EditorUtility.DisplayDialog("警告", "请等待编辑器编译完成", "确定");
return;
}
InjectAll();
}
static AssemblyDefinition LoadAndCheckAssembly(bool bPulse)
{
var assemblyReader = new ReaderParameters
{
ReadSymbols = EnableSymbols,
AssemblyResolver = GetAssemblyResolver()
};
AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyPath, assemblyReader);
var alreadyInjected = assembly.CustomAttributes.Any((attr) =>
{
return attr.AttributeType.FullName == "LuaInterface.UseDefinedAttribute";
});
EditorPrefs.SetInt(Application.dataPath + "InjectStatus", alreadyInjected ? 1 : 0);
if (bPulse)
{
Clean(assembly);
}
return assembly;
}
static void Inject()
{
AssemblyDefinition assembly = null;
try
{
assembly = LoadAndCheckAssembly(false);
if (InjectPrepare(assembly))
{
foreach (var module in assembly.Modules)
{
int cursor = 0;
int typesCount = module.Types.Count;
foreach (var type in module.Types)
{
++cursor;
EditorUtility.DisplayProgressBar("Injecting:" + module.FullyQualifiedName, type.FullName, (float)cursor / typesCount);
if (!InjectProcess(assembly, type))
{
EditorUtility.ClearProgressBar();
return;
}
}
}
EditorUtility.ClearProgressBar();
UpdateInjectionCacheSize();
ExportInjectionBridgeInfo();
WriteInjectedAssembly(assembly, assemblyPath);
resultTableGroup.Clear();
EditorApplication.Beep();
Debug.Log("Lua Injection Finished!");
EditorPrefs.SetInt(Application.dataPath + "InjectStatus", 1);
}
}
catch (Exception e)
{
Debug.LogError(e.ToString());
}
finally
{
if (assembly != null)
{
Clean(assembly);
}
}
}
static bool InjectPrepare(AssemblyDefinition assembly)
{
bool alreadyInjected = EditorPrefs.GetInt(Application.dataPath + "InjectStatus") == 1;
if (alreadyInjected)
{
Debug.Log("Already Injected!");
return false;
}
resultTableGroup.Clear();
var injectAttrType = assembly.MainModule.Types.Single(type => type.FullName == "LuaInterface.UseDefinedAttribute");
var attrCtorInfo = injectAttrType.Methods.Single(method => method.IsConstructor);
assembly.CustomAttributes.Add(new CustomAttribute(attrCtorInfo));
intTypeRef = assembly.MainModule.TypeSystem.Int32;
injectFlagTypeRef = assembly.MainModule.TypeSystem.Byte;
noToLuaAttrTypeRef = assembly.MainModule.Types.Single(type => type.FullName == "LuaInterface.NoToLuaAttribute");
injectStationTypeDef = assembly.MainModule.Types.Single(type => type.FullName == "LuaInterface.LuaInjectionStation");
luaFunctionTypeDef = assembly.MainModule.Types.Single(method => method.FullName == "LuaInterface.LuaFunction");
luaTableTypeDef = assembly.MainModule.Types.Single(method => method.FullName == "LuaInterface.LuaTable");
injectFlagGetter = injectStationTypeDef.Methods.Single(method => method.Name == "GetInjectFlag");
injectedFuncGetter = injectStationTypeDef.Methods.Single(method => method.Name == "GetInjectionFunction");
return true;
}
static BaseAssemblyResolver GetAssemblyResolver()
{
DefaultAssemblyResolver resolver = new DefaultAssemblyResolver();
AppDomain.CurrentDomain
.GetAssemblies()
.Select(assem => Path.GetDirectoryName(assem.ManifestModule.FullyQualifiedName))
.Distinct()
.Foreach(dir => resolver.AddSearchDirectory(dir));
return resolver;
}
static bool InjectProcess(AssemblyDefinition assembly, TypeDefinition type)
{
if (!DoesTypeInjectable(type))
{
return true;
}
foreach (var nestedType in type.NestedTypes)
{
if (!InjectProcess(assembly, nestedType))
{
return false;
}
}
foreach (var target in type.Methods)
{
if (target.IsGenericMethodDefinition())
{
continue;
}
if (!DoesMethodInjectable(target))
{
continue;
}
int methodIndex = AppendMethod(target);
if (methodIndex == -1)
{
return false;
}
if (target.IsEnumerator())
{
InjectCoroutine(assembly, target, methodIndex);
}
else
{
InjectMethod(assembly, target, methodIndex);
}
}
return true;
}
static void FillBegin(MethodDefinition target, int methodIndex)
{
MethodBody targetBody = target.Body;
ILProcessor il = targetBody.GetILProcessor();
targetBody.InitLocals = true;
flagDef = new VariableDefinition(injectFlagTypeRef);
funcDef = new VariableDefinition(luaFunctionTypeDef);
targetBody.Variables.Add(flagDef);
targetBody.Variables.Add(funcDef);
Instruction startInsertPos = targetBody.Instructions[0];
il.InsertBefore(startInsertPos, il.Create(OpCodes.Ldc_I4, methodIndex));
il.InsertBefore(startInsertPos, il.Create(OpCodes.Call, injectFlagGetter));
il.InsertBefore(startInsertPos, il.Create(OpCodes.Stloc, flagDef));
il.InsertBefore(startInsertPos, il.Create(OpCodes.Ldloc, flagDef));
il.InsertBefore(startInsertPos, il.Create(OpCodes.Brfalse, startInsertPos));
il.InsertBefore(startInsertPos, il.Create(OpCodes.Ldc_I4, methodIndex));
il.InsertBefore(startInsertPos, il.Create(OpCodes.Call, injectedFuncGetter));
il.InsertBefore(startInsertPos, il.Create(OpCodes.Stloc, funcDef));
offset = targetBody.Instructions.IndexOf(startInsertPos);
}
#region GenericMethod
static void InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition target, int methodIndex)
{
}
#endregion GenericMethod
#region Coroutine
static void InjectCoroutine(AssemblyDefinition assembly, MethodDefinition target, int methodIndex)
{
InjectType runtimeInjectType = GetMethodRuntimeInjectType(target);
if (runtimeInjectType == InjectType.None)
{
return;
}
target.Body.SimplifyMacros();
FillBegin(target, methodIndex);
FillReplaceCoroutine(target, runtimeInjectType & InjectType.Replace);
FillCoroutineMonitor(target, runtimeInjectType & (~InjectType.Replace), methodIndex);
target.Body.OptimizeMacros();
}
static void FillReplaceCoroutine(MethodDefinition target, InjectType runtimeInjectType)
{
if (runtimeInjectType == InjectType.None)
{
return;
}
MethodBody targetBody = target.Body;
ILProcessor il = targetBody.GetILProcessor();
cursor = GetMethodNextInsertPosition(target, null, false);
if (cursor != null)
{
il.InsertBefore(cursor, il.Create(OpCodes.Ldloc, flagDef));
il.InsertBefore(cursor, il.Create(ldcI4s[(int)InjectType.Replace / 2]));
il.InsertBefore(cursor, il.Create(OpCodes.Bne_Un, cursor));
il.InsertBefore(cursor, il.Create(OpCodes.Ldloc, funcDef));
FillArgs(target, cursor, null);
il.InsertBefore(cursor, il.Create(OpCodes.Call, GetLuaMethodInvoker(target, false, false)));
il.InsertBefore(cursor, il.Create(OpCodes.Ret));
}
}
static void FillCoroutineMonitor(MethodDefinition target, InjectType runtimeInjectType, int methodIndex)
{
if (runtimeInjectType == InjectType.None)
{
return;
}
MethodBody targetBody = target.Body;
FieldDefinition hostField = null;
var coroutineEntity = targetBody.Variables[0].VariableType.Resolve();
if (!target.DeclaringType.NestedTypes.Any(type => coroutineEntity == type))
{
return;
}
cursor = GetMethodNextInsertPosition(target, cursor, true);
CopyCoroutineCreatorReference(target, coroutineEntity, ref hostField);
var coroutineCarrier = coroutineEntity.Methods.Single(method => method.Name == "MoveNext");
CopyCreatorArgsToCarrier(target, coroutineCarrier);
FillBegin(coroutineCarrier, methodIndex);
var fillInjectInfoFunc = GetCoroutineInjectInfoFiller(target, hostField);
FillInjectMethod(coroutineCarrier, fillInjectInfoFunc, runtimeInjectType & InjectType.After);
FillInjectMethod(coroutineCarrier, fillInjectInfoFunc, runtimeInjectType & InjectType.Before);
}
static Action<MethodDefinition, InjectType> GetCoroutineInjectInfoFiller(MethodDefinition coroutineCreator, FieldDefinition hostRef)
{
return (coroutineCarrier, runtimeInjectType) =>
{
MethodBody targetBody = coroutineCarrier.Body;
ILProcessor il = targetBody.GetILProcessor();
il.InsertBefore(cursor, il.Create(OpCodes.Ldloc, funcDef));
if (coroutineCreator.HasThis)
{
il.InsertBefore(cursor, il.Create(OpCodes.Ldarg_0));
il.InsertBefore(cursor, il.Create(OpCodes.Ldfld, hostRef));
}
CopyCarrierFieldsToArg(coroutineCreator, coroutineCarrier);
FillCoroutineState(coroutineCarrier);
il.InsertBefore(cursor, il.Create(OpCodes.Call, GetLuaMethodInvoker(coroutineCreator, true, true)));
};
}
static void CopyCoroutineCreatorReference(MethodDefinition coroutineCreator, TypeDefinition coroutineCarrier, ref FieldDefinition hostField)
{
if (coroutineCreator.HasThis)
{
ILProcessor il = coroutineCreator.Body.GetILProcessor();
hostField = new FieldDefinition("__iHost", Mono.Cecil.FieldAttributes.Public, coroutineCreator.DeclaringType);
coroutineCarrier.Fields.Add(hostField);
il.InsertBefore(cursor, il.Create(OpCodes.Ldloc_0));
il.InsertBefore(cursor, il.Create(OpCodes.Ldarg_0));
il.InsertBefore(cursor, il.Create(OpCodes.Stfld, hostField));
}
}
static void CopyCreatorArgsToCarrier(MethodDefinition coroutineCreator, MethodDefinition coroutineCarrier)
{
ILProcessor il = coroutineCreator.Body.GetILProcessor();
var carrierFields = coroutineCarrier.DeclaringType.Fields;
coroutineCreator
.Parameters
.Foreach(param =>
{
var name = "<$>" + param.Name;
if (!carrierFields.Any(field => field.Name == name))
{
var hostArg = new FieldDefinition(name, Mono.Cecil.FieldAttributes.Public, param.ParameterType);
carrierFields.Add(hostArg);
il.InsertBefore(cursor, il.Create(OpCodes.Ldloc_0));
il.InsertBefore(cursor, il.Create(OpCodes.Ldarg, param));
il.InsertBefore(cursor, il.Create(OpCodes.Stfld, hostArg));
}
});
}
static void CopyCarrierFieldsToArg(MethodDefinition coroutineCreator, MethodDefinition coroutineCarrier)
{
ILProcessor il = coroutineCarrier.Body.GetILProcessor();
var carrierFields = coroutineCarrier.DeclaringType.Fields;
coroutineCreator
.Parameters
.Select(param => "<$>" + param.Name)
.Foreach(name =>
{
var arg = carrierFields.Single(field => field.Name == name);
il.InsertBefore(cursor, il.Create(OpCodes.Ldarg_0));
il.InsertBefore(cursor, il.Create(OpCodes.Ldfld, arg));
});
}
static void FillCoroutineState(MethodDefinition coroutineCarrier)
{
MethodBody targetBody = coroutineCarrier.Body;
ILProcessor il = targetBody.GetILProcessor();
il.InsertBefore(cursor, il.Create(OpCodes.Ldarg_0));
var stateField = coroutineCarrier.DeclaringType.Fields.Single(field => field.Name == "$PC");
il.InsertBefore(cursor, il.Create(OpCodes.Ldfld, stateField));
}
#endregion Coroutine
#region NormalMethod
static void InjectMethod(AssemblyDefinition assembly, MethodDefinition target, int methodIndex)
{
target.Body.SimplifyMacros();
FillBegin(target, methodIndex);
InjectType runtimeInjectType = GetMethodRuntimeInjectType(target);
FillInjectMethod(target, FillInjectInfo, runtimeInjectType & InjectType.After);
FillInjectMethod(target, FillInjectInfo, runtimeInjectType & (~InjectType.After));
target.Body.OptimizeMacros();
}
static void FillInjectMethod(MethodDefinition target, Action<MethodDefinition, InjectType> fillInjectInfo, InjectType runtimeInjectType)
{
if (runtimeInjectType == InjectType.None)
{
return;
}
MethodBody targetBody = target.Body;
ILProcessor il = targetBody.GetILProcessor();
cursor = GetMethodNextInsertPosition(target, null, runtimeInjectType.HasFlag(InjectType.After));
while (cursor != null)
{
bool bAfterInject = runtimeInjectType == InjectType.After;
Instruction startPos = il.Create(OpCodes.Ldloc, flagDef);
if (bAfterInject)
{
/// Replace instruction with references reserved
Instruction endPos = il.Create(OpCodes.Ret);
int replaceIndex = targetBody.Instructions.IndexOf(cursor);
cursor.OpCode = startPos.OpCode;
cursor.Operand = startPos.Operand;
il.InsertAfter(targetBody.Instructions[replaceIndex], endPos);
cursor = targetBody.Instructions[replaceIndex + 1];
}
else il.InsertBefore(cursor, startPos);
il.InsertBefore(cursor, il.Create(ldcI4s[(int)InjectType.After / 2]));
il.InsertBefore(cursor, il.Create(bAfterInject ? OpCodes.Bne_Un : OpCodes.Ble_Un, cursor));
fillInjectInfo(target, runtimeInjectType);
cursor = GetMethodNextInsertPosition(target, cursor, runtimeInjectType.HasFlag(InjectType.After));
}
}
static void FillInjectInfo(MethodDefinition target, InjectType runtimeInjectType)
{
FillBaseCall(target, runtimeInjectType, true);
FillLuaMethodCall(target, runtimeInjectType == InjectType.After);
FillBaseCall(target, runtimeInjectType, false);
FillJumpInfo(target, runtimeInjectType == InjectType.After);
}
static void FillBaseCall(MethodDefinition target, InjectType runtimeInjectType, bool preCall)
{
MethodBody targetBody = target.Body;
ILProcessor il = targetBody.GetILProcessor();
InjectType curBaseInjectType = preCall ? InjectType.ReplaceWithPreInvokeBase : InjectType.ReplaceWithPostInvokeBase;
if (runtimeInjectType.HasFlag(curBaseInjectType))
{
Instruction end = il.Create(OpCodes.Nop);
il.InsertBefore(cursor, end);
il.InsertBefore(end, il.Create(OpCodes.Ldloc, flagDef));
il.InsertBefore(end, il.Create(OpCodes.Ldc_I4, (int)curBaseInjectType));
il.InsertBefore(end, il.Create(OpCodes.Bne_Un, end));
FillArgs(target, end, PostProcessBaseMethodArg);
il.InsertBefore(end, il.Create(OpCodes.Call, target.GetBaseMethodInstance()));
if (!target.ReturnVoid())
{
il.InsertBefore(end, il.Create(OpCodes.Pop));
}
}
}
static void FillLuaMethodCall(MethodDefinition target, bool bConfirmPopReturnValue)
{
ILProcessor il = target.Body.GetILProcessor();
Instruction start = il.Create(OpCodes.Ldloc, funcDef);
if (cursor.Previous.OpCode == OpCodes.Nop)
{
cursor.Previous.OpCode = start.OpCode;
cursor.Previous.Operand = start.Operand;
}
else
{
il.InsertBefore(cursor, start);
}
FillArgs(target, cursor, ParseArgumentReference);
il.InsertBefore(cursor, il.Create(OpCodes.Call, GetLuaMethodInvoker(target, bConfirmPopReturnValue, false)));
CacheResultTable(target, bConfirmPopReturnValue);
UpdatePassedByReferenceParams(target, bConfirmPopReturnValue);
}
static void CacheResultTable(MethodDefinition target, bool bConfirmPopReturnValue)
{
ILProcessor il = target.Body.GetILProcessor();
if (target.GotPassedByReferenceParam())
{
il.InsertBefore(cursor, il.Create(OpCodes.Stloc, GetResultTable(target)));
}
}
static VariableDefinition GetResultTable(MethodDefinition target)
{
VariableDefinition luaTable = null;
resultTableGroup.TryGetValue(target, out luaTable);
if (luaTable == null)
{
luaTable = new VariableDefinition(luaTableTypeDef);
target.Body.Variables.Add(luaTable);
resultTableGroup.Add(target, luaTable);
}
return luaTable;
}
static void UpdatePassedByReferenceParams(MethodDefinition target, bool bConfirmPopReturnValue)
{
if (!target.GotPassedByReferenceParam())
{
return;
}
int updateCount = 0;
ILProcessor il = target.Body.GetILProcessor();
VariableDefinition luaTable = GetResultTable(target);
var rawGetGenericMethod = luaTableTypeDef.Methods.Single(method => method.Name == "RawGetIndex");
foreach (var param in target.Parameters)
{
if (!param.ParameterType.IsByReference)
{
continue;
}
var paramType = ElementType.For(param.ParameterType);
il.InsertBefore(cursor, il.Create(OpCodes.Ldarg, param));
il.InsertBefore(cursor, il.Create(OpCodes.Ldloc, luaTable));
il.InsertBefore(cursor, il.Create(OpCodes.Ldc_I4, ++updateCount));
il.InsertBefore(cursor, il.Create(OpCodes.Call, rawGetGenericMethod.MakeGenericMethod(paramType)));
if (paramType.IsValueType)
{
il.InsertBefore(cursor, il.Create(OpCodes.Stobj, paramType));
}
else
{
il.InsertBefore(cursor, il.Create(OpCodes.Stind_Ref));
}
}
if (!bConfirmPopReturnValue && !target.ReturnVoid())
{
il.InsertBefore(cursor, il.Create(OpCodes.Ldloc, luaTable));
il.InsertBefore(cursor, il.Create(OpCodes.Ldc_I4, ++updateCount));
il.InsertBefore(cursor, il.Create(OpCodes.Call, rawGetGenericMethod.MakeGenericMethod(target.ReturnType)));
}
}
static void FillJumpInfo(MethodDefinition target, bool bConfirmPopReturnValue)
{
MethodBody targetBody = target.Body;
ILProcessor il = targetBody.GetILProcessor();
if (!bConfirmPopReturnValue)
{
Instruction retIns = il.Create(OpCodes.Ret);
if (!injectType.HasFlag(InjectType.Before))
{
if (cursor.Previous.OpCode == OpCodes.Nop)
{
cursor.Previous.OpCode = retIns.OpCode;
cursor.Previous.Operand = retIns.Operand;
retIns = cursor.Previous;
}
else
{
il.InsertBefore(cursor, retIns);
}
}
else
{
Instruction start = il.Create(OpCodes.Ldloc, flagDef);
if (cursor.Previous.OpCode == OpCodes.Nop)
{
cursor.Previous.OpCode = start.OpCode;
cursor.Previous.Operand = start.Operand;
il.InsertAfter(cursor.Previous, retIns);
}
else
{
il.InsertBefore(cursor, retIns);
il.InsertBefore(retIns, start);
}
Instruction popIns = il.Create(OpCodes.Pop);
bool bGotReturnValue = !target.ReturnVoid();
if (bGotReturnValue)
{
il.InsertBefore(cursor, popIns);
}
il.InsertBefore(retIns, il.Create(ldcI4s[(int)InjectType.Before / 2]));
il.InsertBefore(retIns, il.Create(OpCodes.Ble_Un, bGotReturnValue ? popIns : cursor));
}
}
else if (cursor.Previous.OpCode == OpCodes.Nop)
{
targetBody.Instructions.Remove(cursor.Previous);
}
}
#endregion NormalMethod
static void FillArgs(MethodDefinition target, Instruction endPoint, Action<MethodDefinition, Instruction, int> parseReferenceProcess)
{
MethodBody targetBody = target.Body;
ILProcessor il = targetBody.GetILProcessor();
int paramCount = target.Parameters.Count + (target.HasThis ? 1 : 0);
for (int i = 0; i < paramCount; ++i)
{
if (i < ldargs.Length)
{
il.InsertBefore(endPoint, il.Create(ldargs[i]));
}
else if (i <= byte.MaxValue)
{
il.InsertBefore(endPoint, il.Create(OpCodes.Ldarg_S, (byte)i));
}
else
{
il.InsertBefore(endPoint, il.Create(OpCodes.Ldarg, (short)i));
}
if (parseReferenceProcess != null)
{
parseReferenceProcess(target, endPoint, i);
}
}
}
static void PostProcessBaseMethodArg(MethodDefinition target, Instruction endPoint, int paramIndex)
{
var declaringType = target.DeclaringType;
ILProcessor il = target.Body.GetILProcessor();
if (paramIndex == 0 && declaringType.IsValueType)
{
il.InsertBefore(endPoint, il.Create(OpCodes.Ldobj, declaringType));
il.InsertBefore(endPoint, il.Create(OpCodes.Box, declaringType));
}
}
static void ParseArgumentReference(MethodDefinition target, Instruction endPoint, int paramIndex)
{
ParameterDefinition param = null;
ILProcessor il = target.Body.GetILProcessor();
if (target.HasThis)
{
if (paramIndex > 0)
{
param = target.Parameters[paramIndex - 1];
}
else if (target.DeclaringType.IsValueType)
{
il.InsertBefore(endPoint, il.Create(OpCodes.Ldobj, target.DeclaringType));
}
}
else if (!target.HasThis)
{
param = target.Parameters[paramIndex];
}
if (param != null && param.ParameterType.IsByReference)
{
TypeReference paramType = ElementType.For(param.ParameterType);
if (paramType.IsValueType)
{
il.InsertBefore(endPoint, il.Create(OpCodes.Ldobj, paramType));
}
else
{
il.InsertBefore(endPoint, il.Create(OpCodes.Ldind_Ref));
}
}
}
static Instruction GetMethodNextInsertPosition(MethodDefinition target, Instruction curPoint, bool bInsertBeforeRet)
{
MethodBody targetBody = target.Body;
if (target.IsConstructor || bInsertBeforeRet)
{
if (curPoint != null)
{
return targetBody.Instructions
.SkipWhile(ins => ins != curPoint)
.FirstOrDefault(ins => ins != curPoint && ins.OpCode == OpCodes.Ret);
}
else
{
return targetBody.Instructions
.FirstOrDefault(ins => ins.OpCode == OpCodes.Ret);
}
}
else
{
if (curPoint != null) return null;
else return targetBody.Instructions[offset];
}
}
static InjectType GetMethodRuntimeInjectType(MethodDefinition target)
{
InjectType type = injectType;
//bool bOverrideParantMethodFlag = target.IsVirtual && target.IsReuseSlot;
var parantMethod = target.GetBaseMethodInstance();
if (target.IsConstructor)
{
type &= ~InjectType.Before;
type &= ~InjectType.Replace;
type &= ~InjectType.ReplaceWithPostInvokeBase;
type &= ~InjectType.ReplaceWithPreInvokeBase;
}
else if (parantMethod == null || target.IsEnumerator())
{
type &= ~InjectType.ReplaceWithPostInvokeBase;
type &= ~InjectType.ReplaceWithPreInvokeBase;
}
else if (!target.HasBody)
{
type &= ~InjectType.After;
type &= ~InjectType.Before;
}
return type;
}
static MethodReference GetLuaMethodInvoker(MethodDefinition prototypeMethod, bool bIgnoreReturnValue, bool bAppendCoroutineState)
{
MethodReference injectMethod = null;
GetLuaInvoker(prototypeMethod, bIgnoreReturnValue, bAppendCoroutineState, ref injectMethod);
FillLuaInvokerGenericArguments(prototypeMethod, bIgnoreReturnValue, bAppendCoroutineState, ref injectMethod);
return injectMethod;
}
static void GetLuaInvoker(MethodDefinition prototypeMethod, bool bIgnoreReturnValue, bool bAppendCoroutineState, ref MethodReference invoker)
{
bool bRequireResult = prototypeMethod.GotPassedByReferenceParam()
|| (!bIgnoreReturnValue && !prototypeMethod.ReturnVoid());
string methodName = bRequireResult ? "Invoke" : "Call";
int paramCount = prototypeMethod.Parameters.Count;
int paramExtraCount = prototypeMethod.HasThis ? 1 : 0;
paramExtraCount = bAppendCoroutineState ? paramExtraCount + 1 : paramExtraCount;
paramCount += paramExtraCount;
invoker = luaFunctionTypeDef.Methods.FirstOrDefault(method =>
{
return method.Name == methodName && method.Parameters.Count == paramCount && bRequireResult == !method.ReturnVoid();
});
if (invoker == null)
{
Debug.Log(prototypeMethod.FullName + " Got too many parameters!!!Skipped!!!");
}
}
static void FillLuaInvokerGenericArguments(MethodDefinition prototypeMethod, bool bIgnoreReturnValue, bool bAppendCoroutineState, ref MethodReference invoker)
{
if (invoker.HasGenericParameters)
{
GenericInstanceMethod genericInjectMethod = new GenericInstanceMethod(invoker.CloneMethod());
if (prototypeMethod.HasThis)
{
genericInjectMethod.GenericArguments.Add(prototypeMethod.DeclaringType);
}
foreach (ParameterDefinition parameter in prototypeMethod.Parameters)
{
var paramType = parameter.ParameterType.IsByReference ? ElementType.For(parameter.ParameterType) : parameter.ParameterType;
genericInjectMethod.GenericArguments.Add(paramType);
}
if (bAppendCoroutineState)
{
genericInjectMethod.GenericArguments.Add(intTypeRef);
}
if (prototypeMethod.GotPassedByReferenceParam())
{
genericInjectMethod.GenericArguments.Add(luaTableTypeDef);
}
else if (!bIgnoreReturnValue && !prototypeMethod.ReturnVoid())
{
genericInjectMethod.GenericArguments.Add(prototypeMethod.ReturnType);
}
invoker = genericInjectMethod;
}
}
static void UpdateInjectionCacheSize()
{
var staticConstructor = injectStationTypeDef.Methods.Single((method) =>
{
return method.Name == ".cctor";
});
var il = staticConstructor.Body.GetILProcessor();
Instruction loadStaticFieldIns = null;
loadStaticFieldIns = staticConstructor
.Body
.Instructions
.FirstOrDefault(ins =>
{
return ins.OpCode == OpCodes.Ldsfld
&& (ins.Operand as FieldReference).Name == "cacheSize";
});
var loadCacheSizeIns = il.Create(OpCodes.Ldc_I4, methodCounter + 1);
il.InsertBefore(loadStaticFieldIns, loadCacheSizeIns);
il.InsertBefore(loadStaticFieldIns, il.Create(OpCodes.Stsfld, (loadStaticFieldIns.Operand as FieldReference)));
}
static void WriteInjectedAssembly(AssemblyDefinition assembly, string assemblyPath)
{
var writerParameters = new WriterParameters { WriteSymbols = EnableSymbols };
assembly.Write(assemblyPath, writerParameters);
}
static void ExportInjectionBridgeInfo()
{
ExportInjectionPublishInfo(bridgeInfo);
ExportInjectionEditorInfo(bridgeInfo);
}
static void ExportInjectionPublishInfo(SortedDictionary<string, List<InjectedMethodInfo>> data)
{
var temp = data.ToDictionary(
typeInfo => typeInfo.Key,
typeinfo =>
{
return typeinfo.Value
.OrderBy(methodInfo => methodInfo.methodPublishedName)
.ToDictionary(
methodInfo => methodInfo.methodPublishedName,
methodInfo => methodInfo.methodIndex
);
}
);
StringBuilder sb = StringBuilderCache.Acquire();
sb.Append("return ");
ToLuaText.TransferDic(temp, sb);
sb.Remove(sb.Length - 1, 1);
File.WriteAllText(CustomSettings.baseLuaDir + "System/Injection/InjectionBridgeInfo.lua", StringBuilderCache.GetStringAndRelease(sb));
}
static int AppendMethod(MethodDefinition method)
{
string methodSignature = GetMethodSignature(method);
string methodFullSignature = method.FullName;
InjectedMethodInfo newInfo = new InjectedMethodInfo();
string typeName = ToLuaInjectionHelper.GetTypeName(method.DeclaringType, true);
List<InjectedMethodInfo> typeMethodIndexGroup = null;
bridgeInfo.TryGetValue(typeName, out typeMethodIndexGroup);
if (typeMethodIndexGroup == null)
{
typeMethodIndexGroup = new List<InjectedMethodInfo>();
newInfo.methodPublishedName = method.Name;
bridgeInfo.Add(typeName, typeMethodIndexGroup);
}
else
{
InjectedMethodInfo existInfo = typeMethodIndexGroup.Find(info => info.methodOverloadSignature == methodSignature);
if (existInfo == null)
{
existInfo = typeMethodIndexGroup.Find(info => info.methodName == method.Name);
if (existInfo != null)
{
newInfo.methodPublishedName = methodSignature;
existInfo.methodPublishedName = existInfo.methodOverloadSignature;
}
else
{
newInfo.methodPublishedName = method.Name;
}
}
else
{
if (existInfo.methodFullSignature != methodFullSignature)
{
Debug.LogError(typeName + "." + existInfo.methodPublishedName + " 签名跟历史签名不一致无法增量Injection中断请修改函数签名、或者直接删掉InjectionBridgeEditorInfo.xml该操作会导致无法兼容线上版的包体需要强制换包");
EditorPrefs.SetInt(Application.dataPath + "WaitForInjection", 0);
return -1;
}
return existInfo.methodIndex;
}
}
newInfo.methodName = method.Name;
newInfo.methodOverloadSignature = methodSignature;
newInfo.methodFullSignature = methodFullSignature;
newInfo.methodIndex = ++methodCounter;
typeMethodIndexGroup.Add(newInfo);
return methodCounter;
}
static string GetMethodSignature(MethodDefinition method)
{
StringBuilder paramsTypeNameBuilder = StringBuilderCache.Acquire();
paramsTypeNameBuilder.Append(method.Name);
foreach (var param in method.Parameters)
{
paramsTypeNameBuilder
.Append("-")
.Append(ToLuaInjectionHelper.GetTypeName(param.ParameterType));
}
return StringBuilderCache.GetStringAndRelease(paramsTypeNameBuilder);
}
static void ExportInjectionEditorInfo(SortedDictionary<string, List<InjectedMethodInfo>> data)
{
string incrementalFilePath = CustomSettings.injectionFilesPath + "InjectionBridgeEditorInfo.xml";
if (File.Exists(incrementalFilePath))
{
File.Delete(incrementalFilePath);
}
var doc = new XmlDocument();
var fileInforRoot = doc.CreateElement("Root");
doc.AppendChild(fileInforRoot);
foreach (var type in data)
{
XmlElement typeNode = doc.CreateElement("Type");
typeNode.SetAttribute("Name", type.Key);
var sortedMethodsGroup = type.Value.OrderBy(info => info.methodPublishedName);
foreach (var method in sortedMethodsGroup)
{
XmlElement typeMethodNode = doc.CreateElement("Method");
typeMethodNode.SetAttribute("Name", method.methodName);
typeMethodNode.SetAttribute("PublishedName", method.methodPublishedName);
typeMethodNode.SetAttribute("Signature", method.methodOverloadSignature);
typeMethodNode.SetAttribute("FullSignature", method.methodFullSignature);
typeMethodNode.SetAttribute("Index", method.methodIndex.ToString());
typeNode.AppendChild(typeMethodNode);
}
fileInforRoot.AppendChild(typeNode);
}
doc.Save(incrementalFilePath);
}
static bool LoadBridgeEditorInfo()
{
bridgeInfo.Clear();
methodCounter = 0;
string incrementalFilePath = CustomSettings.injectionFilesPath + "InjectionBridgeEditorInfo.xml";
if (!File.Exists(incrementalFilePath))
{
return true;
}
var doc = new XmlDocument();
doc.Load(incrementalFilePath);
var fileInfoRoot = doc.FindChildByName("Root");
if (fileInfoRoot == null)
{
return true;
}
foreach (XmlNode typeChild in fileInfoRoot.ChildNodes)
{
List<InjectedMethodInfo> typeMethodInfo = new List<InjectedMethodInfo>();
string typeName = typeChild.FindAttributeByName("Name").Value;
foreach (XmlNode methodChild in typeChild.ChildNodes)
{
InjectedMethodInfo info = new InjectedMethodInfo();
info.methodName = methodChild.FindAttributeByName("Name").Value;
info.methodPublishedName = methodChild.FindAttributeByName("PublishedName").Value;
info.methodOverloadSignature = methodChild.FindAttributeByName("Signature").Value;
info.methodFullSignature = methodChild.FindAttributeByName("FullSignature").Value;
info.methodIndex = int.Parse(methodChild.FindAttributeByName("Index").Value);
typeMethodInfo.Add(info);
methodCounter = Math.Max(methodCounter, info.methodIndex);
}
bridgeInfo.Add(typeName, typeMethodInfo);
}
return true;
}
static void Clean(AssemblyDefinition assembly)
{
if (assembly.MainModule.SymbolReader != null)
{
assembly.MainModule.SymbolReader.Dispose();
}
}
static void CacheInjectableTypeGroup()
{
injectableTypeGroup.Clear();
Assembly assebly = Assembly.Load("Assembly-CSharp");
foreach (Type t in assebly.GetTypes())
{
if (DoesTypeInjectable(t))
{
injectableTypeGroup.Add(t.FullName);
}
}
}
static bool DoesTypeInjectable(Type type)
{
if (dropTypeGroup.Contains(type.FullName) || (type.DeclaringType != null && dropTypeGroup.Contains(type.DeclaringType.FullName)))
{
return false;
}
if (type.IsGenericType)
{
Type genericTypeDefinition = type.GetGenericTypeDefinition();
if (dropGenericNameGroup.Contains(genericTypeDefinition.FullName))
{
return false;
}
}
if (typeof(System.Delegate).IsAssignableFrom(type))
{
return false;
}
if (type.FullName.Contains("<") || type.IsInterface)
{
return false;
}
if (!injectIgnoring.HasFlag(InjectFilter.IgnoreNoToLuaAttr))
{
foreach (var attr in type.GetCustomAttributes(true))
{
Type attrT = attr.GetType();
if (attrT == typeof(LuaInterface.NoToLuaAttribute))
{
return false;
}
}
}
return true;
}
static bool DoesTypeInjectable(TypeDefinition type)
{
if (dropNamespaceGroup.Contains(type.SafeNamespace()))
{
return false;
}
if (!injectableTypeGroup.Contains(type.FullName.Replace("/", "+")))
{
return false;
}
if (injectIgnoring.HasFlag(InjectFilter.IgnoreConstructor) && type.Methods.Count == 1)
{
return false;
}
if (!injectIgnoring.HasFlag(InjectFilter.IgnoreNoToLuaAttr))
{
if (type.CustomAttributes.Any((attr) => attr.AttributeType == noToLuaAttrTypeRef))
{
return false;
}
}
return true;
}
static bool DoesMethodInjectable(MethodDefinition method)
{
if (method.IsSpecialName)
{
if (method.Name == ".cctor")
{
return false;
}
bool bIgnoreConstructor = injectIgnoring.HasFlag(InjectFilter.IgnoreConstructor)
|| method.DeclaringType.IsAssignableTo("UnityEngine.MonoBehaviour")
|| method.DeclaringType.IsAssignableTo("UnityEngine.ScriptableObject");
if (method.IsConstructor)
{
if (bIgnoreConstructor)
{
return false;
}
}
else
{
///Skip add_、remove_、op_、Finalize
if (!method.IsGetter && !method.IsSetter)
{
return false;
}
}
}
if (method.Name.Contains("<") || method.IsUnmanaged || method.IsAbstract || method.IsPInvokeImpl || !method.HasBody)
{
return false;
}
/// Skip Unsafe
if (method.Body.Variables.Any(var => var.VariableType.IsPointer) || method.Parameters.Any(param => param.ParameterType.IsPinned))
{
return false;
}
/// Hmm... Sometimes method.IsSpecialName Got False
if (method.Name == "Finalize")
{
return false;
}
if ((method.IsGetter || method.IsSetter) && injectIgnoring.HasFlag(InjectFilter.IgnoreProperty))
{
return false;
}
if (!injectIgnoring.HasFlag(InjectFilter.IgnoreNoToLuaAttr))
{
if (method.CustomAttributes.Any((attr) => attr.AttributeType == noToLuaAttrTypeRef))
{
return false;
}
}
if (method.ReturnType.IsAssignableTo("System.Collections.IEnumerable"))
{
return false;
}
MethodReference luaInjector = null;
GetLuaInvoker(method, true, false, ref luaInjector);
if (luaInjector == null)
{
return false;
}
return true;
}
static bool LoadBlackList()
{
if (File.Exists(InjectionBlackListGenerator.blackListFilePath))
{
dropTypeGroup.UnionWith(File.ReadAllLines(InjectionBlackListGenerator.blackListFilePath));
dropTypeGroup.ExceptWith(forceInjectTypeGroup);
}
else
{
if (EditorUtility.DisplayDialog("警告", "由于Injection会额外增加代码量故可以先设置一些Injection跳过的代码目录(比如NGUI插件代码目录),减少生成的代码量", "设置黑名单", "全量生成"))
{
InjectionBlackListGenerator.Open();
InjectionBlackListGenerator.onBlackListGenerated += InjectAll;
return false;
}
}
return true;
}
}
public static class SystemXMLExtension
{
public static XmlNode FindChildByName(this XmlNode root, string childName)
{
var child = root.FirstChild;
while (child != null)
{
if (child.Name.Equals(childName))
{
return child;
}
else
{
child = child.NextSibling;
}
}
return null;
}
public static XmlAttribute FindAttributeByName(this XmlNode node, string attributeName)
{
var attributeCollection = node.Attributes;
for (int i = 0; i < attributeCollection.Count; i++)
{
if (attributeCollection[i].Name.Equals(attributeName))
{
return attributeCollection[i];
}
}
return null;
}
}
#endif