@@ -458,6 +458,44 @@ void* DoGenericLookup(void* genericVarAsPtr, InterpGenericLookup* pLookup)
458458 return result;
459459}
460460
461+ // Filter to ignore SEH exceptions representing C++ exceptions.
462+ LONG IgnoreCppExceptionFilter (PEXCEPTION_POINTERS pExceptionInfo, PVOID pv)
463+ {
464+ return (pExceptionInfo->ExceptionRecord ->ExceptionCode == EXCEPTION_MSVC)
465+ ? EXCEPTION_CONTINUE_SEARCH
466+ : EXCEPTION_EXECUTE_HANDLER;
467+ }
468+
469+ // Wrapper around MethodDesc::PrepareInitialCode to handle possible managed exceptions thrown by it.
470+ void PrepareInitialCode (MethodDesc *pMD)
471+ {
472+ STATIC_STANDARD_VM_CONTRACT;
473+
474+ struct Param
475+ {
476+ MethodDesc *pMethodDesc;
477+ }
478+ param = { pMD };
479+
480+ PAL_TRY (Param *, pParam, ¶m)
481+ {
482+ pParam->pMethodDesc ->PrepareInitialCode (CallerGCMode::Coop);
483+ }
484+ PAL_EXCEPT_FILTER (IgnoreCppExceptionFilter)
485+ {
486+ // There can be both C++ (thrown by the COMPlusThrow) and managed exceptions thrown
487+ // from the PrepareInitialCode call chain.
488+ // We need to process only managed ones here, the C++ ones are handled by the
489+ // INSTALL_/UNINSTALL_UNWIND_AND_CONTINUE_HANDLER in the InterpExecMethod.
490+ // The managed ones are represented by SEH exception, which cannot be handled there
491+ // because it is not possible to handle both SEH and C++ exceptions in the same frame.
492+ GCX_COOP_NO_DTOR ();
493+ OBJECTREF ohThrowable = GET_THREAD ()->LastThrownObject ();
494+ DispatchManagedException (ohThrowable);
495+ }
496+ PAL_ENDTRY
497+ }
498+
461499void InterpExecMethod (InterpreterFrame *pInterpreterFrame, InterpMethodContextFrame *pFrame, InterpThreadContext *pThreadContext, ExceptionClauseArgs *pExceptionClauseArgs)
462500{
463501 CONTRACTL
@@ -1886,6 +1924,9 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
18861924 CallStubHeader *pCallStub = (CallStubHeader*)pMethod->pDataItems [calliCookie];
18871925 ip += 5 ;
18881926
1927+ // Save current execution state for when we return from called method
1928+ pFrame->ip = ip;
1929+
18891930 InvokeCalliStub (LOCAL_VAR (calliFunctionPointerVar, PCODE), pCallStub, stack + callArgsOffset, stack + returnOffset);
18901931 break ;
18911932 }
@@ -1907,6 +1948,9 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
19071948 ? *(PCODE *)pMethod->pDataItems [targetAddrSlot]
19081949 : (PCODE)pMethod->pDataItems [targetAddrSlot];
19091950
1951+ // Save current execution state for when we return from called method
1952+ pFrame->ip = ip;
1953+
19101954 InlinedCallFrame inlinedCallFrame;
19111955 inlinedCallFrame.m_pCallerReturnAddress = (TADDR)ip;
19121956 inlinedCallFrame.m_pCallSiteSP = pFrame;
@@ -1942,6 +1986,9 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
19421986 OBJECTREF targetMethodObj = delegateObj->GetTarget ();
19431987 LOCAL_VAR (callArgsOffset, OBJECTREF) = targetMethodObj;
19441988
1989+ // Save current execution state for when we return from called method
1990+ pFrame->ip = ip;
1991+
19451992 // TODO! Once we are investigating performance here, we may want to optimize this so that
19461993 // delegate calls to interpeted methods don't have to go through the native invoke here, but for
19471994 // now this should work well.
@@ -1959,6 +2006,9 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
19592006CALL_INTERP_SLOT:
19602007 targetMethod = (MethodDesc*)pMethod->pDataItems [methodSlot];
19612008CALL_INTERP_METHOD:
2009+ // Save current execution state for when we return from called method
2010+ pFrame->ip = ip;
2011+
19622012 InterpByteCodeStart* targetIp = targetMethod->GetInterpreterCode ();
19632013 if (targetIp == NULL )
19642014 {
@@ -1975,7 +2025,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
19752025 // Attempt to setup the interpreter code for the target method.
19762026 if ((targetMethod->IsIL () || targetMethod->IsNoMetadata ()) && !targetMethod->IsUnboxingStub ())
19772027 {
1978- targetMethod-> PrepareInitialCode (CallerGCMode::Coop );
2028+ PrepareInitialCode (targetMethod );
19792029 }
19802030 targetIp = targetMethod->GetInterpreterCode ();
19812031 }
@@ -1987,9 +2037,6 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
19872037 }
19882038 }
19892039
1990- // Save current execution state for when we return from called method
1991- pFrame->ip = ip;
1992-
19932040 // Allocate child frame.
19942041 {
19952042 InterpMethodContextFrame *pChildFrame = pFrame->pNext ;
@@ -2587,7 +2634,14 @@ do \
25872634 // Unwind the interpreter stack upto the resume frame
25882635 while (pFrame != pResumeFrame)
25892636 {
2590- assert (pFrame != NULL );
2637+ if (pFrame == NULL )
2638+ {
2639+ // In scenarios when the interpreted code ends up calling other interpreted code through the precode, there are two separate
2640+ // sequences of interpreted frames without any AOTed/JITted frames in between. In such case, the topmost native frame
2641+ // the ResumeAfterCatchException is thrown from may not be the one that corresponds to the target interpreted frame.
2642+ // Thus, we need to rethrow it to let it propagate further.
2643+ throw ;
2644+ }
25912645 pThreadContext->frameDataAllocator .PopInfo (pFrame);
25922646 pFrame->ip = 0 ;
25932647 pFrame = pFrame->pParent ;
0 commit comments