Я использую стандартный компилятор VS2015, предназначенный для .Net 4.6.2.
Компилятор испускает бесконечный цикл после неудачного окончательного блока.
Некоторые примеры:
Debug:
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: nop
IL_0003: leave.s IL_000c
} // end .try
finally
{
IL_0005: nop
IL_0006: br.s IL_000a
// loop start (head: IL_000a)
IL_0008: nop
IL_0009: nop
IL_000a: br.s IL_0008
// end loop
} // end handler
// loop start (head: IL_000c)
IL_000c: br.s IL_000c
// end loop
Release:
.try
{
IL_0000: leave.s IL_0004
} // end .try
finally
{
// loop start
IL_0002: br.s IL_0002
// end loop
} // end handler
// loop start (head: IL_0004)
IL_0004: br.s IL_0004
// end loop
Код источника С#
private void _Simple()
{
try
{
}
finally
{
for (;;) { }
}
}
Как вы видите на IL_000c - бесконечный цикл (сгенерированный компилятором)
Хорошо, теперь я покажу вам немного расширенный случай
Debug:
IL_0000: nop
.try
{
IL_0001: nop
.try
{
IL_0002: nop
IL_0003: nop
IL_0004: leave.s IL_000d
} // end .try
finally
{
IL_0006: nop
IL_0007: newobj instance void [mscorlib]System.Exception::.ctor()
IL_000c: throw
} // end handler
// loop start (head: IL_000d)
IL_000d: br.s IL_000d
// end loop
} // end .try
finally
{
IL_000f: nop
IL_0010: newobj instance void [mscorlib]System.Exception::.ctor()
IL_0015: throw
} // end handler
Release:
.try
{
.try
{
IL_0000: leave.s IL_0008
} // end .try
finally
{
IL_0002: newobj instance void [mscorlib]System.Exception::.ctor()
IL_0007: throw
} // end handler
// loop start (head: IL_0008)
IL_0008: br.s IL_0008
// end loop
} // end .try
finally
{
IL_000a: newobj instance void [mscorlib]System.Exception::.ctor()
IL_000f: throw
} // end handler
После того, как вложенный окончательный бесконечный цикл сгенерирован еще раз, но после второго, наконец, нет. (IL_000d)
Источник С#
private void _DoubleFinallyWithThrowingNewException()
{
try
{
try
{
}
finally
{
throw new Exception();
}
}
finally
{
throw new Exception();
}
}
Еще раз, теперь есть явное исключение, вызванное методом, вызванным в конце блока.
Debug:
IL_0000: nop
.try
{
IL_0001: nop
.try
{
IL_0002: nop
IL_0003: nop
IL_0004: leave.s IL_0010
} // end .try
finally
{
IL_0006: nop
IL_0007: ldarg.0
IL_0008: call instance void System.Reflection.Emit.FactoryTests::ThrowException()
IL_000d: nop
IL_000e: nop
IL_000f: endfinally
} // end handler
IL_0010: nop
IL_0011: leave.s IL_001d
} // end .try
finally
{
IL_0013: nop
IL_0014: ldarg.0
IL_0015: call instance void System.Reflection.Emit.FactoryTests::ThrowException()
IL_001a: nop
IL_001b: nop
IL_001c: endfinally
} // end handler
IL_001d: ret
Release:
.try
{
.try
{
IL_0000: leave.s IL_0010
} // end .try
finally
{
IL_0002: ldarg.0
IL_0003: call instance void System.Reflection.Emit.FactoryTests::ThrowException()
IL_0008: endfinally
} // end handler
} // end .try
finally
{
IL_0009: ldarg.0
IL_000a: call instance void System.Reflection.Emit.FactoryTests::ThrowException()
IL_000f: endfinally
} // end handler
IL_0010: ret
Источник С#
private void ThrowException()
{
throw new Exception();
}
private void _DoubleFinallyWithThrowingNewExceptionNotInline()
{
try
{
try
{
}
finally
{
ThrowException();
}
}
finally
{
ThrowException();
}
}
Почему после генерации первого недостижимого окончательного блока создается бесконечный цикл?
Почему EndFinally OpCode не сгенерирован?
@Edit 1
Добавлен несколько msil в режиме Release.
@Edit 2
Добавлен пример с непустым исключением try
Переменная метаданных .maxStack, установленная в 1, и существующие .local переменные немного запутывают - код не связан с этими переменными.
Debug:
.maxstack 1
.locals init (
[0] object someVar,
[1] valuetype [mscorlib]System.DateTime
)
IL_0000: nop
.try
{
IL_0001: nop
.try
{
IL_0002: nop
IL_0003: ldarg.0
IL_0004: call instance void System.Reflection.Emit.FactoryTests::ThrowException()
IL_0009: nop
IL_000a: nop
IL_000b: leave.s IL_0014
} // end .try
finally
{
IL_000d: nop
IL_000e: newobj instance void [mscorlib]System.Exception::.ctor()
IL_0013: throw
} // end handler
// loop start (head: IL_0014)
IL_0014: br.s IL_0014
// end loop
} // end .try
finally
{
IL_0016: nop
IL_0017: newobj instance void [mscorlib]System.Exception::.ctor()
IL_001c: throw
} // end handler
Предыдущий объект [0] был пропущен, но DateTime все еще существует. Выпуск:
.maxstack 1
.locals init (
[0] valuetype [mscorlib]System.DateTime
)
.try
{
.try
{
IL_0000: ldarg.0
IL_0001: call instance void System.Reflection.Emit.FactoryTests::ThrowException()
IL_0006: leave.s IL_000e
} // end .try
finally
{
IL_0008: newobj instance void [mscorlib]System.Exception::.ctor()
IL_000d: throw
} // end handler
// loop start (head: IL_000e)
IL_000e: br.s IL_000e
// end loop
} // end .try
finally
{
IL_0010: newobj instance void [mscorlib]System.Exception::.ctor()
IL_0015: throw
} // end handler`
С#:
private void _ExceptionLeaveReplacementAtFinallyAfterFinallyNonEmpty()
{
try
{
try
{
ThrowException();
}
finally
{
throw new Exception();
}
object someVar = DateTime.Now.GetHashCode();
}
finally
{
throw new Exception();
}
}
Или (Msil идентичен):
private void _ExceptionLeaveReplacementAtFinallyAfterFinallyNonEmpty()
{
try
{
try
{
ThrowException();
}
finally
{
throw new Exception();
}
}
finally
{
throw new Exception();
}
object someVar = DateTime.Now.GetHashCode();