为什么 ILGenerator 在 Foreach 语句中插入 Leave 指令
Why ILGenerator inserts Leave instruction into Foreach statement
我生成以下代码:
public override void Map(IEnumerable enumerable1)
{
List<int> list = new List<int>();
foreach (object obj2 in enumerable1)
{
}
}
至 Emit
完整代码如下:
MethodBuilder mapMethod = typeBuilder.DefineMethod("Map", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), new[] { typeof(IEnumerable) });
ILGenerator il = mapMethod.GetILGenerator();
LocalBuilder result = il.DeclareLocal(typeof(List<int>)); //0
LocalBuilder item = il.DeclareLocal(typeof(object)); //1
LocalBuilder enumeartor = il.DeclareLocal(typeof(IEnumerator)); //2
LocalBuilder dispose = il.DeclareLocal(typeof(IDisposable)); //3
Label labelWhile = il.DefineLabel();
Label labelReturn = il.DefineLabel();
Label labelMoveNext = il.DefineLabel();
Label labelEndFinally = il.DefineLabel();
//Create result List
ConstructorInfo constructorInfo = (typeof(List<int>).GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Newobj, constructorInfo);
il.Emit(OpCodes.Stloc_0, result);
il.Emit(OpCodes.Ldarg_1);
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerable).GetMethod("GetEnumerator"), Type.EmptyTypes);
il.Emit(OpCodes.Stloc_2, enumeartor);
il.BeginExceptionBlock();
il.Emit(OpCodes.Br_S, labelMoveNext);
il.MarkLabel(labelWhile);
il.Emit(OpCodes.Ldloc_2);
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerator).GetProperty("Current").GetGetMethod(), Type.EmptyTypes);
il.Emit(OpCodes.Stloc_1, item);
il.Emit(OpCodes.Ldloc_1);
il.MarkLabel(labelMoveNext);
il.Emit(OpCodes.Ldloc_2);
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerator).GetMethod("MoveNext"), Type.EmptyTypes);
il.Emit(OpCodes.Brtrue_S, labelWhile);
THE ISSUE IS HERE, I don't insert Leave instruction, but it there
// il.Emit(OpCodes.Leave_S, labelReturn);
il.BeginFinallyBlock();
il.Emit(OpCodes.Ldloc_2);
il.Emit(OpCodes.Isinst, typeof(IDisposable));
il.Emit(OpCodes.Stloc_3, dispose);
il.Emit(OpCodes.Ldloc_3);
il.Emit(OpCodes.Brfalse_S, labelEndFinally);
il.Emit(OpCodes.Ldloc_3);
il.EmitCall(OpCodes.Callvirt, typeof(IDisposable).GetMethod("Dispose"), Type.EmptyTypes);
il.MarkLabel(labelEndFinally);
il.EndExceptionBlock();
il.MarkLabel(labelReturn);
il.Emit(OpCodes.Ret);
这是结果 IL(参见 IL_001f
):
.method public virtual instance void Map(class [mscorlib]System.Collections.IEnumerable A_1) cil managed
{
// Code size 54 (0x36)
.maxstack 5
.locals init (class [mscorlib]System.Collections.Generic.List`1<int32> V_0,
object V_1,
class [mscorlib]System.Collections.IEnumerator V_2,
class [mscorlib]System.IDisposable V_3)
IL_0000: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
IL_0005: stloc.0
IL_0006: ldarg.1
IL_0007: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator()
IL_000c: stloc.2
.try
{
IL_000d: br.s IL_0017
IL_000f: ldloc.2
IL_0010: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
IL_0015: stloc.1
IL_0016: ldloc.1
IL_0017: ldloc.2
IL_0018: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_001d: brtrue.s IL_000f
THE ISSUE IS HERE
IL_001f: leave IL_0035
} // end .try
finally
{
IL_0024: ldloc.2
IL_0025: isinst [mscorlib]System.IDisposable
IL_002a: stloc.3
IL_002b: ldloc.3
IL_002c: brfalse.s IL_0034
IL_002e: ldloc.3
IL_002f: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0034: endfinally
} // end handler
IL_0035: ret
} // end of method ForeachType::Map
请问为什么会出现leave
指令?
Hans Passant thanks for the advice. Following code 解释发生了什么。
public virtual void BeginFinallyBlock()
{
if (m_currExcStackCount==0) {
throw new NotSupportedException(Environment.GetResourceString("Argument_NotInExceptionBlock"));
}
__ExceptionInfo current = m_currExcStack[m_currExcStackCount-1];
int state = current.GetCurrentState();
Label endLabel = current.GetEndLabel();
int catchEndAddr = 0;
if (state != __ExceptionInfo.State_Try)
{
// generate leave for any preceeding catch clause
this.Emit(OpCodes.Leave, endLabel);
catchEndAddr = m_length;
}
MarkLabel(endLabel);
Label finallyEndLabel = this.DefineLabel();
current.SetFinallyEndLabel(finallyEndLabel);
// generate leave for try clause
this.Emit(OpCodes.Leave, finallyEndLabel); HERE'S THE ANSWER
if (catchEndAddr == 0)
catchEndAddr = m_length;
current.MarkFinallyAddr(m_length, catchEndAddr);
}
我生成以下代码:
public override void Map(IEnumerable enumerable1)
{
List<int> list = new List<int>();
foreach (object obj2 in enumerable1)
{
}
}
至 Emit
完整代码如下:
MethodBuilder mapMethod = typeBuilder.DefineMethod("Map", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), new[] { typeof(IEnumerable) });
ILGenerator il = mapMethod.GetILGenerator();
LocalBuilder result = il.DeclareLocal(typeof(List<int>)); //0
LocalBuilder item = il.DeclareLocal(typeof(object)); //1
LocalBuilder enumeartor = il.DeclareLocal(typeof(IEnumerator)); //2
LocalBuilder dispose = il.DeclareLocal(typeof(IDisposable)); //3
Label labelWhile = il.DefineLabel();
Label labelReturn = il.DefineLabel();
Label labelMoveNext = il.DefineLabel();
Label labelEndFinally = il.DefineLabel();
//Create result List
ConstructorInfo constructorInfo = (typeof(List<int>).GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Newobj, constructorInfo);
il.Emit(OpCodes.Stloc_0, result);
il.Emit(OpCodes.Ldarg_1);
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerable).GetMethod("GetEnumerator"), Type.EmptyTypes);
il.Emit(OpCodes.Stloc_2, enumeartor);
il.BeginExceptionBlock();
il.Emit(OpCodes.Br_S, labelMoveNext);
il.MarkLabel(labelWhile);
il.Emit(OpCodes.Ldloc_2);
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerator).GetProperty("Current").GetGetMethod(), Type.EmptyTypes);
il.Emit(OpCodes.Stloc_1, item);
il.Emit(OpCodes.Ldloc_1);
il.MarkLabel(labelMoveNext);
il.Emit(OpCodes.Ldloc_2);
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerator).GetMethod("MoveNext"), Type.EmptyTypes);
il.Emit(OpCodes.Brtrue_S, labelWhile);
THE ISSUE IS HERE, I don't insert Leave instruction, but it there
// il.Emit(OpCodes.Leave_S, labelReturn);
il.BeginFinallyBlock();
il.Emit(OpCodes.Ldloc_2);
il.Emit(OpCodes.Isinst, typeof(IDisposable));
il.Emit(OpCodes.Stloc_3, dispose);
il.Emit(OpCodes.Ldloc_3);
il.Emit(OpCodes.Brfalse_S, labelEndFinally);
il.Emit(OpCodes.Ldloc_3);
il.EmitCall(OpCodes.Callvirt, typeof(IDisposable).GetMethod("Dispose"), Type.EmptyTypes);
il.MarkLabel(labelEndFinally);
il.EndExceptionBlock();
il.MarkLabel(labelReturn);
il.Emit(OpCodes.Ret);
这是结果 IL(参见 IL_001f
):
.method public virtual instance void Map(class [mscorlib]System.Collections.IEnumerable A_1) cil managed
{
// Code size 54 (0x36)
.maxstack 5
.locals init (class [mscorlib]System.Collections.Generic.List`1<int32> V_0,
object V_1,
class [mscorlib]System.Collections.IEnumerator V_2,
class [mscorlib]System.IDisposable V_3)
IL_0000: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
IL_0005: stloc.0
IL_0006: ldarg.1
IL_0007: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator()
IL_000c: stloc.2
.try
{
IL_000d: br.s IL_0017
IL_000f: ldloc.2
IL_0010: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
IL_0015: stloc.1
IL_0016: ldloc.1
IL_0017: ldloc.2
IL_0018: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_001d: brtrue.s IL_000f
THE ISSUE IS HERE
IL_001f: leave IL_0035
} // end .try
finally
{
IL_0024: ldloc.2
IL_0025: isinst [mscorlib]System.IDisposable
IL_002a: stloc.3
IL_002b: ldloc.3
IL_002c: brfalse.s IL_0034
IL_002e: ldloc.3
IL_002f: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0034: endfinally
} // end handler
IL_0035: ret
} // end of method ForeachType::Map
请问为什么会出现leave
指令?
Hans Passant thanks for the advice. Following code 解释发生了什么。
public virtual void BeginFinallyBlock()
{
if (m_currExcStackCount==0) {
throw new NotSupportedException(Environment.GetResourceString("Argument_NotInExceptionBlock"));
}
__ExceptionInfo current = m_currExcStack[m_currExcStackCount-1];
int state = current.GetCurrentState();
Label endLabel = current.GetEndLabel();
int catchEndAddr = 0;
if (state != __ExceptionInfo.State_Try)
{
// generate leave for any preceeding catch clause
this.Emit(OpCodes.Leave, endLabel);
catchEndAddr = m_length;
}
MarkLabel(endLabel);
Label finallyEndLabel = this.DefineLabel();
current.SetFinallyEndLabel(finallyEndLabel);
// generate leave for try clause
this.Emit(OpCodes.Leave, finallyEndLabel); HERE'S THE ANSWER
if (catchEndAddr == 0)
catchEndAddr = m_length;
current.MarkFinallyAddr(m_length, catchEndAddr);
}