public override int Run(InterpretedFrame frame) { // Push fault. frame.PushContinuation(LabelIndex); var prevInstrIndex = frame.InstructionIndex; frame.InstructionIndex++; // Start to run the try/fault blocks var instructions = frame.Interpreter.Instructions.Instructions; // C# 6 has no direct support for fault blocks, but they can be faked or coerced out of the compiler // in several ways. Catch-and-rethrow can work in specific cases, but not generally as the double-pass // will not work correctly with filters higher up the call stack. Iterators can be used to produce real // fault blocks, but it depends on an implementation detail rather than a guarantee, and is rather // indirect. This leaves using a finally block and not doing anything in it if the body ran to // completion, which is the approach used here. var ranWithoutFault = false; try { // run the try block var index = frame.InstructionIndex; while (index >= Handler !.TryStartIndex && index < Handler.TryEndIndex) { index += instructions[index].Run(frame); frame.InstructionIndex = index; } // run the 'Goto' that jumps out of the try/fault blocks Debug.Assert(instructions[index] is GotoInstruction, "should be the 'Goto' instruction that jumps out the try/fault"); // if we've arrived here there was no exception thrown. As the fault block won't run, we need to // pop the continuation for it here, before Gotoing the end of the try/fault. ranWithoutFault = true; frame.RemoveContinuation(); frame.InstructionIndex += instructions[index].Run(frame); } finally { if (!ranWithoutFault) { // run the fault block // we cannot jump out of the finally block, and we cannot have an immediate rethrow in it var index = frame.InstructionIndex = Handler !.FinallyStartIndex; while (index >= Handler.FinallyStartIndex && index < Handler.FinallyEndIndex) { index += instructions[index].Run(frame); frame.InstructionIndex = index; } } } return(frame.InstructionIndex - prevInstrIndex); }
public override int Run(InterpretedFrame frame) { if (_hasFinally) { // Push finally. frame.PushContinuation(LabelIndex); } var prevInstrIndex = frame.InstructionIndex; frame.InstructionIndex++; // Start to run the try/catch/finally blocks var instructions = frame.Interpreter.Instructions.Instructions; try { // run the try block var index = frame.InstructionIndex; while (index >= Handler !.TryStartIndex && index < Handler.TryEndIndex) { index += instructions[index].Run(frame); frame.InstructionIndex = index; } // we finish the try block and is about to jump out of the try/catch blocks if (index == Handler.GotoEndTargetIndex) { // run the 'Goto' that jumps out of the try/catch/finally blocks Debug.Assert(instructions[index] is GotoInstruction, "should be the 'Goto' instruction that jumps out the try/catch/finally"); frame.InstructionIndex += instructions[index].Run(frame); } } catch (Exception exception) when(Handler !.HasHandler(frame, exception, out var exHandler, out var unwrappedException)) { Debug.Assert(!(unwrappedException is RethrowException)); frame.InstructionIndex += frame.Goto(exHandler.LabelIndex, unwrappedException, true); var rethrow = false; try { // run the catch block var index = frame.InstructionIndex; while (index >= exHandler.HandlerStartIndex && index < exHandler.HandlerEndIndex) { index += instructions[index].Run(frame); frame.InstructionIndex = index; } // we finish the catch block and is about to jump out of the try/catch blocks if (index == Handler.GotoEndTargetIndex) { // run the 'Goto' that jumps out of the try/catch/finally blocks Debug.Assert(instructions[index] is GotoInstruction, "should be the 'Goto' instruction that jumps out the try/catch/finally"); frame.InstructionIndex += instructions[index].Run(frame); } } catch (RethrowException) { // a rethrow instruction in a catch block gets to run rethrow = true; } if (rethrow) { throw; } } finally { if (Handler !.IsFinallyBlockExist) { // We get to the finally block in two paths: // 1. Jump from the try/catch blocks. This includes two sub-routes: // a. 'Goto' instruction in the middle of try/catch block // b. try/catch block runs to its end. Then the 'Goto(end)' will be trigger to jump out of the try/catch block // 2. Exception thrown from the try/catch blocks // In the first path, the continuation mechanism works and frame.InstructionIndex will be updated to point to the first instruction of the finally block // In the second path, the continuation mechanism is not involved and frame.InstructionIndex is not updated #if DEBUG var isFromJump = frame.IsJumpHappened(); Debug.Assert(!isFromJump || (isFromJump && Handler.FinallyStartIndex == frame.InstructionIndex), "we should already jump to the first instruction of the finally"); #endif // run the finally block // we cannot jump out of the finally block, and we cannot have an immediate rethrow in it var index = frame.InstructionIndex = Handler.FinallyStartIndex; while (index >= Handler.FinallyStartIndex && index < Handler.FinallyEndIndex) { index += instructions[index].Run(frame); frame.InstructionIndex = index; } } } return(frame.InstructionIndex - prevInstrIndex); }