【发布时间】:2021-09-28 10:56:10
【问题描述】:
例如,考虑以下 C# 代码:
interface IBase { void f(int); }
interface IDerived : IBase { /* inherits f from IBase */ }
...
void SomeFunction()
{
IDerived o = ...;
o.f(5);
}
我知道如何获取与 SomeFunction 对应的MethodDefinition 对象。
然后我可以遍历MethodDefinition.Instructions:
var methodDef = GetMethodDefinitionOfSomeFunction();
foreach (var instruction in methodDef.Body.Instructions)
{
switch (instruction.Operand)
{
case MethodReference mr:
...
break;
}
yield return memberRef;
}
这样我可以发现方法SomeFunction调用了函数IBase.f
现在我想知道调用函数f的对象的声明类型,即o的声明类型。
检查mr.DeclaringType 没有帮助,因为它返回IBase。
这是我目前所拥有的:
TypeReference typeRef = null;
if (instruction.OpCode == OpCodes.Callvirt)
{
// Identify the type of the object on which the call is being made.
var objInstruction = instruction;
if (instruction.Previous.OpCode == OpCodes.Tail)
{
objInstruction = instruction.Previous;
}
for (int i = mr.Parameters.Count; i >= 0; --i)
{
objInstruction = objInstruction.Previous;
}
if (objInstruction.OpCode == OpCodes.Ldloc_0 ||
objInstruction.OpCode == OpCodes.Ldloc_1 ||
objInstruction.OpCode == OpCodes.Ldloc_2 ||
objInstruction.OpCode == OpCodes.Ldloc_3)
{
var localIndex = objInstruction.OpCode.Op2 - OpCodes.Ldloc_0.Op2;
typeRef = locals[localIndex].VariableType;
}
else
{
switch (objInstruction.Operand)
{
case FieldDefinition fd:
typeRef = fd.DeclaringType;
break;
case VariableDefinition vd:
typeRef = vd.VariableType;
break;
}
}
}
其中locals 是methodDef.Body.Variables
但这当然还不够,因为函数的参数可以调用其他函数,例如f(g("hello"))。看起来像上面的情况,我检查以前的指令必须在虚拟机实际执行代码时重复虚拟机的操作。当然,我不执行它,但我需要识别函数调用并将它们及其参数替换为它们各自的返回值(即使是占位符)。看起来很痛苦。
有没有更简单的方法?也许已经内置了一些东西?
【问题讨论】:
-
我不知道实现这一目标的简单方法。我想“最简单”的方法是遍历堆栈并找到用作调用目标的引用被推送的位置(我认为您可以从github.com/lytico/db4o/blob/master/db4o.net/Db4oTool/Db4oTool/… 获得一些灵感)。无论如何,您需要涵盖更多情况,例如 SomeFunction().f(5)。在这种情况下(如果 SomeFunction() 类型为 IBase),您需要找出返回的实际类型。
-
我知道。这真的很痛苦。因此我的问题在这里。也许有人已经做过这样的事情了。遍历堆栈是指IL指令堆栈吗?如果是这样 - 是的,似乎是这样。好痛啊……
-
@Vagaus - 请安排您的评论作为答案。虽然不能满足问题,但还是有用的,值得点赞。
标签: mono.cecil