Skip to content

Commit

Permalink
支持泛化类型
Browse files Browse the repository at this point in the history
  • Loading branch information
chexiongsheng committed Jan 2, 2017
1 parent 67f74af commit 32bc438
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 24 deletions.
33 changes: 33 additions & 0 deletions Assets/XLua/Doc/hotfix.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,39 @@ C#的操作符都有一套内部表示,比如+号的操作符函数名是op_Ad

method_name是"Finalize",传一个self参数。

* 泛化类型

其它规则一致,需要说明的是,每个泛化类型实例化后都是一个独立的类型,只能针对实例化后的类型分别打补丁。比如:

```csharp
public class GenericClass<T>
{
```

你只能对GenericClass<double>GenericClass<int>这些类,而不是对GenericClass打补丁。

另外值得一提的是,要注意泛化类型的命名方式,比如GenericClass<double>的命名是GenericClass`1[System.Double],具体可以看[MSDN](https://msdn.microsoft.com/en-us/library/w3f99sx1.aspx)。
GenericClass<double>打补丁的实例如下:

```csharp
luaenv.DoString(@"
xlua.hotfix(CS['GenericClass`1[System.Double]'], {
['.ctor'] = function(obj, a)
print('GenericClass<double>', obj, a)
end;
Func1 = function(obj)
print('GenericClass<double>.Func1', obj)
end;
Func2 = function(obj)
print('GenericClass<double>.Func2', obj)
return 1314
end
})
");
```

* 整个类

如果要替换整个类,不需要一次次的调用xlua.hotfix去替换,可以整个一次完成。只要给一个table,按method_name = function组织即可
Expand Down
41 changes: 41 additions & 0 deletions Assets/XLua/Examples/08_Hotfix/HotfixTest2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,26 @@ public int Add(int a, int b)
}
}

[Hotfix]
public class GenericClass<T>
{
T a;

public GenericClass(T a)
{
this.a = a;
}

public void Func1()
{

}

public T Func2()
{
return default(T);
}
}

public class HotfixTest2 : MonoBehaviour {

Expand Down Expand Up @@ -219,6 +239,27 @@ void Start () {
System.GC.Collect();
System.GC.WaitForPendingFinalizers();

var genericObj = new GenericClass<double>(1.1);
genericObj.Func1();
Debug.Log(genericObj.Func2());
luaenv.DoString(@"
xlua.hotfix(CS['GenericClass`1[System.Double]'], {
['.ctor'] = function(obj, a)
print('GenericClass<double>', obj, a)
end;
Func1 = function(obj)
print('GenericClass<double>.Func1', obj)
end;
Func2 = function(obj)
print('GenericClass<double>.Func2', obj)
return 1314
end
})
");
genericObj = new GenericClass<double>(1.1);
genericObj.Func1();
Debug.Log(genericObj.Func2());

calc.TestOut(100, out num, ref str, gameObject);
}

Expand Down
87 changes: 64 additions & 23 deletions Assets/XLua/Src/Editor/Hotfix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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)
Expand Down Expand Up @@ -280,18 +278,49 @@ 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;
if (fieldName.StartsWith("."))
{
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;
Expand All @@ -308,35 +337,41 @@ 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;

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.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
Expand All @@ -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)
{
Expand Down Expand Up @@ -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));

Expand All @@ -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)
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ xLua为Unity3D增加Lua脚本编程的能力,进而提供代码逻辑增量更

xLua在功能、性能、易用性都有不少突破,这几方面分别最具代表性的突破是:

* Unity3D全平台热补丁技术,可以运行时把C#实现(整个类,或者单个方法,泛化方法,操作符,属性,事件,构造函数,析构函数)替换成lua实现;
* Unity3D全平台热补丁技术,可以运行时把C#实现(整个类,或者单个方法,操作符,属性,事件,构造函数,析构函数,支持泛化)替换成lua实现;
* 自定义struct,枚举在Lua和C#间传递无C# gc alloc;
* 编辑器下无需生成代码,开发更轻量;

Expand Down

0 comments on commit 32bc438

Please sign in to comment.