提交 6a5ba401 编写于 作者: J johnche(车雄生)

支持泛化函数的热补丁

上级 426d6a5f
......@@ -44,6 +44,7 @@
* 析构函数
* 成员函数
* 静态函数
* 泛化函数
* 操作符重载
* 成员属性
* 静态属性
......
......@@ -72,6 +72,8 @@ end)
普通参数对于lua的参数,ref参数对应lua的一个参数和一个返回值,out参数对于lua的一个返回值。
泛化函数的打补丁规则和普通函数一样。
* 构造函数
构造函数对应的method_name是".ctor"。
......
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using XLua;
[CSharpCallLua]
......@@ -29,6 +29,31 @@ public class HotfixCalc
{
return TestOut(a, out b, ref c);
}
public T Test1<T>()
{
return default(T);
}
public T1 Test2<T1, T2, T3>(T1 a, out T2 b, ref T3 c)
{
b = default(T2);
return a;
}
public static int Test3<T>(T a)
{
return 0;
}
public static void Test4<T>(T a)
{
}
public void Test5<T>(int a, params T[] arg)
{
}
}
public class NoHotfixCalc
......@@ -93,6 +118,47 @@ public class HotfixTest2 : MonoBehaviour {
ret = calc.TestOut(100, out num, ref str);
Debug.Log("ret = " + ret + ", num = " + num + ", str = " + str);
luaenv.DoString(@"
xlua.hotfix(CS.HotfixCalc, {
Test1 = function(self)
print('Test1', self)
return 1
end;
Test2 = function(self, a, b)
print('Test1', self, a, b)
return a + 10, 1024, b
end;
Test3 = function(a)
print(a)
return 10
end;
Test4 = function(a)
print(a)
end;
Test5 = function(self, a, ...)
print('Test4', self, a, ...)
end
})
");
int r1 = calc.Test1<int>();
double r2 = calc.Test1<double>();
Debug.Log("r1:" + r1 + ",r2:" + r2);
string ss = "heihei";
int r3 = calc.Test2(r1, out r2, ref ss);
Debug.Log("r1:" + r1 + ",r2:" + r2 + ",r3:" + r3 + ",ss:" + ss);
r3 = HotfixCalc.Test3("test3");
r3 = HotfixCalc.Test3(2);
r3 = HotfixCalc.Test3(this);
Debug.Log("r3:" + r3);
HotfixCalc.Test4(this);
HotfixCalc.Test4(2);
calc.Test5(10, "a", "b", "c");
calc.Test5(10, 1, 3, 5);
Debug.Log("----------------------before------------------------");
TestStateful();
System.GC.Collect();
......@@ -139,6 +205,9 @@ public class HotfixTest2 : MonoBehaviour {
StaticFunc = function(a, b, c)
print(a, b, c)
end;
GenericTest = function(self, a)
print(self, a)
end;
Finalize = function(self)
print('Finalize', self)
end
......@@ -171,6 +240,8 @@ public class HotfixTest2 : MonoBehaviour {
sft.Start();
StatefullTest.StaticFunc(1, 2);
StatefullTest.StaticFunc("e", 3, 4);
sft.GenericTest(1);
sft.GenericTest("hehe");
}
// Update is called once per frame
......
......@@ -30,6 +30,11 @@ public class StatefullTest {
}
public void GenericTest<T>(T a)
{
}
static public void StaticFunc(int a, int b)
{
}
......
......@@ -25,11 +25,30 @@ namespace XLua
static TypeReference objType = null;
static TypeReference luaTableType = null;
static TypeDefinition luaFunctionType = null;
static MethodDefinition invokeSessionStart = null;
static MethodDefinition functionInvoke = null;
static MethodDefinition invokeSessionEnd = null;
static MethodDefinition invokeSessionEndWithResult = null;
static MethodDefinition inParam = null;
static MethodDefinition inParams = null;
static MethodDefinition outParam = null;
static void init(AssemblyDefinition assembly)
{
objType = assembly.MainModule.Import(typeof(object));
luaTableType = assembly.MainModule.Types.Single(t => t.FullName == "XLua.LuaTable");
luaFunctionType = assembly.MainModule.Types.Single(t => t.FullName == "XLua.LuaFunction");
invokeSessionStart = luaFunctionType.Methods.Single(m => m.Name == "InvokeSessionStart");
functionInvoke = luaFunctionType.Methods.Single(m => m.Name == "Invoke");
invokeSessionEnd = luaFunctionType.Methods.Single(m => m.Name == "InvokeSessionEnd");
invokeSessionEndWithResult = luaFunctionType.Methods.Single(m => m.Name == "InvokeSessionEndWithResult");
inParam = luaFunctionType.Methods.Single(m => m.Name == "InParam");
inParams = luaFunctionType.Methods.Single(m => m.Name == "InParams");
outParam = luaFunctionType.Methods.Single(m => m.Name == "OutParam");
}
static List<TypeDefinition> hotfix_delegates = null;
......@@ -143,7 +162,8 @@ namespace XLua
{
if (method.Name != ".cctor")
{
if (!InjectCode(assembly, method, hotfixType, stateTable))
if (method.HasGenericParameters ? ! InjectGenericMethod(assembly, method, hotfixType, stateTable) :
!InjectMethod(assembly, method, hotfixType, stateTable))
{
return;
}
......@@ -161,7 +181,7 @@ namespace XLua
static readonly int MAX_OVERLOAD = 100;
static bool InjectCode(AssemblyDefinition assembly, MethodDefinition method, int hotfixType, FieldDefinition stateTable)
static bool InjectMethod(AssemblyDefinition assembly, MethodDefinition method, int hotfixType, FieldDefinition stateTable)
{
string fieldName = method.Name;
if (fieldName.StartsWith("."))
......@@ -247,6 +267,166 @@ namespace XLua
return true;
}
static MethodReference MakeGenericMethod(this MethodReference self, params TypeReference[] arguments)
{
if (self.GenericParameters.Count != arguments.Length)
throw new ArgumentException();
var instance = new GenericInstanceMethod(self);
foreach (var argument in arguments)
instance.GenericArguments.Add(argument);
return instance;
}
static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition method, int hotfixType, FieldDefinition stateTable)
{
string fieldName = method.Name;
if (fieldName.StartsWith("."))
{
fieldName = fieldName.Substring(1);
}
string luaDelegateName = null;
var type = method.DeclaringType;
for (int i = 0; i < MAX_OVERLOAD; i++)
{
string tmp = "__Hitfix" + i + "_" + fieldName;
if (!type.Fields.Any(f => f.Name == tmp)) // injected
{
luaDelegateName = tmp;
break;
}
}
if (luaDelegateName == null)
{
Debug.LogError("too many overload!");
return false;
}
FieldDefinition fieldDefinition = new FieldDefinition(luaDelegateName, Mono.Cecil.FieldAttributes.Static | Mono.Cecil.FieldAttributes.Private,
luaFunctionType);
type.Fields.Add(fieldDefinition);
int param_start = method.IsStatic ? 0 : 1;
int param_count = method.Parameters.Count + param_start;
var firstIns = method.Body.Instructions[0];
var processor = method.Body.GetILProcessor();
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
processor.InsertBefore(firstIns, processor.Create(OpCodes.Brfalse, firstIns));
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, invokeSessionStart));
bool isVoid = method.ReturnType.FullName == "System.Void";
int outCout = 0;
for (int i = 0; i < param_count; i++)
{
if (i == 0 && !method.IsStatic)
{
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldarg_0));
if (hotfixType == 1)
{
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)));
}
}
else
{
var param = method.Parameters[i - param_start];
if (param.ParameterType.IsByReference)
{
outCout++;
}
if (!param.IsOut)
{
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
if (i < ldargs.Length)
{
processor.InsertBefore(firstIns, processor.Create(ldargs[i]));
}
else
{
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldarg, (short)i));
}
var paramType = param.ParameterType;
if (param.ParameterType.IsByReference)
{
paramType = ((ByReferenceType)paramType).ElementType;
if (paramType.IsValueType || paramType.IsGenericParameter)
{
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldobj, paramType));
}
else
{
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldind_Ref));
}
}
if (i == param_count - 1 && param.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.ParamArrayAttribute"))
{
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(inParams, ((ArrayType)paramType).ElementType)));
}
else
{
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(inParam, paramType)));
}
}
}
}
int outStart = (isVoid ? 0 : 1);
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldc_I4, outCout + outStart));
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, functionInvoke));
int outPos = outStart;
for (int i = 0; i < method.Parameters.Count; i++)
{
if (method.Parameters[i].ParameterType.IsByReference)
{
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldc_I4, outPos));
int arg_pos = param_start + i;
if (arg_pos < ldargs.Length)
{
processor.InsertBefore(firstIns, processor.Create(ldargs[arg_pos]));
}
else
{
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldarg, (short)arg_pos));
}
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(outParam,
((ByReferenceType)method.Parameters[i].ParameterType).ElementType)));
outPos++;
}
}
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
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.Ret));
return true;
}
}
}
#endif
......@@ -17,6 +17,7 @@ using LuaCSFunction = XLua.LuaDLL.lua_CSFunction;
#endif
using System;
using System.Collections.Generic;
namespace XLua
{
......@@ -229,6 +230,128 @@ namespace XLua
{
return "function :" + luaReference;
}
#if HOTFIX_ENABLE
private int _oldTop = 0;
private Stack<int> _stack = new Stack<int>();
public void InvokeSessionStart()
{
lock (luaEnv.luaEnvLock)
{
var L = luaEnv.L;
_stack.Push(_oldTop);
_oldTop = LuaAPI.lua_gettop(L);
LuaAPI.load_error_func(L, luaEnv.errorFuncRef);
LuaAPI.lua_getref(L, luaReference);
}
}
public void Invoke(int nRet)
{
lock (luaEnv.luaEnvLock)
{
int error = LuaAPI.lua_pcall(luaEnv.L, LuaAPI.lua_gettop(luaEnv.L) - _oldTop - 2, nRet, _oldTop + 1);
if (error != 0)
{
var lastOldTop = _oldTop;
InvokeSessionEnd();
luaEnv.ThrowExceptionFromError(lastOldTop);
}
}
}
public void InvokeSessionEnd()
{
lock (luaEnv.luaEnvLock)
{
LuaAPI.lua_settop(luaEnv.L, _oldTop);
}
_oldTop = _stack.Pop();
}
public TResult InvokeSessionEndWithResult<TResult>()
{
lock (luaEnv.luaEnvLock)
{
if (LuaAPI.lua_gettop(luaEnv.L) < _oldTop + 2)
{
InvokeSessionEnd();
throw new InvalidOperationException("no result!");
}
try
{
TResult ret;
luaEnv.translator.Get(luaEnv.L, _oldTop + 2, out ret);
return ret;
}
finally
{
InvokeSessionEnd();
}
}
}
public void InParam<T>(T p)
{
try
{
lock (luaEnv.luaEnvLock)
{
luaEnv.translator.PushByType(luaEnv.L, p);
}
}
catch (Exception e)
{
InvokeSessionEnd();
throw e;
}
}
public void InParams<T>(T[] ps)
{
try
{
lock (luaEnv.luaEnvLock)
{
for (int i = 0; i < ps.Length; i++)
{
luaEnv.translator.PushByType<T>(luaEnv.L, ps[i]);
}
}
}
catch (Exception e)
{
InvokeSessionEnd();
throw e;
}
}
//pos start from 0
public void OutParam<TResult>(int pos, out TResult ret)
{
lock (luaEnv.luaEnvLock)
{
if (LuaAPI.lua_gettop(luaEnv.L) < _oldTop + 2 + pos)
{
InvokeSessionEnd();
throw new InvalidOperationException("no result in " + pos);
}
try
{
luaEnv.translator.Get(luaEnv.L, _oldTop + 2 + pos, out ret);
}
catch (Exception e)
{
InvokeSessionEnd();
throw e;
}
}
}
#endif
}
}
......@@ -10,7 +10,7 @@ xLua为Unity3D增加Lua脚本编程的能力,进而提供代码逻辑增量更
xLua在功能、性能、易用性都有不少突破,这几方面分别最具代表性的突破是:
* Unity3D全平台热补丁技术,可以运行时把C#实现(整个类,或者单个方法,操作符,属性,事件,构造函数,析构函数)替换成lua实现;
* Unity3D全平台热补丁技术,可以运行时把C#实现(整个类,或者单个方法,泛化方法,操作符,属性,事件,构造函数,析构函数)替换成lua实现;
* 自定义struct,枚举在Lua和C#间传递无C# gc alloc;
* 编辑器下无需生成代码,开发更轻量;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册