// 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);
        }
Example #2
0
        /// <summary>
        /// Emits the raw function implementation into a method body.  Requires both a
        /// code gen for the method and for any initialization that might be required
        /// before the method is run (a module init or type cctor).  True if the method
        /// requires context, false if it doesn't.
        /// </summary>
        /// <param name="cg"></param>
        /// <param name="icg"></param>
        /// <param name="context"></param>
        internal void EmitFunctionImplementation(CodeGen methodCodeGen, CodeGen initCodeGen)
        {
            if (EmitLocalDictionary) {
                PromoteLocalsToEnvironment();
            }

            // emit the actual body
            if (yieldCount > 0) {
                EmitGeneratorBody(methodCodeGen, initCodeGen);
            } else {
                Slot dummySlot = methodCodeGen.GetLocalTmp(typeof(object));
                methodCodeGen.EmitTraceBackTryBlockStart(dummySlot);
                methodCodeGen.FuncOrClassName = name.ToString();
                methodCodeGen.EmitSetTraceBackUpdateStatus(false);

                EmitFunctionBody(methodCodeGen, initCodeGen);

                methodCodeGen.FreeLocalTmp(dummySlot);
                methodCodeGen.EmitTraceBackFaultBlock();
            }
        }