Skip to content

Commit 32bc438

Browse files
committed
支持泛化类型
1 parent 67f74af commit 32bc438

File tree

4 files changed

+139
-24
lines changed

4 files changed

+139
-24
lines changed

Assets/XLua/Doc/hotfix.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,39 @@ C#的操作符都有一套内部表示,比如+号的操作符函数名是op_Ad
100100

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

103+
* 泛化类型
104+
105+
其它规则一致,需要说明的是,每个泛化类型实例化后都是一个独立的类型,只能针对实例化后的类型分别打补丁。比如:
106+
107+
```csharp
108+
public class GenericClass<T>
109+
{
110+
111+
```
112+
113+
你只能对GenericClass<double>GenericClass<int>这些类,而不是对GenericClass打补丁。
114+
115+
另外值得一提的是,要注意泛化类型的命名方式,比如GenericClass<double>的命名是GenericClass`1[System.Double],具体可以看[MSDN](https://msdn.microsoft.com/en-us/library/w3f99sx1.aspx)。
116+
117+
GenericClass<double>打补丁的实例如下:
118+
119+
```csharp
120+
luaenv.DoString(@"
121+
xlua.hotfix(CS['GenericClass`1[System.Double]'], {
122+
['.ctor'] = function(obj, a)
123+
print('GenericClass<double>', obj, a)
124+
end;
125+
Func1 = function(obj)
126+
print('GenericClass<double>.Func1', obj)
127+
end;
128+
Func2 = function(obj)
129+
print('GenericClass<double>.Func2', obj)
130+
return 1314
131+
end
132+
})
133+
");
134+
```
135+
103136
* 整个类
104137

105138
如果要替换整个类,不需要一次次的调用xlua.hotfix去替换,可以整个一次完成。只要给一个table,按method_name = function组织即可

Assets/XLua/Examples/08_Hotfix/HotfixTest2.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,26 @@ public int Add(int a, int b)
6464
}
6565
}
6666

67+
[Hotfix]
68+
public class GenericClass<T>
69+
{
70+
T a;
71+
72+
public GenericClass(T a)
73+
{
74+
this.a = a;
75+
}
76+
77+
public void Func1()
78+
{
79+
80+
}
81+
82+
public T Func2()
83+
{
84+
return default(T);
85+
}
86+
}
6787

6888
public class HotfixTest2 : MonoBehaviour {
6989

@@ -219,6 +239,27 @@ void Start () {
219239
System.GC.Collect();
220240
System.GC.WaitForPendingFinalizers();
221241

242+
var genericObj = new GenericClass<double>(1.1);
243+
genericObj.Func1();
244+
Debug.Log(genericObj.Func2());
245+
luaenv.DoString(@"
246+
xlua.hotfix(CS['GenericClass`1[System.Double]'], {
247+
['.ctor'] = function(obj, a)
248+
print('GenericClass<double>', obj, a)
249+
end;
250+
Func1 = function(obj)
251+
print('GenericClass<double>.Func1', obj)
252+
end;
253+
Func2 = function(obj)
254+
print('GenericClass<double>.Func2', obj)
255+
return 1314
256+
end
257+
})
258+
");
259+
genericObj = new GenericClass<double>(1.1);
260+
genericObj.Func1();
261+
Debug.Log(genericObj.Func2());
262+
222263
calc.TestOut(100, out num, ref str, gameObject);
223264
}
224265

