“Cecil是由Jb Evain 开发,用于生成和浏览ECMA CIL 格式的程序和函数库。它完全支持泛型,支持部分调试符号。简单说来,利用Cecil,你可以加载存在的程序集,浏览其中所有的类型,动态的编辑他们,并保存到磁盘上成为一个新的编辑过的程序集”
以上说明来自:http://www.mono-project.com/Cecil
在这个教程中,我将花一些时间演示在IronPython中使用Cecil的功能。基本上,我就是用Cecil来找到不同的方法,这些方法能从一个方法中被调用。是不是很由意思呀?那么请继续往下看吧。
在继续之前,请下载并安装IronPython和Cecil先。你可以不用下载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);
}
}
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")
>> 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
<Mono.Cecil.AssemblyDefinition object at 0x000000000000002B [CecilCase, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]>
类型
现在,我们来得到程序集中类型。数量是多少?
>>> myAsm.MainModule.Types.Count
3
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
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]>
>>> 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
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)
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
<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__']
['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__']
<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
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
>>> for ins in pMet.Body.Instructions:
print ins.OpCode.Name
nop
ldstr
call
nop
ldarg.0
call
nop
newobj
ldstr
call
nop
ret