在异常块之前需要一个空的评估堆栈

本文关键字:一个 堆栈 评估 异常 | 更新日期: 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 中找到原因

相关文章: