/// <summary> /// If the finally statement contains break, continue, return or yield, we need to /// handle the control flow statement after we exit out of finally via OpCodes.Endfinally. /// </summary> private static void EmitFinallyFlowControl(CodeGen cg, TryFlowResult flow, Slot flag) { if (flow.Return || flow.Yield) { Debug.Assert(flag != null); Label noReturn = cg.DefineLabel(); flag.EmitGet(cg); cg.EmitInt(CodeGen.BranchForReturn); cg.Emit(OpCodes.Bne_Un, noReturn); if (cg.IsGenerator) { // return true from the generator method cg.Emit(OpCodes.Ldc_I4_1); cg.EmitReturn(); } else if (flow.Any) { // return the actual value cg.EmitReturnValue(); cg.EmitReturn(); } cg.MarkLabel(noReturn); } // Only emit break handling if it is actually needed if (flow.Break) { Debug.Assert(flag != null); Label noReturn = cg.DefineLabel(); flag.EmitGet(cg); cg.EmitInt(CodeGen.BranchForBreak); cg.Emit(OpCodes.Bne_Un, noReturn); cg.EmitBreak(); cg.MarkLabel(noReturn); } // Only emit continue handling if it if actually needed if (flow.Continue) { Debug.Assert(flag != null); Label noReturn = cg.DefineLabel(); flag.EmitGet(cg); cg.EmitInt(CodeGen.BranchForContinue); cg.Emit(OpCodes.Bne_Un, noReturn); cg.EmitContinue(); cg.MarkLabel(noReturn); } }
public override void Emit(CodeGen cg) { // Codegen is affected by presence/absence of loop control statements // (break/continue) or return/yield statement in finally clause TryFlowResult flow = TryFlowAnalyzer.Analyze(FinallyStatement); //cg.EmitPosition(Start, _header); // If there's a yield anywhere, go for a complex codegen if (YieldInBlock(_tryYields) || _yieldInCatch || YieldInBlock(_finallyYields)) { EmitGeneratorTry(cg, flow); } else { EmitSimpleTry(cg, flow); } }
private void EmitSimpleTry(CodeGen cg, TryFlowResult flow) { // // Initialize the flow control flag // Slot flowControlFlag = null; if (flow.Any) { Debug.Assert(_finally != null); flowControlFlag = cg.GetLocalTmp(typeof(int)); cg.EmitInt(CodeGen.FinallyExitsNormally); flowControlFlag.EmitSet(cg); // If there is a control flow in finally, emit outer: // try { // // try block body and all catch handling // } catch (Exception all) { // saved = all; // } finally { // finally_body // if (saved != null) { // throw saved; // } // } if (HaveHandlers()) { cg.PushExceptionBlock(TargetBlockType.Try, flowControlFlag); cg.BeginExceptionBlock(); } } //****************************************************************** // 1. ENTERING TRY //****************************************************************** cg.PushExceptionBlock(TargetBlockType.Try, flowControlFlag); cg.BeginExceptionBlock(); //****************************************************************** // 2. Emit the try statement body //****************************************************************** _body.Emit(cg); //cg.EmitSequencePointNone(); //****************************************************************** // 3. Emit the catch blocks //****************************************************************** if (HaveHandlers()) { cg.PushExceptionBlock(TargetBlockType.Catch, flowControlFlag); foreach (CatchBlock cb in _handlers) { // Begin the strongly typed exception block cg.BeginCatchBlock(cb.Test); // Save the exception (if the catch block asked for it) or pop EmitSaveExceptionOrPop(cg, cb); // // Emit the catch block body // cb.Body.Emit(cg); } cg.PopTargets(TargetBlockType.Catch); } //****************************************************************** // 4. Emit the finally block //****************************************************************** if (_finally != null) { Slot rethrow = null; if (flow.Any) { // If there is a control flow in finally, end the catch // statement and emit the catch-all and finally clause // with rethrow at the end. if (HaveHandlers()) { cg.EndExceptionBlock(); cg.PopTargets(TargetBlockType.Try); } cg.PushExceptionBlock(TargetBlockType.Catch, flowControlFlag); cg.BeginCatchBlock(typeof(Exception)); rethrow = cg.GetLocalTmp(typeof(Exception)); rethrow.EmitSet(cg); cg.PopTargets(TargetBlockType.Catch); } cg.PushExceptionBlock(TargetBlockType.Finally, flowControlFlag); cg.BeginFinallyBlock(); // // Emit the finally block body // _finally.Emit(cg); if (flow.Any) { Debug.Assert(rethrow != null); Label noRethrow = cg.DefineLabel(); rethrow.EmitGet(cg); cg.EmitNull(); cg.Emit(OpCodes.Beq, noRethrow); rethrow.EmitGet(cg); cg.Emit(OpCodes.Throw); cg.MarkLabel(noRethrow); } cg.EndExceptionBlock(); cg.PopTargets(TargetBlockType.Finally); } else { cg.EndExceptionBlock(); } cg.PopTargets(TargetBlockType.Try); // // Emit the flow control for finally, if there was any. // EmitFinallyFlowControl(cg, flow, flowControlFlag); cg.FreeLocalTmp(flowControlFlag); }
private void EmitGeneratorTry(CodeGen cg, TryFlowResult flow) { // // Initialize the flow control flag // Slot flowControlFlag = null; if (flow.Any) { flowControlFlag = cg.GetLocalTmp(typeof(int)); cg.EmitInt(CodeGen.FinallyExitsNormally); flowControlFlag.EmitSet(cg); } Slot exception = null; if (_finally != null) { exception = cg.GetTemporarySlot(typeof(Exception)); cg.EmitNull(); exception.EmitSet(cg); } //****************************************************************** // Entering the try block //****************************************************************** if (_target != null) { cg.MarkLabel(_target.EnsureLabel(cg)); } //****************************************************************** // If we have a 'finally', transform it into try..catch..finally // and rethrow //****************************************************************** Label endFinallyBlock = new Label(); if (_finally != null) { cg.PushExceptionBlock(TargetBlockType.Try, flowControlFlag); cg.BeginExceptionBlock(); endFinallyBlock = cg.DefineLabel(); //************************************************************** // If there is a yield in any catch, that catch will be hoisted // and we need to dispatch to it from here //************************************************************** if (_yieldInCatch) { EmitYieldDispatch(_catchYields, cg); } if (YieldInBlock(_finallyYields)) { foreach (YieldTarget yt in _finallyYields) { cg.GotoRouter.EmitGet(cg); cg.EmitInt(yt.Index); cg.Emit(OpCodes.Beq, endFinallyBlock); } } } //****************************************************************** // If we have a 'catch', start a try block to handle all the catches //****************************************************************** Label endCatchBlock = new Label(); if (HaveHandlers()) { cg.PushExceptionBlock(TargetBlockType.Try, flowControlFlag); endCatchBlock = cg.BeginExceptionBlock(); } //****************************************************************** // Emit the try block body //****************************************************************** // First, emit the dispatch within the try block EmitYieldDispatch(_tryYields, cg); // Then, emit the actual body _body.Emit(cg); //cg.EmitSequencePointNone(); //****************************************************************** // Emit the catch blocks //****************************************************************** if (HaveHandlers()) { List <CatchRecord> catches = new List <CatchRecord>(); cg.PushExceptionBlock(TargetBlockType.Catch, flowControlFlag); foreach (CatchBlock cb in _handlers) { cg.BeginCatchBlock(cb.Test); if (cb.Yield) { // The catch block body contains yield, therefore // delay the body emit till after the try block. Slot slot = cg.GetLocalTmp(cb.Test); slot.EmitSet(cg); catches.Add(new CatchRecord(slot, cb)); } else { // Save the exception (if the catch block asked for it) or pop EmitSaveExceptionOrPop(cg, cb); // Emit the body right now, since it doesn't contain yield cb.Body.Emit(cg); } } cg.PopTargets(TargetBlockType.Catch); cg.EndExceptionBlock(); cg.PopTargets(TargetBlockType.Try); //****************************************************************** // Emit the postponed catch block bodies (with yield in them) //****************************************************************** foreach (CatchRecord cr in catches) { Label next = cg.DefineLabel(); cr.Slot.EmitGet(cg); cg.EmitNull(); cg.Emit(OpCodes.Beq, next); if (cr.Block.Slot != null) { cr.Block.Slot.EmitSet(cg, cr.Slot); } cg.FreeLocalTmp(cr.Slot); cr.Block.Body.Emit(cg); cg.MarkLabel(next); //cg.EmitSequencePointNone(); } } //****************************************************************** // Emit the finally body //****************************************************************** if (_finally != null) { cg.MarkLabel(endFinallyBlock); cg.PushExceptionBlock(TargetBlockType.Catch, flowControlFlag); cg.BeginCatchBlock(typeof(Exception)); exception.EmitSet(cg); cg.PopTargets(TargetBlockType.Catch); cg.PushExceptionBlock(TargetBlockType.Finally, flowControlFlag); cg.BeginFinallyBlock(); Label noExit = cg.DefineLabel(); cg.GotoRouter.EmitGet(cg); cg.EmitInt(CodeGen.GotoRouterYielding); cg.Emit(OpCodes.Bne_Un_S, noExit); cg.Emit(OpCodes.Endfinally); cg.MarkLabel(noExit); EmitYieldDispatch(_finallyYields, cg); // Emit the finally body _finally.Emit(cg); // Rethrow the exception, if any Label noThrow = cg.DefineLabel(); exception.EmitGet(cg); cg.EmitNull(); cg.Emit(OpCodes.Beq, noThrow); exception.EmitGet(cg); cg.Emit(OpCodes.Throw); cg.MarkLabel(noThrow); cg.FreeLocalTmp(exception); cg.EndExceptionBlock(); cg.PopTargets(TargetBlockType.Finally); cg.PopTargets(TargetBlockType.Try); // // Emit the flow control for finally, if there was any. // EmitFinallyFlowControl(cg, flow, flowControlFlag); //cg.EmitSequencePointNone(); } // Clear the target labels ClearLabels(_tryYields); ClearLabels(_catchYields); if (_target != null) { _target.Clear(); } }
private void EmitGeneratorTry(CodeGen cg, TryFlowResult flow) { // // Initialize the flow control flag // Slot flowControlFlag = null; if (flow.Any) { flowControlFlag = cg.GetLocalTmp(typeof(int)); cg.EmitInt(CodeGen.FinallyExitsNormally); flowControlFlag.EmitSet(cg); } Slot exception = null; if (_finally != null) { exception = cg.GetTemporarySlot(typeof(Exception)); cg.EmitNull(); exception.EmitSet(cg); } //****************************************************************** // Entering the try block //****************************************************************** if (_target != null) { cg.MarkLabel(_target.EnsureLabel(cg)); } //****************************************************************** // If we have a 'finally', transform it into try..catch..finally // and rethrow //****************************************************************** Label endFinallyBlock = new Label(); if (_finally != null) { cg.PushExceptionBlock(TargetBlockType.Try, flowControlFlag); cg.BeginExceptionBlock(); endFinallyBlock = cg.DefineLabel(); //************************************************************** // If there is a yield in any catch, that catch will be hoisted // and we need to dispatch to it from here //************************************************************** if (_yieldInCatch) { EmitYieldDispatch(_catchYields, cg); } if (YieldInBlock(_finallyYields)) { foreach (YieldTarget yt in _finallyYields) { cg.GotoRouter.EmitGet(cg); cg.EmitInt(yt.Index); cg.Emit(OpCodes.Beq, endFinallyBlock); } } } //****************************************************************** // If we have a 'catch', start a try block to handle all the catches //****************************************************************** Label endCatchBlock = new Label(); if (HaveHandlers()) { cg.PushExceptionBlock(TargetBlockType.Try, flowControlFlag); endCatchBlock = cg.BeginExceptionBlock(); } //****************************************************************** // Emit the try block body //****************************************************************** // First, emit the dispatch within the try block EmitYieldDispatch(_tryYields, cg); // Then, emit the actual body _body.Emit(cg); //cg.EmitSequencePointNone(); //****************************************************************** // Emit the catch blocks //****************************************************************** if (HaveHandlers()) { List<CatchRecord> catches = new List<CatchRecord>(); cg.PushExceptionBlock(TargetBlockType.Catch, flowControlFlag); foreach (CatchBlock cb in _handlers) { cg.BeginCatchBlock(cb.Test); if (cb.Yield) { // The catch block body contains yield, therefore // delay the body emit till after the try block. Slot slot = cg.GetLocalTmp(cb.Test); slot.EmitSet(cg); catches.Add(new CatchRecord(slot, cb)); } else { // Save the exception (if the catch block asked for it) or pop EmitSaveExceptionOrPop(cg, cb); // Emit the body right now, since it doesn't contain yield cb.Body.Emit(cg); } } cg.PopTargets(TargetBlockType.Catch); cg.EndExceptionBlock(); cg.PopTargets(TargetBlockType.Try); //****************************************************************** // Emit the postponed catch block bodies (with yield in them) //****************************************************************** foreach (CatchRecord cr in catches) { Label next = cg.DefineLabel(); cr.Slot.EmitGet(cg); cg.EmitNull(); cg.Emit(OpCodes.Beq, next); if (cr.Block.Slot != null) { cr.Block.Slot.EmitSet(cg, cr.Slot); } cg.FreeLocalTmp(cr.Slot); cr.Block.Body.Emit(cg); cg.MarkLabel(next); //cg.EmitSequencePointNone(); } } //****************************************************************** // Emit the finally body //****************************************************************** if (_finally != null) { cg.MarkLabel(endFinallyBlock); cg.PushExceptionBlock(TargetBlockType.Catch, flowControlFlag); cg.BeginCatchBlock(typeof(Exception)); exception.EmitSet(cg); cg.PopTargets(TargetBlockType.Catch); cg.PushExceptionBlock(TargetBlockType.Finally, flowControlFlag); cg.BeginFinallyBlock(); Label noExit = cg.DefineLabel(); cg.GotoRouter.EmitGet(cg); cg.EmitInt(CodeGen.GotoRouterYielding); cg.Emit(OpCodes.Bne_Un_S, noExit); cg.Emit(OpCodes.Endfinally); cg.MarkLabel(noExit); EmitYieldDispatch(_finallyYields, cg); // Emit the finally body _finally.Emit(cg); // Rethrow the exception, if any Label noThrow = cg.DefineLabel(); exception.EmitGet(cg); cg.EmitNull(); cg.Emit(OpCodes.Beq, noThrow); exception.EmitGet(cg); cg.Emit(OpCodes.Throw); cg.MarkLabel(noThrow); cg.FreeLocalTmp(exception); cg.EndExceptionBlock(); cg.PopTargets(TargetBlockType.Finally); cg.PopTargets(TargetBlockType.Try); // // Emit the flow control for finally, if there was any. // EmitFinallyFlowControl(cg, flow, flowControlFlag); //cg.EmitSequencePointNone(); } // Clear the target labels ClearLabels(_tryYields); ClearLabels(_catchYields); if (_target != null) { _target.Clear(); } }