CIL-реализация следующего возобновления при ошибке

#.net #vb.net #cil

#.net #vb.net #cil

Вопрос:

Я всегда задавался вопросом: как печально известный VB.NET инструкция «При следующем возобновлении работы с ошибкой» преобразована в CIL? Включает ли это перенос каждой строки с помощью try … catches?

Ответ №1:

Переход.

Вы не можете объединить try-catch с устаревшей обработкой ошибок в одном методе. При использовании On Error Resume Next каждая отдельная строка кода в методе получает метку, а также уникальный идентификатор. В псевдо-коде C #:

 int currentId = 0;

Label1:
currentId = 1;
Line1();

Label2:
currentId = 2;
Line2();

Label3:
currentId = 3;
Line3();

Exit:
 

Весь метод оборачивается в try catch . Когда возникает исключение, обработчик catch проверяет currentId и выполняет простой goto переход к следующей метке в строке (и ProjectError устанавливается). В нашем случае это будет выглядеть примерно так:

 try
{
  ...
}
catch (Exception ex)
{
  ProjectData.SetProjectError(ex);

  if (currentId == 1) goto Label2;
  if (currentId == 2) goto Label3;
  if (currentId == 3) goto Exit;
}
 

Имейте в виду, это всего лишь деталь реализации, которую я получил путем декомпиляции VB.NET применение. Единственным договорным поведением является поведение, определенное в On Error Resume Next , которое в основном сводится к «установить ошибку проекта и перейти к следующему оператору».

Ответ №2:

Вот и все:

 Public Sub A()
End Sub
Public Sub B()
End Sub
Public Sub C()
End Sub
Public Sub D()
End Sub
Public Sub E()
End Sub

Public Sub Test()
    A()
    On Error GoTo ErrorHandler
    B()
    Exit Sub
    C()
    ErrorHandler:
    D()
    Resume Next
    E()
End Sub
 

Компилируется в (без дополнительных методов):

 .method public static void  Test() cil managed
{
  // Code size       201 (0xc9)
  .maxstack  3
  .locals init (int32 V_0,
           int32 V_1,
           int32 V_2)
  IL_0000:  ldc.i4.1
  IL_0001:  stloc.2
  IL_0002:  call       void VBTest.Program::A()
  IL_0007:  call       void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError()
  IL_000c:  ldc.i4.2
  IL_000d:  stloc.0
  IL_000e:  ldc.i4.3
  IL_000f:  stloc.2
  IL_0010:  call       void VBTest.Program::B()
  IL_0015:  leave      IL_00c0
  IL_001a:  ldc.i4.5
  IL_001b:  stloc.2
  IL_001c:  call       void VBTest.Program::C()
  IL_0021:  ldc.i4.6
  IL_0022:  stloc.2
  IL_0023:  call       void VBTest.Program::D()
  IL_0028:  ldc.i4.7
  IL_0029:  stloc.2
  IL_002a:  call       void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError()
  IL_002f:  ldloc.1
  IL_0030:  brtrue.s   IL_0047
  IL_0032:  ldc.i4     0x800a0014
  IL_0037:  call       class [mscorlib]System.Exception [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::CreateProjectError(int32)
  IL_003c:  throw
  IL_003d:  ldc.i4.s   9
  IL_003f:  stloc.2
  IL_0040:  call       void VBTest.Program::E()
  IL_0045:  leave.s    IL_00c0
  IL_0047:  ldloc.1
  IL_0048:  ldc.i4.1
  IL_0049:  add
  IL_004a:  ldc.i4.0
  IL_004b:  stloc.1
  IL_004c:  switch     ( 
                        IL_007d,
                        IL_0000,
                        IL_0007,
                        IL_000e,
                        IL_0015,
                        IL_001a,
                        IL_0021,
                        IL_0028,
                        IL_003d,
                        IL_003d,
                        IL_0045)
  IL_007d:  leave.s    IL_00b5
  IL_007f:  ldloc.2
  IL_0080:  stloc.1
  IL_0081:  ldloc.0
  IL_0082:  switch     ( 
                        IL_0093,
                        IL_0047,
                        IL_0021)
  IL_0093:  leave.s    IL_00b5
  IL_0095:  isinst     [mscorlib]System.Exception
  IL_009a:  ldnull
  IL_009b:  cgt.un
  IL_009d:  ldloc.0
  IL_009e:  ldc.i4.0
  IL_009f:  cgt.un
  IL_00a1:  and
  IL_00a2:  ldloc.1
  IL_00a3:  ldc.i4.0
  IL_00a4:  ceq
  IL_00a6:  and
  IL_00a7:  endfilter
  IL_00a9:  castclass  [mscorlib]System.Exception
  IL_00ae:  call       void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::SetProjectError(class [mscorlib]System.Exception)
  IL_00b3:  leave.s    IL_007f
  IL_00b5:  ldc.i4     0x800a0033
  .try IL_0000 to IL_0095 filter IL_0095 handler IL_00a9 to IL_00b5
  IL_00ba:  call       class [mscorlib]System.Exception [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::CreateProjectError(int32)
  IL_00bf:  throw
  IL_00c0:  ldloc.1
  IL_00c1:  brfalse.s  IL_00c8
  IL_00c3:  call       void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError()
  IL_00c8:  ret
} // end of method Program::Test
 

Эта абберация декомпилируется с помощью рефлектора для этого:

 public static void Test()
{
    int num2;
    try
    {
        int num3;
    Label_0000:
        num3 = 1;
        A();
    Label_0007:
        ProjectData.ClearProjectError();
        int num = 2;
    Label_000E:
        num3 = 3;
        B();
        goto Label_00C0;
    Label_001A:
        num3 = 5;
        C();
    Label_0021:
        num3 = 6;
        D();
    Label_0028:
        num3 = 7;
        ProjectData.ClearProjectError();
        if (num2 != 0)
        {
            goto Label_0047;
        }
        throw ProjectData.CreateProjectError(-2146828268);
    Label_003D:
        num3 = 9;
        E();
        goto Label_00C0;
    Label_0047:
        num2 = 0;
        switch ((num2   1))
        {
            case 1:
                goto Label_0000;

            case 2:
                goto Label_0007;

            case 3:
                goto Label_000E;

            case 4:
            case 10:
                goto Label_00C0;

            case 5:
                goto Label_001A;

            case 6:
                goto Label_0021;

            case 7:
                goto Label_0028;

            case 8:
            case 9:
                goto Label_003D;

            default:
                goto Label_00B5;
        }
    Label_007F:
        num2 = num3;
        switch (num)
        {
            case 0:
                goto Label_00B5;

            case 1:
                goto Label_0047;

            case 2:
                goto Label_0021;
        }
    }
    catch (object obj1) when (?)
    {
        ProjectData.SetProjectError((Exception) obj1);
        goto Label_007F;
    }
Label_00B5:
    throw ProjectData.CreateProjectError(-2146828237);
Label_00C0:
    if (num2 != 0)
    {
        ProjectData.ClearProjectError();
    }
}
 

Помимо того факта, что он использует кучу переключателей и переходов, я понятия не имею, что на самом деле делает этот код.