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

1293 lines
46 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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