Skip to content

Commit

Permalink
支持在子类override函数通过base调用父类实现。
Browse files Browse the repository at this point in the history
  • Loading branch information
chexiongsheng committed Mar 9, 2018
1 parent a26aa61 commit b9aad83
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 2 deletions.
17 changes: 15 additions & 2 deletions Assets/XLua/Doc/hotfix.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ Win命令行 copy UnityPath\Editor\Data\Managed\Mono.Cecil.* Project\Assets\XLua

不支持静态构造函数。

不支持在子类override函数通过base调用父类实现。

目前只支持Assets下代码的热补丁,不支持引擎,c#系统库的热补丁。

## API
Expand All @@ -46,6 +44,21 @@ xlua.private_accessible(class)
* 描述 : 让一个类的私有字段,属性,方法等可用
* class : 同xlua.hotfix的class参数

base(csobj)

* 描述 : 子类override函数通过base调用父类实现。
* csobj : 对象
* 返回值 : 新对象,可以通过该对象base上的方法

例子(位于HotfixTest2.cs):

```lua
xlua.hotfix(CS.BaseTest, 'Foo', function(self, p)
print('BaseTest', p)
base(self):Foo(p)
end)
```

util.hotfix_ex(class, method_name, fix)

* 描述 : xlua.hotfix的增强版本,可以在fix函数里头执行原来的函数,缺点是fix的执行会略慢。
Expand Down
29 changes: 29 additions & 0 deletions Assets/XLua/Examples/08_Hotfix/HotfixTest2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,23 @@ _InnerStruct Bar()
}
}

public class BaseTestBase
{
public virtual void Foo(int p)
{
Debug.Log("BaseTestBase.Foo, p = " + p);
}
}

[Hotfix]
public class BaseTest : BaseTestBase
{
public override void Foo(int p)
{
Debug.Log("BaseTest.Foo, p = " + p);
}
}

[Hotfix]
public struct StructTest
{
Expand Down Expand Up @@ -350,6 +367,18 @@ void Start () {
{
Debug.Log("throw in lua an catch in c# ok, e.Message:" + e.Message);
}


BaseTestBase bt = new BaseTest();
bt.Foo(1);

luaenv.DoString(@"
xlua.hotfix(CS.BaseTest, 'Foo', function(self, p)
print('BaseTest', p)
base(self):Foo(p)
end)
");
bt.Foo(2);
}

void TestStateful()
Expand Down
104 changes: 104 additions & 0 deletions Assets/XLua/Src/Editor/Hotfix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,29 @@ static bool injectType(AssemblyDefinition assembly, TypeReference hotfixAttribut
}
}

List<MethodDefinition> toAdd = new List<MethodDefinition>();
foreach (var method in type.Methods)
{
if (ignoreNotPublic && !method.IsPublic)
{
continue;
}
if (ignoreProperty && method.IsSpecialName && (method.Name.StartsWith("get_") || method.Name.StartsWith("set_")))
{
continue;
}
if (method.Name != ".cctor" && !method.IsAbstract && !method.IsPInvokeImpl && method.Body != null && !method.Name.Contains("<"))
{
var proxyMethod = tryAddBaseProxy(method.DeclaringType, method);
if (proxyMethod != null) toAdd.Add(proxyMethod);
}
}

foreach(var md in toAdd)
{
type.Methods.Add(md);
}

return true;
}

Expand Down Expand Up @@ -591,6 +614,87 @@ static Instruction findNextRet(Mono.Collections.Generic.Collection<Instruction>
return null;
}

static MethodDefinition findOverride(TypeDefinition type, MethodReference vmethod)
{
foreach (var method in type.Methods)
{
if (method.Name == vmethod.Name && method.IsVirtual && isSameType(method.ReturnType, vmethod.ReturnType) && method.Parameters.Count == vmethod.Parameters.Count)
{
bool isParamsMatch = true;
for (int i = 0; i < method.Parameters.Count; i++)
{
if (method.Parameters[i].Attributes != vmethod.Parameters[i].Attributes
|| !isSameType(method.Parameters[i].ParameterType, vmethod.Parameters[i].ParameterType))
{
isParamsMatch = false;
break;
}
}
if (isParamsMatch) return method;
}
}
return null;
}

static MethodReference findBase(TypeDefinition type, MethodDefinition method)
{
if (method.IsVirtual && !method.IsNewSlot) //表明override
{
try
{
TypeDefinition tbase = type.BaseType.Resolve();
while (tbase != null)
{
var m = findOverride(tbase, method);
if (m != null)
{
return m;
}
tbase = tbase.BaseType.Resolve();
}
}
catch { }
}
return null;
}

const string BASE_RPOXY_PERFIX = "<>xLuaBaseProxy_";

static MethodDefinition tryAddBaseProxy(TypeDefinition type, MethodDefinition method)
{
var mbase = findBase(type, method);
if (mbase != null)
{
var proxyMethod = new MethodDefinition(BASE_RPOXY_PERFIX + method.Name, Mono.Cecil.MethodAttributes.Public, method.ReturnType);
for (int i = 0; i < method.Parameters.Count; i++)
{
proxyMethod.Parameters.Add(new ParameterDefinition("P" + i, method.Parameters[i].IsOut ? Mono.Cecil.ParameterAttributes.Out : Mono.Cecil.ParameterAttributes.None, method.Parameters[i].ParameterType));
}
var instructions = proxyMethod.Body.Instructions;
var processor = proxyMethod.Body.GetILProcessor();
int paramCount = method.Parameters.Count + 1;
for (int i = 0; i < paramCount; i++)
{
if (i < ldargs.Length)
{
instructions.Add(processor.Create(ldargs[i]));
}
else if (i < 256)
{
instructions.Add(processor.Create(OpCodes.Ldarg_S, (byte)i));
}
else
{
instructions.Add(processor.Create(OpCodes.Ldarg, (short)i));
}
}
instructions.Add(Instruction.Create(OpCodes.Call, mbase));
instructions.Add(Instruction.Create(OpCodes.Ret));
return proxyMethod;
}
return null;
}

static bool injectMethod(AssemblyDefinition assembly, MethodDefinition method, HotfixFlagInTool hotfixType, FieldReference stateTable)
{
var type = method.DeclaringType;
Expand Down
13 changes: 13 additions & 0 deletions Assets/XLua/Src/LuaEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,19 @@ elseif not name then
impl.UnderlyingSystemType = parent[name].UnderlyingSystemType
rawset(parent, name, impl)
end
local base_mt = {
__index = function(t, k)
local csobj = t['__csobj']
local func = csobj['<>xLuaBaseProxy_'..k]
return function(_, ...)
func(csobj, ...)
end
end
}
base = function(csobj)
return setmetatable({__csobj = csobj}, base_mt)
end
";

public delegate byte[] CustomLoader(ref string filepath);
Expand Down
Binary file modified Tools/XLuaHotfixInject.exe
Binary file not shown.
Binary file modified Tools/XLuaHotfixInject.pdb
Binary file not shown.

0 comments on commit b9aad83

Please sign in to comment.