I'm using standard VS2015 compiler targeted for .Net 4.6.2.
Compilator emits infinite loop after failing finally block.
Some examples:
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
Source C# code
private void _Simple()
{
try
{
}
finally
{
for (;;) { }
}
}
As you see at IL_000c is infinite loop (generated by compilator)
Ok, now I'll show you a bit extended case
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
After nested finally infinite loop is generated once again, but after second finally is not. (IL_000d)
Source C#
private void _DoubleFinallyWithThrowingNewException()
{
try
{
try
{
}
finally
{
throw new Exception();
}
}
finally
{
throw new Exception();
}
}
One again, now there is non explicit exception thrown by method called at finally block.
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
C# Source
private void ThrowException()
{
throw new Exception();
}
private void _DoubleFinallyWithThrowingNewExceptionNotInline()
{
try
{
try
{
}
finally
{
ThrowException();
}
}
finally
{
ThrowException();
}
}
Why after first unreachable finally block infinite loop is generated?
Why EndFinally OpCode is not generated?
Added some msil at Release mode.
Added example with non empty try exception
The metadata .maxStack variable setted to 1, and existing .local variables are a bit confusing - there is no code connected with this variables.
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
The previous object[0] has been skipped, but DateTime is still there. Release:
.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`
C#:
private void _ExceptionLeaveReplacementAtFinallyAfterFinallyNonEmpty()
{
try
{
try
{
ThrowException();
}
finally
{
throw new Exception();
}
object someVar = DateTime.Now.GetHashCode();
}
finally
{
throw new Exception();
}
}
Or (Msil is identical):
private void _ExceptionLeaveReplacementAtFinallyAfterFinallyNonEmpty()
{
try
{
try
{
ThrowException();
}
finally
{
throw new Exception();
}
}
finally
{
throw new Exception();
}
object someVar = DateTime.Now.GetHashCode();
An infinite loop occurs when a condition always evaluates to true. Usually, this is an error. For example, you might have a loop that decrements until it reaches 0.
Infinite loops This happens when the true/false expression never returns a false value. While there can be some cases where you might want this to happen, most often an infinite loop is the result of a bug in your program's logic.
do while true java. We can create an infinite loop by passing boolean expression as true in the do while loop. Here is a simple do while java infinite loop example.
Yes, the finally block will be executed even after a return statement in a method. The finally block will always execute even an exception occurred or not in Java. If we call the System.
This is by design. As long as you cannot reach that infinite loop :-)
Thanks for reporting this issue though!!!
=== longer version:
When "finally" does not terminate (contains a throw or an infinite loop), the code after the try statement becomes unreachable form the language prospective. Since it is unreachable, it is allowed to have no code there whatsoever, even if, for example, the method needs to return a value.
In fact, because various invariants, which typically hold in normal code are not enforced in unreachable code, compiler defensively removes code that is unreachable even if it is present. This is not just an optimization, it is often required for correctness. Instead of preventing/detecting/fixing violations in unreachable code it is cleaner to just remove it.
Now, IL specification requires that the "leave" opcode points to a valid target instruction. In particular it does not care whether the branch is blocked by a nonterminating finally. But we do not have any valid code to point to, so we need to inject a "landing" piece of code. It must be small. We also know that it would not be ever reachable, but it also must not endanger already established static correctness of the method.
An infinite loop is a smallest piece of code like that.
BTW, another possibility could be "throw null", but historically an infinite loop is used.
No, NOP would not work because it would make the next instruction verifier-reachable, and that can result in violations of other IL rules like "do not drop through the end of the method, must use ret".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With