2014-10-30 10 views
5

Wenn ich Ldstr "a" und Call Console.WriteLine (vor Ret) entferne, läuft der Code gut, andernfalls wird beim Aufruf ein InvalidProgramException ausgelöst. Bedeutet dies, dass ein leerer Auswertungsstapel benötigt wird?Ist vor einem Ausnahmeblock ein leerer Auswertungsstapel erforderlich?

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

finden könnten Ich vermute, dass dies der Fall ist (dass der Auswertungsstapel leer sein muss, bevor ein neues Ausnahmerahmen eingeführt wird), aber ich kann eine offizielle Referenz jetzt nicht finden. –

Antwort

7

Um zu verstehen, was Sie tun, schlage ich vor, Sie so minimal wie möglich zu machen.

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

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

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

Danach können Sie AssemblyBuilder verwenden den angegebenen Code in das ausführbare dump. Wenn das erledigt ist, zeigt ildasm was generiert wurde.

// 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 

Wie Sie sehen können, werden wir auf die leave Anweisung erreichen, die zu pop springt. Anschließend können Sie Google über leave, wo es heißt:

Der Urlaub Anweisung an die br Anweisung ähnlich ist, aber es kann verwendet, um zu beenden versuchen, einen Filter oder catch-Block während des normalen Zweiges Anweisungen können sein nur in einem solchen Block verwendet werden, um die Steuerung darin zu übertragen. Die Leave-Anweisung leert den Auswertestack und sorgt dafür, dass die entsprechenden umgebenden finally-Blöcke ausgeführt werden.

Warum funktioniert das Folgende dann nicht?

Ich vermute, es könnte nicht "physikalische Grenze" sein, aber ein Verifizierungsproblem. Lassen Sie uns laufen peverify ourapp.exe und sehen, was wir bekommen:

[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 

An dieser Stelle könnten Sie sein mögen, wat? Mit etwas Googeln können Sie den Fehlercode 0x801318A9 eingeben. Quick Scan durch SSCLI2.0 Quellen:

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; 

Nun, dies ist cool, aber wenn Sie geeky sind, könnten Sie sich fragen, warum die Auswertungsstapel leer sein muss?

Dafür möchten Sie wahrscheinlich einen Blick auf ECMA C# and Common Language Infrastructure Standards werfen. Ich vermute, dass Sie den Grund von PartitionIII CIL.pdf

+0

'peverify', fantastisch! – Chris

Verwandte Themen