在异常块之前是否需要空的评估堆栈?

时间:2014-10-30 16:27:05

标签: c# .net reflection reflection.emit

当我删除Ldstr "a"Call Console.WriteLineRet之前)时,代码运行正常,否则调用时会抛出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();
    }
}

1 个答案:

答案 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找到原因