【发布时间】:2016-10-21 17:54:05
【问题描述】:
我正在使用针对 .Net 4.6.2 的标准 VS2015 编译器。
编译器在 finally 块失败后发出无限循环。
一些例子:
调试:
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
发布:
.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
C#源代码
private void _Simple()
{
try
{
}
finally
{
for (;;) { }
}
}
正如您在 IL_000c 看到的那样是无限循环(由编译器生成)
好的,现在我将向您展示一个扩展的案例
调试:
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
发布:
.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
在嵌套 finally 之后再次生成无限循环,但在第二个 finally 之后没有。 (IL_000d)
源 C#
private void _DoubleFinallyWithThrowingNewException()
{
try
{
try
{
}
finally
{
throw new Exception();
}
}
finally
{
throw new Exception();
}
}
再次,现在在 finally 块中调用的方法抛出了非显式异常。
调试:
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
发布:
.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#源代码
private void ThrowException()
{
throw new Exception();
}
private void _DoubleFinallyWithThrowingNewExceptionNotInline()
{
try
{
try
{
}
finally
{
ThrowException();
}
}
finally
{
ThrowException();
}
}
为什么在 first unreachable finally 块之后会生成无限循环?
为什么没有生成 EndFinally OpCode?p>
@编辑 1
在发布模式下添加了一些 msil。
@编辑 2
添加了非空尝试异常的示例
元数据 .maxStack 变量设置为 1,现有的 .local 变量有点混乱 - 没有与此变量相关的代码。
调试:
.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`
C#:
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();
【问题讨论】:
-
在 RELEASE 模式下构建时的输出是什么?
-
我不确定为什么要发出分支操作码,但 finally 块不需要有
endfinally操作码。通过throw操作码离开 finally 块也是完全有效的(两个操作码之一必须存在;对于给定的 finally 块也可能存在多个操作码)。 -
只有当 C#
try块完全为空时才会发生? -
@JeppeStigNielsen 在 LINQPad 中测试:不,编译器仍然会生成无限循环“守卫”,即使 try 块中有内容。
-
我推测 try 结束时的
leave.s指令需要一些目标。需要“无用”的无限循环作为目标。