2014-10-30 2 views
5

Когда я удаляю Ldstr "a" и Call Console.WriteLine (до Ret), код работает нормально, иначе InvalidProgramException вызывается при вызове. Означает ли это, что требуется пустой стек оценки?Является ли пустой стек проверки перед блоком исключений?

class Program 
{ 
    delegate void Del(); 

    static void Main(string[] args) 
    { 
     DynamicMethod dynamicMethod = new DynamicMethod("", null, Type.EmptyTypes); 
     ILGenerator ilGen = dynamicMethod.GetILGenerator(); 
     ilGen.Emit(OpCodes.Ldstr, "a"); 

     ilGen.BeginExceptionBlock(); 
     ilGen.Emit(OpCodes.Ldstr, "b"); 
     ilGen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null)); 
     ilGen.BeginCatchBlock(typeof(Exception)); 
     ilGen.EndExceptionBlock(); 

     ilGen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null)); 
     ilGen.Emit(OpCodes.Ret); 

     ((Del)dynamicMethod.CreateDelegate(typeof(Del))).Invoke(); 
    } 
} 
+0

Я подозреваю, что это так (что стек оценки должен быть пустым, прежде чем вводить новый кадр исключения), но я не могу найти официальную ссылку прямо сейчас. –

ответ

7

Чтобы понять, что вы делаете, я предлагаю вам сделать как можно более минимальный пример.

ilGen.Emit(OpCodes.Ldstr, "a"); 

ilGen.BeginExceptionBlock(); 
ilGen.BeginCatchBlock(typeof(Exception)); 
ilGen.EndExceptionBlock(); 

ilGen.Emit(OpCodes.Pop); 
ilGen.Emit(OpCodes.Ret); 

После этого, вы можете использовать AssemblyBuilder сбросить данный код в исполняемый файл. Если это будет сделано, ildasm покажет, что было создано.

// Code size  17 (0x11) 
    .maxstack 2 
    IL_0000: ldstr  "a" 
    .try 
    { 
    IL_0005: leave  IL_000f 
    } // end .try 
    catch [mscorlib]System.Exception 
    { 
    IL_000a: leave  IL_000f 
    } // end handler 
    IL_000f: pop 
    IL_0010: ret 

Как вы можете видеть, мы достигнем к leave инструкции, которая перескакивает на pop. Вы можете Google о leave, в котором говорится, что:

Инструкция отпуска аналогична инструкции бр, но он может быть используется для выхода попробовать, фильтр, или поймать блок, тогда как в обычной ветви инструкции могут используется только в таком блоке для передачи управления внутри него. Команда «Оставшийся» освобождает стопку оценки и гарантирует, что будут выполнены соответствующие окружающие окончательные блоки.

Однако почему тогда не работает следующая работа?

ilGen.Emit(OpCodes.Ldstr, "a"); 

ilGen.BeginExceptionBlock(); 
ilGen.BeginCatchBlock(typeof(Exception)); 
ilGen.EndExceptionBlock(); 

//ilGen.Emit(OpCodes.Pop); 
ilGen.Emit(OpCodes.Ret); 

Я подозреваю, что это может быть не «физический предел», а проблема проверки. Бежим peverify ourapp.exe и посмотрим, что мы получаем:

[IL]: Error: [C:\temp\test.exe : Program::Main][offset 0x00000005] Attempt to en 
ter a try block with nonempty stack. 
1 Error(s) Verifying C:\temp\test.exe 

На данный момент, вы можете быть как, ват? С небольшим количеством поисковых запросов вы можете найти код ошибки 0x801318A9. Быстрое сканирование через источники SSCLI2.0:

case ReaderBaseNS::RGN_TRY: 
    // Entering a try region, the evaluation stack is required to be empty. 
    if (!m_readerStack->empty()) { 
     BADCODE(MVER_E_TRY_N_EMPTY_STACK);      
    } 
    break; 

Теперь, это здорово, но если вы вызывающие, вы можете спросить, почему стек оценки должны быть пустыми?

Для этого вы, вероятно, захотите взглянуть на ECMA C# and Common Language Infrastructure Standards. Я подозреваю, что вы можете найти причину из PartitionIII CIL.pdf

+0

'peverify', потрясающий! – Chris

 Смежные вопросы

  • Нет связанных вопросов^_^