Assets/XLua/Src/Editor/Hotfix.cs

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ where type.CustomAttributes.Any(ca => ca.AttributeType == hotfixDelegateAttribut
162162
{
163163
if (method.Name != ".cctor")
164164
{
165-
if (method.HasGenericParameters ? ! InjectGenericMethod(assembly, method, hotfixType, stateTable) :
165+
if ((method.HasGenericParameters || method.ContainsGenericParameter) ? ! InjectGenericMethod(assembly, method, hotfixType, stateTable) :
166166
!InjectMethod(assembly, method, hotfixType, stateTable))
167167
{
168168
return;
@@ -205,10 +205,7 @@ static bool InjectMethod(AssemblyDefinition assembly, MethodDefinition method, i
205205
Debug.LogError("too many overload!");
206206
return false;
207207
}
208-
if (method.HasGenericParameters)
209-
{
210-
return true;
211-
}
208+
212209
TypeReference delegateType = null;
213210
MethodReference invoke = null;
214211

@@ -227,21 +224,22 @@ static bool InjectMethod(AssemblyDefinition assembly, MethodDefinition method, i
227224
FieldDefinition fieldDefinition = new FieldDefinition(luaDelegateName, Mono.Cecil.FieldAttributes.Static | Mono.Cecil.FieldAttributes.Private,
228225
delegateType);
229226
type.Fields.Add(fieldDefinition);
227+
FieldReference fieldReference = fieldDefinition.GetGeneric();
230228

231229
bool statefulConstructor = (hotfixType == 1) && method.IsConstructor && !method.IsStatic;
232230

233231

234-
var firstIns = method.Body.Instructions[0];
232+
var firstIns = method.IsConstructor ? method.Body.Instructions[2] : method.Body.Instructions[0];
235233
var processor = method.Body.GetILProcessor();
236234

237-
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
235+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference));
238236
processor.InsertBefore(firstIns, processor.Create(OpCodes.Brfalse, firstIns));
239237

240238
if (statefulConstructor)
241239
{
242240
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldarg_0));
243241
}
244-
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
242+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference));
245243
for (int i = 0; i < param_count; i++)
246244
{
247245
if (i < ldargs.Length)
@@ -280,18 +278,49 @@ static MethodReference MakeGenericMethod(this MethodReference self, params TypeR
280278
return instance;
281279
}
282280

281+
static FieldReference GetGeneric(this FieldDefinition definition)
282+
{
283+
if (definition.DeclaringType.HasGenericParameters)
284+
{
285+
var declaringType = new GenericInstanceType(definition.DeclaringType);
286+
foreach (var parameter in definition.DeclaringType.GenericParameters)
287+
{
288+
declaringType.GenericArguments.Add(parameter);
289+
}
290+
return new FieldReference(definition.Name, definition.FieldType, declaringType);
291+
}
292+
293+
return definition;
294+
}
295+
296+
public static TypeReference GetGeneric(this TypeDefinition definition)
297+
{
298+
if (definition.HasGenericParameters)
299+
{
300+
var genericInstanceType = new GenericInstanceType(definition);
301+
foreach (var parameter in definition.GenericParameters)
302+
{
303+
genericInstanceType.GenericArguments.Add(parameter);
304+
}
305+
return genericInstanceType;
306+
}
307+
308+
return definition;
309+
}
310+
283311
static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition method, int hotfixType, FieldDefinition stateTable)
284312
{
285313
string fieldName = method.Name;
286314
if (fieldName.StartsWith("."))
287315
{
288316
fieldName = fieldName.Substring(1);
289317
}
318+
string ccFlag = method.IsConstructor ? "_c" : "";
290319
string luaDelegateName = null;
291320
var type = method.DeclaringType;
292321
for (int i = 0; i < MAX_OVERLOAD; i++)
293322
{
294-
string tmp = "__Hitfix" + i + "_" + fieldName;
323+
string tmp = ccFlag + "__Hitfix" + i + "_" + fieldName;
295324
if (!type.Fields.Any(f => f.Name == tmp)) // injected
296325
{
297326
luaDelegateName = tmp;
@@ -308,35 +337,41 @@ static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition me
308337
luaFunctionType);
309338
type.Fields.Add(fieldDefinition);
310339

340+
FieldReference fieldReference = fieldDefinition.GetGeneric();
341+
311342
int param_start = method.IsStatic ? 0 : 1;
312343
int param_count = method.Parameters.Count + param_start;
313-
var firstIns = method.Body.Instructions[0];
344+
var firstIns = method.IsConstructor ? method.Body.Instructions[2] : method.Body.Instructions[0];
314345
var processor = method.Body.GetILProcessor();
315346

316-
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
347+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference));
317348
processor.InsertBefore(firstIns, processor.Create(OpCodes.Brfalse, firstIns));
318349

319-
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
350+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference));
320351
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, invokeSessionStart));
321352

322-
bool isVoid = method.ReturnType.FullName == "System.Void";
353+
bool statefulConstructor = (hotfixType == 1) && method.IsConstructor && !method.IsStatic;
354+
355+
TypeReference returnType = statefulConstructor ? luaTableType : method.ReturnType;
356+
357+
bool isVoid = returnType.FullName == "System.Void";
323358

324359
int outCout = 0;
325360

326361
for (int i = 0; i < param_count; i++)
327362
{
328363
if (i == 0 && !method.IsStatic)
329364
{
330-
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
365+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference));
331366
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldarg_0));
332-
if (hotfixType == 1)
367+
if (hotfixType == 1 && !method.IsConstructor)
333368
{
334369
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldfld, stateTable));
335370
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(inParam, luaTableType)));
336371
}
337372
else
338373
{
339-
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(inParam, method.DeclaringType)));
374+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(inParam, method.DeclaringType.GetGeneric())));
340375
}
341376
}
342377
else
@@ -348,7 +383,7 @@ static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition me
348383
}
349384
if (!param.IsOut)
350385
{
351-
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
386+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference));
352387

353388
if (i < ldargs.Length)
354389
{
@@ -387,7 +422,7 @@ static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition me
387422

388423
int outStart = (isVoid ? 0 : 1);
389424

390-
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
425+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference));
391426
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldc_I4, outCout + outStart));
392427
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, functionInvoke));
393428

@@ -396,7 +431,7 @@ static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition me
396431
{
397432
if (method.Parameters[i].ParameterType.IsByReference)
398433
{
399-
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
434+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference));
400435
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldc_I4, outPos));
401436
int arg_pos = param_start + i;
402437
if (arg_pos < ldargs.Length)
@@ -412,17 +447,23 @@ static bool InjectGenericMethod(AssemblyDefinition assembly, MethodDefinition me
412447
outPos++;
413448
}
414449
}
415-
416-
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldDefinition));
450+
if (statefulConstructor)
451+
{
452+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldarg_0));
453+
}
454+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ldsfld, fieldReference));
417455
if (isVoid)
418456
{
419457
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, invokeSessionEnd));
420458
}
421459
else
422460
{
423-
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(invokeSessionEndWithResult, method.ReturnType)));
461+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Callvirt, MakeGenericMethod(invokeSessionEndWithResult, returnType)));
462+
}
463+
if (statefulConstructor)
464+
{
465+
processor.InsertBefore(firstIns, processor.Create(OpCodes.Stfld, stateTable));
424466
}
425-
426467
processor.InsertBefore(firstIns, processor.Create(OpCodes.Ret));
427468

428469
return true;

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ xLua为Unity3D增加Lua脚本编程的能力,进而提供代码逻辑增量更
1010

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

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

0 commit comments

Comments
 (0)