diff --git a/Assets/XLua/Doc/hotfix.md b/Assets/XLua/Doc/hotfix.md index d17e40cca..fec68942b 100644 --- a/Assets/XLua/Doc/hotfix.md +++ b/Assets/XLua/Doc/hotfix.md @@ -100,6 +100,39 @@ C#的操作符都有一套内部表示,比如+号的操作符函数名是op_Ad method_name是"Finalize",传一个self参数。 +* 泛化类型 + +其它规则一致,需要说明的是,每个泛化类型实例化后都是一个独立的类型,只能针对实例化后的类型分别打补丁。比如: + +```csharp +public class GenericClass +{ +} +``` + +你只能对GenericClass,GenericClass这些类,而不是对GenericClass打补丁。 + +另外值得一提的是,要注意泛化类型的命名方式,比如GenericClass的命名是GenericClass`1[System.Double],具体可以看[MSDN](https://msdn.microsoft.com/en-us/library/w3f99sx1.aspx)。 + +对GenericClass打补丁的实例如下: + +```csharp +luaenv.DoString(@" + xlua.hotfix(CS['GenericClass`1[System.Double]'], { + ['.ctor'] = function(obj, a) + print('GenericClass', obj, a) + end; + Func1 = function(obj) + print('GenericClass.Func1', obj) + end; + Func2 = function(obj) + print('GenericClass.Func2', obj) + return 1314 + end + }) +"); +``` + * 整个类 如果要替换整个类,不需要一次次的调用xlua.hotfix去替换,可以整个一次完成。只要给一个table,按method_name = function组织即可 diff --git a/Assets/XLua/Examples/08_Hotfix/HotfixTest2.cs b/Assets/XLua/Examples/08_Hotfix/HotfixTest2.cs index 34c66bbed..12ec7b84b 100644 --- a/Assets/XLua/Examples/08_Hotfix/HotfixTest2.cs +++ b/Assets/XLua/Examples/08_Hotfix/HotfixTest2.cs @@ -64,6 +64,26 @@ public int Add(int a, int b) } } +[Hotfix] +public class GenericClass +{ + T a; + + public GenericClass(T a) + { + this.a = a; + } + + public void Func1() + { + + } + + public T Func2() + { + return default(T); + } +} public class HotfixTest2 : MonoBehaviour { @@ -219,6 +239,27 @@ void Start () { System.GC.Collect(); System.GC.WaitForPendingFinalizers(); + var genericObj = new GenericClass(1.1); + genericObj.Func1(); + Debug.Log(genericObj.Func2()); + luaenv.DoString(@" + xlua.hotfix(CS['GenericClass`1[System.Double]'], { + ['.ctor'] = function(obj, a) + print('GenericClass', obj, a) + end; + Func1 = function(obj) + print('GenericClass.Func1', obj) + end; + Func2 = function(obj) + print('GenericClass.Func2', obj) + return 1314 + end + }) + "); + genericObj = new GenericClass(1.1); + genericObj.Func1(); + Debug.Log(genericObj.Func2()); + calc.TestOut(100, out num, ref str, gameObject); } diff --git a/Assets/XLua/Src/Editor/Hotfix.cs b/Assets/XLua/Src/Editor/Hotfix.cs index e1b2d1f68..fb0afcd57 100644 --- a/Assets/XLua/Src/Editor/Hotfix.cs +++ b/Assets/XLua/Src/Editor/Hotfix.cs @@ -162,7 +162,7 @@ where type.CustomAttributes.Any(ca => ca.AttributeType == hotfixDelegateAttribut { if (method.Name != ".cctor") { - if (method.HasGenericParameters ? ! InjectGenericMethod(assembly, method, hotfixType, stateTable) : + if ((method.HasGenericParameters || method.ContainsGenericParameter) ? ! InjectGenericMethod(assembly, method, hotfixType, stateTable) : !InjectMethod(assembly, method, hotfixType, stateTable)) { return; @@ -205,10 +205,7 @@ static bool InjectMethod(AssemblyDefinition assembly, MethodDefinition method, i Debug.LogError("too many overload!"); return false; } - if (method.HasGenericParameters) - { - return true; - } + TypeReference delegateType = null; MethodReference invoke = null; @@ -227,21 +224,22 @@ static bool InjectMethod(AssemblyDefinition assembly, MethodDefinition method, i FieldDefinition fieldDefinition = new FieldDefinition(luaDelegateName, Mono.Cecil.FieldAttributes.Static | Mono.Cecil.FieldAttributes.Private, delegateType); type.Fields.Add(fieldDefinition); + FieldReference fieldReference = fieldDefinition.GetGeneric(); bool statefulConstructor = (hotfixType == 1) && method.IsConstructor && !method.IsStatic; - var firstIns = method.Body.Instructions[0]; + var firstIns = method.IsConstructor ? method.Body.Instructions[2] : method.Body.Instructions[0]; var processor = method.Body.GetILProcessor(); - processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition)); + processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference)); processor.InsertBefore(firstIns, processor.Create(OpCodes.Brfalse, firstIns)); if (statefulConstructor) { processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldarg_0)); } - processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition)); + processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference)); for (int i = 0; i < param_count; i++) { if (i < ldargs.Length) @@ -280,6 +278,36 @@ static MethodReference MakeGenericMethod(this MethodReference self, params TypeR return instance; } + static FieldReference GetGeneric(this FieldDefinition definition) + { + if (definition.DeclaringType.HasGenericParameters) + { + var declaringType = new GenericInstanceType(definition.DeclaringType); + foreach (var parameter in definition.DeclaringType.GenericParameters) + { + declaringType.GenericArguments.Add(parameter); + } + return new FieldReference(definition.Name, definition.FieldType, declaringType); + } + + return definition; + } + + public static TypeReference GetGeneric(this TypeDefinition definition) + { + if (definition.HasGenericParameters) + { + var genericInstanceType = new GenericInstanceType(definition); + foreach (var parameter in definition.GenericParameters) + { + genericInstanceType.GenericArguments.Add(parameter); + } + return genericInstanceType; + } + + return definition; + } + static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition method, int hotfixType, FieldDefinition stateTable) { string fieldName = method.Name; @@ -287,11 +315,12 @@ static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition me { fieldName = fieldName.Substring(1); } + string ccFlag = method.IsConstructor ? "_c" : ""; string luaDelegateName = null; var type = method.DeclaringType; for (int i = 0; i < MAX_OVERLOAD; i++) { - string tmp = "__Hitfix" + i + "_" + fieldName; + string tmp = ccFlag + "__Hitfix" + i + "_" + fieldName; if (!type.Fields.Any(f => f.Name == tmp)) // injected { luaDelegateName = tmp; @@ -308,18 +337,24 @@ static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition me luaFunctionType); type.Fields.Add(fieldDefinition); + FieldReference fieldReference = fieldDefinition.GetGeneric(); + int param_start = method.IsStatic ? 0 : 1; int param_count = method.Parameters.Count + param_start; - var firstIns = method.Body.Instructions[0]; + var firstIns = method.IsConstructor ? method.Body.Instructions[2] : method.Body.Instructions[0]; var processor = method.Body.GetILProcessor(); - processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition)); + processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference)); processor.InsertBefore(firstIns, processor.Create(OpCodes.Brfalse, firstIns)); - processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition)); + processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference)); processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, invokeSessionStart)); - bool isVoid = method.ReturnType.FullName == "System.Void"; + bool statefulConstructor = (hotfixType == 1) && method.IsConstructor && !method.IsStatic; + + TypeReference returnType = statefulConstructor ? luaTableType : method.ReturnType; + + bool isVoid = returnType.FullName == "System.Void"; int outCout = 0; @@ -327,16 +362,16 @@ static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition me { if (i == 0 && !method.IsStatic) { - processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition)); + processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference)); processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldarg_0)); - if (hotfixType == 1) + if (hotfixType == 1 && !method.IsConstructor) { processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldfld, stateTable)); processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(inParam, luaTableType))); } else { - processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(inParam, method.DeclaringType))); + processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(inParam, method.DeclaringType.GetGeneric()))); } } else @@ -348,7 +383,7 @@ static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition me } if (!param.IsOut) { - processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition)); + processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference)); if (i < ldargs.Length) { @@ -387,7 +422,7 @@ static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition me int outStart = (isVoid ? 0 : 1); - processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition)); + processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference)); processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldc_I4, outCout + outStart)); processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, functionInvoke)); @@ -396,7 +431,7 @@ static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition me { if (method.Parameters[i].ParameterType.IsByReference) { - processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition)); + processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference)); processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldc_I4, outPos)); int arg_pos = param_start + i; if (arg_pos < ldargs.Length) @@ -412,17 +447,23 @@ static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition me outPos++; } } - - processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition)); + if (statefulConstructor) + { + processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldarg_0)); + } + processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference)); if (isVoid) { processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, invokeSessionEnd)); } else { - processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(invokeSessionEndWithResult, method.ReturnType))); + processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(invokeSessionEndWithResult, returnType))); + } + if (statefulConstructor) + { + processor.InsertBefore(firstIns, processor.Create(OpCodes.Stfld, stateTable)); } - processor.InsertBefore(firstIns, processor.Create(OpCodes.Ret)); return true; diff --git a/README.md b/README.md index 9b247ce68..9aa8784e1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ xLua为Unity3D增加Lua脚本编程的能力,进而提供代码逻辑增量更 xLua在功能、性能、易用性都有不少突破,这几方面分别最具代表性的突破是: -* Unity3D全平台热补丁技术,可以运行时把C#实现(整个类,或者单个方法,泛化方法,操作符,属性,事件,构造函数,析构函数)替换成lua实现; +* Unity3D全平台热补丁技术,可以运行时把C#实现(整个类,或者单个方法,操作符,属性,事件,构造函数,析构函数,支持泛化)替换成lua实现; * 自定义struct,枚举在Lua和C#间传递无C# gc alloc; * 编辑器下无需生成代码,开发更轻量;