当我删除Ldstr "a"
和Call Console.WriteLine
(Ret
之前)时,代码运行正常,否则调用时会抛出InvalidProgramException
。这是否意味着需要一个空的评估堆栈?
class Program
{
delegate void Del();
static void Main(string[] args)
{
DynamicMethod dynamicMethod = new DynamicMethod("", null, Type.EmptyTypes);
ILGenerator ilGen = dynamicMethod.GetILGenerator();
ilGen.Emit(OpCodes.Ldstr, "a");
ilGen.BeginExceptionBlock();
ilGen.Emit(OpCodes.Ldstr, "b");
ilGen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null));
ilGen.BeginCatchBlock(typeof(Exception));
ilGen.EndExceptionBlock();
ilGen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null));
ilGen.Emit(OpCodes.Ret);
((Del)dynamicMethod.CreateDelegate(typeof(Del))).Invoke();
}
}
答案 0 :(得分:7)
为了理解你在做什么,我建议你尽量做出最小的例子。
ilGen.Emit(OpCodes.Ldstr, "a");
ilGen.BeginExceptionBlock();
ilGen.BeginCatchBlock(typeof(Exception));
ilGen.EndExceptionBlock();
ilGen.Emit(OpCodes.Pop);
ilGen.Emit(OpCodes.Ret);
之后,您可以使用AssemblyBuilder
将给定代码转储到可执行文件中。如果已完成,ildasm
将显示已生成的内容。
// Code size 17 (0x11)
.maxstack 2
IL_0000: ldstr "a"
.try
{
IL_0005: leave IL_000f
} // end .try
catch [mscorlib]System.Exception
{
IL_000a: leave IL_000f
} // end handler
IL_000f: pop
IL_0010: ret
如您所见,我们将转到leave
指令跳转到pop
。然后,您可以谷歌leave
,其中声明:
leave指令类似于br指令,但它可以 用于退出try,filter或catch块而普通分支 指令只能在这样的块中用于传输控制 在其中。 离开指令清空评估堆栈和 确保执行适当的周围finally块。
但是,为什么以下工作没有呢?
ilGen.Emit(OpCodes.Ldstr, "a");
ilGen.BeginExceptionBlock();
ilGen.BeginCatchBlock(typeof(Exception));
ilGen.EndExceptionBlock();
//ilGen.Emit(OpCodes.Pop);
ilGen.Emit(OpCodes.Ret);
我怀疑它可能不是“物理限制”,而是验证问题。让我们运行peverify ourapp.exe
,看看我们得到了什么:
[IL]: Error: [C:\temp\test.exe : Program::Main][offset 0x00000005] Attempt to en
ter a try block with nonempty stack.
1 Error(s) Verifying C:\temp\test.exe
此时,你可能会像,扫管笏?通过一点谷歌搜索,您可以得到错误代码 0x801318A9 。通过SSCLI2.0快速扫描来源:
case ReaderBaseNS::RGN_TRY:
// Entering a try region, the evaluation stack is required to be empty.
if (!m_readerStack->empty()) {
BADCODE(MVER_E_TRY_N_EMPTY_STACK);
}
break;
现在,这很酷,但如果你很讨厌,你可能想知道为什么评估堆栈必须为空?
为此,您可能想看看ECMA C# and Common Language Infrastructure Standards。我怀疑你可以从PartitionIII CIL.pdf找到原因