技术开发 频道

在IronPython中使用Cecil

什么是Cecil
“Cecil是由Jb Evain 开发,用于生成和浏览ECMA CIL 格式的程序和函数库。它完全支持泛型,支持部分调试符号。简单说来,利用Cecil,你可以加载存在的程序集,浏览其中所有的类型,动态的编辑他们,并保存到磁盘上成为一个新的编辑过的程序集”
以上说明来自:http://www.mono-project.com/Cecil

在这个教程中,我将花一些时间演示在IronPython中使用Cecil的功能。基本上,我就是用Cecil来找到不同的方法,这些方法能从一个方法中被调用。是不是很由意思呀?那么请继续往下看吧。

在继续之前,请下载并安装IronPythonCecil先。你可以不用下载Cecil,因为我已经把它包含在我的这个Solution文件中了。

首先,我们用C#来创建一个简单的Class Library项目,我们用这个项目生成的程序集用于在后面被Cecil加载。源代码如下(CecilCase.dll 是非常简单的):

// MainCase.cs
public class MainCase
    {
        
public void PublicMethod()
        {
            Console.WriteLine(
"Hello");
            PrivateMethod();

            
new SecondCase().Help("Help me");
        }

        
public void AddMethod()
        {
        }

        
private void PrivateMethod()
        {
        }

        
public void MethodWithArgument(string name)
        {
            Console.WriteLine(name);
        }
    }

// SecondCase.cs
class SecondCase
    {
        
public void Help(string message)
        {
            Console.WriteLine(message);
        }
    }


把这个项目编译成dll,然后打开IronPython的控制台。



保证把IronPython启动目录和CecilCase.dll的输入目录是同一个。也要记得把Mono.Cecil.dll拷贝到同一目录。
我们开始添加CLR的支持,并引用Mono.Cecil dll

>> import clr
>> clr.AddReference("Mono.Cecil")

接着我们要导入所有类型

>> from Mono.Cecil import *

用dir()命令来看看我们加载了那些类型

>> dir()

接下来,我们需要把上面编译好的程序集加载进来。根据FAQ ,我们使用AssemblyFactory类来加载目标程序集。

>> myAsm = AssemblyFactory.GetAssembly("CecilCase.dll")
>> myAsm
<Mono.Cecil.AssemblyDefinition object at 0x000000000000002B [CecilCase, Version=1.0.0.0, Culture=neutral,
PublicKeyToken
=null]>

类型

现在,我们来得到程序集中类型。数量是多少?

>>> myAsm.MainModule.Types.Count
3

3个类型?我想我只编写了2个啊?让我们看看为什么会这样?

>>> for caseType in myAsm.MainModule.Types:
     
print caseType

<Module>
CecilCase.MainCase
CecilCase.SecondCase

So 
<Module> was the hidden type. We'll ignore it from now on.

>>> for caseType in myAsm.MainModule.Types:
     
if caseType.Name != '<Module>':
         
print caseType.Name

MainCase
SecondCase

方法

从一个特定的类型得到其中的方法也是很容易。我们从MainCase开始。

>>> mainCaseType = myAsm.MainModule.Types[1]
>>> mainCaseType
<Mono.Cecil.TypeDefinition object at 0x000000000000002D [CecilCase.MainCase]>

记住[0]类型是<Module>。现在,我们得到了MainCase的类型定义。

>>> mainCaseType.Methods.Count
4
>>> for met in mainCaseType.Methods:
     
print met.Name

PublicMethod
AddMethod
PrivateMethod
MethodWithArgument

得到完整的方法定义

>>> for met in mainCaseType.Methods:
     
print met

System.Void CecilCase.MainCase::PublicMethod()
System.Void CecilCase.MainCase::AddMethod()
System.Void CecilCase.MainCase::PrivateMethod()
System.Void CecilCase.MainCase::MethodWithArgument(System.String)

Method Body:

为了看到方法的内部,我们从第一个方法
MainCase.PublicMethod()开始

>>> pMet = mainCaseType.Methods[0]
>>> pMet
<Mono.Cecil.MethodDefinition object at 0x000000000000002F [System.Void CecilCase
.MainCase::PublicMethod()]
>

为了看到pMet包含的方法和属性,使用dir()

>>> dir(pMet)
[
'Accept''Attributes''Body''CallingConvention''Cctor''Clone''Ctor',
'CustomAttributes''DeclaringType''Equals''ExplicitThis''Finalize''Gene
ricParameters''GetHashCode''GetSentinel''GetType''HasBody''HasThis''
ImplAttributes
''IsAbstract''IsConstructor''IsFinal''IsHideBySignature',
'
IsInternalCall''IsNewSlot''IsRuntime''IsRuntimeSpecialName''IsSpecialNa
me''IsStatic''IsVirtual''MakeDynamicType''MemberwiseClone''MetadataTok
en
''Name''Overrides''PInvokeInfo''Parameters''RVA''Reduce''Referen
ceEquals
''ReturnType''SecurityDeclarations''SemanticsAttributes''This',
'
ToString''__class__''__doc__''__init__''__module__''__new__''__redu
ce__''__reduce_ex__''__repr__''__str__']

这里,我们感兴趣的是Body

>>> pMet.Body
<MethodBody object at 0x0000000000000034>
>>> dir(pMet.Body)
[
'Accept''CilWorker''CodeSize''Equals''ExceptionHandlers''Finalize''
GetHashCode''GetType''InitLocals''Instructions''MakeDynamicType''MaxSt
ack
''MemberwiseClone''Method''Reduce''ReferenceEquals''Scopes''Simpl
ify
''ToString''Variables''__class__''__doc__''__init__''__module__',
 '__new__''__reduce__''__reduce_ex__''__repr__']

现在我们来看看pMet.Body中的Instructions(译者注:Instructions就是IL语句的序列,它是一个集合对象,我们需要枚举它。

>>> for ins in pMet.Body.Instructions:
     
print ins

Mono.Cecil.Cil.Instruction
Mono.Cecil.Cil.Instruction
Mono.Cecil.Cil.Instruction
Mono.Cecil.Cil.Instruction
Mono.Cecil.Cil.Instruction
Mono.Cecil.Cil.Instruction
Mono.Cecil.Cil.Instruction
Mono.Cecil.Cil.Instruction
Mono.Cecil.Cil.Instruction
Mono.Cecil.Cil.Instruction
Mono.Cecil.Cil.Instruction
Mono.Cecil.Cil.Instruction

好,它显示出了我们期望的结果。如果你尝试看看ins是否具有智能感知功能的话,目前是无法正常工作的。所以,我们需要添加从Cecil命名空间中添加所有类型。

>>> from Mono.Cecil.Cil import *

>>> for ins in pMet.Body.Instructions:
     
print ins.OpCode.Name

nop
ldstr
call
nop
ldarg.0
call
nop
newobj
ldstr
call
nop
ret

0
相关文章