private void EmitGenerator(CodeGen ncg) { YieldTarget[] targets = YieldLabelBuilder.BuildYieldTargets(this, ncg); Label[] jumpTable = new Label[yieldCount]; for (int i = 0; i < yieldCount; i++) { jumpTable[i] = targets[i].topBranchTarget; } ncg.yieldLabels = jumpTable; ncg.PushTryBlock(); ncg.BeginExceptionBlock(); ncg.Emit(OpCodes.Ldarg_0); ncg.EmitFieldGet(typeof(Generator), "location"); ncg.Emit(OpCodes.Switch, jumpTable); // fall-through on first pass // yield statements will insert the needed labels after their returns body.Emit(ncg); // fall-through is almost always possible in generators, so this // is almost always needed ncg.EmitReturnInGenerator(null); // special handling for StopIteration thrown in body ncg.BeginCatchBlock(typeof(StopIterationException)); ncg.EndExceptionBlock(); ncg.EmitReturnInGenerator(null); ncg.PopTargets(); ncg.Finish(); }
private void EmitFunctionImplementation(CodeGen cg, CodeGen icg, bool context) { if (context) { if (IsClosure) { icg.StaticLinkSlot = icg.GetArgumentSlot(0); } icg.ContextSlot = icg.GetArgumentSlot(0); icg.ModuleSlot = new PropertySlot(icg.ContextSlot, typeof(ICallerContext).GetProperty("Module")); } if (EmitLocalDictionary) { PromoteLocalsToEnvironment(); } if (Options.TracebackSupport) { // push a try for traceback support icg.PushTryBlock(); icg.BeginExceptionBlock(); } // emit the actual body if (yieldCount > 0) { EmitGeneratorBody(icg, cg); } else { EmitFunctionBody(icg, cg); } if (Options.TracebackSupport) { // push a fault block (runs only if there's an exception, doesn't handle the exception) icg.PopTargets(); if (icg.IsDynamicMethod) { icg.BeginCatchBlock(typeof(Exception)); } else { icg.BeginFaultBlock(); } EmitUpdateTraceBack(icg, cg); // end the exception block if (icg.IsDynamicMethod) { icg.Emit(OpCodes.Rethrow); } icg.EndExceptionBlock(); } icg.Finish(); }
public static void EmitStackTraceFaultBlock(CodeGen cg, string name, string displayName) { Contract.RequiresNotNull(cg, "cg"); Contract.RequiresNotNull(name, "name"); Contract.RequiresNotNull(displayName, "displayName"); if (ScriptDomainManager.Options.DynamicStackTraceSupport) { // push a fault block (runs only if there's an exception, doesn't handle the exception) cg.PopTargets(); if (cg.IsDynamicMethod) { cg.BeginCatchBlock(typeof(Exception)); } else { cg.BeginFaultBlock(); } cg.EmitCodeContext(); if (cg.IsDynamicMethod) { cg.ConstantPool.AddData(cg.MethodBase).EmitGet(cg); } else { cg.Emit(OpCodes.Ldtoken, cg.MethodInfo); cg.EmitCall(typeof(MethodBase), "GetMethodFromHandle", new Type[] { typeof(RuntimeMethodHandle) }); } cg.EmitString(name); cg.EmitString(displayName); cg.EmitGetCurrentLine(); cg.EmitCall(typeof(ExceptionHelpers), "UpdateStackTrace"); // end the exception block if (cg.IsDynamicMethod) { cg.Emit(OpCodes.Rethrow); } cg.EndExceptionBlock(); } }
//!!! need to evaluate break/continue through a try block public override void Emit(CodeGen cg) { Slot choiceVar = null; cg.EmitPosition(start, header); if (yieldTargets.Count > 0) { Label startOfBlock = cg.DefineLabel(); choiceVar = cg.GetLocalTmp(typeof(int)); cg.EmitInt(-1); choiceVar.EmitSet(cg); cg.Emit(OpCodes.Br, startOfBlock); int index = 0; foreach (YieldTarget yt in yieldTargets) { cg.MarkLabel(yt.topBranchTarget); cg.EmitInt(index++); choiceVar.EmitSet(cg); cg.Emit(OpCodes.Br, startOfBlock); } cg.MarkLabel(startOfBlock); } Label afterCatch = new Label(); Label afterElse = cg.DefineLabel(); cg.PushTryBlock(); cg.BeginExceptionBlock(); if (yieldTargets.Count > 0) { int index = 0; foreach (YieldTarget yt in yieldTargets) { choiceVar.EmitGet(cg); cg.EmitInt(index); cg.Emit(OpCodes.Beq, yt.tryBranchTarget); index++; } cg.FreeLocalTmp(choiceVar); } body.Emit(cg); if (yieldInExcept) { afterCatch = cg.DefineLabel(); cg.Emit(OpCodes.Leave, afterCatch); } cg.BeginCatchBlock(typeof(Exception)); // Extract state from the carrier exception cg.EmitCallerContext(); cg.EmitCall(typeof(Ops), "ExtractException"); Slot pyExc = cg.GetLocalTmp(typeof(object)); Slot tmpExc = cg.GetLocalTmp(typeof(object)); pyExc.EmitSet(cg); if (yieldInExcept) { cg.EndExceptionBlock(); cg.PopTargets(); } foreach (TryStmtHandler handler in handlers) { cg.EmitPosition(handler.start, handler.header); Label next = cg.DefineLabel(); if (handler.test != null) { pyExc.EmitGet(cg); handler.test.Emit(cg); cg.EmitCall(typeof(Ops), "CheckException"); if (handler.target != null) { tmpExc.EmitSet(cg); tmpExc.EmitGet(cg); } cg.EmitPythonNone(); cg.Emit(OpCodes.Ceq); cg.Emit(OpCodes.Brtrue, next); } if (handler.target != null) { tmpExc.EmitGet(cg); handler.target.EmitSet(cg); } cg.PushExceptionBlock(Targets.TargetBlockType.Catch, null); handler.body.Emit(cg); cg.EmitCallerContext(); cg.EmitCall(typeof(Ops), "ClearException"); cg.PopTargets(); if (yieldInExcept) { cg.Emit(OpCodes.Br, afterElse); } else { cg.Emit(OpCodes.Leave, afterElse); } cg.MarkLabel(next); } cg.FreeLocalTmp(tmpExc); if (yieldInExcept) { pyExc.EmitGet(cg); cg.Emit(OpCodes.Throw); cg.MarkLabel(afterCatch); } else { cg.Emit(OpCodes.Rethrow); cg.EndExceptionBlock(); cg.PopTargets(); } if (elseStmt != null) { elseStmt.Emit(cg); } cg.MarkLabel(afterElse); cg.FreeLocalTmp(pyExc); yieldTargets.Clear(); }
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 EmitWithCatchBlock(CodeGen cg, Slot exc, Slot exit) { cg.BeginCatchBlock(typeof(Exception)); // Extract state from the carrier exception cg.EmitCallerContext(); cg.EmitCall(typeof(Ops), "ExtractException", new Type[] { typeof(Exception), typeof(ICallerContext) }); cg.Emit(OpCodes.Pop); // except body cg.PushExceptionBlock(Targets.TargetBlockType.Catch, null, null); cg.EmitConstantBoxed(false); exc.EmitSet(cg); cg.EmitCallerContext(); exit.EmitGet(cg); cg.EmitObjectArray(new Expression[0]); cg.EmitCallerContext(); cg.EmitCall(typeof(Ops), "ExtractSysExcInfo"); cg.EmitCall(typeof(Ops), "CallWithArgsTupleAndContext", new Type[] { typeof(ICallerContext), typeof(object), typeof(object[]), typeof(object) }); Label afterRaise = cg.DefineLabel(); cg.EmitTestTrue(); cg.Emit(OpCodes.Brtrue, afterRaise); cg.EmitCall(typeof(Ops), "Raise", new Type[0]); //, new Type[] { typeof(object), typeof(SymbolId) }); cg.MarkLabel(afterRaise); cg.EmitCallerContext(); cg.EmitCall(typeof(Ops), "ClearException", new Type[] { typeof(ICallerContext) }); cg.PopTargets(); }
// codegen algorithm for unified try-catch-else-finally // isTryYielded = false // isCatchYielded = false // isFinallyYielded = false // isElseYielded = false // Set up the labels for Try Yield Targets // Set up the labels for Catch Yield Targets // Set up the labels for Else Yield Targets // Set up the labels for Finally Yield Targets // returnVar = false // isElseBlock = false //TRY: // if isCatchYielded : // rethow storedException // if finallyYielded : // goto endOfTry // if isElseYielded : // goto beginElseBlock // if isTryYielded: // isTryYielded = false // goto desired_label_in_TRY-BODY // TRY-BODY // beginElseBlock: # Note we are still under TRY // isElseBlock = true // if isElseYielded : // isElseYielded = false // goto desired_label_in_ELSE-BODY // ELSE-BODY // endOfTry: //EXCEPT: # catches any exception // if isElseBlock: // rethrow // pyExc = ExtractException() // storedException = ExtractSysExcInfo() // if pyExc == handler[0].Test : // if isCatchYielded : // isCatchYielded = false // goto desired_label_in_HANDLER-BODY // HANDLER-BODY // ClearException() // Leave afterFinally // elif pyExc == handler[1].Test : // if isCatchYielded : // isCatchYielded = false // goto desired_label_in_HANDLER-BODY // HANDLER-BODY // ClearException() // Leave afterFinally // . // . // . // Rethrow //FINALLY: // if (isTryYielded or isCatchYielded or isElseYielded ): // goto endOfFinally // if isFinallyYielded : // isFinallyYielded = false // goto desired_label_in_FINALLY-BODY // FINALLY-BODY // endOfFinally: // #try-cathch-finally ends here // afterFinally: // if not returnVar : // goto noReturn // if (finally may yield ): // return 1 // else // return appropriate_return_value // noReturn: internal override void Emit(CodeGen cg) { // environmental slots Slot isTryYielded = null; Slot isCatchYielded = null; Slot isFinallyYielded = null; Slot isElseYielded = null; Slot storedException = null; // local slots Slot tryChoiceVar = null; Slot catchChoiceVar = null; Slot elseChoiceVar = null; Slot finallyChoiceVar = null; Slot flowControlVar = cg.GetLocalTmp(typeof(int)); Slot isElseBlock = null; cg.EmitPosition(Start, header); if (IsBlockYieldable(tryYieldTargets)) { tryChoiceVar = cg.GetLocalTmp(typeof(int)); isTryYielded = cg.Names.GetTempSlot("is", typeof(object)); cg.EmitFieldGet(typeof(Ops).GetField("FALSE")); isTryYielded.EmitSet(cg); } if (IsBlockYieldable(catchYieldTargets)) { catchChoiceVar = cg.GetLocalTmp(typeof(int)); storedException = cg.Names.GetTempSlot("exc", typeof(object)); isCatchYielded = cg.Names.GetTempSlot("is", typeof(object)); cg.EmitFieldGet(typeof(Ops).GetField("FALSE")); isCatchYielded.EmitSet(cg); } if (IsBlockYieldable(finallyYieldTargets)) { finallyChoiceVar = cg.GetLocalTmp(typeof(int)); isFinallyYielded = cg.Names.GetTempSlot("is", typeof(object)); cg.EmitFieldGet(typeof(Ops).GetField("FALSE")); isFinallyYielded.EmitSet(cg); } if (IsBlockYieldable(elseYieldTargets)) { elseChoiceVar = cg.GetLocalTmp(typeof(int)); isElseYielded = cg.Names.GetTempSlot("is", typeof(object)); cg.EmitFieldGet(typeof(Ops).GetField("FALSE")); isElseYielded.EmitSet(cg); } if (elseStmt != null) { isElseBlock = cg.GetLocalTmp(typeof(bool)); cg.EmitInt(0); isElseBlock.EmitSet(cg); } Slot exception = null; bool foundLoopControl; bool returnInFinally = ControlFlowFinder.FindControlFlow(FinallyStatement, out foundLoopControl); if (IsBlockYieldable(finallyYieldTargets)) { exception = cg.Names.GetTempSlot("exception", typeof(Exception)); cg.Emit(OpCodes.Ldnull); exception.EmitSet(cg); } else if (returnInFinally) { exception = cg.GetLocalTmp(typeof(Exception)); cg.Emit(OpCodes.Ldnull); exception.EmitSet(cg); } EmitTopYieldTargetLabels(tryYieldTargets, tryChoiceVar, cg); EmitTopYieldTargetLabels(catchYieldTargets, catchChoiceVar, cg); EmitTopYieldTargetLabels(elseYieldTargets, elseChoiceVar, cg); EmitTopYieldTargetLabels(finallyYieldTargets, finallyChoiceVar, cg); cg.EmitInt(CodeGen.FinallyExitsNormally); flowControlVar.EmitSet(cg); Label afterFinally = cg.DefineLabel(); cg.PushExceptionBlock(Targets.TargetBlockType.Try, flowControlVar, isTryYielded); cg.BeginExceptionBlock(); // if catch yielded, rethow the storedException to be handled by Catch block if (IsBlockYieldable(catchYieldTargets)) { Label testFinally = cg.DefineLabel(); isCatchYielded.EmitGet(cg); cg.EmitUnbox(typeof(bool)); cg.Emit(OpCodes.Brfalse, testFinally); storedException.EmitGet(cg); cg.EmitCall(typeof(Ops), "Raise", new Type[] { typeof(object) }); cg.MarkLabel(testFinally); } // if Finally yielded, Branch to the end of Try block Label endOfTry = cg.DefineLabel(); EmitOnYieldBranchToLabel(finallyYieldTargets, isFinallyYielded, endOfTry, cg); Label beginElseBlock = cg.DefineLabel(); if (IsBlockYieldable(elseYieldTargets)) { // isElseYielded ? Debug.Assert(isElseYielded != null); isElseYielded.EmitGet(cg); cg.EmitUnbox(typeof(bool)); cg.Emit(OpCodes.Brtrue, beginElseBlock); } EmitYieldDispatch(tryYieldTargets, isTryYielded, tryChoiceVar, cg); // if finally block presents, but no exception handler, we add try-fault // to update traceback; otherwise, we update the traceback inside exception handler. if (finallyStmt != null && handlers == null) { Slot dummySlot = cg.GetLocalTmp(typeof(object)); cg.EmitTraceBackTryBlockStart(dummySlot); body.Emit(cg); cg.FreeLocalTmp(dummySlot); cg.EmitTraceBackFaultBlock(); } else { body.Emit(cg); } if (elseStmt != null) { if (IsBlockYieldable(elseYieldTargets)) { cg.MarkLabel(beginElseBlock); } cg.PopTargets(Targets.TargetBlockType.Try); cg.PushExceptionBlock(Targets.TargetBlockType.Else, flowControlVar, isElseYielded); cg.EmitInt(1); isElseBlock.EmitSet(cg); EmitYieldDispatch(elseYieldTargets, isElseYielded, elseChoiceVar, cg); elseStmt.Emit(cg); cg.PopTargets(Targets.TargetBlockType.Else); cg.PushExceptionBlock(Targets.TargetBlockType.Try, flowControlVar, isTryYielded); } cg.MarkLabel(endOfTry); // get the exception if there is a yield / return in finally if (IsBlockYieldable(finallyYieldTargets) || returnInFinally) { cg.BeginCatchBlock(typeof(Exception)); exception.EmitSet(cg); } if (handlers != null) { cg.PushExceptionBlock(Targets.TargetBlockType.Catch, flowControlVar, isCatchYielded); if (IsBlockYieldable(finallyYieldTargets) || returnInFinally) { exception.EmitGet(cg); } else { cg.BeginCatchBlock(typeof(Exception)); } // if in Catch block due to exception in else block -> just rethrow if (elseStmt != null) { Label beginCatchBlock = cg.DefineLabel(); isElseBlock.EmitGet(cg); cg.Emit(OpCodes.Brfalse, beginCatchBlock); cg.Emit(OpCodes.Rethrow); cg.MarkLabel(beginCatchBlock); } cg.EmitCallUpdateTraceBack(); // Extract state from the carrier exception cg.EmitCallerContext(); cg.EmitCall(typeof(Ops), "ExtractException", new Type[] { typeof(Exception), typeof(ICallerContext) }); Slot pyExc = cg.GetLocalTmp(typeof(object)); Slot tmpExc = cg.GetLocalTmp(typeof(object)); pyExc.EmitSet(cg); if (IsBlockYieldable(catchYieldTargets)) { cg.EmitCallerContext(); cg.EmitCall(typeof(Ops), "ExtractSysExcInfo"); storedException.EmitSet(cg); } foreach (TryStatementHandler handler in handlers) { cg.EmitPosition(handler.Start, handler.Header); Label next = cg.DefineLabel(); if (handler.Test != null) { pyExc.EmitGet(cg); handler.Test.Emit(cg); cg.EmitCall(typeof(Ops), "CheckException"); if (handler.Target != null) { tmpExc.EmitSet(cg); tmpExc.EmitGet(cg); } cg.EmitPythonNone(); cg.Emit(OpCodes.Ceq); cg.Emit(OpCodes.Brtrue, next); } if (handler.Target != null) { tmpExc.EmitGet(cg); handler.Target.EmitSet(cg); } if (IsBlockYieldable(finallyYieldTargets) || returnInFinally) { cg.Emit(OpCodes.Ldnull); exception.EmitSet(cg); } EmitYieldDispatch(catchYieldTargets, isCatchYielded, catchChoiceVar, cg); handler.Body.Emit(cg); cg.EmitCallerContext(); cg.EmitCall(typeof(Ops), "ClearException", new Type[] { typeof(ICallerContext) }); cg.EmitSetTraceBackUpdateStatus(false); cg.Emit(OpCodes.Leave, afterFinally); cg.MarkLabel(next); } cg.FreeLocalTmp(tmpExc); cg.FreeLocalTmp(pyExc); cg.Emit(OpCodes.Rethrow); cg.PopTargets(Targets.TargetBlockType.Catch); } if (finallyStmt != null) { cg.PushExceptionBlock(Targets.TargetBlockType.Finally, flowControlVar, isFinallyYielded); cg.BeginFinallyBlock(); Label endOfFinally = cg.DefineLabel(); // if try yielded EmitOnYieldBranchToLabel(tryYieldTargets, isTryYielded, endOfFinally, cg); // if catch yielded EmitOnYieldBranchToLabel(catchYieldTargets, isCatchYielded, endOfFinally, cg); //if else yielded EmitOnYieldBranchToLabel(elseYieldTargets, isElseYielded, endOfFinally, cg); EmitYieldDispatch(finallyYieldTargets, isFinallyYielded, finallyChoiceVar, cg); finallyStmt.Emit(cg); if (IsBlockYieldable(finallyYieldTargets) || returnInFinally) { Label nothrow = cg.DefineLabel(); exception.EmitGet(cg); cg.Emit(OpCodes.Dup); cg.Emit(OpCodes.Brfalse_S, nothrow); cg.Emit(OpCodes.Throw); cg.MarkLabel(nothrow); cg.Emit(OpCodes.Pop); } cg.MarkLabel(endOfFinally); cg.EndExceptionBlock(); cg.PopTargets(Targets.TargetBlockType.Finally); } else { cg.EndExceptionBlock(); } cg.PopTargets(Targets.TargetBlockType.Try); cg.MarkLabel(afterFinally); Label noReturn = cg.DefineLabel(); flowControlVar.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 (returnInFinally) { // return the actual value cg.EmitReturnValue(); cg.EmitReturn(); } cg.MarkLabel(noReturn); if (foundLoopControl) { noReturn = cg.DefineLabel(); flowControlVar.EmitGet(cg); cg.EmitInt(CodeGen.BranchForBreak); cg.Emit(OpCodes.Bne_Un, noReturn); cg.EmitBreak(); cg.MarkLabel(noReturn); noReturn = cg.DefineLabel(); flowControlVar.EmitGet(cg); cg.EmitInt(CodeGen.BranchForContinue); cg.Emit(OpCodes.Bne_Un, noReturn); cg.EmitContinue(); cg.MarkLabel(noReturn); } // clean up if (IsBlockYieldable(tryYieldTargets)) { cg.FreeLocalTmp(tryChoiceVar); tryYieldTargets.Clear(); } if (IsBlockYieldable(catchYieldTargets)) { cg.FreeLocalTmp(catchChoiceVar); catchYieldTargets.Clear(); } if (IsBlockYieldable(finallyYieldTargets)) { cg.FreeLocalTmp(finallyChoiceVar); finallyYieldTargets.Clear(); } if (IsBlockYieldable(elseYieldTargets)) { cg.FreeLocalTmp(elseChoiceVar); elseYieldTargets.Clear(); } if (elseStmt != null) { cg.FreeLocalTmp(isElseBlock); } #if DEBUG cg.EmitInt(-1); flowControlVar.EmitSet(cg); #endif cg.FreeLocalTmp(flowControlVar); }
private void EmitGenerator(CodeGen ncg) { YieldTarget[] targets = YieldLabelBuilder.BuildYieldTargets(this, ncg); Label[] jumpTable = new Label[yieldCount]; for (int i = 0; i < yieldCount; i++) jumpTable[i] = targets[i].TopBranchTarget; ncg.yieldLabels = jumpTable; // Generator will ofcourse yield, but we are not interested in the isBlockYielded value // hence push a dummySlot to pass the Assertion. Slot dummySlot = ncg.GetLocalTmp(typeof(object)); ncg.PushTryBlock(dummySlot); ncg.BeginExceptionBlock(); ncg.Emit(OpCodes.Ldarg_0); ncg.EmitFieldGet(typeof(Generator), "location"); ncg.Emit(OpCodes.Switch, jumpTable); // fall-through on first pass // yield statements will insert the needed labels after their returns Body.Emit(ncg); //free the dummySlot ncg.FreeLocalTmp(dummySlot); // fall-through is almost always possible in generators, so this // is almost always needed ncg.EmitReturnInGenerator(null); // special handling for StopIteration thrown in body ncg.BeginCatchBlock(typeof(StopIterationException)); ncg.EndExceptionBlock(); ncg.EmitReturnInGenerator(null); ncg.PopTargets(); ncg.Finish(); }
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(); } }