在异常块之前需要一个空的评估堆栈
本文关键字:一个 堆栈 评估 异常 | 更新日期: 2023-09-27 18:28:24
当我删除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();
}
}
为了理解你在做什么,我建议你尽可能少地做一个例子。
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
,它指出:
请假指令与br指令类似,但可以用于退出try、filter或catch块,而普通分支指令只能在这样的块中用于转移控制其中。leave指令清空评估堆栈并且确保执行适当的环绕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;
现在,这很酷,但如果你是极客,你可能会想,为什么评估堆栈必须是空的?
为此,您可能需要了解ECMAC#和公共语言基础设施标准。我怀疑你可以从PartitionIII CIL.pdf 中找到原因