internal bool HasHandler(InterpretedFrame frame, Exception exception, [NotNullWhen(true)] out ExceptionHandler?handler, [NotNullWhen(true)] out object?unwrappedException) { frame.SaveTraceToException(exception); if (IsCatchBlockExist) { unwrappedException = exception is RuntimeWrappedException rwe ? rwe.WrappedException : exception; var exceptionType = unwrappedException.GetType(); foreach (var candidate in Handlers) { if (!candidate.Matches(exceptionType) || (candidate.Filter != null && !FilterPasses(frame, ref unwrappedException, candidate.Filter))) { continue; } handler = candidate; return(true); } } else { unwrappedException = null; } handler = null; return(false); }
public override int Run(InterpretedFrame frame) { Debug.Assert(_tryHandler != null, "the tryHandler must be set already"); if (_hasFinally) { // Push finally. frame.PushContinuation(_labelIndex); } int prevInstrIndex = frame.InstructionIndex; frame.InstructionIndex++; // Start to run the try/catch/finally blocks var instructions = frame.Interpreter.Instructions.Instructions; try { // run the try block int index = frame.InstructionIndex; while (index >= _tryHandler.TryStartIndex && index < _tryHandler.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 == _tryHandler.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 jumpes out the try/catch/finally"); frame.InstructionIndex += instructions[index].Run(frame); } } catch (RethrowException) { // a rethrow instruction in the try handler gets to run throw; } catch (Exception exception) { frame.SaveTraceToException(exception); // rethrow if there is no catch blocks defined for this try block if (!_tryHandler.IsCatchBlockExist) { throw; } // Search for the best handler in the TryCatchFianlly block. If no suitable handler is found, rethrow ExceptionHandler exHandler; frame.InstructionIndex += _tryHandler.GotoHandler(frame, exception, out exHandler); if (exHandler == null) { throw; } #if FEATURE_THREAD_ABORT // stay in the current catch so that ThreadAbortException is not rethrown by CLR: var abort = exception as ThreadAbortException; if (abort != null) { Interpreter.AnyAbortException = abort; frame.CurrentAbortHandler = exHandler; } #endif bool rethrow = false; try { // run the catch block int 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 == _tryHandler.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 jumpes 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 (_tryHandler.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 bool isFromJump = frame.IsJumpHappened(); Debug.Assert(!isFromJump || (isFromJump && _tryHandler.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 int index = frame.InstructionIndex = _tryHandler.FinallyStartIndex; while (index >= _tryHandler.FinallyStartIndex && index < _tryHandler.FinallyEndIndex) { index += instructions[index].Run(frame); frame.InstructionIndex = index; } } } return(frame.InstructionIndex - prevInstrIndex); }